diff --git a/space-dodge-game/Crosswalk-6-resize/asteroid.png b/space-dodge-game/Crosswalk-6-resize/asteroid.png new file mode 100644 index 0000000..08989a0 Binary files /dev/null and b/space-dodge-game/Crosswalk-6-resize/asteroid.png differ diff --git a/space-dodge-game/Crosswalk-6-resize/base.css b/space-dodge-game/Crosswalk-6-resize/base.css new file mode 100644 index 0000000..f141d82 --- /dev/null +++ b/space-dodge-game/Crosswalk-6-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-6-resize/control-down.png b/space-dodge-game/Crosswalk-6-resize/control-down.png new file mode 100644 index 0000000..5d589f6 Binary files /dev/null and b/space-dodge-game/Crosswalk-6-resize/control-down.png differ diff --git a/space-dodge-game/Crosswalk-6-resize/control-up.png b/space-dodge-game/Crosswalk-6-resize/control-up.png new file mode 100644 index 0000000..b9ff6df Binary files /dev/null and b/space-dodge-game/Crosswalk-6-resize/control-up.png differ diff --git a/space-dodge-game/Crosswalk-6-resize/index.html b/space-dodge-game/Crosswalk-6-resize/index.html new file mode 100644 index 0000000..0e09771 --- /dev/null +++ b/space-dodge-game/Crosswalk-6-resize/index.html @@ -0,0 +1,36 @@ + + + + + + space dodge game + + + + + +
+ +
+
+

Score

+ + +
+ +
+ +
+
+ +
+

+ +
+ +
+ + + + + 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