Started on a GUI redesign of Snake.
This commit is contained in:
parent
207edcd6c1
commit
68b7bf8da8
44
resources/css/forSize.mixin.scss
Normal file
44
resources/css/forSize.mixin.scss
Normal 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
28
snake/snake-redesign.html
Normal 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>
|
@ -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>
|
||||
|
@ -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
228
snake/snake.scss
Normal 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; } }
|
||||
*/
|
Loading…
x
Reference in New Issue
Block a user