Getting started
Importing engine
Install quixotic.min.js
In a module js file, import QuixoticEngine
which is exported as default
Initialize engine
const game = {
create: {
display: {
canvas: document.querySelector("#canvas")
},
homeURL: "/js/quixotic"
},
style: {
backgroundColor: "#111122"
},
world: {},
engine: {
frameRate: 1000/60,
update: function(deltaTime, engine){},
render: function(display){}
},
setup: function(engine, display, controller, world){}
};
game.create
game.create
takes care of creating the game, it requirs 2 values:
1: display(Object), the display object requirs a canvas key, which value should be a canvas element.
other keys:
key | type | value | default |
---|---|---|---|
width | Number | A number for the canvas width . | min of width and height |
height | Number | A number for the canvas height. | min of width and height |
zAxis | Number | A number for where the camera z coordinates will be. | 6 |
2: homeURL(String), a url to where the index.js of the engine is located.
game.style
game.style
takes care of styling the canvas, style object must be initialized but may be empty.
other keys:
key | type | value |
---|---|---|
stroke | Object | See table below |
backgroundColor | Quixotic.color | A quixotic color for the stroke |
backgroundImage | String | Link to image location |
stroke
key | type | value | required |
---|---|---|---|
width | Number | Stroke width | true |
color | Quixotic.color | A quixotic color for the stroke | true |
game.engine
game.engine
is where the fun stuff are, it requirs 3 values:
1: frameRate(Number), the game frameRate. The game will update as fast as it can, but will only render under the frameRate.
2: Update(Function), the upate function. This function will get called as fast as possible. Use this to update non-engine related stuff as you wish. All Quixotic.gameEntities
will get updated automatically.
3: Render(Function), the render function. This function will get called as fast as possible under the frameRate. Use this to render non-engine related stuff as you wish. All Quixotic.gameEntities
will get rendered automatically.
Update Passed arguments
argument | type | value |
---|---|---|
deltaTime | Number | time passed in meli-seconds |
engine | Engine | The engine instance |
Render Passed arguments
argument | type | value |
---|---|---|
display | Display | The Display instance used by engine |
game.setup
game.setup
is where you setup your game, 4 arguments are being passed down to this function:
Setup Passed arguments
engine, display, controller, world
argument | type | value |
---|---|---|
engine | Engine | The engine instance |
display | Display | The Display instance used by engine |
controller | Controller | The Controller instance used by engine |
world | Object | An Object containing the created world |
starting engine:
Now that we created the initial object, now we actually create the engine.
Quixotic.create(game.create)
.then(engine => {
engine.setup({
style: game.style,
world: game.world,
engine:game.engine,
setup: game.setup,
});
});
Creating objects
There is no game with no objects, so lets create a player.
game.world.objects = [
{
name: "player",
}
];
Now technically the player object have been created, but it’s invisible. Lets fill it with a color.
Extending Entity Class
You might wanna extend the Entity class for 2 reasons, collision detection or having custom methods.
import {Entity} from "/js/quixotic/index.js"
class Player extends Entity {
constructor(){
super();
//Must always call super
}
}
game.world = {
classes: {
Player
},
objects: [
{
name: "player",
class: "Player"
}
],
};
Coloring objects
We color objects in our setup function.
game.setup = function(engine, display, controller, world){
world.objects.player.fill("#ff00000")
};
Attaching images to objects
We attach images to objects also in the setup function.
game.setup = function(engine, display, controller, world){
//world.objects.player.fill("#ff00000")
const marioTexture = display.createTexture("mario.png");
world.objects.player.attachImage(marioTexture);
};
You can use the same texture for as many objects as you like, you can also create copies of the texture for different transformations.
import {Texture} from "/quixotic/index.js"
//...
game.setup = function(engine, display, controller, world){
const marioTexture = display.createTexture("mario.png");
const textureCopy = Texture.from(marioTexture)
world.objects.player.attachImage(textureCopy);
};
Creating multiple objects
Say you want to create 500 trees, creating each one manually is gonna be a living hell. You can simply set the array
key to true.
const treeObjects = {
name: "trees",
array: true,
amount: 10,
};
Positioning objects
The coordinate system in the engine is like looking at a graph, (0, 0) will be the center of the screen, (1, 1) being towrd top right, (-1, -1) being bottom left.
Positioning 1 object
set:
playerObject.position = {
type: "set",
position: [-5, 5]
};
random:
playerObject.position = {
type: "random",
rangeX: [-30, 30],
rangeY: [-30, 30]
};
Positioning multiple objects
treeObjects.position = {
type: "set",
positions: [ [-10,10], [-15,10], [-5,10], [0,10], [5,10], [10,10], [15,10], [20,10], [25,10], [30,10]]
};
Controller
Methods
name | description | arguments | passed arguments |
---|---|---|---|
attachKeyDown | Attach a function to a key, gets called when that key is pressed down | Key name(String), function(Function) | None |
attachKeyUp | Attach a function to a key, gets called when that key is realased | Key name(String), function(Function) | None |
attachMouseButton | Attach a function to a button, gets called when that button is pressed | X, Y, W, H(Number), function(Function) | x(Number), y(Number) |
detachKeyUp | detach key up | Key name | |
detachKeyDown | detach key down | Key name |
Examples
game.setup = function(engine, display, controller, world){
const player = world.objects.player;
const marioTexture = display.createTexture("mario.png");
const lugiTexture = display.createTexture("lugi.png");
player.attachImage(marioTexture);
controller.attachKeyUp("1", function(){
player.attachImage(marioTexture);
});
controller.attachKeyUp("2", function(){
player.attachImage(lugiTexture);
});
controller.attachKeyDown(" ", function(){ //space
player.accelerate(0, 20); //make mario move up
});
const canvas = display.canvas;
controller.attachMouseButton(0, 0, canvas.width, canvas.height, function(x, y){ //0, 0 being top left
if(x > canvas.width/2){ //if pressed on the right side of the screen
player.accelerate(20, 0); //move right
} else {
player.accelerate(-20, 0); //else move left
}
});
};
Collision
Adding collision to your object was made super simple, first you need to make sure that the objects you create is an instance that extends the Entity class. In this example we would be using the Player
class which we created earlier, and a Tree
class we will create now.Tree
class Player extends Entity {
constructor(){
super();
//Must always call super
}
touchedTree(tree){
tree.remove();
//Entity Method wich will remove the object from the game
}
}
class Tree extends Entity {}
game.world = {
classes: {
Player,
Tree
},
objects: [
{
name: "player",
class: "Player",
collision: {
trees: {
call: "touchedTree",
elastic: true //elastic indicates if it can't go through that object or not
},
}
},
{
name: "trees",
class: "Tree",
array: true,
amount: 20,
position: {
type: "random",
rangeX: [-20, 20],
rangeY: [-20, 20],
}
}
],
};
Tile Maps
I know that some people might think that tile maps are an old style, but with this engine the tile map is only used to position the objects, meaning you can move objects as you wish later on.
How it works
The way it works is it takes 5 keys
Example
Example using Player, Stone, Tree
classes:
game.world = {
objects: [
{
name: "stones",
type: "class",
class: "Stone",
array: true,
amount: 0,
collision: {
player: {
call: "touchedPlayer",
elastic: true
},
}
},
{
name: "trees",
type: "class",
class: "Tree",
array: true,
amount: 0,
collision: {
player: {
call: "touchedPlayer",
elastic: true
},
}
},
{
name: "player",
type: "class",
class: "Player",
},
],
classes: {
Tree,
Stone,
Player,
},
tileMap : {
columns: 5,
tileSize: 5,
startCoords: [0, 0],
values: [
Grass,
Tree,
Stone],
tiles: [
[0, 1], 1, 1, 1, 1,
1, 0, 0, 0, 1,
1, 2, 2, 1, 1,
1, 0, 1, 0, 1,
1, 1, 1, 1, 1,
]
}
}
In this example, the Tree
class is the same one we been using, and Stone
class is the exact same but with a different sprite.
As you might have notices the first value of the tiles array is an array as well that is becaue it’s a list of objects on the same position in an order from bottom to top. In this case its a tree tile ontop of a grass tile.
Display
TODO:
Game
TODO: