Created
March 2, 2021 20:14
-
-
Save RonenNess/433eb904e695ba9026769122150ddecd to your computer and use it in GitHub Desktop.
184 lines of code Snake game written in 5mbg.com
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Live demo:
http://5mbg.com/play/snake/