+
+
+
+
+
diff --git a/space-dodge-game/Crosswalk-6-resize/main.js b/space-dodge-game/Crosswalk-6-resize/main.js
new file mode 100644
index 0000000..b04f562
--- /dev/null
+++ b/space-dodge-game/Crosswalk-6-resize/main.js
@@ -0,0 +1,369 @@
+document.addEventListener('DOMContentLoaded', function () {
+ if (screen.lockOrientation) {
+ screen.lockOrientation('landscape');
+ }
+
+ // DOM elements in main game area
+ var scoreDisplay = document.querySelector('#score-display');
+ var controlUp = document.querySelector('#control-up');
+ var controlDown = document.querySelector('#control-down');
+ var playArea = document.querySelector('#play-area');
+
+ // factor by which to modify canvas width and height
+ var scaleCanvas = 1;
+
+ var fitCanvas = function () {
+ var container = document.querySelector('#play-area-container');
+ var containerWidth = container.offsetWidth;
+ var containerHeight = container.offsetHeight;
+
+ var playAreaWidth = playArea.width;
+ var playAreaHeight = playArea.height;
+
+ var scaleWidth = containerWidth / playAreaWidth;
+ var scaleHeight = containerHeight / playAreaHeight;
+ scaleCanvas = (scaleHeight < scaleWidth) ? scaleHeight : scaleWidth;
+
+ var newPlayAreaWidth = playAreaWidth * scaleCanvas;
+ var newPlayAreaHeight = playAreaHeight * scaleCanvas;
+
+ var left = (containerWidth - newPlayAreaWidth) / 2;
+ var top = (containerHeight - newPlayAreaHeight) / 2;
+
+ // resize and position the canvas
+ playArea.width = parseInt(newPlayAreaWidth, 10);
+ playArea.height = parseInt(newPlayAreaHeight, 10);
+ playArea.style.top = top + 'px';
+ playArea.style.left = left + 'px';
+ };
+
+ window.onresize = fitCanvas;
+ fitCanvas();
+
+ // DOM elements in stop screen
+ var restart = document.querySelector('#restart');
+ restart.addEventListener('click', start);
+ var finalScore = document.querySelector('#final-score');
+ var stopScreen = document.querySelector('#finish-screen');
+
+ // player image (to draw onto canvas)
+ var player = new Image();
+ player.src = 'rocket.png';
+
+ // asteroid image (to draw onto canvas)
+ var asteroid = new Image();
+ asteroid.src = 'asteroid.png';
+
+ // canvas context
+ var ctx = playArea.getContext('2d');
+
+ // time of current frame
+ var currentTime;
+
+ // time since last frame, in seconds
+ var delta;
+
+ // distance to move on y axis
+ var moveY;
+
+ // previous y position for player
+ var oldY;
+
+ // time of last frame
+ var lastTime;
+
+ // x position of player (fixed)
+ var x;
+
+ // y position of player
+ var y;
+
+ // asteroids
+ var asteroids;
+
+ // base speed of each asteroid when added to screen
+ // (incremented as game progresses);
+ // the actual speed is this plus some small random amount
+ var asteroidSpeed;
+
+ // maximum base speed of asteroids
+ var asteroidSpeedMax;
+
+ // num of asteroids to keep on screen (incremented as game progresses)
+ var asteroidNum;
+
+ // maximum number of asteroids to have on the screen at any time
+ var asteroidNumMax;
+
+ // set to 'up' or 'down' if a control is active;
+ // otherwise, set to null
+ var controlPressed;
+
+ // player score
+ var score;
+
+ // set to true if an asteroid collides with the player; used
+ // to stop the game
+ var hasCollided;
+
+ // do we need to add asteroids?
+ var needsReplenish;
+
+ // set to false if the game isn't running (e.g. on collision)
+ var running;
+
+ // control button event handlers
+ function handleControlOn(e) {
+ if (e.target === controlUp) {
+ controlPressed = 'up';
+ }
+ else if (e.target === controlDown) {
+ controlPressed = 'down';
+ }
+ };
+
+ function handleControlOff() {
+ controlPressed = null;
+ }
+
+ // turn off controls if the touch move event happens outside the
+ // up and down controls
+ function handleTouchMove(e) {
+ e.preventDefault();
+
+ var elt = document.elementFromPoint(
+ e.touches[0].clientX,
+ e.touches[0].clientY
+ );
+
+ if (elt === controlDown) {
+ controlPressed = 'down';
+ }
+ else if (elt === controlUp) {
+ controlPressed = 'up';
+ }
+ else {
+ controlPressed = null;
+ }
+ }
+
+ controlUp.addEventListener('mousedown', handleControlOn);
+ controlDown.addEventListener('mousedown', handleControlOn);
+ controlUp.addEventListener('mouseout', handleControlOff);
+ controlDown.addEventListener('mouseout', handleControlOff);
+ controlUp.addEventListener('mouseup', handleControlOff);
+ controlDown.addEventListener('mouseup', handleControlOff);
+
+ controlUp.addEventListener('touchstart', handleControlOn);
+ controlDown.addEventListener('touchstart', handleControlOn);
+ controlUp.addEventListener('touchend', handleControlOff);
+ controlDown.addEventListener('touchend', handleControlOff);
+ document.addEventListener('touchmove', handleTouchMove);
+
+ // get dimensions of an Image, taking scaleCanvas into account
+ function getImgWidth(img) {
+ return img.width * scaleCanvas;
+ }
+
+ function getImgHeight(img) {
+ return img.height * scaleCanvas;
+ }
+
+ // draw an image to the canvas; this rounds off the x,y coordinates
+ // first
+ function drawImage(image, x, y, width, height) {
+ ctx.drawImage(image, x, y, width, height);
+ }
+
+ // asteroidY: starting y position of the asteroid (px)
+ // speed: > 0
+ // the asteroid starts at x = canvas width, so is initially not visible
+ function addAsteroid(asteroidY, speed) {
+ // enough asteroids already
+ if (asteroids.length >= asteroidNumMax) {
+ return;
+ }
+
+ var asteroidX = playArea.width;
+
+ var obj = {
+ x: asteroidX,
+ y: asteroidY,
+ speed: speed,
+ offscreen: false
+ };
+
+ asteroids.push(obj);
+
+ // increment the number of asteroids which can be on screen
+ // at once
+ asteroidNum *= 1 + (0.5 / (asteroidNum * asteroidNum));
+
+ // increment the base speed for asteroids
+ if (asteroidSpeed < asteroidSpeedMax) {
+ asteroidSpeed *= 1.02;
+ }
+ }
+
+ // top up asteroids on screen; the starting speed for the asteroid
+ // is the current asteroidSpeed, plus up to 1.5 extra
+ function replenishAsteroids() {
+ // remove any offscreen asteroids
+ for (var i = 0; i < asteroids.length; i++) {
+ if (asteroids[i].offscreen) {
+ asteroids.splice(i, 1);
+ }
+ }
+
+ // add enough asteroids to fill the quota
+ for (var i = 1; i <= (asteroidNum - asteroids.length); i++) {
+ var asteroidY = (Math.random() * playArea.height) - getImgHeight(asteroid);
+
+ if (asteroidY < 0) {
+ asteroidY = 0;
+ }
+
+ addAsteroid(asteroidY, asteroidSpeed + (Math.random() * 1.5));
+ }
+ }
+
+ function showScore() {
+ scoreDisplay.innerText = score;
+ }
+
+ // runs on every animation frame
+ function gameLoop() {
+ if (!running) {
+ return;
+ }
+
+ window.requestAnimationFrame(gameLoop);
+
+ // apply movement to player
+ oldY = y;
+
+ currentTime = (new Date()).getTime();
+ delta = (currentTime - lastTime) / 1000;
+
+ // player moves 3 times own height per second
+ moveY = (getImgHeight(player) * delta * 3);
+
+ if (controlPressed === 'up') {
+ y -= moveY;
+ }
+ else if (controlPressed === 'down') {
+ y += moveY;
+ }
+
+ if (y < 0) {
+ y = 0;
+ }
+ else if ((y + getImgHeight(player)) >= playArea.height) {
+ y = playArea.height - getImgHeight(player);
+ }
+
+ // clear the whole canvas
+ ctx.clearRect(0, 0, playArea.width, playArea.height);
+
+ // draw the player with some jitter
+ drawImage(
+ player,
+ x + (Math.random() - 1) * 2,
+ y + (Math.random() - 1) * 2,
+ getImgWidth(player),
+ getImgHeight(player)
+ );
+
+ // move asteroids and draw them
+ hasCollided = false;
+
+ // set to true if at least one asteroid goes offscreen
+ needReplenish = false;
+
+ for (var i = 0; i < asteroids.length; i++) {
+ // check whether asteroid has hit player; the numbers subtracted
+ // below give more realistic collisions for the sprite shapes
+ // involved
+ if (!hasCollided) {
+ hasCollided = (x + getImgWidth(player) - 10 >= asteroids[i].x) &&
+ (x <= asteroids[i].x + getImgWidth(asteroid) - 10) &&
+ (y + getImgHeight(player) - 4 >= asteroids[i].y) &&
+ (y <= asteroids[i].y + getImgHeight(asteroid) - 4);
+ }
+
+ // asteroid has left the screen
+ if (asteroids[i].x < (0 - getImgWidth(asteroid))) {
+ asteroids[i].offscreen = true;
+ score++;
+ needReplenish = true;
+ }
+ // asteroid needs to be drawn
+ else {
+ var newX = asteroids[i].x
+ - (delta * asteroids[i].speed * getImgWidth(asteroid));
+
+ drawImage(
+ asteroid,
+ asteroids[i].x,
+ asteroids[i].y,
+ getImgWidth(asteroid),
+ getImgHeight(asteroid)
+ );
+
+ asteroids[i].x = newX;
+ }
+ }
+
+ if (needReplenish) {
+ showScore();
+ replenishAsteroids();
+ }
+
+ // check for game end
+ if (hasCollided) {
+ stop();
+ }
+
+ lastTime = currentTime;
+ }
+
+ function start() {
+ lastTime = 0;
+ x = 30;
+ y = (playArea.height / 2) - (getImgHeight(player) / 2);
+ asteroids = [];
+ asteroidSpeed = 3;
+ asteroidSpeedMax = 4;
+ asteroidNum = 1;
+ asteroidNumMax = 5;
+ controlPressed = null;
+ score = 0;
+ lastTime = (new Date()).getTime();
+
+ stopScreen.setAttribute('data-visible', 'false');
+ running = true;
+
+ showScore();
+ replenishAsteroids();
+
+ gameLoop();
+ }
+
+ function stop() {
+ running = false;
+ stopScreen.setAttribute('data-visible', 'true');
+ finalScore.innerHTML = 'Your final score was ' + score;
+ }
+
+ // track loading of assets and start game when they're ready
+ var assetsLoaded = 0;
+
+ function startWhenAssetsLoaded() {
+ assetsLoaded++;
+ if (assetsLoaded == 2) {
+ start();
+ }
+ }
+
+ player.onload = asteroid.onload = startWhenAssetsLoaded;
+});
diff --git a/space-dodge-game/Crosswalk-6-resize/manifest.json b/space-dodge-game/Crosswalk-6-resize/manifest.json
new file mode 100644
index 0000000..e97cfe5
--- /dev/null
+++ b/space-dodge-game/Crosswalk-6-resize/manifest.json
@@ -0,0 +1,9 @@
+{
+ "name": "space_dodge_game",
+ "version": "0.0.0.1",
+ "app": {
+ "launch": {
+ "local_path": "index.html"
+ }
+ }
+}
diff --git a/space-dodge-game/Crosswalk-6-resize/rocket.png b/space-dodge-game/Crosswalk-6-resize/rocket.png
new file mode 100644
index 0000000..d2e10af
Binary files /dev/null and b/space-dodge-game/Crosswalk-6-resize/rocket.png differ
diff --git a/space-dodge-game/Crosswalk-6-scale/asteroid.png b/space-dodge-game/Crosswalk-6-scale/asteroid.png
new file mode 100644
index 0000000..08989a0
Binary files /dev/null and b/space-dodge-game/Crosswalk-6-scale/asteroid.png differ
diff --git a/space-dodge-game/Crosswalk-6-scale/base.css b/space-dodge-game/Crosswalk-6-scale/base.css
new file mode 100644
index 0000000..7876cd9
--- /dev/null
+++ b/space-dodge-game/Crosswalk-6-scale/base.css
@@ -0,0 +1,73 @@
+* {
+ user-select: none;
+ -webkit-user-select: none;
+ user-drag: none;
+ -webkit-user-drag: none;
+}
+
+body {
+ margin: 0;
+}
+
+#container {
+ position: relative;
+ top: 0;
+ left: 0;
+ width: 750px;
+ height: 450px;
+}
+
+#game-screen, #finish-screen {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: #11F;
+}
+
+#finish-screen {
+ z-index: 100;
+}
+
+#play-area {
+ float: right;
+ width: 80%;
+}
+
+#controls {
+ height: 100%;
+ width: 20%;
+ padding: 5%;
+ text-align: center;
+ box-sizing: border-box;
+ float: left;
+}
+
+.vbox {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+p, button {
+ font-family: sans-serif;
+ font-size: 1.5em;
+}
+
+#control-up, #control-down {
+ margin: 20% 0;
+}
+
+#finish-screen > * {
+ text-align: center;
+}
+
+#score, #final-score {
+ color: white;
+}
+
+[data-visible="false"] {
+ display: none !important;
+}
diff --git a/space-dodge-game/Crosswalk-6-scale/control-down.png b/space-dodge-game/Crosswalk-6-scale/control-down.png
new file mode 100644
index 0000000..5d589f6
Binary files /dev/null and b/space-dodge-game/Crosswalk-6-scale/control-down.png differ
diff --git a/space-dodge-game/Crosswalk-6-scale/control-up.png b/space-dodge-game/Crosswalk-6-scale/control-up.png
new file mode 100644
index 0000000..b9ff6df
Binary files /dev/null and b/space-dodge-game/Crosswalk-6-scale/control-up.png differ
diff --git a/space-dodge-game/Crosswalk-6-scale/index.html b/space-dodge-game/Crosswalk-6-scale/index.html
new file mode 100644
index 0000000..7a3c1ac
--- /dev/null
+++ b/space-dodge-game/Crosswalk-6-scale/index.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+ space dodge game
+
+
+
+
+
+
+
+
+
+
Score
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/space-dodge-game/Crosswalk-6-scale/main.js b/space-dodge-game/Crosswalk-6-scale/main.js
new file mode 100644
index 0000000..000c2ca
--- /dev/null
+++ b/space-dodge-game/Crosswalk-6-scale/main.js
@@ -0,0 +1,356 @@
+document.addEventListener('DOMContentLoaded', function () {
+ if (screen.lockOrientation) {
+ screen.lockOrientation('landscape');
+ }
+
+ // DOM elements in main game area
+ var scoreDisplay = document.querySelector('#score-display');
+ var controlUp = document.querySelector('#control-up');
+ var controlDown = document.querySelector('#control-down');
+ var playArea = document.querySelector('#play-area');
+
+ // DOM elements in stop screen
+ var restart = document.querySelector('#restart');
+ restart.addEventListener('click', start);
+ var finalScore = document.querySelector('#final-score');
+ var stopScreen = document.querySelector('#finish-screen');
+
+ // scale function
+ var scale = function () {
+ var container = document.querySelector('#container');
+ var containerWidth = container.offsetWidth;
+ var containerHeight = container.offsetHeight;
+
+ var viewportWidth = document.documentElement.clientWidth;
+ var viewportHeight = document.documentElement.clientHeight;
+
+ var scaleWidth = viewportWidth / containerWidth;
+ var scaleHeight = viewportHeight / containerHeight;
+ var scaleBoth = (scaleHeight < scaleWidth) ? scaleHeight : scaleWidth;
+
+ var newContainerWidth = containerWidth * scaleBoth;
+ var newContainerHeight = containerHeight * scaleBoth;
+
+ var left = (viewportWidth - newContainerWidth) / 2;
+ left = parseInt(left * (1 / scaleBoth), 10);
+
+ var top = (viewportHeight - newContainerHeight) / 2;
+ top = parseInt(top * (1 / scaleBoth), 10);
+
+ // scale the whole container
+ var transform = 'scale(' + scaleBoth + ',' + scaleBoth + ') ' +
+ 'translate(' + left + 'px, ' + top + 'px)';
+ container.style['-webkit-transform-origin'] = 'top left 0';
+ container.style['-webkit-transform'] = transform;
+ container.style['transform-origin'] = 'top left 0';
+ container.style['transform'] = transform;
+ };
+
+ window.onresize = scale;
+ scale();
+
+ // player image (to draw onto canvas)
+ var player = new Image();
+ player.src = 'rocket.png';
+
+ // asteroid image (to draw onto canvas)
+ var asteroid = new Image();
+ asteroid.src = 'asteroid.png';
+
+ // canvas context
+ var ctx = playArea.getContext('2d');
+
+ // time of current frame
+ var currentTime;
+
+ // time since last frame, in seconds
+ var delta;
+
+ // distance to move on y axis
+ var moveY;
+
+ // previous y position for player
+ var oldY;
+
+ // time of last frame
+ var lastTime;
+
+ // x position of player (fixed)
+ var x;
+
+ // y position of player
+ var y;
+
+ // asteroids
+ var asteroids;
+
+ // base speed of each asteroid when added to screen
+ // (incremented as game progresses);
+ // the actual speed is this plus some small random amount
+ var asteroidSpeed;
+
+ // maximum base speed of asteroids
+ var asteroidSpeedMax;
+
+ // num of asteroids to keep on screen (incremented as game progresses)
+ var asteroidNum;
+
+ // maximum number of asteroids to have on the screen at any time
+ var asteroidNumMax;
+
+ // set to 'up' or 'down' if a control is active;
+ // otherwise, set to null
+ var controlPressed;
+
+ // player score
+ var score;
+
+ // set to true if an asteroid collides with the player; used
+ // to stop the game
+ var hasCollided;
+
+ // do we need to add asteroids?
+ var needsReplenish;
+
+ // set to false if the game isn't running (e.g. on collision)
+ var running;
+
+ // control button event handlers
+ function handleControlOn(e) {
+ if (e.target === controlUp) {
+ controlPressed = 'up';
+ }
+ else if (e.target === controlDown) {
+ controlPressed = 'down';
+ }
+ };
+
+ function handleControlOff() {
+ controlPressed = null;
+ }
+
+ // turn off controls if the touch move event happens outside the
+ // up and down controls
+ function handleTouchMove(e) {
+ e.preventDefault();
+
+ var elt = document.elementFromPoint(
+ e.touches[0].clientX,
+ e.touches[0].clientY
+ );
+
+ if (elt === controlDown) {
+ controlPressed = 'down';
+ }
+ else if (elt === controlUp) {
+ controlPressed = 'up';
+ }
+ else {
+ controlPressed = null;
+ }
+ }
+
+ controlUp.addEventListener('mousedown', handleControlOn);
+ controlDown.addEventListener('mousedown', handleControlOn);
+ controlUp.addEventListener('mouseout', handleControlOff);
+ controlDown.addEventListener('mouseout', handleControlOff);
+ controlUp.addEventListener('mouseup', handleControlOff);
+ controlDown.addEventListener('mouseup', handleControlOff);
+
+ controlUp.addEventListener('touchstart', handleControlOn);
+ controlDown.addEventListener('touchstart', handleControlOn);
+ controlUp.addEventListener('touchend', handleControlOff);
+ controlDown.addEventListener('touchend', handleControlOff);
+ document.addEventListener('touchmove', handleTouchMove);
+
+ // draw an image to the canvas; this rounds off the x,y coordinates
+ // first
+ function drawImage(image, x, y, width, height) {
+ ctx.drawImage(image, x, y, width, height);
+ }
+
+ // asteroidY: starting y position of the asteroid (px)
+ // speed: > 0
+ // the asteroid starts at x = canvas width, so is initially not visible
+ function addAsteroid(asteroidY, speed) {
+ // enough asteroids already
+ if (asteroids.length >= asteroidNumMax) {
+ return;
+ }
+
+ var asteroidX = playArea.width;
+
+ var obj = {
+ x: asteroidX,
+ y: asteroidY,
+ speed: speed,
+ offscreen: false
+ };
+
+ asteroids.push(obj);
+
+ // increment the number of asteroids which can be on screen
+ // at once
+ asteroidNum *= 1 + (0.5 / (asteroidNum * asteroidNum));
+
+ // increment the base speed for asteroids
+ if (asteroidSpeed < asteroidSpeedMax) {
+ asteroidSpeed *= 1.02;
+ }
+ }
+
+ // top up asteroids on screen; the starting speed for the asteroid
+ // is the current asteroidSpeed, plus up to 1.5 extra
+ function replenishAsteroids() {
+ // remove any offscreen asteroids
+ for (var i = 0; i < asteroids.length; i++) {
+ if (asteroids[i].offscreen) {
+ asteroids.splice(i, 1);
+ }
+ }
+
+ // add enough asteroids to fill the quota
+ for (var i = 1; i <= (asteroidNum - asteroids.length); i++) {
+ var asteroidY = (Math.random() * playArea.height) - asteroid.height;
+
+ if (asteroidY < 0) {
+ asteroidY = 0;
+ }
+
+ addAsteroid(asteroidY, asteroidSpeed + (Math.random() * 1.5));
+ }
+ }
+
+ function showScore() {
+ scoreDisplay.innerText = score;
+ }
+
+ // runs on every animation frame
+ function gameLoop() {
+ if (!running) {
+ return;
+ }
+
+ window.requestAnimationFrame(gameLoop);
+
+ // apply movement to player
+ oldY = y;
+
+ currentTime = (new Date()).getTime();
+ delta = (currentTime - lastTime) / 1000;
+
+ // player moves 3 times own height per second
+ moveY = (player.height * delta * 3);
+
+ if (controlPressed === 'up') {
+ y -= moveY;
+ }
+ else if (controlPressed === 'down') {
+ y += moveY;
+ }
+
+ if (y < 0) {
+ y = 0;
+ }
+ else if ((y + player.height) >= playArea.height) {
+ y = playArea.height - player.height;
+ }
+
+ // clear the whole canvas
+ ctx.clearRect(0, 0, playArea.width, playArea.height);
+
+ // draw the player with some jitter
+ drawImage(
+ player,
+ x + (Math.random() - 1) * 2,
+ y + (Math.random() - 1) * 2,
+ player.width,
+ player.height
+ );
+
+ // move asteroids and draw them
+ hasCollided = false;
+
+ // set to true if at least one asteroid goes offscreen
+ needReplenish = false;
+
+ for (var i = 0; i < asteroids.length; i++) {
+ // check whether asteroid has hit player; the numbers subtracted
+ // below give more realistic collisions for the sprite shapes
+ // involved
+ if (!hasCollided) {
+ hasCollided = (x + player.width - 10 >= asteroids[i].x) &&
+ (x <= asteroids[i].x + asteroid.width - 10) &&
+ (y + player.height - 4 >= asteroids[i].y) &&
+ (y <= asteroids[i].y + asteroid.height - 4);
+ }
+
+ // asteroid has left the screen
+ if (asteroids[i].x < (0 - asteroid.width)) {
+ asteroids[i].offscreen = true;
+ score++;
+ needReplenish = true;
+ }
+ // asteroid needs to be drawn
+ else {
+ var newX = asteroids[i].x - (delta * asteroids[i].speed * asteroid.width);
+
+ drawImage(asteroid, asteroids[i].x, asteroids[i].y, asteroid.width, asteroid.height);
+
+ asteroids[i].x = newX;
+ }
+ }
+
+ if (needReplenish) {
+ showScore();
+ replenishAsteroids();
+ }
+
+ // check for game end
+ if (hasCollided) {
+ stop();
+ }
+
+ lastTime = currentTime;
+ }
+
+ function start() {
+ lastTime = 0;
+ x = 30;
+ y = (playArea.height / 2) - (player.height / 2);
+ asteroids = [];
+ asteroidSpeed = 3;
+ asteroidSpeedMax = 4;
+ asteroidNum = 1;
+ asteroidNumMax = 5;
+ controlPressed = null;
+ score = 0;
+ lastTime = (new Date()).getTime();
+
+ stopScreen.setAttribute('data-visible', 'false');
+ running = true;
+
+ showScore();
+ replenishAsteroids();
+
+ gameLoop();
+ }
+
+ function stop() {
+ running = false;
+ stopScreen.setAttribute('data-visible', 'true');
+ finalScore.innerHTML = 'Your final score was ' + score;
+ }
+
+ // track loading of assets and start game when they're ready
+ var assetsLoaded = 0;
+
+ function startWhenAssetsLoaded() {
+ assetsLoaded++;
+ if (assetsLoaded == 2) {
+ start();
+ }
+ }
+
+ player.onload = asteroid.onload = startWhenAssetsLoaded;
+});
diff --git a/space-dodge-game/Crosswalk-6-scale/manifest.json b/space-dodge-game/Crosswalk-6-scale/manifest.json
new file mode 100644
index 0000000..e97cfe5
--- /dev/null
+++ b/space-dodge-game/Crosswalk-6-scale/manifest.json
@@ -0,0 +1,9 @@
+{
+ "name": "space_dodge_game",
+ "version": "0.0.0.1",
+ "app": {
+ "launch": {
+ "local_path": "index.html"
+ }
+ }
+}
diff --git a/space-dodge-game/Crosswalk-6-scale/rocket.png b/space-dodge-game/Crosswalk-6-scale/rocket.png
new file mode 100644
index 0000000..d2e10af
Binary files /dev/null and b/space-dodge-game/Crosswalk-6-scale/rocket.png differ
diff --git a/space-dodge-game/Crosswalk-8-resize/asteroid.png b/space-dodge-game/Crosswalk-8-resize/asteroid.png
new file mode 100644
index 0000000..08989a0
Binary files /dev/null and b/space-dodge-game/Crosswalk-8-resize/asteroid.png differ
diff --git a/space-dodge-game/Crosswalk-8-resize/base.css b/space-dodge-game/Crosswalk-8-resize/base.css
new file mode 100644
index 0000000..f141d82
--- /dev/null
+++ b/space-dodge-game/Crosswalk-8-resize/base.css
@@ -0,0 +1,80 @@
+* {
+ user-select: none;
+ -webkit-user-select: none;
+ user-drag: none;
+ -webkit-user-drag: none;
+}
+
+body {
+ margin: 0;
+}
+
+#container {
+ position: relative;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+}
+
+#game-screen, #finish-screen {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: #11F;
+}
+
+#finish-screen {
+ z-index: 100;
+}
+
+#play-area-container {
+ float: right;
+ width: 80%;
+ height: 100%;
+}
+
+#play-area {
+ position: relative;
+ box-sizing: border-box;
+ border: 2px solid darkblue;
+}
+
+#controls {
+ height: 100%;
+ width: 20%;
+ padding: 0.5em;
+ text-align: center;
+ box-sizing: border-box;
+ float: left;
+}
+
+.vbox {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+p, button {
+ font-family: sans-serif;
+ font-size: 1.5em;
+}
+
+#control-down {
+ margin: 0.5em 0 0 0;
+}
+
+#finish-screen > * {
+ text-align: center;
+}
+
+#score, #final-score {
+ color: white;
+}
+
+[data-visible="false"] {
+ display: none !important;
+}
diff --git a/space-dodge-game/Crosswalk-8-resize/control-down.png b/space-dodge-game/Crosswalk-8-resize/control-down.png
new file mode 100644
index 0000000..5d589f6
Binary files /dev/null and b/space-dodge-game/Crosswalk-8-resize/control-down.png differ
diff --git a/space-dodge-game/Crosswalk-8-resize/control-up.png b/space-dodge-game/Crosswalk-8-resize/control-up.png
new file mode 100644
index 0000000..b9ff6df
Binary files /dev/null and b/space-dodge-game/Crosswalk-8-resize/control-up.png differ
diff --git a/space-dodge-game/Crosswalk-8-resize/fg.png b/space-dodge-game/Crosswalk-8-resize/fg.png
new file mode 100644
index 0000000..e60bb6f
Binary files /dev/null and b/space-dodge-game/Crosswalk-8-resize/fg.png differ
diff --git a/space-dodge-game/Crosswalk-8-resize/index.html b/space-dodge-game/Crosswalk-8-resize/index.html
new file mode 100644
index 0000000..0e09771
--- /dev/null
+++ b/space-dodge-game/Crosswalk-8-resize/index.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+ space dodge game
+
+
+
+
+
+
+
+
+
+
Score
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/space-dodge-game/Crosswalk-8-resize/main.js b/space-dodge-game/Crosswalk-8-resize/main.js
new file mode 100644
index 0000000..27d71ab
--- /dev/null
+++ b/space-dodge-game/Crosswalk-8-resize/main.js
@@ -0,0 +1,371 @@
+document.addEventListener('DOMContentLoaded', function () {
+ // DOM elements in main game area
+ var scoreDisplay = document.querySelector('#score-display');
+ var controlUp = document.querySelector('#control-up');
+ var controlDown = document.querySelector('#control-down');
+ var playArea = document.querySelector('#play-area');
+
+ // factor by which to modify canvas width and height
+ var scaleCanvas = 1;
+
+ var fitCanvas = function () {
+ var container = document.querySelector('#play-area-container');
+ var containerWidth = container.offsetWidth;
+ var containerHeight = container.offsetHeight;
+
+ var playAreaWidth = playArea.width;
+ var playAreaHeight = playArea.height;
+
+ var scaleWidth = containerWidth / playAreaWidth;
+ var scaleHeight = containerHeight / playAreaHeight;
+ scaleCanvas = (scaleHeight < scaleWidth) ? scaleHeight : scaleWidth;
+
+ var newPlayAreaWidth = playAreaWidth * scaleCanvas;
+ var newPlayAreaHeight = playAreaHeight * scaleCanvas;
+
+ var left = (containerWidth - newPlayAreaWidth) / 2;
+ var top = (containerHeight - newPlayAreaHeight) / 2;
+
+ // resize and position the canvas
+ playArea.width = parseInt(newPlayAreaWidth, 10);
+ playArea.height = parseInt(newPlayAreaHeight, 10);
+ playArea.style.top = top + 'px';
+ playArea.style.left = left + 'px';
+ };
+
+ window.onresize = fitCanvas;
+ fitCanvas();
+
+ // DOM elements in stop screen
+ var restart = document.querySelector('#restart');
+ restart.addEventListener('click', start);
+ var finalScore = document.querySelector('#final-score');
+ var stopScreen = document.querySelector('#finish-screen');
+
+ // player image (to draw onto canvas)
+ var player = new Image();
+ player.src = 'rocket.png';
+
+ // asteroid image (to draw onto canvas)
+ var asteroid = new Image();
+ asteroid.src = 'asteroid.png';
+
+ // canvas context
+ var ctx = playArea.getContext('2d');
+
+ // time of current frame
+ var currentTime;
+
+ // time since last frame, in seconds
+ var delta;
+
+ // distance to move on y axis
+ var moveY;
+
+ // previous y position for player
+ var oldY;
+
+ // time of last frame
+ var lastTime;
+
+ // x position of player (fixed)
+ var x;
+
+ // y position of player
+ var y;
+
+ // asteroids
+ var asteroids;
+
+ // base speed of each asteroid when added to screen
+ // (incremented as game progresses);
+ // the actual speed is this plus some small random amount
+ var asteroidSpeed;
+
+ // maximum base speed of asteroids
+ var asteroidSpeedMax;
+
+ // num of asteroids to keep on screen (incremented as game progresses)
+ var asteroidNum;
+
+ // maximum number of asteroids to have on the screen at any time
+ var asteroidNumMax;
+
+ // set to 'up' or 'down' if a control is active;
+ // otherwise, set to null
+ var controlPressed;
+
+ // player score
+ var score;
+
+ // set to true if an asteroid collides with the player; used
+ // to stop the game
+ var hasCollided;
+
+ // do we need to add asteroids?
+ var needsReplenish;
+
+ // set to false if the game isn't running (e.g. on collision)
+ var running;
+
+ // control button event handlers
+ function handleControlOn(e) {
+ if (e.target === controlUp) {
+ controlPressed = 'up';
+ }
+ else if (e.target === controlDown) {
+ controlPressed = 'down';
+ }
+ };
+
+ function handleControlOff() {
+ controlPressed = null;
+ }
+
+ // turn off controls if the touch move event happens outside the
+ // up and down controls
+ function handleTouchMove(e) {
+ e.preventDefault();
+
+ var elt = document.elementFromPoint(
+ e.touches[0].clientX,
+ e.touches[0].clientY
+ );
+
+ if (elt === controlDown) {
+ controlPressed = 'down';
+ }
+ else if (elt === controlUp) {
+ controlPressed = 'up';
+ }
+ else {
+ controlPressed = null;
+ }
+ }
+
+ controlUp.addEventListener('mousedown', handleControlOn);
+ controlDown.addEventListener('mousedown', handleControlOn);
+ controlUp.addEventListener('mouseout', handleControlOff);
+ controlDown.addEventListener('mouseout', handleControlOff);
+ controlUp.addEventListener('mouseup', handleControlOff);
+ controlDown.addEventListener('mouseup', handleControlOff);
+
+ controlUp.addEventListener('touchstart', handleControlOn);
+ controlDown.addEventListener('touchstart', handleControlOn);
+ controlUp.addEventListener('touchend', handleControlOff);
+ controlDown.addEventListener('touchend', handleControlOff);
+ document.addEventListener('touchmove', handleTouchMove);
+
+ // get dimensions of an Image, taking scaleCanvas into account
+ function getImgWidth(img) {
+ return img.width * scaleCanvas;
+ }
+
+ function getImgHeight(img) {
+ return img.height * scaleCanvas;
+ }
+
+ // draw an image to the canvas; this rounds off the x,y coordinates
+ // first
+ function drawImage(image, x, y, width, height) {
+ ctx.drawImage(image, x, y, width, height);
+ }
+
+ // asteroidY: starting y position of the asteroid (px)
+ // speed: > 0
+ // the asteroid starts at x = canvas width, so is initially not visible
+ function addAsteroid(asteroidY, speed) {
+ // enough asteroids already
+ if (asteroids.length >= asteroidNumMax) {
+ return;
+ }
+
+ var asteroidX = playArea.width;
+
+ var obj = {
+ x: asteroidX,
+ y: asteroidY,
+ speed: speed,
+ offscreen: false
+ };
+
+ asteroids.push(obj);
+
+ // increment the number of asteroids which can be on screen
+ // at once
+ asteroidNum *= 1 + (0.5 / (asteroidNum * asteroidNum));
+
+ // increment the base speed for asteroids
+ if (asteroidSpeed < asteroidSpeedMax) {
+ asteroidSpeed *= 1.02;
+ }
+ }
+
+ // top up asteroids on screen; the starting speed for the asteroid
+ // is the current asteroidSpeed, plus up to 1.5 extra
+ function replenishAsteroids() {
+ // remove any offscreen asteroids
+ for (var i = 0; i < asteroids.length; i++) {
+ if (asteroids[i].offscreen) {
+ asteroids.splice(i, 1);
+ }
+ }
+
+ // add enough asteroids to fill the quota
+ for (var i = 1; i <= (asteroidNum - asteroids.length); i++) {
+ var asteroidY = (Math.random() * playArea.height) - getImgHeight(asteroid);
+
+ if (asteroidY < 0) {
+ asteroidY = 0;
+ }
+
+ addAsteroid(asteroidY, asteroidSpeed + (Math.random() * 1.5));
+ }
+ }
+
+ function showScore() {
+ scoreDisplay.innerText = score;
+ }
+
+ // runs on every animation frame
+ function gameLoop() {
+ if (!running) {
+ return;
+ }
+
+ window.requestAnimationFrame(gameLoop);
+
+ // apply movement to player
+ oldY = y;
+
+ currentTime = (new Date()).getTime();
+ delta = (currentTime - lastTime) / 1000;
+
+ // player moves 3 times own height per second
+ moveY = (getImgHeight(player) * delta * 3);
+
+ if (controlPressed === 'up') {
+ y -= moveY;
+ }
+ else if (controlPressed === 'down') {
+ y += moveY;
+ }
+
+ if (y < 0) {
+ y = 0;
+ }
+ else if ((y + getImgHeight(player)) >= playArea.height) {
+ y = playArea.height - getImgHeight(player);
+ }
+
+ // clear the whole canvas
+ ctx.clearRect(0, 0, playArea.width, playArea.height);
+
+ // draw the player with some jitter
+ drawImage(
+ player,
+ x + (Math.random() - 1) * 2,
+ y + (Math.random() - 1) * 2,
+ getImgWidth(player),
+ getImgHeight(player)
+ );
+
+ // move asteroids and draw them
+ hasCollided = false;
+
+ // set to true if at least one asteroid goes offscreen
+ needReplenish = false;
+
+ for (var i = 0; i < asteroids.length; i++) {
+ // check whether asteroid has hit player; the numbers subtracted
+ // below give more realistic collisions for the sprite shapes
+ // involved
+ if (!hasCollided) {
+ hasCollided = (x + getImgWidth(player) - 10 >= asteroids[i].x) &&
+ (x <= asteroids[i].x + getImgWidth(asteroid) - 10) &&
+ (y + getImgHeight(player) - 4 >= asteroids[i].y) &&
+ (y <= asteroids[i].y + getImgHeight(asteroid) - 4);
+ }
+
+ // asteroid has left the screen
+ if (asteroids[i].x < (0 - getImgWidth(asteroid))) {
+ asteroids[i].offscreen = true;
+ score++;
+ needReplenish = true;
+ }
+ // asteroid needs to be drawn
+ else {
+ var newX = asteroids[i].x
+ - (delta * asteroids[i].speed * getImgWidth(asteroid));
+
+ drawImage(
+ asteroid,
+ asteroids[i].x,
+ asteroids[i].y,
+ getImgWidth(asteroid),
+ getImgHeight(asteroid)
+ );
+
+ asteroids[i].x = newX;
+ }
+ }
+
+ if (needReplenish) {
+ showScore();
+ replenishAsteroids();
+ }
+
+ // check for game end
+ if (hasCollided) {
+ stop();
+ }
+
+ lastTime = currentTime;
+ }
+
+ function start() {
+ lastTime = 0;
+ x = 30;
+ y = (playArea.height / 2) - (getImgHeight(player) / 2);
+ asteroids = [];
+ asteroidSpeed = 3;
+ asteroidSpeedMax = 4;
+ asteroidNum = 1;
+ asteroidNumMax = 5;
+ controlPressed = null;
+ score = 0;
+ lastTime = (new Date()).getTime();
+
+ stopScreen.setAttribute('data-visible', 'false');
+ running = true;
+
+ showScore();
+ replenishAsteroids();
+
+ setTimeout(function () {
+ if (screen.show) {
+ screen.show();
+ }
+
+ gameLoop();
+ }, 5000);
+ }
+
+ function stop() {
+ running = false;
+ stopScreen.setAttribute('data-visible', 'true');
+ finalScore.innerHTML = 'Your final score was ' + score;
+ }
+
+ // track loading of assets and start game when they're ready
+ var assetsLoaded = 0;
+
+ function startWhenAssetsLoaded() {
+ assetsLoaded++;
+ if (assetsLoaded == 2) {
+ start();
+ }
+ }
+
+ player.onload = asteroid.onload = startWhenAssetsLoaded;
+});
diff --git a/space-dodge-game/Crosswalk-8-resize/manifest.json b/space-dodge-game/Crosswalk-8-resize/manifest.json
new file mode 100644
index 0000000..b8d313e
--- /dev/null
+++ b/space-dodge-game/Crosswalk-8-resize/manifest.json
@@ -0,0 +1,14 @@
+{
+ "name": "space_dodge_game",
+ "version": "0.0.0.1",
+ "start_url": "index.html",
+ "orientation": "landscape",
+ "display": "fullscreen",
+ "xwalk_launch_screen": {
+ "ready_when": "custom",
+ "landscape": {
+ "background_color": "#11f",
+ "image": "fg.png"
+ }
+ }
+}
diff --git a/space-dodge-game/Crosswalk-8-resize/rocket.png b/space-dodge-game/Crosswalk-8-resize/rocket.png
new file mode 100644
index 0000000..d2e10af
Binary files /dev/null and b/space-dodge-game/Crosswalk-8-resize/rocket.png differ
diff --git a/space-dodge-game/Crosswalk-8-scale/asteroid.png b/space-dodge-game/Crosswalk-8-scale/asteroid.png
new file mode 100644
index 0000000..08989a0
Binary files /dev/null and b/space-dodge-game/Crosswalk-8-scale/asteroid.png differ
diff --git a/space-dodge-game/Crosswalk-8-scale/base.css b/space-dodge-game/Crosswalk-8-scale/base.css
new file mode 100644
index 0000000..7876cd9
--- /dev/null
+++ b/space-dodge-game/Crosswalk-8-scale/base.css
@@ -0,0 +1,73 @@
+* {
+ user-select: none;
+ -webkit-user-select: none;
+ user-drag: none;
+ -webkit-user-drag: none;
+}
+
+body {
+ margin: 0;
+}
+
+#container {
+ position: relative;
+ top: 0;
+ left: 0;
+ width: 750px;
+ height: 450px;
+}
+
+#game-screen, #finish-screen {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: #11F;
+}
+
+#finish-screen {
+ z-index: 100;
+}
+
+#play-area {
+ float: right;
+ width: 80%;
+}
+
+#controls {
+ height: 100%;
+ width: 20%;
+ padding: 5%;
+ text-align: center;
+ box-sizing: border-box;
+ float: left;
+}
+
+.vbox {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+p, button {
+ font-family: sans-serif;
+ font-size: 1.5em;
+}
+
+#control-up, #control-down {
+ margin: 20% 0;
+}
+
+#finish-screen > * {
+ text-align: center;
+}
+
+#score, #final-score {
+ color: white;
+}
+
+[data-visible="false"] {
+ display: none !important;
+}
diff --git a/space-dodge-game/Crosswalk-8-scale/control-down.png b/space-dodge-game/Crosswalk-8-scale/control-down.png
new file mode 100644
index 0000000..5d589f6
Binary files /dev/null and b/space-dodge-game/Crosswalk-8-scale/control-down.png differ
diff --git a/space-dodge-game/Crosswalk-8-scale/control-up.png b/space-dodge-game/Crosswalk-8-scale/control-up.png
new file mode 100644
index 0000000..b9ff6df
Binary files /dev/null and b/space-dodge-game/Crosswalk-8-scale/control-up.png differ
diff --git a/space-dodge-game/Crosswalk-8-scale/fg.png b/space-dodge-game/Crosswalk-8-scale/fg.png
new file mode 100644
index 0000000..e60bb6f
Binary files /dev/null and b/space-dodge-game/Crosswalk-8-scale/fg.png differ
diff --git a/space-dodge-game/Crosswalk-8-scale/index.html b/space-dodge-game/Crosswalk-8-scale/index.html
new file mode 100644
index 0000000..7a3c1ac
--- /dev/null
+++ b/space-dodge-game/Crosswalk-8-scale/index.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+ space dodge game
+
+
+
+
+
+
+
+
+
+
Score
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/space-dodge-game/Crosswalk-8-scale/main.js b/space-dodge-game/Crosswalk-8-scale/main.js
new file mode 100644
index 0000000..74ed23f
--- /dev/null
+++ b/space-dodge-game/Crosswalk-8-scale/main.js
@@ -0,0 +1,358 @@
+document.addEventListener('DOMContentLoaded', function () {
+ // DOM elements in main game area
+ var scoreDisplay = document.querySelector('#score-display');
+ var controlUp = document.querySelector('#control-up');
+ var controlDown = document.querySelector('#control-down');
+ var playArea = document.querySelector('#play-area');
+
+ // DOM elements in stop screen
+ var restart = document.querySelector('#restart');
+ restart.addEventListener('click', start);
+ var finalScore = document.querySelector('#final-score');
+ var stopScreen = document.querySelector('#finish-screen');
+
+ // scale function
+ var scale = function () {
+ var container = document.querySelector('#container');
+ var containerWidth = container.offsetWidth;
+ var containerHeight = container.offsetHeight;
+
+ var viewportWidth = document.documentElement.clientWidth;
+ var viewportHeight = document.documentElement.clientHeight;
+
+ var scaleWidth = viewportWidth / containerWidth;
+ var scaleHeight = viewportHeight / containerHeight;
+ var scaleBoth = (scaleHeight < scaleWidth) ? scaleHeight : scaleWidth;
+
+ var newContainerWidth = containerWidth * scaleBoth;
+ var newContainerHeight = containerHeight * scaleBoth;
+
+ var left = (viewportWidth - newContainerWidth) / 2;
+ left = parseInt(left * (1 / scaleBoth), 10);
+
+ var top = (viewportHeight - newContainerHeight) / 2;
+ top = parseInt(top * (1 / scaleBoth), 10);
+
+ // scale the whole container
+ var transform = 'scale(' + scaleBoth + ',' + scaleBoth + ') ' +
+ 'translate(' + left + 'px, ' + top + 'px)';
+ container.style['-webkit-transform-origin'] = 'top left 0';
+ container.style['-webkit-transform'] = transform;
+ container.style['transform-origin'] = 'top left 0';
+ container.style['transform'] = transform;
+ };
+
+ window.onresize = scale;
+ scale();
+
+ // player image (to draw onto canvas)
+ var player = new Image();
+ player.src = 'rocket.png';
+
+ // asteroid image (to draw onto canvas)
+ var asteroid = new Image();
+ asteroid.src = 'asteroid.png';
+
+ // canvas context
+ var ctx = playArea.getContext('2d');
+
+ // time of current frame
+ var currentTime;
+
+ // time since last frame, in seconds
+ var delta;
+
+ // distance to move on y axis
+ var moveY;
+
+ // previous y position for player
+ var oldY;
+
+ // time of last frame
+ var lastTime;
+
+ // x position of player (fixed)
+ var x;
+
+ // y position of player
+ var y;
+
+ // asteroids
+ var asteroids;
+
+ // base speed of each asteroid when added to screen
+ // (incremented as game progresses);
+ // the actual speed is this plus some small random amount
+ var asteroidSpeed;
+
+ // maximum base speed of asteroids
+ var asteroidSpeedMax;
+
+ // num of asteroids to keep on screen (incremented as game progresses)
+ var asteroidNum;
+
+ // maximum number of asteroids to have on the screen at any time
+ var asteroidNumMax;
+
+ // set to 'up' or 'down' if a control is active;
+ // otherwise, set to null
+ var controlPressed;
+
+ // player score
+ var score;
+
+ // set to true if an asteroid collides with the player; used
+ // to stop the game
+ var hasCollided;
+
+ // do we need to add asteroids?
+ var needsReplenish;
+
+ // set to false if the game isn't running (e.g. on collision)
+ var running;
+
+ // control button event handlers
+ function handleControlOn(e) {
+ if (e.target === controlUp) {
+ controlPressed = 'up';
+ }
+ else if (e.target === controlDown) {
+ controlPressed = 'down';
+ }
+ };
+
+ function handleControlOff() {
+ controlPressed = null;
+ }
+
+ // turn off controls if the touch move event happens outside the
+ // up and down controls
+ function handleTouchMove(e) {
+ e.preventDefault();
+
+ var elt = document.elementFromPoint(
+ e.touches[0].clientX,
+ e.touches[0].clientY
+ );
+
+ if (elt === controlDown) {
+ controlPressed = 'down';
+ }
+ else if (elt === controlUp) {
+ controlPressed = 'up';
+ }
+ else {
+ controlPressed = null;
+ }
+ }
+
+ controlUp.addEventListener('mousedown', handleControlOn);
+ controlDown.addEventListener('mousedown', handleControlOn);
+ controlUp.addEventListener('mouseout', handleControlOff);
+ controlDown.addEventListener('mouseout', handleControlOff);
+ controlUp.addEventListener('mouseup', handleControlOff);
+ controlDown.addEventListener('mouseup', handleControlOff);
+
+ controlUp.addEventListener('touchstart', handleControlOn);
+ controlDown.addEventListener('touchstart', handleControlOn);
+ controlUp.addEventListener('touchend', handleControlOff);
+ controlDown.addEventListener('touchend', handleControlOff);
+ document.addEventListener('touchmove', handleTouchMove);
+
+ // draw an image to the canvas; this rounds off the x,y coordinates
+ // first
+ function drawImage(image, x, y, width, height) {
+ ctx.drawImage(image, x, y, width, height);
+ }
+
+ // asteroidY: starting y position of the asteroid (px)
+ // speed: > 0
+ // the asteroid starts at x = canvas width, so is initially not visible
+ function addAsteroid(asteroidY, speed) {
+ // enough asteroids already
+ if (asteroids.length >= asteroidNumMax) {
+ return;
+ }
+
+ var asteroidX = playArea.width;
+
+ var obj = {
+ x: asteroidX,
+ y: asteroidY,
+ speed: speed,
+ offscreen: false
+ };
+
+ asteroids.push(obj);
+
+ // increment the number of asteroids which can be on screen
+ // at once
+ asteroidNum *= 1 + (0.5 / (asteroidNum * asteroidNum));
+
+ // increment the base speed for asteroids
+ if (asteroidSpeed < asteroidSpeedMax) {
+ asteroidSpeed *= 1.02;
+ }
+ }
+
+ // top up asteroids on screen; the starting speed for the asteroid
+ // is the current asteroidSpeed, plus up to 1.5 extra
+ function replenishAsteroids() {
+ // remove any offscreen asteroids
+ for (var i = 0; i < asteroids.length; i++) {
+ if (asteroids[i].offscreen) {
+ asteroids.splice(i, 1);
+ }
+ }
+
+ // add enough asteroids to fill the quota
+ for (var i = 1; i <= (asteroidNum - asteroids.length); i++) {
+ var asteroidY = (Math.random() * playArea.height) - asteroid.height;
+
+ if (asteroidY < 0) {
+ asteroidY = 0;
+ }
+
+ addAsteroid(asteroidY, asteroidSpeed + (Math.random() * 1.5));
+ }
+ }
+
+ function showScore() {
+ scoreDisplay.innerText = score;
+ }
+
+ // runs on every animation frame
+ function gameLoop() {
+ if (!running) {
+ return;
+ }
+
+ window.requestAnimationFrame(gameLoop);
+
+ // apply movement to player
+ oldY = y;
+
+ currentTime = (new Date()).getTime();
+ delta = (currentTime - lastTime) / 1000;
+
+ // player moves 3 times own height per second
+ moveY = (player.height * delta * 3);
+
+ if (controlPressed === 'up') {
+ y -= moveY;
+ }
+ else if (controlPressed === 'down') {
+ y += moveY;
+ }
+
+ if (y < 0) {
+ y = 0;
+ }
+ else if ((y + player.height) >= playArea.height) {
+ y = playArea.height - player.height;
+ }
+
+ // clear the whole canvas
+ ctx.clearRect(0, 0, playArea.width, playArea.height);
+
+ // draw the player with some jitter
+ drawImage(
+ player,
+ x + (Math.random() - 1) * 2,
+ y + (Math.random() - 1) * 2,
+ player.width,
+ player.height
+ );
+
+ // move asteroids and draw them
+ hasCollided = false;
+
+ // set to true if at least one asteroid goes offscreen
+ needReplenish = false;
+
+ for (var i = 0; i < asteroids.length; i++) {
+ // check whether asteroid has hit player; the numbers subtracted
+ // below give more realistic collisions for the sprite shapes
+ // involved
+ if (!hasCollided) {
+ hasCollided = (x + player.width - 10 >= asteroids[i].x) &&
+ (x <= asteroids[i].x + asteroid.width - 10) &&
+ (y + player.height - 4 >= asteroids[i].y) &&
+ (y <= asteroids[i].y + asteroid.height - 4);
+ }
+
+ // asteroid has left the screen
+ if (asteroids[i].x < (0 - asteroid.width)) {
+ asteroids[i].offscreen = true;
+ score++;
+ needReplenish = true;
+ }
+ // asteroid needs to be drawn
+ else {
+ var newX = asteroids[i].x - (delta * asteroids[i].speed * asteroid.width);
+
+ drawImage(asteroid, asteroids[i].x, asteroids[i].y, asteroid.width, asteroid.height);
+
+ asteroids[i].x = newX;
+ }
+ }
+
+ if (needReplenish) {
+ showScore();
+ replenishAsteroids();
+ }
+
+ // check for game end
+ if (hasCollided) {
+ stop();
+ }
+
+ lastTime = currentTime;
+ }
+
+ function start() {
+ lastTime = 0;
+ x = 30;
+ y = (playArea.height / 2) - (player.height / 2);
+ asteroids = [];
+ asteroidSpeed = 3;
+ asteroidSpeedMax = 4;
+ asteroidNum = 1;
+ asteroidNumMax = 5;
+ controlPressed = null;
+ score = 0;
+ lastTime = (new Date()).getTime();
+
+ stopScreen.setAttribute('data-visible', 'false');
+ running = true;
+
+ showScore();
+ replenishAsteroids();
+
+ setTimeout(function () {
+ if (screen.show) {
+ screen.show();
+ }
+
+ gameLoop();
+ }, 5000);
+ }
+
+ function stop() {
+ running = false;
+ stopScreen.setAttribute('data-visible', 'true');
+ finalScore.innerHTML = 'Your final score was ' + score;
+ }
+
+ // track loading of assets and start game when they're ready
+ var assetsLoaded = 0;
+
+ function startWhenAssetsLoaded() {
+ assetsLoaded++;
+ if (assetsLoaded == 2) {
+ start();
+ }
+ }
+
+ player.onload = asteroid.onload = startWhenAssetsLoaded;
+});
diff --git a/space-dodge-game/Crosswalk-8-scale/manifest.json b/space-dodge-game/Crosswalk-8-scale/manifest.json
new file mode 100644
index 0000000..b8d313e
--- /dev/null
+++ b/space-dodge-game/Crosswalk-8-scale/manifest.json
@@ -0,0 +1,14 @@
+{
+ "name": "space_dodge_game",
+ "version": "0.0.0.1",
+ "start_url": "index.html",
+ "orientation": "landscape",
+ "display": "fullscreen",
+ "xwalk_launch_screen": {
+ "ready_when": "custom",
+ "landscape": {
+ "background_color": "#11f",
+ "image": "fg.png"
+ }
+ }
+}
diff --git a/space-dodge-game/Crosswalk-8-scale/rocket.png b/space-dodge-game/Crosswalk-8-scale/rocket.png
new file mode 100644
index 0000000..d2e10af
Binary files /dev/null and b/space-dodge-game/Crosswalk-8-scale/rocket.png differ
diff --git a/space-dodge-game/README.md b/space-dodge-game/README.md
new file mode 100644
index 0000000..ab112f2
--- /dev/null
+++ b/space-dodge-game/README.md
@@ -0,0 +1,77 @@
+# space dodge game
+
+A simple side-scrolling HTML5 game where you fly a spaceship and you
+dodge stuff. The game is used as a basis for demonstrating various
+techniques for scaling a game to a device screen using Crosswalk.
+
+The tutorial which uses this code is available at:
+http://crosswalk-project.org/#documentation/screens
+
+## Acknowledgements
+
+Spaceship sprite by J.M. Atencia (http://opengameart.org/users/jmatencia)
+from http://opengameart.org/content/rocket
+CC BY 3.0
+
+Asteroid sprite by phaelax (http://opengameart.org/users/phaelax)
+from http://opengameart.org/content/asteroids
+CC BY-SA 3.0
+
+## Code organisation
+
+There are 5 versions of the code.
+
+The master version includes the base code before any optimisations are
+applied.
+
+The four Crosswalk* versions have different code, depending
+on the version of Crosswalk used and the approach used to
+fit the application into the device screen:
+
+* Crosswalk-6-scale
+
+ * landscape orientation set with screen.lockOrientation()
+ * fullscreen set with --fullscreen option to make_apk.py
+ * whole game scaled in CSS to fit screen
+
+* Crosswalk-6-resize
+
+ * landscape orientation set with screen.lockOrientation()
+ * fullscreen set with --fullscreen option to make_apk.py
+ * game elements resized to fit screen
+
+* Crosswalk-8-scale
+
+ * landscape orientation and fullscreen set in manifest
+ * xwalk_launch_screen enabled in manifest
+ * whole game scaled in CSS to fit screen
+
+* Crosswalk-8-resize
+
+ * landscape orientation and fullscreen set in manifest
+ * xwalk_launch_screen enabled in manifest
+ * game elements resized to fit screen
+
+## Android packages
+
+To create an Android package for the game, follow the instructions at:
+https://crosswalk-project.org/#documentation/getting_started/run_on_android
+
+You will need the correct version of Crosswalk for the version of
+the code you intend to package (see above).
+
+(The Crosswalk 6 version should also work for Crosswalk 5 and 7, but
+6 is the version it was tested with.)
+
+When building the Crosswalk 6 versions, you will need to pass the
+`--fullscreen` option for the game to run in fullscreen, e.g.
+
+ python make_apk.py --fullscreen --manifest=Crosswalk-6-scale/manifest.json
+
+ python make_apk.py --fullscreen --manifest=Crosswalk-6-resize/manifest.json
+
+This is not required for the Crosswalk 8 versions, e.g.
+
+ python make_apk.py --manifest=Crosswalk-8-scale/manifest.json
+
+ python make_apk.py --manifest=Crosswalk-8-resize/manifest.json
diff --git a/space-dodge-game/master/asteroid.png b/space-dodge-game/master/asteroid.png
new file mode 100644
index 0000000..08989a0
Binary files /dev/null and b/space-dodge-game/master/asteroid.png differ
diff --git a/space-dodge-game/master/base.css b/space-dodge-game/master/base.css
new file mode 100644
index 0000000..7876cd9
--- /dev/null
+++ b/space-dodge-game/master/base.css
@@ -0,0 +1,73 @@
+* {
+ user-select: none;
+ -webkit-user-select: none;
+ user-drag: none;
+ -webkit-user-drag: none;
+}
+
+body {
+ margin: 0;
+}
+
+#container {
+ position: relative;
+ top: 0;
+ left: 0;
+ width: 750px;
+ height: 450px;
+}
+
+#game-screen, #finish-screen {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: #11F;
+}
+
+#finish-screen {
+ z-index: 100;
+}
+
+#play-area {
+ float: right;
+ width: 80%;
+}
+
+#controls {
+ height: 100%;
+ width: 20%;
+ padding: 5%;
+ text-align: center;
+ box-sizing: border-box;
+ float: left;
+}
+
+.vbox {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+p, button {
+ font-family: sans-serif;
+ font-size: 1.5em;
+}
+
+#control-up, #control-down {
+ margin: 20% 0;
+}
+
+#finish-screen > * {
+ text-align: center;
+}
+
+#score, #final-score {
+ color: white;
+}
+
+[data-visible="false"] {
+ display: none !important;
+}
diff --git a/space-dodge-game/master/control-down.png b/space-dodge-game/master/control-down.png
new file mode 100644
index 0000000..5d589f6
Binary files /dev/null and b/space-dodge-game/master/control-down.png differ
diff --git a/space-dodge-game/master/control-up.png b/space-dodge-game/master/control-up.png
new file mode 100644
index 0000000..b9ff6df
Binary files /dev/null and b/space-dodge-game/master/control-up.png differ
diff --git a/space-dodge-game/master/index.html b/space-dodge-game/master/index.html
new file mode 100644
index 0000000..1efd4a4
--- /dev/null
+++ b/space-dodge-game/master/index.html
@@ -0,0 +1,32 @@
+
+
+
+
+ space dodge game
+
+
+
+
+
+
+
+
+
+
Score
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/space-dodge-game/master/main.js b/space-dodge-game/master/main.js
new file mode 100644
index 0000000..3dce962
--- /dev/null
+++ b/space-dodge-game/master/main.js
@@ -0,0 +1,318 @@
+document.addEventListener('DOMContentLoaded', function () {
+ // DOM elements in main game area
+ var scoreDisplay = document.querySelector('#score-display');
+ var controlUp = document.querySelector('#control-up');
+ var controlDown = document.querySelector('#control-down');
+ var playArea = document.querySelector('#play-area');
+
+ // DOM elements in stop screen
+ var restart = document.querySelector('#restart');
+ restart.addEventListener('click', start);
+ var finalScore = document.querySelector('#final-score');
+ var stopScreen = document.querySelector('#finish-screen');
+
+ // player image (to draw onto canvas)
+ var player = new Image();
+ player.src = 'rocket.png';
+
+ // asteroid image (to draw onto canvas)
+ var asteroid = new Image();
+ asteroid.src = 'asteroid.png';
+
+ // canvas context
+ var ctx = playArea.getContext('2d');
+
+ // time of current frame
+ var currentTime;
+
+ // time since last frame, in seconds
+ var delta;
+
+ // distance to move on y axis
+ var moveY;
+
+ // previous y position for player
+ var oldY;
+
+ // time of last frame
+ var lastTime;
+
+ // x position of player (fixed)
+ var x;
+
+ // y position of player
+ var y;
+
+ // asteroids
+ var asteroids;
+
+ // base speed of each asteroid when added to screen
+ // (incremented as game progresses);
+ // the actual speed is this plus some small random amount
+ var asteroidSpeed;
+
+ // maximum base speed of asteroids
+ var asteroidSpeedMax;
+
+ // num of asteroids to keep on screen (incremented as game progresses)
+ var asteroidNum;
+
+ // maximum number of asteroids to have on the screen at any time
+ var asteroidNumMax;
+
+ // set to 'up' or 'down' if a control is active;
+ // otherwise, set to null
+ var controlPressed;
+
+ // player score
+ var score;
+
+ // set to true if an asteroid collides with the player; used
+ // to stop the game
+ var hasCollided;
+
+ // do we need to add asteroids?
+ var needsReplenish;
+
+ // set to false if the game isn't running (e.g. on collision)
+ var running;
+
+ // control button event handlers
+ function handleControlOn(e) {
+ if (e.target === controlUp) {
+ controlPressed = 'up';
+ }
+ else if (e.target === controlDown) {
+ controlPressed = 'down';
+ }
+ };
+
+ function handleControlOff() {
+ controlPressed = null;
+ }
+
+ // turn off controls if the touch move event happens outside the
+ // up and down controls
+ function handleTouchMove(e) {
+ e.preventDefault();
+
+ var elt = document.elementFromPoint(
+ e.touches[0].clientX,
+ e.touches[0].clientY
+ );
+
+ if (elt === controlDown) {
+ controlPressed = 'down';
+ }
+ else if (elt === controlUp) {
+ controlPressed = 'up';
+ }
+ else {
+ controlPressed = null;
+ }
+ }
+
+ controlUp.addEventListener('mousedown', handleControlOn);
+ controlDown.addEventListener('mousedown', handleControlOn);
+ controlUp.addEventListener('mouseout', handleControlOff);
+ controlDown.addEventListener('mouseout', handleControlOff);
+ controlUp.addEventListener('mouseup', handleControlOff);
+ controlDown.addEventListener('mouseup', handleControlOff);
+
+ controlUp.addEventListener('touchstart', handleControlOn);
+ controlDown.addEventListener('touchstart', handleControlOn);
+ controlUp.addEventListener('touchend', handleControlOff);
+ controlDown.addEventListener('touchend', handleControlOff);
+ document.addEventListener('touchmove', handleTouchMove);
+
+ // draw an image to the canvas; this rounds off the x,y coordinates
+ // first
+ function drawImage(image, x, y, width, height) {
+ ctx.drawImage(image, x, y, width, height);
+ }
+
+ // asteroidY: starting y position of the asteroid (px)
+ // speed: > 0
+ // the asteroid starts at x = canvas width, so is initially not visible
+ function addAsteroid(asteroidY, speed) {
+ // enough asteroids already
+ if (asteroids.length >= asteroidNumMax) {
+ return;
+ }
+
+ var asteroidX = playArea.width;
+
+ var obj = {
+ x: asteroidX,
+ y: asteroidY,
+ speed: speed,
+ offscreen: false
+ };
+
+ asteroids.push(obj);
+
+ // increment the number of asteroids which can be on screen
+ // at once
+ asteroidNum *= 1 + (0.5 / (asteroidNum * asteroidNum));
+
+ // increment the base speed for asteroids
+ if (asteroidSpeed < asteroidSpeedMax) {
+ asteroidSpeed *= 1.02;
+ }
+ }
+
+ // top up asteroids on screen; the starting speed for the asteroid
+ // is the current asteroidSpeed, plus up to 1.5 extra
+ function replenishAsteroids() {
+ // remove any offscreen asteroids
+ for (var i = 0; i < asteroids.length; i++) {
+ if (asteroids[i].offscreen) {
+ asteroids.splice(i, 1);
+ }
+ }
+
+ // add enough asteroids to fill the quota
+ for (var i = 1; i <= (asteroidNum - asteroids.length); i++) {
+ var asteroidY = (Math.random() * playArea.height) - asteroid.height;
+
+ if (asteroidY < 0) {
+ asteroidY = 0;
+ }
+
+ addAsteroid(asteroidY, asteroidSpeed + (Math.random() * 1.5));
+ }
+ }
+
+ function showScore() {
+ scoreDisplay.innerText = score;
+ }
+
+ // runs on every animation frame
+ function gameLoop() {
+ if (!running) {
+ return;
+ }
+
+ window.requestAnimationFrame(gameLoop);
+
+ // apply movement to player
+ oldY = y;
+
+ currentTime = (new Date()).getTime();
+ delta = (currentTime - lastTime) / 1000;
+
+ // player moves 3 times own height per second
+ moveY = (player.height * delta * 3);
+
+ if (controlPressed === 'up') {
+ y -= moveY;
+ }
+ else if (controlPressed === 'down') {
+ y += moveY;
+ }
+
+ if (y < 0) {
+ y = 0;
+ }
+ else if ((y + player.height) >= playArea.height) {
+ y = playArea.height - player.height;
+ }
+
+ // clear the whole canvas
+ ctx.clearRect(0, 0, playArea.width, playArea.height);
+
+ // draw the player with some jitter
+ drawImage(
+ player,
+ x + (Math.random() - 1) * 2,
+ y + (Math.random() - 1) * 2,
+ player.width,
+ player.height
+ );
+
+ // move asteroids and draw them
+ hasCollided = false;
+
+ // set to true if at least one asteroid goes offscreen
+ needReplenish = false;
+
+ for (var i = 0; i < asteroids.length; i++) {
+ // check whether asteroid has hit player; the numbers subtracted
+ // below give more realistic collisions for the sprite shapes
+ // involved
+ if (!hasCollided) {
+ hasCollided = (x + player.width - 10 >= asteroids[i].x) &&
+ (x <= asteroids[i].x + asteroid.width - 10) &&
+ (y + player.height - 4 >= asteroids[i].y) &&
+ (y <= asteroids[i].y + asteroid.height - 4);
+ }
+
+ // asteroid has left the screen
+ if (asteroids[i].x < (0 - asteroid.width)) {
+ asteroids[i].offscreen = true;
+ score++;
+ needReplenish = true;
+ }
+ // asteroid needs to be drawn
+ else {
+ var newX = asteroids[i].x - (delta * asteroids[i].speed * asteroid.width);
+
+ drawImage(asteroid, asteroids[i].x, asteroids[i].y, asteroid.width, asteroid.height);
+
+ asteroids[i].x = newX;
+ }
+ }
+
+ if (needReplenish) {
+ showScore();
+ replenishAsteroids();
+ }
+
+ // check for game end
+ if (hasCollided) {
+ stop();
+ }
+
+ lastTime = currentTime;
+ }
+
+ function start() {
+ lastTime = 0;
+ x = 30;
+ y = (playArea.height / 2) - (player.height / 2);
+ asteroids = [];
+ asteroidSpeed = 3;
+ asteroidSpeedMax = 4;
+ asteroidNum = 1;
+ asteroidNumMax = 5;
+ controlPressed = null;
+ score = 0;
+ lastTime = (new Date()).getTime();
+
+ stopScreen.setAttribute('data-visible', 'false');
+ running = true;
+
+ showScore();
+ replenishAsteroids();
+
+ gameLoop();
+ }
+
+ function stop() {
+ running = false;
+ stopScreen.setAttribute('data-visible', 'true');
+ finalScore.innerHTML = 'Your final score was ' + score;
+ }
+
+ // track loading of assets and start game when they're ready
+ var assetsLoaded = 0;
+
+ function startWhenAssetsLoaded() {
+ assetsLoaded++;
+ if (assetsLoaded == 2) {
+ start();
+ }
+ }
+
+ player.onload = asteroid.onload = startWhenAssetsLoaded;
+});
diff --git a/space-dodge-game/master/manifest.json b/space-dodge-game/master/manifest.json
new file mode 100644
index 0000000..e97cfe5
--- /dev/null
+++ b/space-dodge-game/master/manifest.json
@@ -0,0 +1,9 @@
+{
+ "name": "space_dodge_game",
+ "version": "0.0.0.1",
+ "app": {
+ "launch": {
+ "local_path": "index.html"
+ }
+ }
+}
diff --git a/space-dodge-game/master/rocket.png b/space-dodge-game/master/rocket.png
new file mode 100644
index 0000000..d2e10af
Binary files /dev/null and b/space-dodge-game/master/rocket.png differ