A light-weight, un-opinionated game engine/library based on HTML5 Canvas game development.
Built along the course CSC 481/591 Game Engine Development, @NC State, 2017.
npm install --save zion
import zion from 'zion';
To build a game, whatever platform it's based on, we can think of the whole game as a (infinite) state machine. The flow is all about updating state and rendering the canvas in a constant interval (game loop).
The more you decouple between the state updating logic and the game rendering, the easier your game is to maintain and scale.
import zion from 'zion';
const { Game } = zion.createComponents();
// in your entry file you'll have the main class extend the base class `Game`
class MyGame extends Game {
constructor() {
this.gameloop = this.gameloop.bind(this);
}
update() {
}
render() {
}
init() {
// run update and render consecutively, in a constant interval
const timer = setInterval(this.gameLoop, TIME_INTERVAL);
}
}
To conform to the Object-Oriented Paradigm, a few base classes are provided for extension.
const { Sprite } = zion.createComponents();
class SnakeSegment extends Sprite {
// ...
}
Game
The game entry class. We suggest you extend it in your main game file.Sprite
The basic build block which are integrated into a larger scene.SpriteSheet
A bitmap image class that contains several smaller graphics in a tiled grid arrangement. Used in animations.Particle
andParticleSystem
See Particle Systems.Obstacle
An extendable collision-detection system. See Physics.
Zion provides a few rendering utilities on top of the native canvas API, like clearCanvas()
, coordinateConversion()
, insertText()
, etc, which save you from diving too deep into the fuss.
Specifically, for image rendering, Zion uses the canvas-image-cache, which enhances the performance in a way that image instances will not be recreated from scratch for each rendering after being loaded at first.
const cu = zion.createCanvasUtils();
cu.clearCanvas(canvas, context);
The basic APIs below:
clearCanvas(canvas, context)
coordinateConversion(canvas, x, y)
: get the coordination with respect to the canvas boundariesgetBoundaries({ x, y }, size)
generateRandomPosition(canvas, middle = false, spriteSize)
createImageCache()
: create the canvas-image-cache utilitydrawRotate(context, { img, x, y, degrees })
: draw rotate spritesinsertText(context, options = {})
: insert text into canvas
Zion provides a basic physics engine for sprite-sprite collisions called Obstacle
. Unlike traditional bounding-box based systems, the engine sub-divides each obstacle into a pre-set number of low, medium, or high boundary levels. This implementation eliminiates the need for entire sprite collision checks as the system can more finely detect intersections at specific sprite edges.
Obstacles can be managed in groups or individually, depending on your game's structure:
// Create a new obstacle. The last parameter specifies the divisionType for creating the different
// boundary levels (0 - 2: Low, Medium, High).
let obstacle = new Obstacle(src, {width: 16, height: 16}, {x: 0, y: 0}, 0);
Collision between obstacles and sprites is not strictly enforced. You may choose to apply collision through calling the getCollision()
method from any obstacle. In this manner, obstacles and sprites that should not have collision are ignored.
let car = new Sprite('car.png', size, position);
/**
* objOffset specifies how close the sprite being examined should be allowed to come within
* the obstacle's range. boundsOffset extends the base distance at which it collides with
* the obstacle.
*/
if(obstacle.getCollision(car, objOffset, boundsOffset)) {
// do something
}
Zion provides additional support for debugging collision:
getDetails()
Returns a string representation of this Obstacle's properties (size, position, divisionType, boundaries)drawBoundariesDebug()
Draws red squares indicating the on-screen location of an Obstacle's boundaries (use in yourdraw()
function).
Zion supports an extendable particle system for grouped-sprite management. For visual effects, Zion provides two functions for easy object creation: createUniformParticles()
and createRandomizedParticles()
. The former allows simple creation of uniform particle sets, providing an efficient solution for multiple like-objects. The latter allows for randomized particle set creation, suitable for varied graphical effects and unevenly distributed objects (e.g. varying sprite sizes, speeds, etc.). All particles extend our Obstacle Physics System, requiring no additional collision detection.
You can create a new Particle System by supplying a Particle's desired properties:
// Create two new ParticleSystems: one uniform, one random
let boulders = new ParticleSystem();
let fire = new ParticleSystem();
// Supply properties for the uniform system
boulders.createUniformParticles(src, size, position, speed, numParticles, divisionType);
// Specify options and create random particles
let properties = {
src: 'fire.png',
size: {width: 10, height: 10},
maxHorizontal: 30,
maxVertical: 40,
speed: 5,
divisionType: 0
};
fire.createRandomizedParticles(options);
Zion provides a wrapper for managing audio assets. The AudioManager
class allows you to create, load, and play game audio. This class can be easily extended to support more advanced game features:
const audioMgr = zion.createAudioManager('/src/'); // absolute path from root folder
audioMgr.loadAudio({
collision: 'collision.mp3' // file name under the folder
});
audioMgr.findByName('collision').play();
Zion uses keybus for keyboard handler, which supports simultaneous keydown events essential in direction control.
// pass in the DOM element to which all the events are binded
const kb = zion.createKeybus(document)
// simple keydown event
let token = kb.down(13, () => console.log('Enter keydown!'))
or multi-key handlers
kb.simulDown(38, () => console.log('up'))
kb.simulDown(39, () => console.log('right'))
function game_loop() {
// the only thing you need to do is to call this method in every game loop,
// the keybus will automatically check if anykey is pressed and run the according handlers (could be more than one)
kb.executeMultiKeyHandlers();
}
token.remove()
Supports basic drag and drop utilities:
getDraggingItemIndex()
isCollapsed()
: dragging collision detection
Zion uses Express.js
and Socket.io
to host online multi-player games. This event-based communication between client and server makes it easy to figure out the data flow.
// in server
const app = require('express')();
const http = require('http').Server(app);
const zion = require('zion');
const network = zion.createIO(http);
network.listen({
'event-A': fnA.bind(fnA, params),
'event-B': fnB.bind(fnB, params)
});
With most game AI, gameplay will revolve around player movement and interaction. Zion provides a fairly robust AI system, including steering behaviors, A* path-finding, and path-following. All player-centered functionality is provided by the AI
class:
let params = {
velocity: {0,0},
accleration: {0,0},
maxForce: 0.1,
maxSpeed: 2,
maxAcceleration: 3,
timeToTarget: 0.8
};
let character = new AI(src, size, position, params);
For movement, AI
provides the following extendable behaviors:
seek(target)
: seek the given target's position.flee(target)
: flee from the given target's position.arrive(target)
: stop at the given target's position. The AI will slow and approach the target based on the initialized Radius of Satisfaction (ROS) and Radius of Deceleration (ROD)follow(path, target)
: follow the given path to reach the intended target.
For more information, you may check out an overview of each behavior's description here
AStar
complements AI
by providing path-finding for any given weighted graph. The default hueristic utilizes Manhattan distance to find the optimal path for character movement:
let aStar = new AStar();
let graph = [{x: 0, y: 0}, {x: 1, y: 1}, {x: 1, y: 3}, {x: 2, y: 4}, {x: 3, y: 2}];
let shortestPath = AStar.AStarPathFind(graph, graph[0], graph[3]);
Zion also provides a lightweight Decision Tree system for managing character states. Each decision tree is based on a system of DecisionNodes
that constitute one of two action types: CheckMethod
and ActionMethod
. CheckMethod
nodes determine the next node to traverse. These include any type of inquiries about a character's state or relationship to the game environment. ActionMethod
nodes are the result of a single or sequence of CheckMethod
nodes. These nodes change a character's state. A Decision Tree can be built as follows:
function defineChecks() {
let checkDistance = (character) => { ... }; // check character's distance to some game object
let checkState = (character) => { ... }; // check the AI's state
return { checkDistance, checkState };
}
function defineActions() {
let nextState = (player, currState) { ... }; // transition character states
let changeAppearance(state) { ... }; // change the character's appearance based on their current state
return { nextStatet, changeAppearance };
}
// Store new checks and actions
let checks = defineChecks;
let actions = defineActions;
// Establish tree nodes...
let root = new DecisionNode(checks.checkDistance);
let leftChild = new DecisionNode(checks.checkState);
let leftAction = new DecisionNode(actions.nextState);
let rightChild = new DecisionNode(actions.changeAppearance);
// ...and parent them
leftChild.setTrueNode(leftAction);
root.setTrueNode(leftAction);
root.setFalseNode(rightChild);
To execute the tree, call makeDecision()
for the appropriate character. This should generally be done in update()
as you will want to alter player states before rendering:
update() {
// Run our decision tree
root.makeDecision();
}
In game development you will propably use some of the calculation / operation utilities that are provided by Zion:
const _ = zion.createUtils();
_.getRandomInt(1, 6); // range from 1 to 6
-
getRandomInt(min, max);
-
removeMultiElementFromArray(arr, ...indexes);
-
getDistance(x1, y1, x2, y2);
-
calculateCenter(x, y, width, height);
: x and y is the left top origin of the box. -
removeMultiElement
Check out the games below on the gallery page of Zion:
Get the general idea and best practice with games built along with Zion:
- Alchemy Game (drag n drop)
- Local Snake Game (Basic rendering)
- Mutilplayer Online Snake Game (Network)
- Asteroids (Particle System, collision, etc)
- Cops and Robbers (AI)
All PRs, issues and bug report are welcomed. Here are the steps to contribute:
- Fork and clone this repo.
- Make some changes.
- Commit, push the changes to your forked repo.
- Compare and make a pull request.