Created with <3 with dartpad.dev.
-
-
Save mykdavies/8c811949fc8a269f23d9fff4131fb0eb to your computer and use it in GitHub Desktop.
Snake Game in Dartpad
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
<!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> |
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
/* | |
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); | |
} |
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
/* | |
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); | |
} |
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
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