Skip to content

Instantly share code, notes, and snippets.

@RonenNess
Created March 2, 2021 20:14
Show Gist options
  • Save RonenNess/433eb904e695ba9026769122150ddecd to your computer and use it in GitHub Desktop.
Save RonenNess/433eb904e695ba9026769122150ddecd to your computer and use it in GitHub Desktop.
184 lines of code Snake game written in 5mbg.com
// general defs
const LEVEL_WIDTH = 40; // playable region width
const LEVEL_HEIGHT = 20; // playable region height
const SCALE = 4; // scale factor for rendering
const SCREEN_WIDTH = LEVEL_WIDTH * SCALE; // screen width
const SCREEN_HEIGHT = LEVEL_HEIGHT * SCALE; // screen height
// set engine init flags
initFlags.gfx.crispRendering = true; // make rendering pixelated / crisp
initFlags.gfx.fixedResolutionX = SCREEN_WIDTH; // lock x resolution
initFlags.gfx.fullscreen = false; // play in windowed mode
initFlags.gfx.defaultViewportSize = {x:SCREEN_WIDTH, y:SCREEN_HEIGHT}; // set default viewport size
// set fixed update to slow rate of 4 per second. we put all our logic in the fixed updates
initFlags.game.fixedUpdatesPerSecond = 10;
// game states
var timeForStep = 0; // time until we perform next update
var snake = []; // snake body (head is index 0)
var food; // the food that currently appears in level
var direction; // current direction
var lastDirection; // last direction we moved to, to prevent going opposite direction
var isDead = false; // did we lose?
var gameOverText; // game over text
// generate random position
function generateRandomPos()
{
var x = 1 + Math.floor(Math.random() * (LEVEL_WIDTH - 2));
var y = 1 + Math.floor(Math.random() * (LEVEL_HEIGHT - 2));
return gfx.Point(x, y);
}
// get snake's head
function head()
{
return snake[0];
}
// create new food at a random position
function createFood()
{
food = generateRandomPos();
logger.info("New food at: " + food.x + ", " + food.y);
}
// start a new game
function newGame()
{
// cancel death state
isDead = false;
// reset snake and reiction
snake = [gfx.Point(1, 1)];
lastDirection = direction = 'right';
// place starting food
createFood();
}
// init game
onInit(() =>
{
// init game over text
gameOverText = gfx.TextSprite("Game Over\nSpace = Restart",
gfx.Point(SCREEN_WIDTH / 2, 30),
{fontSize: 18, alignment: gfx.TextAlignment.Center});
// create new game
newGame();
});
// do per-frame updates
// we only get input from user here
onUpdate(() =>
{
// handle death screen
if (isDead && input.down('space')) {
newGame();
}
// change direction (input always works, not just during steps)
if (input.down('left_arrow') || input.down('a')) {
if (lastDirection !== 'right') direction = 'left';
}
if (input.down('right_arrow') || input.down('d')) {
if (lastDirection !== 'left') direction = 'right';
}
if (input.down('up_arrow') || input.down('w')) {
if (lastDirection !== 'down') direction = 'up';
}
if (input.down('down_arrow') || input.down('s')) {
if (lastDirection !== 'up') direction = 'down';
}
});
// do fixed updates - here we actually do logic
onFixedUpdate(() =>
{
// skip updates on death
if (isDead) {
return;
}
// are we growing this frame?
var growNow = false;
// are we eating the food?
if (food.equals(head()))
{
createFood();
growNow = true;
}
// move snake body
var prevTail = snake[snake.length-1].clone();
for (var i = snake.length-1; i > 0; --i) {
snake[i] = snake[i-1].clone();
}
// get head part
var snakehead = head();
// move snake head
var prevHead = head().clone();
switch (direction) {
case "left":
snakehead.x--;
break;
case "right":
snakehead.x++;
break;
case "up":
snakehead.y--;
break;
case "down":
snakehead.y++;
break;
}
lastDirection = direction;
// do growing
if (growNow) {
snake.push(prevTail);
}
// check death condition by level border
if ((snakehead.x <= 0) || (snakehead.y <= 0) || (snakehead.x >= LEVEL_WIDTH-1) || (snakehead.y >= LEVEL_HEIGHT-1)) {
isDead = true;
}
// check death condition by stepping on self
for (var i = 1; i < snake.length; ++i) {
if (snakehead.equals(snake[i])) {
isDead = true;
}
}
});
// draw game
onDraw(() =>
{
// clear screen
gfx.clear(isDead ? gfx.Color.darkred : gfx.Color.black);
// draw snake head and body
gfx.drawPixel(head(), gfx.Color.lime, SCALE);
for (var i = 1; i < snake.length; ++i) {
gfx.drawPixel(snake[i], i % 2 == 0 ? gfx.Color.green : gfx.Color.darkgreen, SCALE);
}
// draw food
gfx.drawPixel(food, gfx.Color.blue, SCALE);
// draw level borders
gfx.drawRectangle(gfx.Rectangle(0,0,LEVEL_WIDTH*SCALE,SCALE), gfx.Color.red);
gfx.drawRectangle(gfx.Rectangle(0,(LEVEL_HEIGHT-1)*SCALE,LEVEL_WIDTH*SCALE,SCALE), gfx.Color.red);
gfx.drawRectangle(gfx.Rectangle(0,0,SCALE,LEVEL_HEIGHT*SCALE), gfx.Color.red);
gfx.drawRectangle(gfx.Rectangle((LEVEL_WIDTH-1)*SCALE,0,SCALE,LEVEL_HEIGHT*SCALE), gfx.Color.red);
// draw death state
if (isDead) {
gfx.drawPixel(head(), gfx.Color.red, SCALE);
gfx.drawText(gameOverText);
}
});
@RonenNess
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment