mirror of
https://github.com/Aviortheking/games.git
synced 2025-07-03 14:49:18 +00:00
42
src/GameEngine/2D/Collision/BoxCollider2D.ts
Normal file
42
src/GameEngine/2D/Collision/BoxCollider2D.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import Component2D from 'GameEngine/Component2D'
|
||||
import Vector2D from '../Vector2D'
|
||||
|
||||
type BuiltinCollisionTypes = 'click'
|
||||
|
||||
export default class BoxCollider2D {
|
||||
public constructor(
|
||||
private component: Component2D,
|
||||
public type: BuiltinCollisionTypes | string = 'collision',
|
||||
private center = new Vector2D(0, 0),
|
||||
private scale = new Vector2D(1, 1)
|
||||
) {}
|
||||
|
||||
public pointColliding(point: Vector2D, type: BuiltinCollisionTypes | string = 'collision'): boolean {
|
||||
if (this.type !== type) {
|
||||
return false
|
||||
}
|
||||
return point.isIn(
|
||||
...this.pos()
|
||||
)
|
||||
}
|
||||
|
||||
public pos(): [Vector2D, Vector2D] {
|
||||
const scale = this.scale.multiply(this.component.scale)
|
||||
const positionCenter = this.component.origin.sub(
|
||||
new Vector2D(
|
||||
this.component.position.x,
|
||||
this.component.position.y
|
||||
)
|
||||
)
|
||||
|
||||
const center = this.center.sum(positionCenter)
|
||||
return [new Vector2D(
|
||||
center.x - scale.x / 2,
|
||||
center.y - scale.y / 2
|
||||
),
|
||||
new Vector2D(
|
||||
center.x + scale.x / 2,
|
||||
center.y + scale.y / 2
|
||||
)]
|
||||
}
|
||||
}
|
25
src/GameEngine/2D/Debug/ColliderDebugger.ts
Normal file
25
src/GameEngine/2D/Debug/ColliderDebugger.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import Component2D, { ComponentState } from 'GameEngine/Component2D'
|
||||
import RectRenderer from 'GameEngine/Renderer/RectRenderer'
|
||||
import BoxCollider2D from '../Collision/BoxCollider2D'
|
||||
import Vector2D from '../Vector2D'
|
||||
|
||||
export default class ColliderDebugger extends Component2D {
|
||||
public constructor(component: Component2D, collider: BoxCollider2D) {
|
||||
super()
|
||||
this.collider = collider
|
||||
const [topLeft, bottomRight] = collider.pos()
|
||||
const size = topLeft.sub(bottomRight)
|
||||
this.position = topLeft
|
||||
this.scale = size
|
||||
this.origin = new Vector2D(-(this.scale.x / 2), -(this.scale.y / 2))
|
||||
this.renderer = new RectRenderer(this, {stroke: 'black'})
|
||||
}
|
||||
|
||||
public update(state: ComponentState) {
|
||||
if (state.isColliding) {
|
||||
(this.renderer as RectRenderer).material = 'rgba(0, 255, 0, .7)'
|
||||
} else {
|
||||
(this.renderer as RectRenderer).material = undefined
|
||||
}
|
||||
}
|
||||
}
|
14
src/GameEngine/2D/Debug/ComponentDebug.ts
Normal file
14
src/GameEngine/2D/Debug/ComponentDebug.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import Component2D from 'GameEngine/Component2D'
|
||||
import RectRenderer from 'GameEngine/Renderer/RectRenderer'
|
||||
import Vector2D from '../Vector2D'
|
||||
|
||||
export default class ComponentDebug extends Component2D {
|
||||
public constructor(component: Component2D) {
|
||||
super()
|
||||
this.position = component.position
|
||||
this.origin = component.origin
|
||||
this.scale = new Vector2D(.1, .1)
|
||||
console.log('Position of the origin point', this.position)
|
||||
this.renderer = new RectRenderer(this, {material: 'red'})
|
||||
}
|
||||
}
|
14
src/GameEngine/2D/Debug/PointDebugger.ts
Normal file
14
src/GameEngine/2D/Debug/PointDebugger.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import Component2D from 'GameEngine/Component2D'
|
||||
import RectRenderer from 'GameEngine/Renderer/RectRenderer'
|
||||
import Vector2D from '../Vector2D'
|
||||
|
||||
export default class PointDebugger extends Component2D {
|
||||
public constructor(point: Vector2D) {
|
||||
super()
|
||||
this.scale = new Vector2D(.1, .1)
|
||||
this.position = point
|
||||
console.log('Debugging point at location', point)
|
||||
// this.origin = component.origin
|
||||
this.renderer = new RectRenderer(this, {material: 'red'})
|
||||
}
|
||||
}
|
30
src/GameEngine/2D/Debug/TillingDebugger.ts
Normal file
30
src/GameEngine/2D/Debug/TillingDebugger.ts
Normal file
@ -0,0 +1,30 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
import Component2D, { ComponentState } from 'GameEngine/Component2D'
|
||||
import RectRenderer from 'GameEngine/Renderer/RectRenderer'
|
||||
import Vector2D from '../Vector2D'
|
||||
|
||||
export default class TilingDebugger extends Component2D {
|
||||
public constructor() {
|
||||
super()
|
||||
for (let i0 = 0; i0 < 10; i0++) {
|
||||
for (let i1 = 0; i1 < 10; i1++) {
|
||||
this.childs.push(
|
||||
new CaseDebugger(new Vector2D(i0, i1)),
|
||||
// new CaseDebugger(new Vector2D(i0 + .5, i1 + .5)),
|
||||
// new CaseDebugger(new Vector2D(i0 + .75, i1 + .75)),
|
||||
// new CaseDebugger(new Vector2D(i0 + .25, i1 + .75)),
|
||||
// new CaseDebugger(new Vector2D(i0 + .75, i1 + .25)),
|
||||
// new CaseDebugger(new Vector2D(i0 + .25, i1 + .25))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CaseDebugger extends Component2D {
|
||||
public renderer: RectRenderer = new RectRenderer(this, {stroke: 'black'})
|
||||
public constructor(pos: Vector2D) {
|
||||
super()
|
||||
this.position = pos
|
||||
}
|
||||
}
|
30
src/GameEngine/2D/Scale2D.ts
Normal file
30
src/GameEngine/2D/Scale2D.ts
Normal file
@ -0,0 +1,30 @@
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
export default class Size2D {
|
||||
private _width: number
|
||||
private _height?: number
|
||||
|
||||
public constructor(
|
||||
width: number,
|
||||
height?: number
|
||||
) {
|
||||
this._width = width
|
||||
this._height = height
|
||||
}
|
||||
|
||||
public get width() {
|
||||
return this._width
|
||||
}
|
||||
|
||||
public set width(v: number) {
|
||||
this._width = v
|
||||
}
|
||||
|
||||
|
||||
public get height() {
|
||||
return this._height ?? this._width
|
||||
}
|
||||
|
||||
public set height(v: number) {
|
||||
this._height = v
|
||||
}
|
||||
}
|
35
src/GameEngine/2D/Vector2D.ts
Normal file
35
src/GameEngine/2D/Vector2D.ts
Normal file
@ -0,0 +1,35 @@
|
||||
export default class Vector2D {
|
||||
public constructor(
|
||||
public x: number,
|
||||
public y: number
|
||||
) {}
|
||||
|
||||
|
||||
public multiply(v: Vector2D): Vector2D {
|
||||
return new Vector2D(
|
||||
v.x * this.x,
|
||||
v.y * this.y
|
||||
)
|
||||
}
|
||||
|
||||
public sum(v: Vector2D): Vector2D {
|
||||
return new Vector2D(
|
||||
v.x + this.x,
|
||||
v.y + this.y
|
||||
)
|
||||
}
|
||||
|
||||
public sub(v: Vector2D): Vector2D {
|
||||
return new Vector2D(
|
||||
v.x - this.x,
|
||||
v.y - this.y
|
||||
)
|
||||
}
|
||||
|
||||
public isIn(topLeft: Vector2D, bottomRight: Vector2D): boolean {
|
||||
return this.x >= topLeft.x &&
|
||||
this.y >= topLeft.y &&
|
||||
this.x <= bottomRight.x &&
|
||||
this.y <= bottomRight.y
|
||||
}
|
||||
}
|
5
src/GameEngine/Camera.ts
Normal file
5
src/GameEngine/Camera.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import Vector2D from './2D/Vector2D'
|
||||
|
||||
export default class Camera {
|
||||
public topLeft = new Vector2D(0.5, 0.5)
|
||||
}
|
@ -1,34 +1,35 @@
|
||||
import BoxCollider2D from './2D/Collision/BoxCollider2D'
|
||||
import Vector2D from './2D/Vector2D'
|
||||
import Renderer from './Renderer'
|
||||
|
||||
export interface ComponentState {
|
||||
mouseHovering: boolean
|
||||
mouseClicking: boolean
|
||||
mouseClicked: boolean
|
||||
/**
|
||||
* is it is collinding return the type of collision
|
||||
*/
|
||||
isColliding?: string
|
||||
}
|
||||
|
||||
export default abstract class Component2D {
|
||||
public id?: number
|
||||
|
||||
public renderer?: Renderer
|
||||
public pos?: {x: number, y: number, z?: number, rotation?: number}
|
||||
|
||||
public position: Vector2D = new Vector2D(0, 0)
|
||||
|
||||
protected size?: number | {width: number, height: number}
|
||||
public scale: Vector2D = new Vector2D(1, 1)
|
||||
|
||||
public collider?: BoxCollider2D
|
||||
|
||||
/**
|
||||
* Change the origin point (default to middle)
|
||||
*/
|
||||
public origin: Vector2D = new Vector2D(0 , 0)
|
||||
|
||||
public childs: Array<Component2D> = []
|
||||
|
||||
public debug?: boolean
|
||||
|
||||
public init?(): Promise<void> | void
|
||||
|
||||
public update?(state: ComponentState): Promise<void> | void
|
||||
|
||||
public width() {
|
||||
if (!this.size) {
|
||||
return undefined
|
||||
}
|
||||
return typeof this.size === 'number' ? this.size : this.size?.width
|
||||
}
|
||||
|
||||
public height() {
|
||||
if (!this.size) {
|
||||
return undefined
|
||||
}
|
||||
return typeof this.size === 'number' ? this.size : this.size?.height
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
import GameEngine from 'GameEngine'
|
||||
import Component2D from 'GameEngine/Component2D'
|
||||
import Renderer from '.'
|
||||
|
||||
export default class ColorRenderer implements Renderer {
|
||||
|
||||
public constructor(
|
||||
private component: Component2D,
|
||||
private color: string
|
||||
) {}
|
||||
|
||||
public async render(ge: GameEngine, ctx: CanvasRenderingContext2D) {
|
||||
if (!this.component.pos) {
|
||||
return
|
||||
}
|
||||
ctx.fillStyle = this.color
|
||||
ctx.fillRect(
|
||||
this.component.pos.x * (ge.caseSize[0]),
|
||||
this.component.pos.y * (ge.caseSize[1]),
|
||||
(this.component.width() ?? ge.caseSize[0]) * ge.caseSize[0],
|
||||
(this.component.height() ?? ge.caseSize[1]) * ge.caseSize[1]
|
||||
)
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import GameEngine from 'GameEngine'
|
||||
import Asset from 'GameEngine/Asset'
|
||||
import Component2D from 'GameEngine/Component2D'
|
||||
import Renderer from '.'
|
||||
|
||||
export default class ImageRenderer implements Renderer {
|
||||
|
||||
public constructor(
|
||||
private component: Component2D,
|
||||
private image: Asset
|
||||
) {}
|
||||
|
||||
public async render(ge: GameEngine, ctx: CanvasRenderingContext2D) {
|
||||
if (!this.component.pos) {
|
||||
return
|
||||
}
|
||||
ctx.drawImage(
|
||||
await this.image.get(),
|
||||
this.component.pos.x * (ge.caseSize[0]),
|
||||
this.component.pos.y * (ge.caseSize[1]),
|
||||
(this.component.width() ?? ge.caseSize[0]) * ge.caseSize[0],
|
||||
(this.component.height() ?? ge.caseSize[1]) * ge.caseSize[1]
|
||||
)
|
||||
}
|
||||
}
|
52
src/GameEngine/Renderer/RectRenderer.ts
Normal file
52
src/GameEngine/Renderer/RectRenderer.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { objectLoop } from '@dzeio/object-util'
|
||||
import GameEngine from 'GameEngine'
|
||||
import Asset from 'GameEngine/Asset'
|
||||
import Component2D from 'GameEngine/Component2D'
|
||||
import Renderer from '.'
|
||||
|
||||
interface Params {
|
||||
material?: string | Asset
|
||||
stroke?: string
|
||||
}
|
||||
|
||||
export default class RectRenderer extends Renderer implements Partial<Params> {
|
||||
|
||||
public material?: string | Asset
|
||||
public stroke?: string
|
||||
|
||||
public constructor(component: Component2D, params?: Params) {
|
||||
super(component)
|
||||
objectLoop(params ?? {}, (v, k) => {this[k as 'material'] = v})
|
||||
}
|
||||
|
||||
public async render(ge: GameEngine, ctx: CanvasRenderingContext2D) {
|
||||
const position = this.getPosition()
|
||||
const item: [number, number, number, number] = [
|
||||
// source x
|
||||
// 0 - 1.5 - -1.5
|
||||
position.x * (ge.caseSize.x),
|
||||
// source y
|
||||
position.y * (ge.caseSize.y),
|
||||
// source end X
|
||||
this.component.scale.x * (ge.caseSize.x),
|
||||
// source end Y
|
||||
this.component.scale.y * (ge.caseSize.y)
|
||||
]
|
||||
|
||||
if (this.material instanceof Asset) {
|
||||
ctx.drawImage(
|
||||
await this.material.get(),
|
||||
...item
|
||||
)
|
||||
return
|
||||
}
|
||||
if (this.material) {
|
||||
ctx.fillStyle = this.material
|
||||
ctx.fillRect(...item)
|
||||
}
|
||||
if (this.stroke) {
|
||||
ctx.strokeStyle = this.stroke
|
||||
ctx.strokeRect(...item)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,31 +1,44 @@
|
||||
import { objectLoop } from '@dzeio/object-util'
|
||||
import GameEngine from 'GameEngine'
|
||||
import Vector2D from 'GameEngine/2D/Vector2D'
|
||||
import Component2D from 'GameEngine/Component2D'
|
||||
import Tileset from 'GameEngine/Tileset'
|
||||
import Renderer from '.'
|
||||
|
||||
export default class TileRenderer implements Renderer {
|
||||
interface Params {
|
||||
tileset?: Tileset
|
||||
id?: number
|
||||
}
|
||||
|
||||
public constructor(
|
||||
private component: Component2D,
|
||||
private tileset: Tileset,
|
||||
private id: number
|
||||
) {}
|
||||
/**
|
||||
* TODO: Add origin support
|
||||
*/
|
||||
export default class TileRenderer extends Renderer implements Params {
|
||||
|
||||
public tileset?: Tileset
|
||||
public id?: number
|
||||
|
||||
public constructor(component: Component2D, params?: Params) {
|
||||
super(component)
|
||||
objectLoop(params ?? {}, (v, k) => {this[k as 'id'] = v})
|
||||
}
|
||||
|
||||
public async render(ge: GameEngine, ctx: CanvasRenderingContext2D) {
|
||||
if (!this.component.pos) {
|
||||
if (!this.tileset || typeof this.id !== 'number') {
|
||||
return
|
||||
}
|
||||
const {sx, sy} = this.tileset.getSourceData(this.id)
|
||||
const position = this.getPosition()
|
||||
ctx.drawImage(
|
||||
await this.tileset.asset.get(),
|
||||
sx,
|
||||
sy,
|
||||
this.tileset.width(),
|
||||
this.tileset.height(),
|
||||
this.component.pos.x * (ge.caseSize[0]),
|
||||
this.component.pos.y * (ge.caseSize[1]),
|
||||
(this.component.width() ?? ge.caseSize[0]) * ge.caseSize[0],
|
||||
(this.component.height() ?? ge.caseSize[1]) * ge.caseSize[1]
|
||||
this.tileset.width(this.id),
|
||||
this.tileset.height(this.id),
|
||||
position.x * (ge.caseSize.x),
|
||||
position.y * (ge.caseSize.y),
|
||||
(this.component.scale.x ?? ge.caseSize.x) * ge.caseSize.x,
|
||||
(this.component.scale.y ?? ge.caseSize.y) * ge.caseSize.y
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,21 @@
|
||||
import GameEngine from 'GameEngine'
|
||||
import Vector2D from 'GameEngine/2D/Vector2D'
|
||||
import Component2D from 'GameEngine/Component2D'
|
||||
|
||||
export default interface Renderer {
|
||||
render(ge: GameEngine, ctx: CanvasRenderingContext2D): Promise<void>
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export default abstract class Renderer {
|
||||
public constructor(
|
||||
protected component: Component2D
|
||||
) {}
|
||||
|
||||
protected getPosition(): Vector2D {
|
||||
const ge = GameEngine.getGameEngine()
|
||||
const realPosition = ge.currentScene.camera.topLeft.sum(this.component.position)
|
||||
return new Vector2D(
|
||||
realPosition.x - this.component.scale.x / 2 - this.component.origin.x,
|
||||
realPosition.y - this.component.scale.y / 2 - this.component.origin.y
|
||||
)
|
||||
}
|
||||
|
||||
public abstract render(ge: GameEngine, ctx: CanvasRenderingContext2D): Promise<void>
|
||||
}
|
||||
|
@ -1,18 +1,23 @@
|
||||
import GameEngine from 'GameEngine'
|
||||
import { ComponentState } from 'react'
|
||||
import AssetsManager from './Asset'
|
||||
import Component2D from './Component2D'
|
||||
import Camera from './Camera'
|
||||
import Component2D, { ComponentState } from './Component2D'
|
||||
|
||||
export default class Scene {
|
||||
public static scenes: Record<string, Scene> = {}
|
||||
|
||||
public background?: string
|
||||
public id: string
|
||||
|
||||
public camera: Camera = new Camera()
|
||||
|
||||
private components: Array<Component2D> = []
|
||||
private ge!: GameEngine
|
||||
|
||||
|
||||
public constructor(sceneId: string) {
|
||||
Scene.scenes[sceneId] = this
|
||||
this.id = sceneId
|
||||
}
|
||||
|
||||
public addComponent(...cp: Array<Component2D>) {
|
||||
@ -32,25 +37,53 @@ export default class Scene {
|
||||
}
|
||||
|
||||
public async update() {
|
||||
this.components.forEach(async (v) => {
|
||||
const state: Partial<ComponentState> = {}
|
||||
const width = (v.size?.width ?? 1) * this.ge.caseSize[0]
|
||||
const height = (v.size?.height ?? 1) * this.ge.caseSize[1]
|
||||
if (v.pos) {
|
||||
const ax = v.pos.x * this.ge.caseSize[0]
|
||||
const ay = v.pos.y * this.ge.caseSize[1]
|
||||
state.mouseHovering =
|
||||
this.ge.cursor.x >= ax && this.ge.cursor.x < (ax + width) &&
|
||||
this.ge.cursor.y >= ay && this.ge.cursor.y < (ay + height)
|
||||
state.mouseClicking = state.mouseHovering && this.ge.cursor.isDown
|
||||
state.mouseClicked = state.mouseClicking && !this.ge.cursor.wasDown
|
||||
for (const component of this.components) {
|
||||
await this.updateComponent(component)
|
||||
}
|
||||
}
|
||||
|
||||
private async updateComponent(v: Component2D) {
|
||||
const debug = v.debug
|
||||
if (debug) {
|
||||
console.log('Processing Component', v)
|
||||
}
|
||||
const state: Partial<ComponentState> = {}
|
||||
// const width = (v.width() ?? 1) * this.ge.caseSize[0]
|
||||
// const height = (v.height() ?? 1) * this.ge.caseSize[1]
|
||||
if (v.collider && v.collider.type === 'click' && this.ge.cursor.isDown && !this.ge.cursor.wasDown) {
|
||||
if (v.collider.pointColliding(this.ge.cursor.position, 'click')) {
|
||||
state.isColliding = 'click'
|
||||
}
|
||||
if (v.renderer) {
|
||||
await v.renderer.render(this.ge, this.ge.ctx)
|
||||
}
|
||||
// if (v.pos) {
|
||||
// const ax = v.pos.x * this.ge.caseSize[0]
|
||||
// const ay = v.pos.y * this.ge.caseSize[1]
|
||||
// state.mouseHovering =
|
||||
// this.ge.cursor.x >= ax && this.ge.cursor.x < (ax + width) &&
|
||||
// this.ge.cursor.y >= ay && this.ge.cursor.y < (ay + height)
|
||||
// state.mouseClicking = state.mouseHovering && this.ge.cursor.isDown
|
||||
// state.mouseClicked = state.mouseClicking && !this.ge.cursor.wasDown
|
||||
// }
|
||||
if (v.renderer) {
|
||||
if (debug) {
|
||||
console.log('Rendering Component', v)
|
||||
}
|
||||
if (v.update) {
|
||||
v.update(state as ComponentState)
|
||||
// console.log('is rendering new element')
|
||||
await v.renderer.render(this.ge, this.ge.ctx)
|
||||
}
|
||||
if (v.update) {
|
||||
if (debug) {
|
||||
console.log('Updating Component', v)
|
||||
}
|
||||
})
|
||||
v.update(state as ComponentState)
|
||||
}
|
||||
if (v.childs) {
|
||||
if (debug) {
|
||||
console.log('Processing childs', v)
|
||||
}
|
||||
for (const child of v.childs) {
|
||||
await this.updateComponent(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,17 @@
|
||||
import Asset from './Asset'
|
||||
|
||||
export interface TilesetDeclaration {
|
||||
export type TilesetDeclaration = {
|
||||
// id: string
|
||||
// padding?: number
|
||||
fileSize: {width: number, height: number}
|
||||
tileSize: number | {width: number, height: number}
|
||||
spacing?: number
|
||||
}
|
||||
} | Array<{
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
}>
|
||||
|
||||
export default class Tileset {
|
||||
|
||||
@ -15,26 +20,37 @@ export default class Tileset {
|
||||
private declaration: TilesetDeclaration
|
||||
) {}
|
||||
|
||||
public getPosFromId(id: number): {x: number, y: number} {
|
||||
const cols = Math.trunc(this.declaration.fileSize.width / this.width())
|
||||
const x = id % cols
|
||||
const y = Math.trunc(id / cols)
|
||||
return {x, y}
|
||||
}
|
||||
// public getPosFromId(id: number): {x: number, y: number} {
|
||||
|
||||
// return {x, y}
|
||||
// }
|
||||
|
||||
public getSourceData(id: number): {sx: number ,sy: number} {
|
||||
const {x, y} = this.getPosFromId(id)
|
||||
const sx = x * this.width() + x * (this.declaration.spacing ?? 0)
|
||||
const sy = y * this.height() + y * (this.declaration.spacing ?? 0)
|
||||
if (Array.isArray(this.declaration)) {
|
||||
const item = this.declaration[id]
|
||||
return {sx: item.x, sy: item.y}
|
||||
}
|
||||
// const {x, y} = this.getPosFromId(id)
|
||||
const cols = Math.trunc(this.declaration.fileSize.width / this.width(id))
|
||||
const x = id % cols
|
||||
const y = Math.trunc(id / cols)
|
||||
const sx = x * this.width(id) + x * (this.declaration.spacing ?? 0)
|
||||
const sy = y * this.height(id) + y * (this.declaration.spacing ?? 0)
|
||||
return {sx, sy}
|
||||
}
|
||||
|
||||
public width() {
|
||||
public width(id: number) {
|
||||
if (Array.isArray(this.declaration)) {
|
||||
return this.declaration[id].width
|
||||
}
|
||||
const item = this.declaration.tileSize
|
||||
return typeof item === 'number' ? item : item.width
|
||||
}
|
||||
|
||||
public height() {
|
||||
public height(id: number) {
|
||||
if (Array.isArray(this.declaration)) {
|
||||
return this.declaration[id].height
|
||||
}
|
||||
const item = this.declaration.tileSize
|
||||
return typeof item === 'number' ? item : item.height
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import Vector2D from './2D/Vector2D'
|
||||
import Scene from './Scene'
|
||||
|
||||
/**
|
||||
@ -7,42 +8,43 @@ import Scene from './Scene'
|
||||
* Collision
|
||||
*/
|
||||
export default class GameEngine {
|
||||
private static ge: GameEngine
|
||||
public ctx: CanvasRenderingContext2D
|
||||
public canvas: HTMLCanvasElement
|
||||
public caseSize: [number, number] = [1, 1]
|
||||
public caseSize: Vector2D = new Vector2D(1, 1)
|
||||
public cursor: {
|
||||
x: number
|
||||
y: number
|
||||
position: Vector2D
|
||||
isDown: boolean
|
||||
wasDown: boolean
|
||||
} = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
position: new Vector2D(0, 0),
|
||||
isDown: false,
|
||||
wasDown: false
|
||||
}
|
||||
private currentScene?: Scene
|
||||
public currentScene!: Scene
|
||||
private isRunning = false
|
||||
|
||||
public constructor(
|
||||
private id: string,
|
||||
private options?: {
|
||||
public options?: {
|
||||
caseCount?: number | [number, number]
|
||||
background?: string
|
||||
debugColliders?: boolean
|
||||
}
|
||||
) {
|
||||
GameEngine.ge = this
|
||||
const canvas = document.querySelector<HTMLCanvasElement>(id)
|
||||
if (!canvas) {
|
||||
throw new Error('Error, canvas not found!')
|
||||
}
|
||||
this.canvas = canvas
|
||||
if (this.options?.caseCount) {
|
||||
this.caseSize = [
|
||||
this.caseSize = new Vector2D(
|
||||
// @ts-expect-error idc
|
||||
this.canvas.width / ((typeof this.options.caseCount) !== 'number' ? this.options.caseCount[0] : this.options.caseCount ),
|
||||
// @ts-expect-error idc2 lol
|
||||
this.canvas.height / ((typeof this.options.caseCount) !== 'number' ? this.options.caseCount[1] : this.options.caseCount)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
const ctx = canvas.getContext('2d')
|
||||
@ -53,6 +55,10 @@ export default class GameEngine {
|
||||
this.ctx = ctx
|
||||
}
|
||||
|
||||
public static getGameEngine(): GameEngine {
|
||||
return this.ge
|
||||
}
|
||||
|
||||
public start() {
|
||||
if (this.isRunning) {
|
||||
console.warn('Game is already running')
|
||||
@ -63,8 +69,10 @@ export default class GameEngine {
|
||||
this.update()
|
||||
})
|
||||
document.addEventListener('mousemove', (ev) => {
|
||||
this.cursor.x = ev.clientX
|
||||
this.cursor.y = ev.clientY
|
||||
this.cursor.position = new Vector2D(
|
||||
ev.clientX / this.caseSize.x - this.currentScene.camera.topLeft.x,
|
||||
ev.clientY / this.caseSize.y - this.currentScene.camera.topLeft.y
|
||||
)
|
||||
if (this.cursor.isDown) {
|
||||
this.cursor.wasDown = true
|
||||
}
|
||||
@ -83,11 +91,11 @@ export default class GameEngine {
|
||||
}
|
||||
|
||||
public setScene(scene: Scene | string) {
|
||||
console.log('Setting scene', typeof scene === 'string' ? scene : scene.id)
|
||||
this.currentScene = typeof scene === 'string' ? Scene.scenes[scene] : scene
|
||||
this.currentScene.setGameEngine(this)
|
||||
}
|
||||
|
||||
|
||||
private update() {
|
||||
if (!this.isRunning) {
|
||||
return
|
||||
@ -103,3 +111,8 @@ export default class GameEngine {
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
|
||||
export interface GameState<UserState = any> {
|
||||
gameEngine: GameEngine
|
||||
userState: UserState
|
||||
}
|
||||
|
Reference in New Issue
Block a user