Signed-off-by: Avior <github@avior.me>
This commit is contained in:
2022-06-14 18:27:20 +02:00
parent 8952b61651
commit 79b74d16d5
23 changed files with 344 additions and 5028 deletions

View File

@ -1,7 +1,7 @@
import Component2D from 'GameEngine/Component2D'
import Vector2D from '../Vector2D'
type BuiltinCollisionTypes = 'click'
type BuiltinCollisionTypes = 'click' | 'pointerDown' | 'pointerUp'
export default class BoxCollider2D {
public constructor(

View File

@ -1,14 +1,20 @@
import Component2D from 'GameEngine/Component2D'
import RectRenderer from 'GameEngine/Renderer/RectRenderer'
import Vector2D from '../Vector2D'
import PointDebugger from './PointDebugger'
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)
this.position = new Vector2D(0, 0)
// this.origin = component.origin
this.scale = component.scale
console.log('Position of the origin point', this.position)
this.renderer = new RectRenderer(this, {material: 'red'})
// this.renderer = new RectRenderer(this, {material: 'red'})
this.childs = [
new PointDebugger(new Vector2D(0, 0), 'aqua'),
new PointDebugger(this.origin, 'green'),
new PointDebugger(component.position.sum(component.scale), 'aqua')
]
}
}

View File

@ -3,12 +3,12 @@ import RectRenderer from 'GameEngine/Renderer/RectRenderer'
import Vector2D from '../Vector2D'
export default class PointDebugger extends Component2D {
public constructor(point: Vector2D) {
public constructor(point: Vector2D, color = 'red') {
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'})
this.renderer = new RectRenderer(this, {material: color})
}
}

View File

@ -1,30 +0,0 @@
/* 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
}
}

View File

@ -4,7 +4,6 @@ export default class Vector2D {
public y: number
) {}
public multiply(v: Vector2D): Vector2D {
return new Vector2D(
v.x * this.x,

View File

@ -1,3 +1,6 @@
/**
* Asset management Class
*/
export default class Asset {
public static assets: Record<string, Asset> = {}

View File

@ -1,5 +0,0 @@
import Vector2D from './2D/Vector2D'
export default class Camera {
public topLeft = new Vector2D(0.5, 0.5)
}

View File

@ -10,14 +10,45 @@ export interface ComponentState {
isColliding?: string
}
/**
* 2D Component
*/
export default abstract class Component2D {
/**
* Indicate how the component is rendered
*
* @type {Renderer}
* @memberof Component2D
*/
public renderer?: Renderer
/**
* Component position relative to the parent position and to the component origin
*
* (see also: Component2D.origin)
*
* @type {Vector2D}
* @memberof Component2D
*/
public position: Vector2D = new Vector2D(0, 0)
/**
* Component scale relative to 1 case size
*
* (see also: GameEngine.caseSize)
*
* @type {Vector2D}
* @memberof Component2D
*/
public scale: Vector2D = new Vector2D(1, 1)
/**
* Component collider for events
*
* @type {BoxCollider2D}
* @memberof Component2D
*/
public collider?: BoxCollider2D
/**
@ -25,11 +56,35 @@ export default abstract class Component2D {
*/
public origin: Vector2D = new Vector2D(0 , 0)
/**
* Component Child Components
*
* @type {Array<Component2D>}
* @memberof Component2D
*/
public childs: Array<Component2D> = []
/**
* Component in debug mode
* It will display more informations depending on the Collider and other items
*
* note: Does not apply to childs components
*
* @type {boolean}
* @memberof Component2D
*/
public debug?: boolean
/**
* Function run when the component is initialized
*/
public init?(): Promise<void> | void
/**
* Function run on each game ticks
* @param state the component state
*/
public update?(state: ComponentState): Promise<void> | void
public destroy?(): Promise<void> | void
}

View File

@ -0,0 +1,8 @@
import Vector2D from '../2D/Vector2D'
/**
* Currently not working Camera implementation
*/
export default class Camera {
public topLeft = new Vector2D(0.5, 0.5)
}

View File

@ -0,0 +1,25 @@
import ComponentDebug from 'GameEngine/2D/Debug/ComponentDebug'
import Vector2D from 'GameEngine/2D/Vector2D'
import Component2D from 'GameEngine/Component2D'
import Renderer from 'GameEngine/Renderer'
import TextRenderer from 'GameEngine/Renderer/TextRenderer'
export default class FPSCounter extends Component2D {
public position: Vector2D = new Vector2D(0,0)
public scale: Vector2D = new Vector2D(1, 1)
public origin: Vector2D = new Vector2D(0, 0)
public childs: Array<Component2D> = [new ComponentDebug(this)]
public renderer: TextRenderer = new TextRenderer(this, {text: 'pouet'})
private lastUpdate: number = new Date().getTime()
public update() {
const now = new Date().getTime()
this.renderer.text = (1000 / (now - this.lastUpdate)).toFixed(2)
this.lastUpdate = now
}
}

View File

@ -0,0 +1,31 @@
import Event from '.'
export default class PointerEvents extends Event {
public override init(): void {
document.addEventListener('mousemove', this.basicEvent)
document.addEventListener('mousedown', this.mouseDown)
document.addEventListener('mouseup', this.mouseUp)
}
public update() {
// pouet
}
public override destroy() {
document.removeEventListener('mousemove', this.basicEvent)
document.removeEventListener('mousedown', this.mouseDown)
document.removeEventListener('mouseup', this.mouseUp)
}
private basicEvent = (ev: MouseEvent) => {
console.log('Mouse Event :D')
}
private mouseUp = (ev: MouseEvent) => {
this.basicEvent(ev)
}
private mouseDown = (ev: MouseEvent) => {
this.basicEvent(ev)
}
}

View File

@ -0,0 +1,11 @@
import GameEngine from 'GameEngine'
export default abstract class Event {
public constructor(
protected ge: GameEngine
) {}
abstract init(): void
abstract update(): void
abstract destroy(): void
}

View File

@ -9,7 +9,7 @@ interface Params {
stroke?: string
}
export default class RectRenderer extends Renderer implements Partial<Params> {
export default class RectRenderer extends Renderer implements Params {
public material?: string | Asset
public stroke?: string

View File

@ -0,0 +1,41 @@
import { objectLoop } from '@dzeio/object-util'
import GameEngine from 'GameEngine'
import Component2D from 'GameEngine/Component2D'
import Renderer from '.'
interface Params {
text?: string
}
export default class TextRenderer extends Renderer {
public text?: string
public size?: number
public constructor(component: Component2D, params?: Params) {
super(component)
objectLoop(params ?? {}, (v, k) => {this[k as 'text'] = v})
}
public async render(ge: GameEngine, ctx: CanvasRenderingContext2D) {
const position = this.getPosition()
const item: [number, number] = [
// source x
// 0 - 1.5 - -1.5
position.x * (ge.caseSize.x),
// source y
position.y * (ge.caseSize.y)
]
const size = this.component.scale.y * ge.caseSize.y
// console.log
if (this.text) {
ctx.fillStyle = 'black'
ctx.textBaseline = 'top'
ctx.font = `${size}px sans-serif`
ctx.fillText(this.text, ...item)
}
}
}

View File

@ -10,7 +10,7 @@ export default abstract class Renderer {
protected getPosition(): Vector2D {
const ge = GameEngine.getGameEngine()
const realPosition = ge.currentScene.camera.topLeft.sum(this.component.position)
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

View File

@ -1,6 +1,6 @@
import GameEngine from 'GameEngine'
import AssetsManager from './Asset'
import Camera from './Camera'
import Camera from './Components/Camera'
import Component2D, { ComponentState } from './Component2D'
export default class Scene {
@ -42,6 +42,12 @@ export default class Scene {
}
}
public async destroy() {
for await (const component of this.components) {
await component.destroy?.()
}
}
private async updateComponent(v: Component2D) {
const debug = v.debug
if (debug) {
@ -64,6 +70,13 @@ export default class Scene {
// state.mouseClicking = state.mouseHovering && this.ge.cursor.isDown
// state.mouseClicked = state.mouseClicking && !this.ge.cursor.wasDown
// }
if (v.update) {
if (debug) {
console.log('Updating Component', v)
}
v.update(state as ComponentState)
}
if (v.renderer) {
if (debug) {
console.log('Rendering Component', v)
@ -71,17 +84,12 @@ export default class Scene {
// 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) {
for await (const child of v.childs) {
await this.updateComponent(child)
}
}

View File

@ -21,15 +21,22 @@ export default class GameEngine {
isDown: false,
wasDown: false
}
public currentScene!: Scene
public currentScene?: Scene
private isRunning = false
private timer = 16.6
public constructor(
private id: string,
id: string,
public options?: {
caseCount?: number | [number, number]
background?: string
debugColliders?: boolean
/**
* Maximum framerate you want to achieve
*
* note: -1 mean infinite
*/
goalFramerate?: number
}
) {
GameEngine.ge = this
@ -53,6 +60,14 @@ export default class GameEngine {
}
ctx.imageSmoothingEnabled = false
this.ctx = ctx
if (options?.goalFramerate) {
if (options.goalFramerate === -1) {
this.timer = 0
} else {
this.timer = 1000 / options.goalFramerate
}
}
}
public static getGameEngine(): GameEngine {
@ -70,8 +85,8 @@ export default class GameEngine {
})
document.addEventListener('mousemove', (ev) => {
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
ev.clientX / this.caseSize.x - (this.currentScene?.camera?.topLeft?.x ?? 0),
ev.clientY / this.caseSize.y - (this.currentScene?.camera?.topLeft?.y ?? 0)
)
if (this.cursor.isDown) {
this.cursor.wasDown = true
@ -90,13 +105,15 @@ export default class GameEngine {
this.isRunning = false
}
public setScene(scene: Scene | string) {
public async setScene(scene: Scene | string) {
console.log('Setting scene', typeof scene === 'string' ? scene : scene.id)
await this.currentScene?.destroy()
this.currentScene = typeof scene === 'string' ? Scene.scenes[scene] : scene
this.currentScene.setGameEngine(this)
}
private update() {
const now = new Date().getTime()
if (!this.isRunning) {
return
}
@ -106,9 +123,19 @@ export default class GameEngine {
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height)
}
this.currentScene?.update()
setTimeout(() => {
this.update()
}, 0)
const diff = new Date().getTime() - now
if (diff > this.timer) {
requestAnimationFrame(() => {
this.update()
})
} else {
setTimeout(() => {
// this.update()
requestAnimationFrame(() => {
this.update()
})
}, this.timer - diff)
}
}
}