Zion
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.
Table of Contents
Get Started
npm install --save zion-engine
;
Features
Game Flow
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.
; const Game = zion;// in your entry file you'll have the main class extend the base class `Game` { thisgameloop = thisgameloop; } { } { } { // run update and render consecutively, in a constant interval const timer = ; }
Base Classes
To conform to the Object-Oriented Paradigm, a few base classes are provided for extension.
const Sprite = zion; // ...
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.
Basic Rendering
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; cu;
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
Physics
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 = 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 = '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. */ifobstacle // 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).
Particle Systems
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 = ; let fire = ; // Supply properties for the uniform system boulders; // 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;
I/O
Keyboard Handler
Zion uses KeyBus
as its keyboard handling component. It supports basic keyboard event, and wraps up a hash-based simultaneous multikey handler functionality(Why?).
Basic Usage:
const kb = document let enterKeydownHandler = kb // to remove the keydown event handler for a specific key and evententerKeydownHandler
Multikey handler:
const canvas = documentconst kb = canvas kbkb { // 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;}
Audio Manager
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; // absolute path from root folder audioMgr;audioMgr;
Keyboard Input and Output
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 bindedconst kb = zion // simple keydown eventlet token = kb
or multi-key handlers
kbkb { // 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;} token
Drag and Drop
Supports basic drag and drop utilities:
getDraggingItemIndex()
isCollapsed()
: dragging collision detection
Network
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 serverconst app = ;const http = ;const zion = ; const network = zion; network;
AI
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: 00 accleration: 00 maxForce: 01 maxSpeed: 2 maxAcceleration: 3 timeToTarget: 08;let character = 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 = ;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;
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:
{ let { ... }; // check character's distance to some game object let { ... }; // check the AI's state return checkDistance checkState ;} { let nextState = player currState ... ; // transition character states let { ... }; // change the character's appearance based on their current state return nextStatet changeAppearance ;}// Store new checks and actionslet checks = defineChecks;let actions = defineActions;// Establish tree nodes...let root = checkscheckDistance;let leftChild = checkscheckState;let leftAction = actionsnextState;let rightChild = actionschangeAppearance;// ...and parent themleftChild;root;root;
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:
{ // Run our decision tree root;}
Appendix: Function Utils
In game development you will propably use some of the calculation / operation utilities that are provided by Zion:
const _ = zion; _; // 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
Gallery Page
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)
Contribution
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.