Entities, keyboard input, vector2d class, pong-raw example start

This commit is contained in:
Nathan Kunicki 2015-12-21 01:18:04 +00:00
parent 000b64f004
commit 7ff89a21b9
10 changed files with 669 additions and 8 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.idea/
.DS_Store
dist/
node_modules/

View File

@ -0,0 +1,11 @@
<!doctype html>
<html>
<head>
<title>Pong - MomentumEngine</title>
<script type="application/javascript" src="../../dist/es5.js"></script>
<script type="application/javascript" src="./pong.js"></script>
</head>
<body>
<canvas id="game"></canvas>
</body>
</html>

129
examples/pong-raw/pong.js Normal file
View File

@ -0,0 +1,129 @@
"use strict";
window.onload = function () {
var pong = new MomentumEngine.Game({
canvas: document.getElementById("game"),
width: 640,
height: 360,
fixRatio: true,
desiredFps: 60,
inputs: {
keyboard: true
}
});
var multiply = pong.width / 640;
// All of these are instances of MomentumEngine.Entity;
var mainScene = pong.createChildEntity(),
ball = mainScene.createChildEntity(),
leftPaddle = mainScene.createChildEntity(),
rightPaddle = mainScene.createChildEntity();
// Wipe the screen
pong.render = function () {
pong.context.fillStyle = "rgba(0, 0, 0, 1)";
pong.context.fillRect(0, 0, pong.width, pong.height);
return true;
};
// Update and render the ball
ball.state.pos = new MomentumEngine.Vector2D(pong.width / 2, pong.height / 2); // Current ball position;
ball.state.width = multiply * 10;
ball.state.height = multiply * 10;
ball.state.vector = new MomentumEngine.Vector2D(0.1, 0.05); // Current ball vector
ball.update = function () {
this.state.pos.add(this.state.vector.clone().multiply(pong.lastFrameDelta));
if ((this.state.pos.x + 5 > pong.width) || (this.state.pos.x - 5 < 0)) {
//this.state.vector.multiply(1.1);
this.state.vector.x = -this.state.vector.x;
}
if ((this.state.pos.y + 5 > pong.height) || (this.state.pos.y - 5 < 0)) {
//this.state.vector.multiply(1.1);
this.state.vector.y = -this.state.vector.y;
}
};
ball.render = function () {
pong.context.fillStyle = "rgba(255, 255, 255, 1)";
pong.context.fillRect(ball.state.pos.x - (ball.state.width / 2), ball.state.pos.y - (ball.state.height / 2), ball.state.width, ball.state.height);
};
// Update and render the left paddle
leftPaddle.state.left = multiply * 10;
leftPaddle.state.top = multiply * 10;
leftPaddle.state.width = multiply * 10;
leftPaddle.state.height = multiply * 70;
leftPaddle.update = function () {
if (pong.inputs.keyboard.isPressed(pong.consts.keys.CHAR_Q) || pong.inputs.keyboard.isPressed(pong.consts.keys.UP)) {
leftPaddle.state.top -= (0.5 * pong.lastFrameDelta);
}
if (pong.inputs.keyboard.isPressed(pong.consts.keys.CHAR_A) || pong.inputs.keyboard.isPressed(pong.consts.keys.DOWN)) {
leftPaddle.state.top += (0.5 * pong.lastFrameDelta);
}
if (leftPaddle.state.top > pong.height - (multiply * 80)) {
leftPaddle.state.top = pong.height - (multiply * 80);
}
if (leftPaddle.state.top < (multiply * 10)) {
leftPaddle.state.top = (multiply * 10);
}
};
leftPaddle.render = function () {
pong.context.fillStyle = "rgba(255, 255, 255, 1)";
pong.context.fillRect(leftPaddle.state.left, leftPaddle.state.top, leftPaddle.state.width, leftPaddle.state.height);
};
// Render the right paddle
rightPaddle.state.left = pong.width - (multiply * 10 * 2);
rightPaddle.state.top = multiply * 10;
rightPaddle.state.width = multiply * 10;
rightPaddle.state.height = multiply * 70;
rightPaddle.update = function () {
if (pong.inputs.keyboard.isPressed(pong.consts.keys.CHAR_O)) {
rightPaddle.state.top -= (0.5 * pong.lastFrameDelta);
}
if (pong.inputs.keyboard.isPressed(pong.consts.keys.CHAR_L)) {
rightPaddle.state.top += (0.5 * pong.lastFrameDelta);
}
if (rightPaddle.state.top > pong.height - (multiply * 80)) {
rightPaddle.state.top = pong.height - (multiply * 80);
}
if (rightPaddle.state.top < (multiply * 10)) {
rightPaddle.state.top = (multiply * 10);
}
};
rightPaddle.render = function () {
pong.context.fillStyle = "rgba(255, 255, 255, 1)";
pong.context.fillRect(rightPaddle.state.left, rightPaddle.state.top, rightPaddle.state.width, rightPaddle.state.height);
};
pong.start();
};

View File

@ -8,7 +8,7 @@ var gulp = require("gulp"),
var build = function (options, callback) {
var plugins = [];
let plugins = [];
if (options.minify) {
plugins = [
@ -32,7 +32,7 @@ var build = function (options, callback) {
plugins: plugins,
output: {
path: path.join(__dirname, "dist"),
filename: "index.js"
filename: "es5.js"
},
module: {
loaders: [{
@ -40,14 +40,18 @@ var build = function (options, callback) {
test: /\.js$/,
include: [
path.join(__dirname, "src")
]
],
query: {
plugins: ["transform-runtime"],
presets: ["es2015", "stage-0"]
}
}]
}
}, (error, stats) => {
if (error) {
var pluginError = new gutil.PluginError("webpack", error);
let pluginError = new gutil.PluginError("webpack", error);
if (callback) {
callback(pluginError);

View File

@ -2,7 +2,7 @@
"name": "momentumengine",
"version": "0.0.1",
"description": "An ES6 game and animation engine.",
"main": "dist/index.js",
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
@ -14,8 +14,13 @@
"devDependencies": {
"babel-core": "6.3.17",
"babel-loader": "6.2.0",
"babel-plugin-transform-runtime": "6.3.13",
"babel-preset-es2015": "6.3.13",
"babel-preset-stage-0": "6.3.13",
"babel-runtime": "6.3.19",
"gulp": "3.9.0",
"gulp-util": "3.0.7",
"webpack": "1.12.9"
}
},
"dependencies": {}
}

64
src/classes/entity.js Normal file
View File

@ -0,0 +1,64 @@
"use strict";
class Entity {
constructor () {
this.state = {};
this.children = [];
}
createChildEntity () {
let child = new Entity();
this.children.push(child);
return child;
}
addChildEntity (entity) {
this.children.push(entity);
return entity;
}
detachChildEntity (entity) {
// Not implemented
}
_updateEntity () {
if ((this.update && this.update()) || (typeof this.update === "undefined")) {
this.children.forEach((child) => {
child._updateEntity();
});
}
}
_renderEntity () {
if ((this.render && this.render()) || (typeof this.render === "undefined")) {
this.children.forEach((child) => {
child._renderEntity();
});
}
}
}
export default Entity;

181
src/classes/game.js Normal file
View File

@ -0,0 +1,181 @@
"use strict";
import Entity from "./entity.js";
import KeyboardInput, {keyConsts} from "./keyboardinput.js";
class Game extends Entity {
constructor (config) {
super(); // Call entity constructor
config = config || {};
config.inputs = config.inputs || {};
// Required params
if (config.canvas) {
this.canvas = config.canvas;
} else {
throw new Error("MomentumEngine.Game must be constructed with a canvas");
}
if (config.width) {
this.width = config.width;
} else {
throw new Error("MomentumEngine.Game must be constructed with canvas width");
}
if (config.height) {
this.height = config.height;
} else {
throw new Error("MomentumEngine.Game must be constructed with canvas height");
}
// Optional params
this.desiredFps = config.desiredFps || 60;
if (config.fixRatio) {
let deviceRatio = window.devicePixelRatio,
backingStoreRatio = 0;
// Unfortunately Ejecta requires calling getContext last, so we cannot get the backingStorePixelRatio. So for Ejecta's case, we set it to 1, and call getContext later.
if (typeof ejecta !== "undefined") {
backingStoreRatio = 1;
} else {
this.context = this.canvas.getContext("2d");
backingStoreRatio = this.context.webkitBackingStorePixelRatio ||
this.context.mozBackingStorePixelRatio ||
this.context.msBackingStorePixelRatio ||
this.context.oBackingStorePixelRatio ||
this.context.backingStorePixelRatio || 1;
}
this.scale = deviceRatio / backingStoreRatio;
this.canvas.width = this.width * this.scale;
this.canvas.height = this.height * this.scale;
this.canvas.style.width = this.width + "px";
this.canvas.style.height = this.height + "px";
// Call getContext last for Ejecta only.
if (typeof ejecta !== "undefined") {
this.context = this.canvas.getContext("2d");
}
this.context.scale(deviceRatio, deviceRatio);
} else {
this.canvas.width = this.width;
this.canvas.height = this.height;
this.context = this.canvas.getContext("2d");
}
if (typeof this.context.imageSmoothingEnabled !== "undefined") {
this.context.imageSmoothingEnabled = config.imageSmoothing || false;
}
// Initialize defaults
this.lastFrameDelta = 0;
this.frameCounter = 0;
this.consts = {
keys: keyConsts
};
this.inputs = {};
if (config.inputs.keyboard) {
this.inputs.keyboard = new KeyboardInput(this);
}
this._lastFrameTimestamp = 0;
this._wantPause = true;
}
start () {
var self = this; // NK: Hate doing this...better way plz?
if (self._wantPause) {
self._wantPause = false;
} else {
console.log("MomentumEngine.Game.start called, game instance is already started");
return false; // Game is already running
}
self._wantPause = false;
var requestFrame = (function () {
return (window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / self.desiredFps);
});
})();
self._lastFrameTimestamp = +(new Date());
var loop = function () {
self.frameCounter++;
let currentTimestamp = +(new Date());
self.lastFrameDelta = currentTimestamp - self._lastFrameTimestamp;
self._lastFrameTimestamp = currentTimestamp;
if (self.lastFrameDelta > (1000 / self.desiredFps)) {
self.lastFrameDelta = (1000 / self.desiredFps);
}
if (self._wantPause) {
return;
}
self._updateEntity.bind(self);
self._updateEntity();
self._renderEntity.bind(self);
self._renderEntity();
requestFrame(loop);
};
loop();
return true;
}
pause () {
if (!this._wantPause) {
this._wantPause = true;
return true;
} else {
console.log("MomentumEngine.Game.pause called, game instance is already paused");
return false;
}
}
}
export default Game;

View File

@ -0,0 +1,140 @@
"use strict";
const keyConsts = {
SPACE: 32,
BACKSPACE: 8,
TAB: 9,
ENTER: 13,
SHIFT: 16,
CTRL: 17,
ALT: 18,
PAUSE: 19,
CAPS_LOCK: 20,
ESCAPE: 27,
PAGE_UP: 33,
PAGE_DOWN: 34,
END: 35,
HOME: 36,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
INSERT: 45,
DELETE: 46,
NUM_0: 48,
NUM_1: 49,
NUM_2: 50,
NUM_3: 51,
NUM_4: 52,
NUM_5: 53,
NUM_6: 54,
NUM_7: 55,
NUM_8: 56,
NUM_9: 57,
CHAR_A: 65,
CHAR_B: 66,
CHAR_C: 67,
CHAR_D: 68,
CHAR_E: 69,
CHAR_F: 70,
CHAR_G: 71,
CHAR_H: 72,
CHAR_I: 73,
CHAR_J: 74,
CHAR_K: 75,
CHAR_L: 76,
CHAR_M: 77,
CHAR_N: 78,
CHAR_O: 79,
CHAR_P: 80,
CHAR_Q: 81,
CHAR_R: 82,
CHAR_S: 83,
CHAR_T: 84,
CHAR_U: 85,
CHAR_V: 86,
CHAR_W: 87,
CHAR_X: 88,
CHAR_Y: 89,
CHAR_Z: 90,
NUM_PAD_0: 96,
NUM_PAD_1: 97,
NUM_PAD_2: 98,
NUM_PAD_3: 99,
NUM_PAD_4: 100,
NUM_PAD_5: 101,
NUM_PAD_6: 102,
NUM_PAD_7: 103,
NUM_PAD_8: 104,
NUM_PAD_9: 105,
MULTIPLY: 106,
ADD: 107,
SUBTRACT: 109,
DECIMAL: 110,
DIVIDE: 111,
F1: 112,
F2: 113,
F3: 114,
F4: 115,
F5: 116,
F6: 117,
F7: 118,
F8: 119,
F9: 120,
F10: 121,
F11: 122,
F12: 123,
SEMICOLON: 186,
EQUALS: 187,
COMMA: 188,
DASH: 189,
PERIOD: 190,
FORWARD_SLASH: 191,
GRAVE: 192,
OPEN_BRACKET: 219,
BACK_SLASH: 220,
CLOSE_BRACKET: 221,
SINGLE_QUOTE: 222
};
class KeyboardInput {
constructor () {
var self = this;
self._keyState = {};
window.addEventListener("keydown", (event) => {
self._keyDownHandler(event);
}, false);
window.addEventListener("keyup", (event) => {
self._keyUpHandler(event);
}, false);
}
isPressed (keyCode) {
return !!this._keyState[keyCode];
}
_keyDownHandler (event) {
this._keyState[event.keyCode] = true;
}
_keyUpHandler (event) {
this._keyState[event.keyCode] = false;
}
}
export default KeyboardInput;
export {keyConsts};

112
src/classes/vector2d.js Normal file
View File

@ -0,0 +1,112 @@
"use strict";
class Vector2D {
constructor (x, y) {
this.x = x || 0; this.y = y || 0;
}
invert () {
this.x = -this.x; this.y = -this.y;
return this;
}
add (val) {
if (val instanceof Vector2D) {
this.x += val.x; this.y += val.y;
} else {
this.x += val; this.y += val;
}
return this;
}
subtract (val) {
if (val instanceof Vector2D) {
this.x -= val.x; this.y -= val.y;
} else {
this.x -= val; this.y -= val;
}
return this;
}
multiply (val) {
if (val instanceof Vector2D) {
this.x *= val.x; this.y *= val.y;
} else {
this.x *= val; this.y *= val;
}
return this;
}
divide (val) {
if (val instanceof Vector2D) {
this.x /= val.x; this.y /= val.y;
} else {
this.x /= val; this.y /= val;
}
return this;
}
equals (val) {
return (this.x == val.x && this.y == val.y);
}
dot (val) {
return this.x * val.x + this.y * val.y;
}
length () {
return Math.sqrt(this.dot(this));
}
unit () {
return this.divide(this.length());
}
min () {
return Math.min(this.x, this.y);
}
max () {
return Math.max(this.x, this.y);
}
toArray () {
return [this.x, this.y];
}
clone () {
return new Vector2D(this.x, this.y);
}
}
export default Vector2D;

View File

@ -1 +1,15 @@
console.log("Hello world!");
"use strict";
import Game from "./classes/game.js";
import Entity from "./classes/entity.js";
import Vector2D from "./classes/vector2d.js";
window.MomentumEngine = {
Game: Game,
Entity: Entity,
Vector2D: Vector2D
};
export {Game};
export {Entity};
export {Vector2D};