Entities, keyboard input, vector2d class, pong-raw example start
This commit is contained in:
parent
000b64f004
commit
7ff89a21b9
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
.idea/
|
||||
.DS_Store
|
||||
dist/
|
||||
node_modules/
|
11
examples/pong-raw/index.html
Normal file
11
examples/pong-raw/index.html
Normal 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
129
examples/pong-raw/pong.js
Normal 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();
|
||||
|
||||
};
|
12
gulpfile.js
12
gulpfile.js
@ -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);
|
||||
|
@ -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
64
src/classes/entity.js
Normal 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
181
src/classes/game.js
Normal 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;
|
140
src/classes/keyboardinput.js
Normal file
140
src/classes/keyboardinput.js
Normal 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
112
src/classes/vector2d.js
Normal 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;
|
16
src/index.js
16
src/index.js
@ -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};
|
Loading…
x
Reference in New Issue
Block a user