Hello.
I'm working on a game project based that uses NeoPixel Matrix 16x16 [knockoff] as a screen.
Most of the time it works, unless the loop starting at //#SHIPS
runs.
When that double loop (two for loops traversing a two-dimensional array of color indexes, essentially a bitmap) works, the matrix stops updating (e.g. I change colors in the static border).
The code is quite entangled, but feel free to take a look at the loop
.
Using logs I'm sure the data I try to display is fine, here is a sample of the displayMatrix
, which corresponds to colors in gameColors
:
updateShipPosition xy00
0 2100000000
1 1100000000
2 0000000000
3 0000000000
4 0000000000
5 0000000000
6 0000000000
7 0000000000
8 0000000000
9 0000000000
The code:
#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#include <Fonts/TomThumb.h>
#ifndef PSTR
#define PSTR // Make Arduino Due happy
#endif
#define MTRX_PIN 8
#define BTN_A_PIN 3
#define J_X A1
#define J_Y A0
#define J_SW 7
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(16, 16, MTRX_PIN ,
NEO_MATRIX_TOP + NEO_MATRIX_LEFT +
NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
NEO_GRB + NEO_KHZ800);
const uint16_t colors[7] = {
// 0 red
matrix.Color(255, 0, 0),
// 1 green
matrix.Color(0, 255, 0),
// 2 blue
matrix.Color(0, 0, 255),
// 3 border
matrix.Color(100, 120, 100),
// 4 enemy
matrix.Color(230, 57, 70),
// 5 friendly
matrix.Color(80, 140, 207),
// 6 margin
matrix.Color(115, 27, 35),
};
const short CELL_EMPTY = 0;
const short CELL_BORDER = 1;
const short CELL_SHIP = 2;
const short CELL_HIT = 3;
const short CELL_MISS = 5;
short GAME_MODE = 0;
const short GAME_SET_SHIPS = 1;
const short GAME_PLAYER_TURN = 2;
const short GAME_ENEMY_TURN = 3;
const short GAME_END = 4;
const uint16_t gameColors[6] = {
// 0 empty
matrix.Color(100, 0, 0),
// 1 border
matrix.Color(0, 10, 0),
// 2 ship
matrix.Color(80, 140, 207),
// 3 hit or invalid
matrix.Color(230, 57, 70),
// 4 hit or invalid
matrix.Color(230, 57, 70),
// 5 miss
matrix.Color(255, 255, 0),
};
const int OFFSET = 3;
const int JOYSTICK_AXIS_DELAY = 30;
bool btn_a = false;
int j_read_x = 0;
int j_read_y = 0;
int j_read_sw = 0;
int x = 0;
int y = 0;
bool joystickValUpdated = false;
bool shipVertical = false;
bool transition = false;
String text[10] = "";
short textIndex = 0;
// const String texts[5] = {
// "",
// "GO!",
// "FOE",
// "HIT",
// "MISS"
// };
const short TEXT_GO = 1;
const short TEXT_FOE = 2;
const short TEXT_HIT = 3;
const short TEXT_MISS = 4;
bool logShipPosition = false;
//// Ships Matrix
// All cells have a number with meaning
// 0 - empty
// 1 - ship border (transparent blue)
// 2 - ship (blue)
// 3 - invalid ship position or hit (red)
// 4 - invalid ship position or hit (red)
short ownShips[10][10] = {};
short enemyShips[10][10] = {};
short ownShots[10][10] = {};
short displayMatrix[10][10] = {};
// temp
short newShipMargin[10][10] = {};
short currentNewShipIndex = 9;
short ownShipsHitPoints[10][2] = {
{4, 4},
{3, 3},
{3, 3},
{2, 2},
{2, 2},
{2, 2},
{1, 1},
{1, 1},
{1, 1},
{1, 1},
};
bool newShipPositionValid = true;
void fillNewShip() {
short shipSize = ownShipsHitPoints[currentNewShipIndex][0];
int endX = shipVertical ? x : x - 1 + shipSize;
int endY = !shipVertical ? y : y - 1 + shipSize;
if (shipVertical) {
for (short i = y; i <= endY; i++) {
ownShips[i][x] = CELL_SHIP;
}
} else {
for (short i = x; i <= endX; i++) {
ownShips[y][i] = CELL_SHIP;
Serial.println("fillNewShip");
}
}
currentNewShipIndex++;
Serial.print("currentNewShipIndex: ");
Serial.println(currentNewShipIndex);
if (currentNewShipIndex > 9) {
GAME_MODE = GAME_PLAYER_TURN;
transition = true;
textIndex = TEXT_GO;
Serial.println("GAME_MODE: GAME_PLAYER_TURN");
currentNewShipIndex = 0;
}
}
void rotateShip() {
shipVertical = !shipVertical;
short shipSize = ownShipsHitPoints[currentNewShipIndex][0];
int startX = x;
int startY = y;
int endX = shipVertical ? startX : startX - 1 + shipSize;
int endY = !shipVertical ? startY : startY - 1 + shipSize;
Serial.println("rotateShip; vertical " + (String)shipVertical);
if (shipVertical) {
if (endY > 9) {
y = y - 1;
}
} else {
if (endX > 9) {
x = x - 1;
}
}
updateShipPosition();
}
// updates position of a ship before it being added
void updateShipPosition() {
// Serial.println("updateShipPosition index " + currentNewShipIndex);
Serial.print("updateShipPosition xy");
joystickValUpdated = false;
// Serial.print(currentNewShipIndex);
Serial.print(x);
Serial.println(y);
newShipPositionValid = true;
short shipSize = ownShipsHitPoints[currentNewShipIndex][0];
int startX = x;
int startY = y;
int endX = shipVertical ? startX : startX - 1 + shipSize;
int endY = !shipVertical ? startY : startY - 1 + shipSize;
// clean temp array
for(short row = 0; row < 10; row++) {
for(short col = 0; col < 10; col++) {
newShipMargin[row][col] = CELL_EMPTY;
}
}
if (shipVertical) {
// vertical
short lowEdgeX = max(startX - 1, 0);
short highEdgeX = min(endX + 1, 9);
short lowEdgeY = max(startY - 1, 0);
short highEdgeY = min(startY + 1, 9);
for (short i = lowEdgeY; i <= highEdgeY; i++) {
newShipMargin[i][lowEdgeX] = CELL_BORDER;
newShipMargin[i][highEdgeX] = CELL_BORDER;
}
newShipMargin[max(startY - 1, 0)][startX] = CELL_BORDER;
newShipMargin[min(endY + 1, 9)][startX] = CELL_BORDER;
// ship
for (short i = startY; i <= endY; i++) {
newShipMargin[i][startX] = CELL_SHIP;
}
} else {
// horizontal
short lowEdgeX = max(startX - 1, 0);
short highEdgeX = min(endX + 1, 9);
short lowEdgeY = max(startY - 1, 0);
short highEdgeY = min(startY + 1, 9);
for (short i = lowEdgeX; i <= highEdgeX; i++) {
newShipMargin[lowEdgeY][i] = CELL_BORDER;
newShipMargin[highEdgeY][i] = CELL_BORDER;
}
newShipMargin[startY][max(startX - 1, 0)] = CELL_BORDER;
newShipMargin[startY][min(endX + 1, 9)] = CELL_BORDER;
// ship
for (short i = startX; i <= endX; i++) {
newShipMargin[startY][i] = CELL_SHIP;
}
}
for(short row = 0; row < 10; row++) {
// Serial.println("");
for(short col = 0; col < 10; col++) {
short cell = ownShips[row][col];
displayMatrix[row][col] = cell + newShipMargin[row][col];
// Serial.print(displayMatrix[row][col]);
if (displayMatrix[row][col] > 2) {
newShipPositionValid = false;
}
}
}
}
void addNewShip() {
fillNewShip();
updateShipPosition();
delay(500);
}
int processCoordinateChange(int prevValue, int sensorValue, bool sizeByAxisMatters) {
int newValue = prevValue;
bool sizeMatters = sizeByAxisMatters && GAME_MODE == GAME_SET_SHIPS;
if (sensorValue > 900) {
// increase
joystickValUpdated = true;
int maxVal = 9;
if (sizeMatters) {
// one point doesn't matter
maxVal = maxVal + 1 - ownShipsHitPoints[currentNewShipIndex][0];
}
newValue = min(++newValue, maxVal);
} else if (sensorValue < 120) {
// decrease
joystickValUpdated = true;
newValue = max(--newValue, 0);
}
return newValue;
}
void updateAimPosition() {
Serial.println("updateAimPosition");
for(short row = 0; row < 10; row++) {
for(short col = 0; col < 10; col++) {
displayMatrix[row][col] = CELL_EMPTY;
}
}
displayMatrix[y][x] = CELL_HIT;
}
void shoot() {
Serial.println("SHOT @ " + (String)x + (String)y);
return;
if (ownShots[y][x] == CELL_HIT || ownShots[y][x] == CELL_MISS) {
// can't shoot on previously shot
Serial.println("SHOT NOT EMPTY" + (String)ownShots[y][x]);
return;
}
if (enemyShips[y][x] == CELL_SHIP) {
ownShots[y][x] = CELL_HIT;
transition = true;
textIndex = TEXT_HIT;
Serial.println("HIT!");
// player turn continues
} else {
ownShots[y][x] = CELL_MISS;
transition = true;
textIndex = TEXT_MISS;
Serial.println("MISS!");
// enemy turn
}
for(short row = 0; row < 10; row++) {
for(short col = 0; col < 10; col++) {
short cell = 0 + ownShots[row][col];
displayMatrix[row][col] = cell;
}
}
}
void joystickPosUpdated() {
if (GAME_MODE == GAME_SET_SHIPS) {
updateShipPosition();
} else {
updateAimPosition();
}
}
void joystickPressed() {
if (GAME_MODE == GAME_SET_SHIPS) {
if (newShipPositionValid) {
addNewShip();
}
} else if (GAME_MODE == GAME_PLAYER_TURN) {
shoot();
}
}
void btnAPressed() {
Serial.println("BTN A PRESSED");
if (GAME_MODE == GAME_SET_SHIPS) {
rotateShip();
}
}
void btnAReleased() {
Serial.println("BTN A RELEASED");
}
void setup() {
Serial.begin(9600);
matrix.begin();
matrix.setFont(&TomThumb);
matrix.setTextWrap(false);
matrix.setBrightness(3);
matrix.setTextColor(colors[0]);
matrix.setCursor(0, 0);
GAME_MODE = GAME_SET_SHIPS;
pinMode(J_SW, INPUT_PULLUP);
pinMode(BTN_A_PIN, INPUT);
updateShipPosition();
// test enemy ships
enemyShips[0][0] = CELL_SHIP;
enemyShips[3][3] = CELL_SHIP;
enemyShips[5][5] = CELL_SHIP;
matrix.fillScreen(0);
}
void loop() {
if (joystickValUpdated) {
joystickPosUpdated();
logShipPosition = true;
}
bool btnAVal = digitalRead(BTN_A_PIN);
if (btnAVal && !btn_a) {
btn_a = true;
btnAPressed();
} else if (!btnAVal && btn_a) {
btn_a = false;
btnAReleased();
}
j_read_sw = digitalRead(J_SW);
if (j_read_sw == LOW) {
joystickPressed();
} else {
if (joystickValUpdated) {
joystickValUpdated = false;
delay(JOYSTICK_AXIS_DELAY);
} else {
j_read_x = analogRead(J_X);
j_read_y = analogRead(J_Y);
x = processCoordinateChange(x, j_read_x, !shipVertical);
y = processCoordinateChange(y, j_read_y, shipVertical);
}
}
// top left red
matrix.drawPixel(0, 0, colors[0]);
// bottom right green
matrix.drawPixel(15, 15, colors[1]);
// bottom left blue
matrix.drawPixel(0, 15, colors[2]);
if (transition == true) {
Serial.println("TRANSITION");
if (textIndex > 0) {
Serial.println("TRANSITION TEXT: " + textIndex);
matrix.setCursor(3, 8);
matrix.setTextSize(0);
matrix.setTextColor(gameColors[2]);
matrix.print("GO!");
matrix.show();
delay(2500);
}
transition = false;
} else {
// border
matrix.drawRect(0 + OFFSET - 1, 0 + OFFSET - 1, 12, 12, colors[0]);
if (logShipPosition) {
//#SHIPS
for(int row = 0; row < 10; row++) {
if (logShipPosition) {
Serial.println("");
Serial.print(row);
Serial.print(" ");
}
for(int col = 0; col < 10; col++) {
int cell = 0 + displayMatrix[row][col];
matrix.drawPixel(col + OFFSET, row + OFFSET, gameColors[cell]);
if (logShipPosition) {
Serial.print(displayMatrix[row][col]);
}
}
}
logShipPosition = false;
}
matrix.show();
}
}
If anyone has an idea, I'd be very grateful.