sparkle-engine

sparkle-engine

图片

SPARKLE.JS GAME ENGINE

Sparkle.js is a lightweight and compact game engine based on WebGL for HTML5 with zero dependencies, making game development simple and fun!

^_^ 中文文档

Contents

Quick Start

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";

First Project HelloWorld

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

Second Project: Ping Pong

Use the SPARKLE GAME ENGINE to write a ping pong game. source codelink 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.

What to do next...:

  • Read the source code of the Bird Running demo
  • Read the Tutorial
  • Read the API
  • Or give a star? *w*