A Simple Canvas game engine with native Javascript, doing it the old fashion, education purposes only.
This engine run a timer to drawer on canvas with javascript. With a Detla time, so it work on every CPU with same speed, but lower/Higher FPS.
To use this project, first, download it. After that, create a javascript file, call it what ever you want, and add it in the html file.
// First of all, wait to the document is ready
$(window).on("load", function() {
// Get the canas Element
var canvas = $("#canvas");
var ctx = canvas[0].getContext("2d");
// Init the Engine
engine = new Engine(canvas[0]);
});
Impliment the OnCreate and OnUpdate functions.
// First of all, wait to the document is ready
$(window).on("load", function() {
// Get the canas Element
var canvas = $("#canvas");
var ctx = canvas[0].getContext("2d");
// Init the Engine
engine = new Engine(canvas[0]);
engine.OnCreate = function() {
return true;
};
engine.OnUpdate = function(elapsedTime) {
return true;
};
});
To run the game, on the load event of the window, execute the start function.
// First of all, wait to the document is ready
$(window).on("load", function() {
// Get the canas Element
var canvas = $("#canvas");
var ctx = canvas[0].getContext("2d");
// Init the Engine
engine = new Engine(canvas[0]);
engine.OnCreate = function() {
return true;
};
engine.OnUpdate = function(elapsedTime) {
return true;
};
engine.start();
});
The Engine class has the main function to override, OnCreate and OnUpdate.
This function run once when game start, mainly it used to initilize game objects, or set defaut variables values...etc.
engine.OnCreate = function() {
// Player Positino
PLAYER_SPAWN_X = (engine.screenSize().width / 2) - 65;
PLAYER_SPAWN_Y = engine.screenSize().height - 75;
// Create the runner
player = new GameObject(new Sprite(65, 70), new Position(PLAYER_SPAWN_X, PLAYER_SPAWN_Y));
// Create the running sprtieSheet
spriteSheet = new SpriteSheet('running', 65, 70, 10, 0, 11, 'assets/sprites/man_running.png');
// Create the animation for the runner and include the spritesheet
playerAnimations = new Animation();
playerAnimations.registerAnimation(spriteSheet);
player.registerAnimation(playerAnimations);
// set the current animation to running, because the name of the spriteSheet is running
player.setAnimation('running');
// Create the background as simple sprite only
background = new Sprite(engine.screenSize().width, engine.screenSize().height - 150);
background.loadImage('./assets/sprites/background.jpg');
// Create a camera
camera = new FixedCamera(engine.screenSize().width, engine.screenSize().height, engine.screenSize().width, engine.screenSize().height);
engine.registerGameObject(player);
};
This function run's every frame, this is where the logic of the game is implemented, like calcule, drawing, mouse and keyboard detection.
// From runner demo
engine.OnUpdate = function(elapsedTime) {
// Draw the background sprite
engine.drawer.sprite(background, new Position(0, 0));
// run the move function for the building
buildings.move(elapsedTime);
buildings0.move(elapsedTime);
buildings1.move(elapsedTime);
buildings2.move(elapsedTime);
buildings3.move(elapsedTime);
};
This function is used to add you game objects to the engine, so they will be drawed automatically wihtout you calling the Drawer Class.
engine.OnCreate = function() {
// Create the runner
player = new GameObject(new Sprite(65, 70), new Position(100, 100));
engine.registerGameObject(player);
};
This function return the canvas Size where the game is drawed.
if (buildings1.position.X >= engine.screenSize().width - 10)
buildings1.position.X = -500;
This function return the Mouse position relative to the canvas.
player.position = engine.mousePosition();
This function return if the mouse cursor is on top of a gameobject
if(engine.mouseOnTopOf(box))
box.setAnimation("boxOn");
else
box.setAnimation("boxOff");
This function return a bool value, when a mouse button is clicked.
if (engine.mouseClicked(MouseKeys.LEFT))
engine.drawer.tex("Mouse left button clicked", new Position(100, 100), 10, "Arial");
This function return a bool value if a specific key is clicked
if (engine.keyClicked(Keys.Enter))
return false;
This function will help switch from scene to another, like from the menu to credit, or menu to game level
creditScene = new CreditScene(engine);
engine.registerScene(finalscene);
menuScene = new MenuScene(engine);
engine.registerScene(menuScene);
//Launch the menu scene
engine.goToScene(menuScene);
FPS properties are public, you can display them where every you want. By default the FPS is not calculed. To display the FPS by default, set the DispalyFPS property to true and the CalculeFPS to true. Also you can get the FPS propety as Integer.
engine.displayFPS = true;
engine.calculeFPS = true;
This class used to draw everything to the canvas, like sprites, pictures, text...
This function clean the picturebox with a color of you choice.
engine.drawer.clear();
This function draw sprite object.
sprite(sprite, position, camera = null)
This function draw spritesheet object.
spriteSheet(sprite, position, camera = null)
This function draw gameobjects.
gameObject(gameObject, camera = null)
This function allow you to draw a line between two points.
line(point1, point2, lineWidth = 5, color = 'red', camera = null)
This function allows to create games with world bigger then the canvas Size. For now, there are two Camera to use:
- FixedCamera : this only take a size smaller or equal to the picturebox size, so the game will stay only in the screen, but only the objects in the camera range with be displayed.
//Example
let camera = new FixedCamera(200, 200, grid.Width * grid.resolution, grid.height * grid.resolution);
- WorldCamera : This camera take the size of the canvas and you can move the world around, or follow a player around the game world.
//Example
let camera = new WorldCamera(engine.screenSizeh().width, engine.screenSizeh().height, grid.Width * grid.resolution, grid.height * grid.resolution);
You can create you own camera by inheriting the Camera Class.
class CustomCamera extends Camera
{
constructor(screenWidth, screenHeight, levelWidth, levelHeight, speed = 10) {
super(screenWidth, screenHeight, levelWidth, levelHeight, speed);
}
/**
* Set the position to a gameObject
* @param {GameObject} gameObject
*/
setPositionTo(gameObject) {
this.location = gameObject.position;
this.getOffset()
}
/**
* Get the camera offset to set the world movements
* @returns {Point}
*/
getOffset() {
this.offset = new Point((this.cameraSize.width / 2) - this.location.X, (this.cameraSize.height / 2) - this.location.Y);
this.checkOffset();
return this.offset;
}
/**
* Compard the camera location to the level size, so the camera stay's inside the level
*/
checkOffset() {
if (this.location.X < 0) this.location = new Point(0, this.location.Y);
if (this.location.Y < 0) this.location = new Point(this.location.X, 0);
if (this.location.X > this.layoutSize.width - this.cameraSize.width) this.location = new Point(this.layoutSize.width - this.cameraSize.width, this.location.Y);
if (this.location.Y > this.layoutSize.height - this.cameraSize.height) this.location = new Point(location.X, this.layoutSize.height - this.cameraSize.height);
}
}
You can override some functions to your needs in your Custom Camera.
- SetPositionTo : By default, this function used to follow an object in the game
- getOffset : By default, the offset that can be added to the world to move around, but if you create a new camera, you have to override this function.
- UpdateMaxPosition : By default, this function get the right bottom point of the camera.
You can use this object to draw images to screen. Aslo, GameObject use sprites for it graphic parte, means that you need a sprite to create GameObject. Sprite have only Size and picture file.
engine.OnCreate = function() {
// Create the background as simple sprite only
background = new Sprite(engine.screenSize().width, engine.screenSize().height - 150);
background.loadImage('./assets/sprites/background.jpg');
};
This object is used to draw objects with animation. SpriteSheet is a collection of Sprites arranged in a grid. The Sprites are then compiled into an Animation Clip that will play each Sprite in order to create the animation, much like a flipbook.
engine.OnCreate = function() {
// Create the running sprtieSheet
spriteSheet = new SpriteSheet('running', 65, 70, 10, 0, 11, 'assets/sprites/man_running.png');
};
The difference between Sprite and GameObject is the position, by default GameObeject are objects that will be moving or have any animation. To make them simple, you can define a GameObject and registred to the Engine, so it will be drawed by default, without you set that manually.
engine.OnCreate = function() {
// Player Positino
PLAYER_SPAWN_X = (engine.screenSize().width / 2) - 65;
PLAYER_SPAWN_Y = engine.screenSize().height - 75;
// Create the runner
player = new GameObject(new Sprite(65, 70), new Position(PLAYER_SPAWN_X, PLAYER_SPAWN_Y));
engine.registerGameObject(player);
};
To Hide a registred Gameobject, you can use the Hide and Show functions
playeer.hide();
To check if an object is colliding with another, you can use the function CollisionWith.
let player = new GameObject(playerSprite, new Position(10, 120));
let ball = new GameObject(BallSprite, new Position(50, 120));
if(player.collisionWith(ball)){
//Do something
}
Animation is a collection of SpriteSheets saved with a name. This will help you run different animations according to one or more conditions.
//Character frames
let Stand;
let RunLeft;
let RunRight;
let JumpLeft;
let JumpRight;
engine.OnCreate = function() {
//Init the character animations and frames
Stand = new SpriteSheet("stand", 32, 32, 1, 5, 5, Test.Properties.Resources.charachter);
RunLeft = new SpriteSheet("run_left", 32, 32, 15, 6, 9, Test.Properties.Resources.charachter);
RunRight = new SpriteSheet("run_right", 32, 32, 15, 0, 3, Test.Properties.Resources.charachter);
JumpLeft = new SpriteSheet("jump_left", 32, 32, 5, 10, 10, Test.Properties.Resources.charachter);
JumpRight = new SpriteSheet("jump_right", 32, 32, 5, 4, 4, Test.Properties.Resources.charachter);
//Set the spawn position to 200
PLAYER_SPAWN_Y = 600;
//Create character and set the animation
character = new GameObject(Stand, new Position(100, 100));
character.animations.registerAnimation(Stand);
character.animations.registerAnimation(RunLeft);
character.animations.registerAnimation(RunRight);
character.animations.registerAnimation(JumpLeft);
character.animations.registerAnimation(JumpRight);
//Add character to the platform
engine.registerGameObject(character);
//Default animation
character.SetAnimation("stand");
}
engine.OnUpdate = function(elapsedTime) {
if (!character.onGround)
{
if (character.velocity.X < 0)
character.setAnimation("jump_left");
else
character.setAnimation("jump_right");
}
else
{
if (character.velocity.X < 0)
character.setAnimation("run_left");
else if(character.velocity.X > 0)
character.setAnimation("run_right");
else
character.setAnimation("stand");
}
return true;
}
A scene it's like a smaller engine peace, it has the two important functions like the engine, OnCreate and OnUpdate. You put your logic there and the engine will execute scene by scene, when the previous is done. You can use the scene to create a Menu scene, a game scene and credit scene, and put condition to end a scene and the engine will switch to the next one.
By default, there is a default scene in an empty engine instance, use it to create your objects, so you can use the layers.
To create a scene, you create a class and inherit from Scene class, and override the OnCreate and OnUpdate function.
class MenuScene extends Scene
{
constructor(engine) {
super('MenuScene', engine);
}
/**
* OnCreate function run's Once
* @returns {boolean}
*/
OnCreate() { return true; }
/**
* Run every second
* @param {float} elapsedTime
* @returns {boolean}
*/
OnUpdate(elapsedTime) { return true; }
/**
* Run's once after the end of the scene
* @returns {boolean}
*/
OnDestroy() { return true; }
}
To end the scene, call the Ended function in your OnUpdate function (mainly).
class MenuScene extends Scene
{
constructor(engine) {
super('MenuScene', engine);
}
public override bool OnCreate()
{
return base.OnCreate();
}
public override bool OnUpdate(double ElapsedTime)
{
}
/**
* OnCreate function run's Once
* @returns {boolean}
*/
OnCreate() { return true; }
/**
* Run every second
* @param {float} elapsedTime
* @returns {boolean}
*/
OnUpdate(elapsedTime) {
engine.drawer.String("This is a menu", new Position(100, 100));
engine.drawer.String("Clique Space to go out of menu", new Position(100, 100));
if (engine.KeyClicked(Keys.Space))
{
this.ended();
}
return true;
}
/**
* Run's once after the end of the scene
* @returns {boolean}
*/
OnDestroy() { return true; }
}
To get sprite with z-order, you can use layer, by default there is a layer with z-order equal to 0. The layer has two public properties, z-order and Name.
This function is used to add you game objects to the layer, so they will be drawed automatically wihtout you calling the Drawer Class.
//From pingpong project
OnCreate() {
playerSprite = new Sprite(30, 100);
playerSprite.LoadFromFile('bar.png');
player = new GameObject(playerSprite, new Position(10, 120));
//Add layer
layer = new Layer("top");
//Add object to the layer
layer.registerGameObject(player);
this.scenes[0].registerLayer(layer);
return true;
}
For now, there are one demo, runnig man, this engine is for education purposes only.