Skip to content

Instantly share code, notes, and snippets.

@mykdavies
Forked from mdecourse/index.html
Created November 15, 2023 13:32
Show Gist options
  • Save mykdavies/8c811949fc8a269f23d9fff4131fb0eb to your computer and use it in GitHub Desktop.
Save mykdavies/8c811949fc8a269f23d9fff4131fb0eb to your computer and use it in GitHub Desktop.
Snake Game in Dartpad

Snake Game in Dartpad

Created with <3 with dartpad.dev.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snake Game in Dartpad</title>
<link rel="stylesheet" href="styles.css">
<script type="application/dart" src="main.dart"></script>
</head>
<body>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snake Game in Dartpad</title>
<link rel="stylesheet" href="styles.css">
<script type="application/dart" src="main.dart"></script>
</head>
<body>
<div id="wrapper">
<canvas id="canvas" width="450" height="450"></canvas>
</div>
</body>
</html>
</body>
</html>
/*
https://dart.academy/web-games-with-dart-and-the-html5-canvas/
https://github.com/montyr75/dart_snake
*/
import 'dart:async';
// ignore: avoid_web_libraries_in_flutter
import 'dart:html';
import 'dart:math';
import 'dart:collection';
const int cellSize = 10;
late CanvasElement canvas;
late CanvasRenderingContext2D ctx;
Keyboard keyboard = Keyboard();
void main() {
canvas = (querySelector('#canvas')! as CanvasElement)..focus();
ctx = canvas.getContext('2d')! as CanvasRenderingContext2D;
Game().run();
}
void drawCell(Point<int> coords, String color) {
ctx..fillStyle = color
..strokeStyle = "white";
final int x = coords.x * cellSize;
final int y = coords.y * cellSize;
ctx..fillRect(x, y, cellSize, cellSize)
..strokeRect(x, y, cellSize, cellSize);
}
void clear() {
ctx..fillStyle = "white"
..fillRect(0, 0, canvas.width!, canvas.height!);
}
class Game {
// smaller numbers make the game run faster
static const num gameSpeed = 50;
num _lastTimeStamp = 0;
// a few convenience variables to simplify calculations
late int _rightEdgeX;
late int _bottomEdgeY;
late Snake _snake;
late Point<int> _food;
Game() {
_rightEdgeX = canvas.width! ~/ cellSize;
_bottomEdgeY = canvas.height! ~/ cellSize;
init();
}
void init() {
_snake = Snake();
_food = _randomPoint();
}
Point<int> _randomPoint() {
Random random = Random();
return Point(random.nextInt(_rightEdgeX), random.nextInt(_bottomEdgeY));
}
void _checkForCollisions() {
// check for collision with food
if (_snake.head == _food) {
_snake.grow();
_food = _randomPoint();
}
// check death conditions
if (_snake.head.x <= -1 || _snake.head.x >= _rightEdgeX ||
_snake.head.y <= -1 || _snake.head.y >= _bottomEdgeY ||
_snake.checkForBodyCollision()) {
init();
}
}
Future run() async {
update(await window.animationFrame);
}
void update(num delta) {
final num diff = delta - _lastTimeStamp;
if (diff > gameSpeed) {
_lastTimeStamp = delta;
clear();
drawCell(_food, "blue");
_snake.update();
_checkForCollisions();
}
// keep looping
run();
}
}
class Snake {
static const Point<int> pLeft = Point(-1, 0);
static const Point<int> pRight = Point(1, 0);
static const Point<int> pUp = Point(0, -1);
static const Point<int> pDown = Point(0, 1);
static const int startLength = 6;
late List<Point<int>> _body; // coordinates of the body segments
Point<int> _dir = pRight; // current travel direction
Snake() {
int i = startLength - 1;
_body = List<Point<int>>.generate(startLength, (int index) => Point(i--, 0));
}
Point<int> get head => _body.first;
void _checkInput() {
if (keyboard.isPressed(KeyCode.LEFT) && _dir != pRight) {
_dir = pLeft;
}
else if (keyboard.isPressed(KeyCode.RIGHT) && _dir != pLeft) {
_dir = pRight;
}
else if (keyboard.isPressed(KeyCode.UP) && _dir != pDown) {
_dir = pUp;
}
else if (keyboard.isPressed(KeyCode.DOWN) && _dir != pUp) {
_dir = pDown;
}
}
void grow() {
// add new head based on current direction
_body.insert(0, head + _dir);
}
void _move() {
// add a new head segment
grow();
// remove the tail segment
_body.removeLast();
}
void _draw() {
// starting with the head, draw each body segment
for (Point<int> p in _body) {
drawCell(p, "green");
}
}
bool checkForBodyCollision() {
for (Point p in _body.skip(1)) {
if (p == head) {
return true;
}
}
return false;
}
void update() {
_checkInput();
_move();
_draw();
}
}
class Keyboard {
final HashMap<int, num> _keys = HashMap<int, num>();
Keyboard() {
window.onKeyDown.listen((KeyboardEvent event) {
_keys.putIfAbsent(event.keyCode, () => event.timeStamp!);
});
window.onKeyUp.listen((KeyboardEvent event) {
_keys.remove(event.keyCode);
});
}
bool isPressed(int keyCode) => _keys.containsKey(keyCode);
}
/*
https://dart.academy/web-games-with-dart-and-the-html5-canvas/
https://github.com/montyr75/dart_snake
*/
import 'dart:async';
import 'dart:html';
import 'dart:math';
import 'dart:collection';
const int CELL_SIZE = 10;
CanvasElement canvas;
CanvasRenderingContext2D ctx;
Keyboard keyboard = new Keyboard();
void main() {
canvas = querySelector('#canvas')..focus();
ctx = canvas.getContext('2d');
new Game()..run();
}
void drawCell(Point coords, String color) {
ctx..fillStyle = color
..strokeStyle = "white";
final int x = coords.x * CELL_SIZE;
final int y = coords.y * CELL_SIZE;
ctx..fillRect(x, y, CELL_SIZE, CELL_SIZE)
..strokeRect(x, y, CELL_SIZE, CELL_SIZE);
}
void clear() {
ctx..fillStyle = "white"
..fillRect(0, 0, canvas.width, canvas.height);
}
class Game {
// smaller numbers make the game run faster
static const num GAME_SPEED = 50;
num _lastTimeStamp = 0;
// a few convenience variables to simplify calculations
int _rightEdgeX;
int _bottomEdgeY;
Snake _snake;
Point _food;
Game() {
_rightEdgeX = canvas.width ~/ CELL_SIZE;
_bottomEdgeY = canvas.height ~/ CELL_SIZE;
init();
}
void init() {
_snake = new Snake();
_food = _randomPoint();
}
Point _randomPoint() {
Random random = new Random();
return new Point(random.nextInt(_rightEdgeX), random.nextInt(_bottomEdgeY));
}
void _checkForCollisions() {
// check for collision with food
if (_snake.head == _food) {
_snake.grow();
_food = _randomPoint();
}
// check death conditions
if (_snake.head.x <= -1 || _snake.head.x >= _rightEdgeX ||
_snake.head.y <= -1 || _snake.head.y >= _bottomEdgeY ||
_snake.checkForBodyCollision()) {
init();
}
}
Future run() async {
update(await window.animationFrame);
}
void update(num delta) {
final num diff = delta - _lastTimeStamp;
if (diff > GAME_SPEED) {
_lastTimeStamp = delta;
clear();
drawCell(_food, "blue");
_snake.update();
_checkForCollisions();
}
// keep looping
run();
}
}
class Snake {
static const Point LEFT = const Point(-1, 0);
static const Point RIGHT = const Point(1, 0);
static const Point UP = const Point(0, -1);
static const Point DOWN = const Point(0, 1);
static const int START_LENGTH = 6;
List<Point> _body; // coordinates of the body segments
Point _dir = RIGHT; // current travel direction
Snake() {
int i = START_LENGTH - 1;
_body = new List<Point>.generate(START_LENGTH, (int index) => new Point(i--, 0));
}
Point get head => _body.first;
void _checkInput() {
if (keyboard.isPressed(KeyCode.LEFT) && _dir != RIGHT) {
_dir = LEFT;
}
else if (keyboard.isPressed(KeyCode.RIGHT) && _dir != LEFT) {
_dir = RIGHT;
}
else if (keyboard.isPressed(KeyCode.UP) && _dir != DOWN) {
_dir = UP;
}
else if (keyboard.isPressed(KeyCode.DOWN) && _dir != UP) {
_dir = DOWN;
}
}
void grow() {
// add new head based on current direction
_body.insert(0, head + _dir);
}
void _move() {
// add a new head segment
grow();
// remove the tail segment
_body.removeLast();
}
void _draw() {
// starting with the head, draw each body segment
for (Point p in _body) {
drawCell(p, "green");
}
}
bool checkForBodyCollision() {
for (Point p in _body.skip(1)) {
if (p == head) {
return true;
}
}
return false;
}
void update() {
_checkInput();
_move();
_draw();
}
}
class Keyboard {
HashMap<int, num> _keys = new HashMap<int, num>();
Keyboard() {
window.onKeyDown.listen((KeyboardEvent event) {
_keys.putIfAbsent(event.keyCode, () => event.timeStamp);
});
window.onKeyUp.listen((KeyboardEvent event) {
_keys.remove(event.keyCode);
});
}
bool isPressed(int keyCode) => _keys.containsKey(keyCode);
}
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#wrapper {
width: 450px;
margin: auto;
border: solid thin black;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment