Started on a GUI redesign of Snake.

This commit is contained in:
Joanthan Bernard 2014-12-11 22:52:55 -06:00
parent 207edcd6c1
commit 68b7bf8da8
5 changed files with 350 additions and 73 deletions

View File

@ -0,0 +1,44 @@
$smallScreen: 640px;
$wideScreen: 1200px;
$ultraWideScreen: 1600px;
/** ### forSize
* This mixin allows us to apply some rules selectively based on the screen
* size. There are three primary sizes: `small`, `medium`, and `large`, which
* are mutually exclusive. Additionally there are two additional sizes:
* `notSmall` and `ultraLarge`. `notSmall`, as the name implies matches any
* value which is not the small screen size, so it overlaps with medium,
* large, and ultraLarge. `ultraLarge` defines a wider minimum screen size
* than large, but neither large nor ultraLarge specify maximum widths,
* so ultraLarge is a strict subset of large. A screen large enough to match
* ultraLarge will also match large (compare with medium and large: matching
* medium means it will not match large, and vice versa). */
@mixin forSize($size) {
@if $size == small {
@media screen and (max-width: $smallScreen) { @content; } }
@else if $size == notSmall {
@media screen and (min-width: $smallScreen + 1) { @content; } }
@else if $size == medium {
@media screen and (min-width: $smallScreen + 1) and (max-width: $wideScreen - 1) { @content; } }
@else if $size == large {
@media screen and (min-width: $wideScreen) { @content; } }
@else if $size == ultraLarge {
@media screen and (min-width: $ultraWideScreen) { @content; } }
}
@mixin forAspect($aspect) {
@if $aspect == tall {
@media screen and (max-aspect-ratio: 1/2) { @content; } }
@else if $aspect == portrait {
@media screen and (min-aspect-ratio: 11/20) and (max-aspect-ratio: 5/6) { @content; } }
@else if $aspect == even {
@media screen and (min-aspect-ratio: 11/12) and (max-aspect-ratio: 6/5) { @content; } }
@else if $aspect == landscape {
@media screen and (min-aspect-ratio: 12/11) and (max-aspect-ratio: 31/18) { @content; } }
@else if $aspect == wide {
@media screen and (min-aspect-ratio: 16/9) { @content; } }
}

28
snake/snake-redesign.html Normal file
View File

@ -0,0 +1,28 @@
<!doctype html>
<html>
<head>
<link href='http://fonts.googleapis.com/css?family=Press+Start+2P' rel='stylesheet' type='text/css'>
<link href="snake.css" type="text/css" rel="stylesheet">
</head>
<body>
<div class=corner></div><div class=corner></div><div class=corner></div><div class=corner></div>
<div id=container>
<div class=corner></div><div class=corner></div><div class=corner></div><div class=corner></div>
<header><h1>SNAKE</h1></header>
<section id=description>
Play SNAKE! You are the <span style="color: #88F">BLUE</span>
snake. Eat the <span style="color: green">GREEN</span> food to
grow longer! But be careful! Don't run into the walls or
yourself!
<section>
<section id=levelSelect>
</section>
<section id=gameContainer>
</section>
<section id=controls>
</section>
<section id=editorControls>
</section>
</div>
</body>
</html>

View File

@ -4,64 +4,8 @@
<meta name=charset value=utf-8>
<title>Snake</title>
<link href='http://fonts.googleapis.com/css?family=Exo+2:400,900' rel='stylesheet' type='text/css'>
<link href="snake.css" type="text/css" rel="stylesheet">
<script src="snake.js" type="application/javascript"></script>
<style type="text/css">
/* Pallete: http://www.colourlovers.com/palette/1832769/100_Following_Me
* A70407 - Red
* F4A612 - Orange
* FFFB51 - Soft Yellow
* 1E4119 - Green
* 0B0C38 - Blue */
* { margin: 0;
padding: 0; }
html {
background-color: white;
color: #354650;
font-family: sans-serif;
}
body {
padding: 1em;
}
header {
background-color: #1E4119;
color: #FFFB51;
padding: 0.5rem 1rem;
font-family: "Exo 2";
font-weight: 900;
position: absolute;
left: 0; right: 0; top: 0;
}
header > h1 { font-size: 300%; }
canvas { border: solid thin gray; }
section {
display: inline-block;
margin: 0.5rem;
position: relative; }
section:first-of-type { margin-top: 6rem; }
label {
display: inline-block;
width: 12rem; }
input[type=button] {
position: absolute;
right: 0; }
#editorControls { display: none; }
@media (max-width: 600px) {
header { text-align: center; } }
</style>
</head>
<body>
<header><h1>Snake</h1></header>

View File

@ -2,30 +2,41 @@
var S = window.Snake = {};
// True constants
var SPACE = 0; var WALL = 1; var SNAKE = 2; var FOOD = 3;
// Fake constants (constant after board initialization).
var ROWS = S.ROWS = 50;
var COLS = S.COLS = 50;
var board;
var startBoard;
var tileWidth, tileHeight;
// The current game board.
var board;
// The starting game board.
var startBoard;
var body = new Array();
var headCur;
var direction = 1;
var foodEaten = false;
var g;
// HTML Elements
var canvas;
var g;
var editor = false;
var editorControls;
var editorDataTextarea;
var cursorIdx;
var cursorBlock;
// Flags
var editor = false;
var dead = true;
var pause = false;
// Time-management
var skipTicks;
var nextGameTick;
@ -75,9 +86,11 @@
// Check to see if they want to use the editor or the game.
editor = Boolean(options.editor);
// Clear the canvas.
g.fillStyle = "white";
g.fillRect(0, 0, canvas.width, canvas.height);
// They want the game
if (!editor) {
canvas.addEventListener('keydown', handleGameKey);
@ -88,6 +101,7 @@
if (options.fps) skipTicks = Math.ceil(1000 / options.fps);
else skipTicks = 150; }
// They want the editor
else {
canvas.addEventListener('keydown', handleEditorKey);
canvas.addEventListener('click', handleEditorClick);
@ -158,9 +172,10 @@
requestAnimationFrame(gameLoop); }
// For this game I want the game logic and the refresh rate to be tied
// together. The snake moves on step per animation frame. We do limit the
// animation frame to 1fps. This works because there are no other elements
// being animated other than the snake.
// together. The snake moves one step per animation frame. We do limit the
// animation frame to a given fps. This works because there are no other
// elements being animated other than the snake and no partial animation
// states.
function updateAndDraw() {
if (!g) return false;
@ -185,11 +200,9 @@
// 4. Remove the tail.
if (!foodEaten) {
var tailIdx = body.shift();
// Value on the board doesn't matter, just not W,S, or F
board[tailIdx] = SPACE
paintIdx(tailIdx); }
// 5. Detect wall and snake collisions
if (board[headCur] == WALL || board[headCur] == SNAKE) {
die(); return }
@ -205,12 +218,14 @@
board[foodLoc] = FOOD;
paintIdx(foodLoc); } }
// Find a new location for a piece of food.
function newFoodLocation() {
// Find all the spaces on the board that are not occupied
var emptySpaces = new Array();
for (var i = 0; i < board.length; i++)
if (board[i] != WALL && board[i] != SNAKE)
emptySpaces.push(i);
if (board[i] == SPACE) emptySpaces.push(i);
// Choose one of those spaces at random.
return emptySpaces[Math.floor(Math.random() * emptySpaces.length)] }
function die() {
@ -225,6 +240,9 @@
function handleGameKey(ke) {
var key = ke.keyCode ? ke.keyCode : ke.which;
// We will only intercept key events which we care about. This makes it
// safe to attach the handler to the window or anywhere else without
// worrying about the game becoming a black-hole for events.
var dontCare = false;
// Enter, start a new game.
@ -232,7 +250,7 @@
dead = true;
requestAnimationFrame(startGame); }
// Pause, pause the game.
// Pause, pause or unpause the game.
else if (key == 19) {
if (pause == true) {
pause = false;
@ -243,24 +261,34 @@
// Queue directions in the command queue
else if ((key > 36) && (key < 41)) cmdQueue.push(key);
// Anything else we don't care about.
else dontCare = true;
// If we *do* care about this key, consume it and prevent it from
// bubbling up.
if (!dontCare) ke.preventDefault();
return false; }
// Handle input for the game editor. The editor is entirely input driven.
// All of it's actions and logic are in response to user input. There is no
// "game" loop and most of the editor's logic happens in this function.
function handleEditorKey(ke) {
var key = ke.keyCode ? ke.keyCode : ke.which;
// Again, we will pass along input we are not interested in.
var dontCare = false;
switch (key) {
// Enter: place a tile.
case 13: board[cursorIdx] = cursorBlock; break;
// Direction keys: move the cursor.
case 37: cursorIdx -= 1; break; // Left
case 38: cursorIdx -= COLS; break; // Up
case 39: cursorIdx += 1; break; // Right
case 40: cursorIdx += COLS; break; // Down
// Number keys: select a tile type.
case 49: case 97: cursorBlock = SPACE; break; // 1
case 50: case 98: cursorBlock = WALL; break; // 2
case 51: case 99: cursorBlock = SNAKE; break; // 3
@ -269,20 +297,27 @@
default: dontCare = true;
}
// Our board may have changed, let's spit out the data.
emitEditorLevelData();
// Redraw the board.
paintBoard();
// Draw the current cursor position.
g.strokeStyle = "solid 4px gray";
g.strokeRect(
(cursorIdx % COLS) * tileWidth,
Math.floor(cursorIdx / COLS) * tileHeight,
tileWidth, tileHeight);
// Consume events we care about and prevent them from bubbling up.
if (!dontCare) ke.preventDefault(); }
function handleEditorClick(ke) {
}
// Write out the editor level data as a JSON string to the textarea.
function emitEditorLevelData() {
editorDataTextarea.value = JSON.stringify({
board: board,
@ -290,8 +325,10 @@
cols: COLS,
fps: Math.ceil(1000 / skipTicks)}); }
// Load the editor data as a JSON string from the textarea.
function loadEditorLevelData() { }
// Draw the entire board.
function paintBoard() {
for (i = 0; i < board.length; i++) { paintIdx(i); } }
@ -325,8 +362,4 @@
S.initialize = initialize;
// Exports for debugging
S.board = board;
S.body = body;
S.updateAndDraw = updateAndDraw;
})();

228
snake/snake.scss Normal file
View File

@ -0,0 +1,228 @@
/* Pallete: http://www.colourlovers.com/palette/1832769/100_Following_Me
* A70407 - Red
* F4A612 - Orange
* FFFB51 - Soft Yellow
* 1E4119 - Green
* 0B0C38 - Blue */
//$fg: #333;
$fg: #FFF;
$out: #A70407;
$in: #0B0C38;
$accent1: #F4A612;
$accent2: #FFFB51;
$accent3: #1E4119;
@import "../resources/css/forSize.mixin.scss";
* { margin: 0;
padding: 0;
box-sizing: border-box; }
html {
background-color: $out;
font-family: "Press Start 2P";
}
.corner {
border-style: solid;
position: absolute;
height: 1rem;
width: 1rem;
&:nth-child(1) {
top: -0.5rem; left: -0.5rem;
border-width: 0.5rem 0 0 0.5rem; }
&:nth-child(2) {
top: -0.5rem; right: -0.5rem;
border-width: 0.5rem 0.5rem 0 0; }
&:nth-child(3) {
bottom: -0.5rem; right: -0.5rem;
border-width: 0 0.5rem 0.5rem 0; }
&:nth-child(4) {
bottom: -0.5rem; left: -0.5rem;
border-width: 0 0 0.5rem 0.5rem; } }
body {
border: solid 0.5rem $accent1;
background-color: $accent2;
padding: 0.5rem;
position: relative;
& > .corner {
width: 1.5rem;
height: 1.5rem;
background-color: $accent1;
border-color: $out; }
#container {
border: solid 0.5rem $accent3;
background-color: $in;
color: $fg;
position: relative;
& > .corner {
background-color: $accent3;
border-color: $accent2; }
header {
text-align: center;
& > h1 { padding-top: 0.5rem; } } } }
button {
background-color: $out;
border-color: $accent2 $accent1 $accent1 $accent2;
border-style: solid;
border-width: 0.25rem;
color: $fg;
display: block;
font-family: inherit;
padding: 0.1rem;
position: relative;
.corner {
border: none;
background-color: $in;
height: 0.25rem;
width: 0.25rem;
&:nth-child(1) {
top: -0.25rem; left: -0.25rem;
border-width: 0.25rem 0 0 0.25rem; }
&:nth-child(2) {
top: -0.25rem; right: -0.25rem;
border-width: 0.25rem 0.25rem 0 0; }
&:nth-child(3) {
bottom: -0.25rem; right: -0.25rem;
border-width: 0 0.25rem 0.25rem 0; }
&:nth-child(4) {
bottom: -0.25rem; left: -0.25rem;
border-width: 0 0 0.25rem 0.25rem; } } }
@include forAspect(wide) {
body {
width: 73rem;
height: 37rem;
margin-top: 4rem;
#container {
height: 35rem;
width: 71rem;
section { margin: 0.5rem; }
section#description, section#levelSelect, section#controls {
width: 18rem; } } }
}
@include forAspect(landscape) {
body {
width: 73rem;
height: 37rem;
margin-top: 4rem;
#container {
height: 35rem;
width: 71rem; } }
}
@include forSize(ultraLarge) {
html { font-size: 125%; } }
@include forSize(medium) {
html { font-size: 55%; } }
@include forSize(notSmall) {
body {
margin-left: auto;
margin-right: auto; }
}
/* @include forSize(medium) {
html { font-size: 80%; }
body {
height: 36rem;
width: 62rem;
margin-top: 2rem;
#container {
height: 34rem;
width: 60rem; } } }
@include forSize(large) {
body {
height: 36rem;
width: 62rem;
margin-top: 2rem;
#container {
height: 34rem;
width: 60rem; } } }
@include forSize(ultraLarge) {
body {
height: 42rem;
width: 72rem;
margin-top: 4rem;
#container {
height: 40rem;
width: 70rem; } } }
*/
/*
html {
background-color: white;
color: #354650;
font-family: sans-serif;
}
body {
padding: 1em;
}
header {
background-color: #1E4119;
color: #FFFB51;
padding: 0.5rem 1rem;
font-family: "Exo 2";
font-weight: 900;
position: absolute;
left: 0; right: 0; top: 0;
}
header > h1 { font-size: 300%; }
canvas { border: solid thin gray; }
section {
display: inline-block;
margin: 0.5rem;
position: relative; }
section:first-of-type { margin-top: 6rem; }
label {
display: inline-block;
width: 12rem; }
input[type=button] {
position: absolute;
right: 0; }
#editorControls { display: none; }
@media (max-width: 600px) {
header { text-align: center; } }
*/