fix(snakegame): remap controls to fixed buttons, replace footer labels with arrows, and improve game feel (food placement, pacing, game-over lockout)
This commit is contained in:
@@ -67,10 +67,15 @@ bool SnakeActivity::isSnakeCell(int x, int y) const {
|
|||||||
void SnakeActivity::placeFood() {
|
void SnakeActivity::placeFood() {
|
||||||
if (length_ >= cols_ * rows_) return;
|
if (length_ >= cols_ * rows_) return;
|
||||||
|
|
||||||
|
int minX = (cols_ > 2) ? 1 : 0;
|
||||||
|
int maxX = (cols_ > 2) ? cols_ - 2 : cols_ - 1;
|
||||||
|
int minY = (rows_ > 2) ? 1 : 0;
|
||||||
|
int maxY = (rows_ > 2) ? rows_ - 2 : rows_ - 1;
|
||||||
|
|
||||||
int fx, fy;
|
int fx, fy;
|
||||||
do {
|
do {
|
||||||
fx = random(0, cols_);
|
fx = random(minX, maxX + 1);
|
||||||
fy = random(0, rows_);
|
fy = random(minY, maxY + 1);
|
||||||
} while (isSnakeCell(fx, fy));
|
} while (isSnakeCell(fx, fy));
|
||||||
|
|
||||||
foodX_ = fx;
|
foodX_ = fx;
|
||||||
@@ -113,7 +118,8 @@ void SnakeActivity::updateGame() {
|
|||||||
int newY = snakeY_[headIdx_] + dy;
|
int newY = snakeY_[headIdx_] + dy;
|
||||||
|
|
||||||
if (newX < 0 || newX >= cols_ || newY < 0 || newY >= rows_) {
|
if (newX < 0 || newX >= cols_ || newY < 0 || newY >= rows_) {
|
||||||
gameOver_ = true;
|
gameOver_ = true;
|
||||||
|
gameOverAt_ = millis();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +129,8 @@ void SnakeActivity::updateGame() {
|
|||||||
if (!willEat && i == length_ - 1) continue;
|
if (!willEat && i == length_ - 1) continue;
|
||||||
int idx = (headIdx_ + i) % MAX_SNAKE_LEN;
|
int idx = (headIdx_ + i) % MAX_SNAKE_LEN;
|
||||||
if (snakeX_[idx] == newX && snakeY_[idx] == newY) {
|
if (snakeX_[idx] == newX && snakeY_[idx] == newY) {
|
||||||
gameOver_ = true;
|
gameOver_ = true;
|
||||||
|
gameOverAt_ = millis();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,8 +144,9 @@ void SnakeActivity::updateGame() {
|
|||||||
score_ += POINTS_PER_FOOD;
|
score_ += POINTS_PER_FOOD;
|
||||||
|
|
||||||
if (length_ >= cols_ * rows_) {
|
if (length_ >= cols_ * rows_) {
|
||||||
gameOver_ = true;
|
gameOver_ = true;
|
||||||
won_ = true;
|
won_ = true;
|
||||||
|
gameOverAt_ = millis();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +170,8 @@ void SnakeActivity::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gameOver_) {
|
if (gameOver_) {
|
||||||
if (mappedInput.getPressedFrontButton() != -1) {
|
if (millis() - gameOverAt_ >= GAME_OVER_LOCKOUT_MS &&
|
||||||
|
mappedInput.getPressedFrontButton() != -1) {
|
||||||
resetGame();
|
resetGame();
|
||||||
renderPending_ = true;
|
renderPending_ = true;
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
@@ -184,20 +193,49 @@ void SnakeActivity::loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SnakeActivity::drawArrow(Dir dir, int cx, int cy, int size) const {
|
||||||
|
int half = size / 2;
|
||||||
|
int xs[3];
|
||||||
|
int ys[3];
|
||||||
|
|
||||||
|
switch (dir) {
|
||||||
|
case Dir::Left:
|
||||||
|
xs[0] = cx - half; ys[0] = cy;
|
||||||
|
xs[1] = cx + half; ys[1] = cy - half;
|
||||||
|
xs[2] = cx + half; ys[2] = cy + half;
|
||||||
|
break;
|
||||||
|
case Dir::Right:
|
||||||
|
xs[0] = cx + half; ys[0] = cy;
|
||||||
|
xs[1] = cx - half; ys[1] = cy - half;
|
||||||
|
xs[2] = cx - half; ys[2] = cy + half;
|
||||||
|
break;
|
||||||
|
case Dir::Up:
|
||||||
|
xs[0] = cx; ys[0] = cy - half;
|
||||||
|
xs[1] = cx - half; ys[1] = cy + half;
|
||||||
|
xs[2] = cx + half; ys[2] = cy + half;
|
||||||
|
break;
|
||||||
|
case Dir::Down:
|
||||||
|
xs[0] = cx; ys[0] = cy + half;
|
||||||
|
xs[1] = cx - half; ys[1] = cy - half;
|
||||||
|
xs[2] = cx + half; ys[2] = cy - half;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.fillPolygon(xs, ys, 3, true);
|
||||||
|
}
|
||||||
|
|
||||||
void SnakeActivity::drawFooterLabels() const {
|
void SnakeActivity::drawFooterLabels() const {
|
||||||
static const char* labels[4] = {"Left", "Right", "Up", "Down"};
|
static const Dir dirs[4] = {Dir::Left, Dir::Right, Dir::Up, Dir::Down};
|
||||||
|
|
||||||
int footerTop = screenH_ - FOOTER_H;
|
int footerTop = screenH_ - FOOTER_H;
|
||||||
renderer.drawLine(0, footerTop, screenW_, footerTop);
|
renderer.drawLine(0, footerTop, screenW_, footerTop);
|
||||||
|
|
||||||
int colW = screenW_ / 4;
|
int colW = screenW_ / 4;
|
||||||
int textH = renderer.getTextHeight(SMALL_FONT_ID);
|
int cy = screenH_ - ARROW_BOTTOM_PAD - ARROW_SIZE / 2;
|
||||||
int labelY = footerTop + (FOOTER_H - textH) / 2;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
int w = renderer.getTextWidth(SMALL_FONT_ID, labels[i]);
|
|
||||||
int colCenterX = i * colW + colW / 2;
|
int colCenterX = i * colW + colW / 2;
|
||||||
renderer.drawText(SMALL_FONT_ID, colCenterX - w / 2, labelY, labels[i], true);
|
drawArrow(dirs[i], colCenterX, cy, ARROW_SIZE);
|
||||||
|
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
renderer.drawLine(i * colW, footerTop, i * colW, screenH_);
|
renderer.drawLine(i * colW, footerTop, i * colW, screenH_);
|
||||||
@@ -243,15 +281,11 @@ void SnakeActivity::render(RenderLock&&) {
|
|||||||
drawFooterLabels();
|
drawFooterLabels();
|
||||||
|
|
||||||
if (gameOver_) {
|
if (gameOver_) {
|
||||||
char line2[24];
|
int baseY = fieldTop_ + fieldH / 2 - 30;
|
||||||
snprintf(line2, sizeof(line2), "Score: %d", score_);
|
|
||||||
|
|
||||||
int baseY = fieldTop_ + fieldH / 2 - 40;
|
|
||||||
renderer.drawCenteredText(UI_12_FONT_ID, baseY, won_ ? "YOU WIN!" : "GAME OVER",
|
renderer.drawCenteredText(UI_12_FONT_ID, baseY, won_ ? "YOU WIN!" : "GAME OVER",
|
||||||
true, EpdFontFamily::BOLD);
|
true, EpdFontFamily::BOLD);
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, baseY + 30, line2);
|
renderer.drawCenteredText(UI_10_FONT_ID, baseY + 30, "Any button to Restart");
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, baseY + 56, "Any button to Restart");
|
renderer.drawCenteredText(UI_10_FONT_ID, baseY + 52, "Power button to Exit");
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, baseY + 78, "Power button to Exit");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
|
|||||||
@@ -16,15 +16,18 @@ public:
|
|||||||
private:
|
private:
|
||||||
enum class Dir { Up, Down, Left, Right };
|
enum class Dir { Up, Down, Left, Right };
|
||||||
|
|
||||||
static constexpr int BLOCK = 18;
|
static constexpr int BLOCK = 28;
|
||||||
static constexpr int HEADER_H = 34;
|
static constexpr int HEADER_H = 34;
|
||||||
static constexpr int FOOTER_H = 30;
|
static constexpr int FOOTER_H = 38;
|
||||||
|
static constexpr int ARROW_SIZE = 16;
|
||||||
|
static constexpr int ARROW_BOTTOM_PAD = 14;
|
||||||
static constexpr int MAX_SNAKE_LEN = 2048;
|
static constexpr int MAX_SNAKE_LEN = 2048;
|
||||||
static constexpr unsigned long TICK_START_MS = 170;
|
static constexpr unsigned long TICK_START_MS = 320;
|
||||||
static constexpr unsigned long TICK_MIN_MS = 80;
|
static constexpr unsigned long TICK_MIN_MS = 200;
|
||||||
static constexpr unsigned long TICK_STEP_MS = 6;
|
static constexpr unsigned long TICK_STEP_MS = 6;
|
||||||
static constexpr int POINTS_PER_FOOD = 10;
|
static constexpr int POINTS_PER_FOOD = 10;
|
||||||
static constexpr int SPEEDUP_EVERY_POINTS = 50;
|
static constexpr int SPEEDUP_EVERY_POINTS = 50;
|
||||||
|
static constexpr unsigned long GAME_OVER_LOCKOUT_MS = 3000;
|
||||||
|
|
||||||
int screenW_ = 0;
|
int screenW_ = 0;
|
||||||
int screenH_ = 0;
|
int screenH_ = 0;
|
||||||
@@ -51,7 +54,8 @@ private:
|
|||||||
bool won_ = false;
|
bool won_ = false;
|
||||||
bool renderPending_ = false;
|
bool renderPending_ = false;
|
||||||
|
|
||||||
unsigned long lastMove_ = 0;
|
unsigned long lastMove_ = 0;
|
||||||
|
unsigned long gameOverAt_ = 0;
|
||||||
|
|
||||||
void resetGame();
|
void resetGame();
|
||||||
void placeFood();
|
void placeFood();
|
||||||
@@ -59,4 +63,5 @@ private:
|
|||||||
void handleDirectionInput();
|
void handleDirectionInput();
|
||||||
void updateGame();
|
void updateGame();
|
||||||
void drawFooterLabels() const;
|
void drawFooterLabels() const;
|
||||||
|
void drawArrow(Dir dir, int cx, int cy, int size) const;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user