Snake game Update #2

In a previous post I shared a schematic how to wire two 74HC595N shift registers, PNP transistors and 8×8 LED matrix, but I did not thought everything carefully enough and did not put a resistor in between second shift register and transistor, so I manage to “burn” shift register that controls row of cathodes on matrix. Thankfully they are not so expensive and I have ordered new one on eBay.

New design after improvements should look like this:

Led matrix 8x8 74hc595n pnp transistor

…………………………………………………..

Also, I did some work on Matrix class and Snake game by itself. Game code if far from complete and what I expect to see, but some functionality is working.

In matrix class only thing I changed is that now I turn off all leds at the end of cycle, otherwise last row stays longer on that others and look little bit brighter. Otherwise no changes.

void MatrixClass::display()
{
  for (int i = 0; i < 8; i++)
  {
    byte row = B00000001 << (7 - i);
    byte col = B00000000;

    for (int j = 0; j < 8; j++)
    {
      if (leds[i][j] == 1)
      {
        col |= B00000001 << (7 - j);
      }
    }

    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, LSBFIRST, col);
    shiftOut(dataPin, clockPin, LSBFIRST, row);
    digitalWrite(latchPin, HIGH);

    delay(2);
  }

  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, B00000000);
  shiftOut(dataPin, clockPin, LSBFIRST, B00000000);
  digitalWrite(latchPin, HIGH);
}

And first tests to see how Matrix is working.

#include "Matrix.h"

enum Direction
{
  UP, RIGHT, DOWN, LEFT
};

struct Position
{
  int x;
  int y;
};

// Matrix pins
int DATA_PIN = 2;
int CLOCK_PIN = 3;
int LATCH_PIN = 4;
MatrixClass matrix;

// Joystick pins
// A1 - X axis
// A0 - Y axis

int deadZone = 32;

// Snake
int size;
Direction direction;
Position snake[64];

int frameLenght = 1000;
int frameTime = 0;
long lastUpdate = 0;

bool gameOver;

void setup()
{
  // Initialize led matrix
  matrix.init(DATA_PIN, CLOCK_PIN, LATCH_PIN);

  // Setup input
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);

  restartGame();

  lastUpdate = millis();
}

void loop()
{
  // Update input
  getInput();

  /* Update game
  I do update only every second
  */
  frameTime = frameTime + millis() - lastUpdate;
  lastUpdate = millis();
  if (frameTime > frameLenght)
  {
    // Update game
    updateSnake();
    frameTime = frameTime - frameLenght;
  }

  // Draw game
  matrix.clear();
  drawSnake();
  matrix.display();
}

void restartGame()
{
  direction = RIGHT;
  size = 3;
  snake[0].x = 4;
  snake[0].y = 4;
  snake[1].x = 3;
  snake[1].y = 4;
  snake[2].x = 2;
  snake[2].y = 4;
  gameOver = false;
}

void getInput()
{
  int x = analogRead(A1);
  int y = analogRead(A0);

  if (x < (512 - deadZone))
  {
    direction = RIGHT;
  }
  else if (x > 512 + deadZone)
  {
    direction = LEFT;
  }
  else if (y < 512 - deadZone)
  {
    direction = UP;
  }
  else if (y > 512 + deadZone)
  {
    direction = DOWN;
  }
}

void updateSnake()
{
  // Move all joints up by one pisition in array
  for (int i = size - 1; i > 0; i--)
  {
    snake[i] = snake[i - 1];
  }

  // Set a heads position depending on which side snake is facing
  if (direction == RIGHT)
  {
    snake[0].x = snake[1].x + 1;
    snake[0].y = snake[1].y;
  }
  else if (direction == DOWN)
  {
    snake[0].x = snake[1].x;
    snake[0].y = snake[1].y + 1;
  }
  else if (direction == LEFT)
  {
    snake[0].x = snake[1].x - 1;
   snake[0].y = snake[1].y;
  }
  else if (direction == UP)
  {
    snake[0].x = snake[1].x;
    snake[0].y = snake[1].y - 1;
   }
}

void drawSnake()
{
  for (int i = 0; i < size; i++)
  {
    matrix.setHigh(snake[i].y, snake[i].x);
  }
}

And of course final result

Snake game Update #1

Yesterday got a new Arduino Mega 2560 board in my hands, so decided to make something interesting. I do not have a lot of components, but I have 8×8 LED matrix and a couple of 74HC595 shift registers, so my plan is to make a Snake game. After a little bit reading about Arduino and how the shift registers work I came up with design how to wire an LED matrix.

matrix

I was unable to find a specification for this led matrix, so the only way to figure out which pins are anodes and which are cathodes I had to use a multimeter and test them all. …. I have played a little bit with code I wrote a class to control a led matrix. I don’t know is it a good design, but it works for me and I can change it in future.

Matrix class


class MatrixClass
{
  private:
    bool leds[8][8];

    int dataPin;
    int clockPin;
    int latchPin;

  public:
    void init(int data, int clock, int latch);
    void clear();
    void setHigh(int row, int col);
    void display();
};

void MatrixClass::init(int data, int clock, int latch)
{
  dataPin = data;
  clockPin = clock;
  latchPin = latch;

  pinMode(data, OUTPUT);
  pinMode(clock, OUTPUT);
  pinMode(latch, OUTPUT);
}

void MatrixClass::clear()
{
  for (int i = 0; i < 8; i++)
  {
    for (int j = 0; j < 8; j++)
    {
      leds[i][j] = 0;
    }
  }
}

void MatrixClass::setHigh(int row, int col)
{
  leds[row - 1][col - 1] = 1;
}

void MatrixClass::display()
{
  for (int i = 0; i < 8; i++)
  {
    byte row = B00000001 << (7 - i);
    byte col = B00000000;

    for (int j = 0; j < 8; j++)
    {
      if (leds[i][j] == 1)
      {
        col |= B00000001 << (7 - j);
      }
   }

   digitalWrite(latchPin, LOW);
   shiftOut(dataPin, clockPin, LSBFIRST, col);
   shiftOut(dataPin, clockPin, LSBFIRST, row);
   digitalWrite(latchPin, HIGH);
  }
}

Pāreja uz MonoGame

Pirms pāris nedēļām iegādājos jauno Windows 8.1. Viss strādā lieliski, bet ir arī daži pārsteigumi, kā piemēram ierobežojumi veidot XNA spēles priekš Windows 8 un Windows Phone 8. Itkā ir iespējams turpināt rakstīt parastas Windows aplikācijas, bet nevar izmanot WP8 un W8 specifiskas fīčas. Tapēc nolēmu migrēt uz MonoGame. Nekādas lielas atšķirības nemanu, bij problēmas ar Visual Studio + MonoGame templates, bet kombinācijā VS2012 Pro un MonoGame 3 viss strādā. Nopirku arī Nokia Lumia 925, tapēc centīšos veidot spēli tā, lai varētu palaist arī uz telefona.

Tālu neesu ticis, uz doto mirkli izskatās šādi:

Main Menu

Šodien mazliet padarbojos pie galvenās izvēlnes. Mākslinieks no manis nekāds, bet iesākumam būs labi. Arī rakstnieks no manis tikpat liels cik mākslinieks, tapēc saprātīgāk būs vienkārši atrādīt progresu, pieminot problēmas un izceļot interesantākas vietas.

Pievienoju InputManager, Button klasi un veicu dazas izmainas MainMenuScreen, visu var lieliski rezēt uz BitBucket, tapēc neredzu jēgu te postot kodu.

Screen menedžers

Nolēmu jau pašā sākumā izveidot ļoti vienkaršu Screen(State) menedžeri.

Kas ir screen vai state menedžers?

Spēle var būt dažādos “stāvokļos”, piemeram MENU, PLAY, PAUSE utt. Katram no šiem stāvokļiem būs atbilstoša klase, Screen menedžers koordinēs pārslēgšanos no viena Screen uz nakamo. No sākuma es izveidošu ļoti elementāru medžedzeri bez pārslēgšanās efektiem, loading screeniem utt.

Tātad pievienoju projektam mapi SCREENS, ScreenManager klasi, abstraktu klasi Screen un iesākumam divas konkrētas klases MenuScreen un PlayScreen, kuras izriet no Screen. ScreenMenedzer izriet no DrawableGameComponent un tiek pievienots compontntiem Game klases konstruktorā.

ScreenManager screenManager = new ScreenManager(this);
screenManager.SetScreen(new MainMenuScreen(screenManager));
Components.Add(screenManager);

 


public class ScreenManager : DrawableGameComponent
{
#region FIELDS
private Stack<Screen> screens;
private Screen screenToUpdate;

private SpriteBatch spriteBatch;
#endregion

#region CONSTRUCTORS
public ScreenManager(Game game)
: base(game)
{
screens = new Stack<Screen>();
screenToUpdate = null;
}
#endregion

#region SCREEN MANAGER INTERFACE
public void SetScreen(Screen screen)
{
foreach (Screen scr in screens)
{
scr.UnloadContent();
}
screens.Clear();

screen.LoadContent();
screens.Push(screen);
}
#endregion

#region GAME INTERFACE
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
}

public override void Update(GameTime gameTime)
{
if (screens.Count > 0)
{
screenToUpdate = screens.Peek();
screenToUpdate.Update(gameTime);
}
}

public override void Draw(GameTime gameTime)
{
if (screens.Count > 0)
{
foreach (Screen screen in screens)
{
screen.Draw(spriteBatch);
}
}
}

protected override void UnloadContent()
{
foreach (Screen scr in screens)
{
scr.UnloadContent();
}
base.UnloadContent();
}
}

public class Screen
{
#region FIELDS
private ScreenManager screenManager;
#endregion

#region PROPERTIES
public Screen(ScreenManager screenManager)
{
this.screenManager = screenManager;
}
#endregion

#region CONSTRUCTORS
public ScreenManager ScreenManager
{
get { return screenManager; }
}
#endregion

#region PUBLIC METHODS
public virtual void LoadContent()
{

}

public virtual void Update(GameTime gameTime)
{

}

public virtual void Draw(SpriteBatch spriteBatch)
{

}

public virtual void UnloadContent()
{

}
#endregion
}
class MainMenuScreen : Screen
{
#region TMP
private int testFrames = 0;
#endregion

#region CONSTRUCTORS
public MainMenuScreen(ScreenManager screenManager)
: base(screenManager)
{

}
#endregion

#region GAME INTERFACE
public override void LoadContent()
{
Console.WriteLine("MenuScreen LoadContent()");
}

public override void Update(GameTime gameTime)
{
Console.WriteLine("MenuScreen update()");

#region TMP
if (testFrames > 10)
{
ScreenManager.SetScreen(new PlayScreen(ScreenManager));
}

testFrames++;
#endregion
}

public override void Draw(SpriteBatch spriteBatch)
{
Console.WriteLine("MenuScreen draw()");
}

public override void UnloadContent()
{
Console.WriteLine("MenuScreen UnloadContent()");
}
}
#endregion