Awesome
Blackbeard
Blackbeard is a very simple but powerful game engine for HTML Canvas, to make games in the browser.
Two somewhat complex examples that use Blackbeard are an interactive traffic thing and the Muskets and Bayonets web game. These two examples use slightly older and modified versions of the engine, but they are close enough to use as reference.
Put blackbeard.js
and link to it in <head>
, like so:
<!DOCTYPE html>
<html>
<head>
...boilerplate
<script src="/path/to/blackbeard.js"></script>
</head>
<body>
...site here
</body>
</html>
Blackbeard is roughly 100 lines so there should be no worries about size. For this reason, no CDN link is provided. You can do it, I believe in you.
Documentation
Initializing
In the <body>
, add your javascript (<script>
tag, inline or with src
, either is fine).
You can put the following to initialize a Blackbeard Canvas.
let canvas = new Canvas([width, height], id);
Where width
and height
are integers, and id
is a string that will be the id of the HTML element, like so:
let canvas = new Canvas([1200, 700], "game-canvas");
This will create the <canvas>
HTML element for you. Adding styling to the element is fine, but be careful. Adding padding directly will result in click coordinates being wrong. Instead, wrap in a <div>
and apply the padding to the <div>
.
By default, the <canvas>
will be created as a direct child of the <body>
element, but you can change that by passing in a third parameter (an HTMLElement
that the <canvas>
should be a child of):
let canvas = new Canvas([1200, 700], "game-canvas", document.getElementById("game-div"));
If you do this, make sure the <script>
tag is at the very bottom. If you have HTML elements (such as your intended parent element) after the <script>
tag, your script will not be able to see them.
Updating the Canvas
Now, you want to call the canvas.update()
function whenever you want the canvas to be updated. Typically, you want this done automatically every x milliseconds.
For example, if you want 12 frames (updates) per second, you can add a setInterval
function that runs a update every 1000/12
milliseconds (1000 milliseconds is 1 second):
setInterval(function() {
canvas.update();
}, 1000/12);
Keep in mind setInterval
and is not precise to the millisecond.
12 is a pretty arbitrary number, and may not work for all purposes. For animations, you may want a higher number, around 24 maybe?
If you set it too high, you may experience performance problems.
Adding Components
Great! Now you should have a blank, probably white (or whatever the background color is) canvas. Now what?
Add some components! There are many prewritten components under examples/
that you can use, but you probably want to write your own.
If you do write your own, it is suggested to be familiar with the Canvas API. This MDN page has links to good resources.
Let's write a Blackbeard component together.
A Blackbeard component is a class with at least two functions: constructor
(mandatory for javascript classes), and update
(called every time canvas.update()
is called).
To practice, we will be writing a very simple rectangle component.
Here's the outline:
class Rectangle {
constructor() {
//
}
update() {
//
}
}
Now let's see how to register the component with the canvas:
class Rectangle {
constructor(canvas) {
this.canvas = canvas;
this.canvas.components.push(this);
}
update() {
//
}
}
this.canvas.components
is the array of components. push
adds the component to the end of the array. You can use splice instead to put the component at any index.
The components are drawn in order. For example, if the first element of the this.canvas.components
array is a rectangle at a position, and the second is a triangle at the same position, the triangle will be on top of the rectangle.
And accept some parameters for upper left hand corner coord, fill color, stroke color, line_width, size.
class Rectangle {
constructor(canvas, coord, size, fill_color, stroke_color) {
//coord is [x, y]
this.coord = coord;
this.width = size[0];
this.height = size[1];
this.fill_color = fill_color;
this.stroke_color = stroke_color;
this.canvas = canvas;
this.canvas.components.push(this);
}
update() {
//
}
}
We also want to have the update
function actually do something when called. Specifically, we want it to draw a rectangle. Shocker.
class Rectangle {
constructor(canvas, coord, size, fill_color, stroke_color, line_width) {
//coord is [x, y]
this.coord = coord;
this.width = size[0];
this.height = size[1];
this.fill_color = fill_color;
this.stroke_color = stroke_color;
this.line_width = line_width
this.canvas = canvas;
this.canvas.components.push(this);
}
update() {
this.canvas.context.beginPath();
this.canvas.context.rect(this.coord[0], this.coord[1], this.width, this.height);
if (this.fill_color) {
this.canvas.context.fillStyle = this.fill_color;
this.canvas.context.fill();
}
if (this.stroke_color) {
this.canvas.context.lineWidth = this.lineWidth;
this.canvas.context.strokeStyle = this.stroke_color;
this.canvas.context.stroke();
}
}
}
And that's it. Hopefully simple enough.
You can now create a rectangle component.
new Rectangle(canvas, [50, 100], [75, 60], "green", "black", 3);
Beautiful!
Events
Blackbeard also takes full advantage of HTML events. Components can respond to various events quite easily.
Let's modify our previous rectangle component to change color to blue when clicked.
This will require two changes.
First, registering the click
event with the Blackbeard canvas, in the constructor
.
this.canvas.addEvent("click", [this]);
Simple, now we need to add a click
function to our class, to handle the event.
class Rectangle {
...
click(e) {
//check to see if within bounds
if ((e.offsetX >= this.coord[0] && e.offsetX < this.coord[0]+this.width) && (e.offsetY >= this.coord[1] && e.offsetY < this.coord[1]+this.height)) {
this.fill_color = "blue";
}
}
...
}
Full code:
class Rectangle {
constructor(canvas, coord, size, fill_color, stroke_color, line_width) {
//coord is [x, y]
this.coord = coord;
this.width = size[0];
this.height = size[1];
this.fill_color = fill_color;
this.stroke_color = stroke_color;
this.line_width = line_width
this.canvas = canvas;
this.canvas.addEvent("click", [this]);
this.canvas.components.push(this);
}
click(e) {
//check to see if within bounds
if ((e.offsetX >= this.coord[0] && e.offsetX < this.coord[0]+this.width) && (e.offsetY >= this.coord[1] && e.offsetY < this.coord[1]+this.height)) {
this.fill_color = "blue";
}
}
update() {
this.canvas.context.beginPath();
this.canvas.context.rect(this.coord[0], this.coord[1], this.width, this.height);
if (this.fill_color) {
this.canvas.context.fillStyle = this.fill_color;
this.canvas.context.fill();
}
if (this.stroke_color) {
this.canvas.context.lineWidth = this.lineWidth;
this.canvas.context.strokeStyle = this.stroke_color;
this.canvas.context.stroke();
}
}
}
Works great!
Functions
This section documents Blackbeard's the Canvas
class functions.
constructor
Create a Blackbeard Canvas class.
Example:
new Canvas([500, 500], "game-canvas");
Parameters:
- size (number[]): width, height
- id (string): html id of
<canvas>
element - parent (HTMLElement | undefined): if
undefined
, will be the child of the<body>
, if element passed in, will be child of that element - contextOptions (object | undefined): See MDN
update
Call to redraw Canvas, and call all components' update
function.
Example:
setInterval(function() {
canvas.update();
}, 1000/12);
Parameters: none
clear
Clear canvas (blank).
Example:
canvas.clear();
Parameters: none
reset
Remove all components and all event listeners.
Example:
canvas.reset();
Parameters: none
addEvent
Subscribe components to a HTML event (custom events work too).
Example:
//as part of a component's `constructor`
this.canvas.addEvent("click", [this], false);
Parameters:
- event (string): Name of event
- objects: Array of components that will subscribe to events
- overwrite (bool, default=false): Replace existing components
clearDeadEvents
Remove event subscription for components which no longer exist.
Example:
canvas.clearDeadEvents();
Parameters: none