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() {
|
||||
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;
|
||||
do {
|
||||
fx = random(0, cols_);
|
||||
fy = random(0, rows_);
|
||||
fx = random(minX, maxX + 1);
|
||||
fy = random(minY, maxY + 1);
|
||||
} while (isSnakeCell(fx, fy));
|
||||
|
||||
foodX_ = fx;
|
||||
@@ -113,7 +118,8 @@ void SnakeActivity::updateGame() {
|
||||
int newY = snakeY_[headIdx_] + dy;
|
||||
|
||||
if (newX < 0 || newX >= cols_ || newY < 0 || newY >= rows_) {
|
||||
gameOver_ = true;
|
||||
gameOver_ = true;
|
||||
gameOverAt_ = millis();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -123,7 +129,8 @@ void SnakeActivity::updateGame() {
|
||||
if (!willEat && i == length_ - 1) continue;
|
||||
int idx = (headIdx_ + i) % MAX_SNAKE_LEN;
|
||||
if (snakeX_[idx] == newX && snakeY_[idx] == newY) {
|
||||
gameOver_ = true;
|
||||
gameOver_ = true;
|
||||
gameOverAt_ = millis();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -137,8 +144,9 @@ void SnakeActivity::updateGame() {
|
||||
score_ += POINTS_PER_FOOD;
|
||||
|
||||
if (length_ >= cols_ * rows_) {
|
||||
gameOver_ = true;
|
||||
won_ = true;
|
||||
gameOver_ = true;
|
||||
won_ = true;
|
||||
gameOverAt_ = millis();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -162,7 +170,8 @@ void SnakeActivity::loop() {
|
||||
}
|
||||
|
||||
if (gameOver_) {
|
||||
if (mappedInput.getPressedFrontButton() != -1) {
|
||||
if (millis() - gameOverAt_ >= GAME_OVER_LOCKOUT_MS &&
|
||||
mappedInput.getPressedFrontButton() != -1) {
|
||||
resetGame();
|
||||
renderPending_ = true;
|
||||
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 {
|
||||
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;
|
||||
renderer.drawLine(0, footerTop, screenW_, footerTop);
|
||||
|
||||
int colW = screenW_ / 4;
|
||||
int textH = renderer.getTextHeight(SMALL_FONT_ID);
|
||||
int labelY = footerTop + (FOOTER_H - textH) / 2;
|
||||
int cy = screenH_ - ARROW_BOTTOM_PAD - ARROW_SIZE / 2;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int w = renderer.getTextWidth(SMALL_FONT_ID, labels[i]);
|
||||
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) {
|
||||
renderer.drawLine(i * colW, footerTop, i * colW, screenH_);
|
||||
@@ -243,15 +281,11 @@ void SnakeActivity::render(RenderLock&&) {
|
||||
drawFooterLabels();
|
||||
|
||||
if (gameOver_) {
|
||||
char line2[24];
|
||||
snprintf(line2, sizeof(line2), "Score: %d", score_);
|
||||
|
||||
int baseY = fieldTop_ + fieldH / 2 - 40;
|
||||
int baseY = fieldTop_ + fieldH / 2 - 30;
|
||||
renderer.drawCenteredText(UI_12_FONT_ID, baseY, won_ ? "YOU WIN!" : "GAME OVER",
|
||||
true, EpdFontFamily::BOLD);
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, baseY + 30, line2);
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, baseY + 56, "Any button to Restart");
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, baseY + 78, "Power button to Exit");
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, baseY + 30, "Any button to Restart");
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, baseY + 52, "Power button to Exit");
|
||||
}
|
||||
|
||||
renderer.displayBuffer();
|
||||
|
||||
@@ -16,15 +16,18 @@ public:
|
||||
private:
|
||||
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 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 unsigned long TICK_START_MS = 170;
|
||||
static constexpr unsigned long TICK_MIN_MS = 80;
|
||||
static constexpr unsigned long TICK_START_MS = 320;
|
||||
static constexpr unsigned long TICK_MIN_MS = 200;
|
||||
static constexpr unsigned long TICK_STEP_MS = 6;
|
||||
static constexpr int POINTS_PER_FOOD = 10;
|
||||
static constexpr int SPEEDUP_EVERY_POINTS = 50;
|
||||
static constexpr unsigned long GAME_OVER_LOCKOUT_MS = 3000;
|
||||
|
||||
int screenW_ = 0;
|
||||
int screenH_ = 0;
|
||||
@@ -51,7 +54,8 @@ private:
|
||||
bool won_ = false;
|
||||
bool renderPending_ = false;
|
||||
|
||||
unsigned long lastMove_ = 0;
|
||||
unsigned long lastMove_ = 0;
|
||||
unsigned long gameOverAt_ = 0;
|
||||
|
||||
void resetGame();
|
||||
void placeFood();
|
||||
@@ -59,4 +63,5 @@ private:
|
||||
void handleDirectionInput();
|
||||
void updateGame();
|
||||
void drawFooterLabels() const;
|
||||
void drawArrow(Dir dir, int cx, int cy, int size) const;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user