Sparkle.js is a lightweight and compact game engine based on WebGL for HTML5 with zero dependencies, making game development simple and fun!
Installation
npm i sparkle-engine
Or use unpkg
<script src="https://unpkg.com/sparkle-engine/dist/sparkle.umd.cjs"></script>
Import
import { SparkleEngine } from "sparkle-engine";
Write a HelloWorld using SPARKLE GAME ENGINE
<canvas id="game"></canvas>
const engine = new SparkleEngine({
// Specify the game canvas element
canvas: document.getElementById("game"),
})
Then create a scene and switch to that scene
class MainScene extends Scene {
preload(){
// Load resources here, but this HelloWorld project does not need to load resources
// So no need to write any code
}
create(){
const text = new Text({
text: "Hello World!",
font: "40px Arial"
})
return text
}
}
// Switch scene
engine.changeToScene(MainScene)
Then you will see a 'Hello World' on the screen
Use the SPARKLE GAME ENGINE to write a ping pong game. source code ,link to play the project online This project requires an audio resource. You can download it here, or use your own.
First, create an engine instance:
<canvas id="game"></canvas>
const engine = new SparkleEngine({
// Specify the game canvas element
canvas: document.getElementById("game"),
backgroundColor: Color.fromHex("#FFFFCC"),
width: 600,
height: 300
})
Then write a Scene. This scene has an additional preload
method compared to the scene in Helloworld. Load the resources that need to be preloaded in the preload
method. When all the resources in the preload method are loaded, the node returned by the create
method of the scene will be used as the root node of the scene. Resources can be obtained using engine.getAssets("jump")
.
class MainScene extends Scene {
preload(){
engine.loader.baseUrl = "."
// Load a resource
// "Jump" is the resource ID, and "jump. mp3" is the path to the resource
engine.resource.loadAudio("jump","jump.mp3")
}
create() {
// Create a root node
const root = new Container()
return root
}
}
// Switch to the target scene
engine.changeToScene(MainScene)
Next, create a board for the table tennis game, which receives a position as a coordinate. Graphical can be used to display graphics (polygons, circles, squares, etc.), and then create a Collision
, As a component. In Sparklejs, a component is also a node, for example, Collision
and Timer
are both components
For more details, please review node
class Board extends Graphical {
constructor(position) {
super({
type: GraphicalType.RECT,
// A rectangle
rect: new Rect(0, 0, 20, 80),
// Whether to fill this shape
fill: true,
// The color of the shape
color: Color.fromHex("#003300"),
position, // Two boards are needed, so the position is not fixed, let the user set it
offset: new Vector2(0, 40),// Offset
})
this.addChild(
// Add this collision child node
new Collision({
shape: Collision.rectShape(0, 0, 20, 80), // The shape of the collision
offset: new Vector2(0, 40),
tags: ["board"] // A tag, can be used to find or judge nodes
})
)
}
onUpdate(dt) {
// Set your own coordinates to the mouse coordinates every frame
this.position.y = this.getMouseGlobalPositon().y
}
}
Tags are a very useful thing that can simplify a lot of work. For more information, please see Tag Search
Then instantiate two boards and add them to the main scene
class MainScene extends Scene {
preload(){
engine.loader.baseUrl = "."
engine.resource.loadAudio("jump","jump.mp3")
}
create() {
const root = new Container()
root.addChild(new Panel(new Vector2(30, 150)))
root.addChild(new Panel(new Vector2(560, 150)))
return root
}
}
Then open and see the effect, you can use Ctrl+B
to open the debug mode, you can see the collision body, and the center coordinates, etc., next create a ball
class Ball extends Graphical {
constructor() {
super({
// Create a shape
type: GraphicalType.CIRCLE,
radius: 20, // Radius
fill: true,
color: Color.fromHex("#808080"),
position: new Vector2(300, 150),
})
this.speed = 300
// Create a direction vector, the direction of the ball
this.direction = Vector2.fromAngle(1)
// Create a collision body, you can also use the inherited method to create a collision body
this.collision = new Collision({
// Shape
shape: Collision.rectShape(0, 0, 40, 40),
// Offset
offset: new Vector2(20, 20),
})
// Add this collision body
this.addChild(
this.collision
)
}
// Game restart
reStart() {
// Reset direction
this.direction = Vector2.fromAngle(1)
// Reset coordinates
this.position.set(300, 150)
}
onUpdate(dt) {
// Coordinate acceleration, the true behind scale represents creating a new value is a new vector that scales this vector
// Because scaling should not change direction but create a new one
this.position.add(this.direction.scale(dt * this.speed, true))
// Hit the upper wall and lower wall
if (this.position.y + 20 > 300 || this.position.y - 20 < 0) {
// Reverse the speed of the y direction
this.direction.y = -this.direction.y
}
// Check if it has hit the left and right boundaries
if (this.position.x > 600 || this.position.x < 0) {
// Restart
this.reStart()
}
}
}
onUpdate
is a function that will be called every frame, onReady
is called when this node is ready and its child nodes are also ready. For details, please see Lifecycle
The two-dimensional vector operation function generally has a create
parameter behind it, which means whether to create a new vector or modify the original vector, and then add the ball to the main scene
class MainScene extends Scene {
//...
create() {
const root = new Container()
root.addChild(new Panel(new Vector2(30, 150)))
root.addChild(new Panel(new Vector2(560, 150)))
// new
root.addChild(new Ball())
return root
}
}
Now you should be able to see a bouncing ball, but it won’t bounce off when it hits the board. Next, write the logic of hitting the board
Note: It is implemented using SAT
collision, so it only supports convex polygons
class Ball extends Graphical {
constructor() {
// ...
this.collision.onBodyEnter = (res) => {
// `res` returns the collision object encountered and overlay
const body = res.body
if (body.tag.has("board")) { // Determine if it is a board
// Obtain the difference in coordinates with the board
const rebound = this.globalPosition.sub(body.globalPosition,true)
// Set direction
this.direction.direction = rebound.direction
// Play Sound
engine.getAssets("jump").play()
}
}
}
}
engine.getAssets("jump")
is used to get resources, onBodyEnter
will be called when a physical body enters the ball, but you can also use events to listen. Now you can see the ball bounce off the board.
Next, add a score
class scoreText extends Text{
constructor(){
// Inherits from text
super({
position: new Vector2(300, 0),
text: "0", // Initially display a 0
font: "32px Arial", // Font
color: Color.black(), // Color
anchor: TextAnchor.CENTER, // Text in the center
tags: ['scoreText'] // A tag for easy access by other nodes
})
// Score
this.score = 0
}
addScore(){
this.score++
// To modify the text, you can directly set the text property
this.text = this.score.toString()
}
reStart(){
this.score = 0
this.text = "0"
}
}
You might be curious why the writing style of scoreText and collision is a bit different, both are possible, for details see nodes, then add scoreText to the main scene, and add points when it collides with the board, restart when the ball runs off the screen
class Ball extends Graphical {
constructor() {
// ...
this.collision.onBodyEnter = (res) => {
const body = res.body
if (body.tag.has("board")) {
this.scoreText.addScore() // Add this
// ...
}
}
// ...
}
onReady() {
this.scoreText = engine.root.findByTag("scoreText")
}
reStart() {
// ...
this.scoreText.reStart() // Add this
}
}
class MainScene extends Scene {
//...
create() {
//...
root.addChild(new scoreText())
return root
}
}
Alright, now you should be able to run this game. If you cannot run or encounter problems, you can check the source code.