Skip to content

Commit

Permalink
Refactored code for the new tiled-rendering demo.
Browse files Browse the repository at this point in the history
  • Loading branch information
serbanghita committed Oct 30, 2024
1 parent 4d1e64b commit 24ff9e3
Show file tree
Hide file tree
Showing 40 changed files with 655 additions and 634 deletions.
8 changes: 2 additions & 6 deletions packages/assets/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
{
"name": "@serbanghita-gamedev/assets",
"description": "Asset related utils (JSON, Images, etc)",
"version": "0.0.1",
"description": "Opinionated asset (JSON, Images, etc) related utility library",
"version": "0.0.2",
"author": "Serban Ghita <[email protected]> (https://ghita.org)",
"license": "MIT",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"main": "src/index.ts",
"type": "module",
"scripts": {
Expand Down
52 changes: 38 additions & 14 deletions packages/assets/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
import { SpriteSheetAnimation } from "@serbanghita-gamedev/component";
import { EntityDeclaration } from "@serbanghita-gamedev/ecs";
import { TiledMapFile } from "@serbanghita-gamedev/tiled";
import { PhysicsBodyPropsDeclaration } from "@serbanghita-gamedev/component/PhysicsBody";
import { SpriteSheetPropsDeclaration } from "@serbanghita-gamedev/component/SpriteSheet";

export async function loadLocalImage(data: string): Promise<HTMLImageElement> {
const img = new Image();
const test1 = data.match(/([a-z0-9-_]+).(png|gif|jpg)$/i);
const test2 = data.match(/^data:image\//i);
if (!test1 && !test2) {
throw new Error(`Trying to an load an invalid image ${data}.`);
}

return new Promise((resolve) => {
img.src = data;
img.onload = function() {
resolve(this as HTMLImageElement);
}
});
}
const img = new Image();
const test1 = data.match(/([a-z0-9-_]+).(png|gif|jpg)$/i);
const test2 = data.match(/^data:image\//i);
if (!test1 && !test2) {
throw new Error(`Trying to an load an invalid image ${data}.`);
}

return new Promise((resolve) => {
img.src = data;
img.onload = function () {
resolve(this as HTMLImageElement);
};
});
}

export type Assets = {
"entities/images": { [key: string]: HTMLImageElement };
"entities/animations": { [key: string]: { [key: string]: SpriteSheetAnimation } };
"entities/declarations": EntityDeclaration[];
"maps/images": { [key: string]: HTMLImageElement };
"maps/declarations": { [key: string]: TiledMapFile };
};

export type EntityDeclaration = {
id: string;
components: {
[componentName: string]: object;

PhysicsBody: PhysicsBodyPropsDeclaration;
SpriteSheet: SpriteSheetPropsDeclaration;
};
};
27 changes: 27 additions & 0 deletions packages/assets/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"module": "commonjs", /* Specify what module code is generated. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
"rootDirs": ["src"],
"baseUrl": "src",
"sourceMap": true,
"paths": {
"@serbanghita-gamedev/component/*": ["../../component/src/*"],
"@serbanghita-gamedev/input/*": ["../../input/src/*"],
"@serbanghita-gamedev/renderer/*": ["../../renderer/src/*"],
"@serbanghita-gamedev/ecs/*": ["../../ecs/src/*"],
"@serbanghita-gamedev/tiled/*": ["../../tiled/src/*"],
}
},
"include": [
"src/index.ts"
],
"exclude": [
"node_modules",
"dist"
]
}
19 changes: 19 additions & 0 deletions packages/component/src/PhysicsBody.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Component } from "@serbanghita-gamedev/ecs";
import { Rectangle, Point } from "@serbanghita-gamedev/geometry";

export interface PhysicsBodyProps {
width: number;
height: number;
x: number;
y: number;
rectangle: Rectangle;
point: Point;
}

export type PhysicsBodyPropsDeclaration = Exclude<PhysicsBodyProps, { rectangle: Rectangle; point: Point }>;

export default class PhysicsBody extends Component {
constructor(public properties: PhysicsBodyProps) {
super(properties);
}
}
82 changes: 42 additions & 40 deletions packages/component/src/SpriteSheet.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,54 @@
import Component from "../../ecs/src/Component";
import {loadLocalImage} from "@serbanghita-gamedev/assets";
import { loadLocalImage } from "@serbanghita-gamedev/assets";

export type Animation = {
frames: AnimationFrame[];
speed: number;
hitboxOffset: {x: number, y: number};
}
frames: AnimationFrame[];
speed: number;
hitboxOffset: { x: number; y: number };
};

export type AnimationFrame = {
width: number;
height: number;
width: number;
height: number;
x: number;
y: number;
};

export type SpriteSheetAnimation = {
defaultAnimation?: boolean;
parent?: string; // An animation based on a previous animation (doesn't count as offset on Y).
width: number;
height: number;
frames: number[];
speedTicks: number;
hitboxOffset: {
x: number;
y: number;
}
};
};

export type SpriteSheetAnimation = {
defaultAnimation?: boolean;
parent?: string; // An animation based on a previous animation (doesn't count as offset on Y).
width: number;
height: number;
frames: number[];
speedTicks: number;
hitboxOffset: {
x: number;
y: number;
};
}
export type SpriteSheetProps = {
name: string;
offset_x: number;
offset_y: number;
animations: Map<string, Animation>; // This is added and computed in PreRenderSystem.
animationCurrentFrame: string;
animationDefaultFrame: string;
spriteSheetImgPath: string;
spriteSheetAnimationsPath: string;
};

export type SpriteSheetProperties = {
name: string;
offset_x: number;
offset_y: number;
animations: Map<string, Animation>; // This is added and computed in PreRenderSystem.
animationCurrentFrame: string;
animationDefaultFrame: string;
spriteSheetImgPath: string;
spriteSheetAnimationsPath: string;
}
export type SpriteSheetPropsDeclaration = Pick<SpriteSheetProps, "name" | "offset_x" | "offset_y" | "spriteSheetImgPath" | "spriteSheetAnimationsPath">;

export default class SpriteSheet extends Component {
constructor(public properties: SpriteSheetProperties) {
super(properties);
constructor(public properties: SpriteSheetProps) {
super(properties);

// this.properties.img = loadLocalImage(
// // eslint-disable-next-line @typescript-eslint/no-var-requires
// require(this.properties.spriteSheetImgPath)
// );
// // eslint-disable-next-line @typescript-eslint/no-var-requires
// this.properties.animationsDeclaration = require(this.properties.spriteSheetAnimationsPath) as ISpriteSheetAnimation[];
}
}
// this.properties.img = loadLocalImage(
// // eslint-disable-next-line @typescript-eslint/no-var-requires
// require(this.properties.spriteSheetImgPath)
// );
// // eslint-disable-next-line @typescript-eslint/no-var-requires
// this.properties.animationsDeclaration = require(this.properties.spriteSheetAnimationsPath) as ISpriteSheetAnimation[];
}
}
20 changes: 10 additions & 10 deletions packages/component/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export {default as MatrixConfig} from './MatrixConfig';
export {default as IsOnMatrix} from './IsOnMatrix';
export {default as Body} from './Body';
export {default as Direction, Directions} from './Direction';
export {default as Keyboard} from './Keyboard';
export {default as Position} from './Position';
export {default as Renderable} from './Renderable';
export {default as SpriteSheet, SpriteSheetAnimation, SpriteSheetProperties, Animation, AnimationFrame} from './SpriteSheet';
export {default as State} from './State';
export {default as IsTiledMap} from './IsTiledMap';
export { default as MatrixConfig } from "./MatrixConfig";
export { default as IsOnMatrix } from "./IsOnMatrix";
export { default as Body } from "./Body";
export { default as Direction, Directions } from "./Direction";
export { default as Keyboard } from "./Keyboard";
export { default as Position } from "./Position";
export { default as Renderable } from "./Renderable";
export { default as SpriteSheet, SpriteSheetAnimation, SpriteSheetProps, Animation, AnimationFrame, SpriteSheetPropsDeclaration } from "./SpriteSheet";
export { default as State } from "./State";
export { default as IsTiledMap } from "./IsTiledMap";
2 changes: 1 addition & 1 deletion packages/demos/game-playground/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ root = true
[*.{js,json,ts}]
indent_style = space
indent_size = 2
max_line_length = 140
max_line_length = 160
2 changes: 1 addition & 1 deletion packages/demos/game-playground/.prettierrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"printWidth": 140
"printWidth": 160
}
65 changes: 65 additions & 0 deletions packages/demos/game-playground/src/AttackingWithClubSystem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Direction, Directions } from "@serbanghita-gamedev/component";
import { Entity, System } from "@serbanghita-gamedev/ecs";
import { StateStatus } from "./state";

import IsAttackingWithClub from "./IsAttackingWithClub";

export default class AttackingWithClubSystem extends System {
private onEnter(entity: Entity, component: IsAttackingWithClub) {
component.properties.tick = 0;
component.properties.animationTick = 0;
component.properties.status = StateStatus.STARTED;
}

private onUpdate(entity: Entity, component: IsAttackingWithClub) {
const direction = entity.getComponent(Direction);

if (component.properties.animationTick === 5) {
this.onExit(entity, component);
}

if (direction.properties.y === Directions.UP) {
component.properties.animationStateName = "club_attack_one_up";
} else if (direction.properties.y === Directions.DOWN) {
component.properties.animationStateName = "club_attack_one_down";
}

if (direction.properties.x === Directions.LEFT) {
component.properties.animationStateName = "club_attack_one_left";
} else if (direction.properties.x === Directions.RIGHT) {
component.properties.animationStateName = "club_attack_one_right";
}

component.properties.tick++;
if (component.properties.tick % 15 === 0) {
component.properties.animationTick += 1;
}
// console.log(component.properties.tick);
// console.log(component.properties.animationTick);
}

private onExit(entity: Entity, component: IsAttackingWithClub) {
component.properties.status = StateStatus.FINISHED;
}

public update(now: number): void {
this.query.execute().forEach((entity) => {
const component = entity.getComponent(IsAttackingWithClub);

console.log("IsAttackingWithClub", entity.id);

if (component.properties.status === StateStatus.FINISHED) {
entity.removeComponent(IsAttackingWithClub);
return;
}

if (component.properties.status === StateStatus.NOT_STARTED) {
this.onEnter(entity, component);
}

this.onUpdate(entity, component);

return true;
});
}
}
58 changes: 58 additions & 0 deletions packages/demos/game-playground/src/IdleSystem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Direction } from "@serbanghita-gamedev/component";
import { System, Entity } from "@serbanghita-gamedev/ecs";
import IsIdle from "./IsIdle";
import { StateStatus } from "./state";

export default class IdleSystem extends System {
private onEnter(entity: Entity, component: IsIdle) {
component.properties.tick = 0;
component.properties.animationTick = 0;
component.properties.status = StateStatus.STARTED;
}

private onUpdate(entity: Entity, component: IsIdle) {
// Loop. @todo: move logic
// if (component.properties.tick === 10) {
// this.onEnter(entity, component);
// }

const direction = entity.getComponent(Direction);
const directionLiteral = direction.properties.literal || "";

// console.log(`idle_${directionLiteral}`);

component.properties.animationStateName = directionLiteral ? `idle_${directionLiteral}` : "idle";
component.properties.tick++;
if (component.properties.tick % 15 === 0) {
component.properties.animationTick += 1;
}

// console.log(component.properties.animationTick);
}

private onExit(entity: Entity, component: IsIdle) {
component.properties.status = StateStatus.FINISHED;
}

public update(now: number): void {
this.query.execute().forEach((entity: Entity) => {
const component = entity.getComponent(IsIdle);

// console.log('IsIdle', entity.id);

if (component.properties.status === StateStatus.FINISHED) {
console.log("IsIdle finished and removed");
entity.removeComponent(IsIdle);
return;
}

if (component.properties.status === StateStatus.NOT_STARTED) {
this.onEnter(entity, component);
}

this.onUpdate(entity, component);

return true;
});
}
}
Loading

0 comments on commit 24ff9e3

Please sign in to comment.