
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
game-reactor
Advanced tools
█████ █████
▄▄▄██ ██▄▄▄
▀▀▀██ ██▀▀▀
█████ █████
DEVELOPMENT
The Game Reactor is a Game Development SDK that is used to create small sized games that run on a web browser. It utilizes ReactJS and organizes game development by abstracting developers from the inner complexities of managing assets and web component UI behaviours from them.
The latest built library can be found in https://www.npmjs.com/package/game-reactor
npm i game-reactor
Use storybook to preview the GameComponent in action.
npm run storybook
To start creating your own web game, create a new class that extends the Game class like so.
import { Game, GameLogLevels } from 'game-reactor/dist';
class DemoGame extends Game {
constructor() {
super({
name: 'My demo game',
logLevel: GameLogLevels.debug,
viewport: {
showCollisions: false,
showPerfStats: false,
fps: 24,
width: 360,
height: 270,
bgColor: 'red',
},
}, {
someFlag: true,
clickCount: 0
})
}
onReady() { }
onDisengaged() { }
onUpdate(timeDelta: number) {
this.Elements.update(this, timeDelta);
}
onDraw(timeDelta: number, sysPerf: any) {
this.Eiewport.clear();
this.Elements.redraw(this, timeDelta)
}
}
The abstract Game class provides the basis for creating a new Game object. You need to provide it with some initial information about your game thru the main constructor.
You must also implement the 4 main methods
The react GameComponent provides the HTMLCanvasElement that the Game instance will utilize. In your tsx file, you cn utilize it like so
import { GameComponent } from 'game-reactor/dist';
export default function MyGamePage() {
const dg = new DemoGame();
return (
<div>
<h1>My game using Game-Reactor</h1>
<GameComponent id="demoGame" game={dg} />
</div>
)
}
The Gamecomponent props are as follows
A GameElement is an object within the Game world. Be it a playable character, NPC, house, chest, whatever you can think of.
class MyGameElement extends GameElement {
constructor(game: Game) {
super(game, {
name: 'my-element',
sprite: 'my-elemnt-sprite',
pos: {
x: 20,
y: 20,
},
},{
someState: 1
});
}
onUpdate(game: Game, timeDelta: number) {
//do nothing for now...
}
onDraw(game: Game, timeDelta: number) {
this.Game.Viewport.drawElement(this);
}
}
Your GameElements must extend the GameElement class and define itself in the base constructor.
The GameElement also has 2 abstract methods you must implement.
Game.Elements.update()
is triggered (mostly in the Game's onUpdate method).
Game.Elements.redraw()
is triggered (mostly in the Game's onDraw method).
Here is a simple Hello World based on the details above. Create a react component that uses our gameComponent and Game class like so. Note: Right now it will do nothing sensible.
import { Game, GameComponent } from 'game-reactor/dist';
class DemoGame extends Game {
constructor() {
super({
name: 'My demo game'
}, {
someFlag: true,
clickCount: 0
})
}
onReady() { }
onDisengaged() { }
onUpdate(timeDelta: number) {
this.Elements.update(this, timeDelta);
}
onDraw(timeDelta: number, sysPerf: any) {
this.Viewport.clear();
this.Elements.redraw(this, timeDelta)
}
}
export default function MyGamePage() {
const dg = new DemoGame();
return (
<div style={{ width: '800px ' }}>
<h1>My game using Game-Reactor</h1>
<GameComponent id="demoGame" game={dg} />
</div>
)
}
This will run and provide you with the very first basic game. The blue screen of death! It does nothing but behind the scenes, redraws are being fired. try adding console.log('ondraw')
inside the onDraw() method for example and check your browser's console. Try that for onUpdate as well. Note that we need to wrap the GameComponent inside a DIV of specific size because it will stretch to its parent's dimension meaning if it is just placed on the body, it will consume the entire space (unless that was intended).
Lets add our first GameElement
class Avatar extends GameElement {
xDirRight: boolean
yDirDown: boolean
constructor(game: Game) {
super(game, {
name: 'my-avatar',
sprite: 'avatar',
pos: {
x: 20,
y: 20,
},
}, {
speed: 10
});
this.xDirRight = true;
this.yDirDown = true;
}
onUpdate(timeDelta: number) { }
onDraw(timeDelta: number) {
this.Game.Viewport.drawElement(this);
}
}
This will still do nothing as the GameElement is not registered to our Game. First lets add a sprite source in our project and generate a new instance of our GameElement. Inside the Game's constructor
constructor() {
super({
name: 'My demo game'
}, {
someFlag: true,
clickCount: 0
})
// add some stuffs like this sprite
this.Sprites.addSource('avatar', 'https://pixeljoint.com/files/icons/ladyhazy.gif');
this.Elements.add(new Avatar(this));
}
Once you run it, you would seee the icon being rendered in position x20-y20 as expected. However it does nothing at all. Lets start adding updates to it. Lets make it move at 240 pixels per second. Since in the GameElement above, we said the speed will run at 10 on 24 frames that will make it move at 240 total in 24 seconds right?
inside the GameElement's onUpdate method, add the following
onUpdate(timeDelta: number) {
const yDelta = this.State.speed;
const xDelta = this.State.speed;
if (this.yDirDown) {
this.Config.pos!.y! += yDelta;
if (this.Config.pos!.y! + 50 >= game.Viewport.Config.height!) {
this.Config.pos!.y! = 2 * (game.Viewport.Config.height! - 50) - this.Config.pos!.y!;
this.yDirDown = false;
}
} else {
this.Config.pos!.y -= yDelta;
if (this.Config.pos!.y <= 0) {
this.Config.pos!.y! = this.Config.pos!.y! * -1;
this.yDirDown = true;
}
}
if (this.xDirRight) {
this.Config.pos!.x += xDelta;
if (this.Config.pos!.x + 50 >= game.Viewport.Config.width!) {
this.Config.pos!.x! = 2 * (game.Viewport.Config.width! - 50) - this.Config.pos!.x!;
this.xDirRight = false;
}
} else {
this.Config.pos!.x -= xDelta;
if (this.Config.pos!.x <= 0) {
this.Config.pos!.x! = this.Config.pos!.x! * -1;
this.xDirRight = true;
}
}
}
You will now see the GameElement's sprite being re-rendered on each update in the new position causing it to appear to be bouncing around the canvas. Now here is something you will need to understand, right now the game is runing at the default fps of 24. meaning the speed of the update you might be seeing now is 24x the value of speed in our state (which is 10). since the height is 240, you can expect that the GameElement is colliding the canvas and changing direction exactly every 1 second. Try it with a stopwatch and you will see.
Now what if we cut our fps to lets say... 6 fps? change the Game's constructor to these
constructor() {
super({
name: 'My demo game'
viewport: {
fps: 6
}
}, {
someFlag: true,
clickCount: 0
})
}
run it again and you will see that it updates more slowly at 6 updates per second. So why is it running as slow? that is because it is now updating at 60 pixel movement per second (10 movement every 6 frames). If our goal is that it consistently moves at 240 pixels per second no matter the framerate, how do we do that? That's where the timeDelta comes into play. Revise the code to the following
onUpdate(game: Game, timeDelta: number) {
const yDelta = (this.State.speed * timeDelta);
const xDelta = (this.State.speed * timeDelta);
if (this.yDirDown) {
this.Config.pos!.y! += yDelta;
if (this.Config.pos!.y! + 50 >= game.Viewport.Config.height!) {
this.Config.pos!.y! = 2 * (game.Viewport.Config.height! - 50) - this.Config.pos!.y!;
this.yDirDown = false;
}
} else {
this.Config.pos!.y -= yDelta;
if (this.Config.pos!.y <= 0) {
this.Config.pos!.y! = this.Config.pos!.y! * -1;
this.yDirDown = true;
}
}
if (this.xDirRight) {
this.Config.pos!.x += xDelta;
if (this.Config.pos!.x + 50 >= game.Viewport.Config.width!) {
this.Config.pos!.x! = 2 * (game.Viewport.Config.width! - 50) - this.Config.pos!.x!;
this.xDirRight = false;
}
} else {
this.Config.pos!.x -= xDelta;
if (this.Config.pos!.x <= 0) {
this.Config.pos!.x! = this.Config.pos!.x! * -1;
this.xDirRight = true;
}
}
}
then, change the speed of the GameElement to be the actual value per second you wanted (which is 240)
super(game, {
name: 'my-avatar',
sprite: 'avatar',
pos: {
x: 20,
y: 20,
},
}, {
speed: 240 //you should specify values like these as a "per second", ex: attacks per second
});
Run it! Now you should be able to see a consistent motion now whether your run in 6, 12, 24, 30 or even 60 fps. timeDelta is a multiplier you will use to adjust your values.
FAQs
Game development SDK for the web using ReactJS with Typescript
The npm package game-reactor receives a total of 2 weekly downloads. As such, game-reactor popularity was classified as not popular.
We found that game-reactor demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.