mirror of https://github.com/mode777/rayjs.git
make game framework more functional
This commit is contained in:
parent
9d7397b513
commit
5bd2820ec8
Binary file not shown.
|
@ -24,7 +24,7 @@ export interface HasColor {
|
|||
color: Color
|
||||
}
|
||||
|
||||
export type EntityOf<T> = HasIdentity & Partial<HasResources<T>> & Partial<Updatable<T>> & Partial<Drawable<T>> & T
|
||||
export type EntityOf<T> = HasIdentity & Partial<HasResources<EntityOf<T>>> & Partial<Updatable<EntityOf<T>>> & Partial<Drawable<EntityOf<T>>> & T
|
||||
|
||||
let ID = 0
|
||||
export const makeEntity = () => ({
|
||||
|
@ -42,3 +42,6 @@ export const makeColorRgb = (r = 255, g = 255, b = 255, a = 255) => ({
|
|||
export const makeColorClone = (c = WHITE) => ({
|
||||
color: new Color(c.r, c.g, c.b, c.a)
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import { EntityOf } from "./entity";
|
||||
import { resourceUnloadAll } from "./resource";
|
||||
|
||||
const promiseUpdateList: (()=>boolean)[] = []
|
||||
const entitiyList: EntityOf<any>[] = []
|
||||
|
||||
|
||||
const dispatchPromises = () => {
|
||||
for (var i = promiseUpdateList.length - 1; i >= 0; i--) {
|
||||
|
@ -23,34 +28,37 @@ export const makeUpdateablePromise = (updateFn: () => boolean) => {
|
|||
return promise
|
||||
}
|
||||
|
||||
|
||||
export abstract class Game {
|
||||
public clearColor = BLACK
|
||||
private quit = false
|
||||
|
||||
constructor(public readonly width: number,
|
||||
public readonly height: number,
|
||||
public readonly title: string){
|
||||
export const entityAdd = (entity: EntityOf<any>) => {
|
||||
if (entity.load) entity.load(entity)
|
||||
entitiyList.push(entity)
|
||||
}
|
||||
|
||||
public run(){
|
||||
initWindow(this.width,this.height,this.title)
|
||||
export const entityRemove = (entity: EntityOf<any>) => {
|
||||
// TODO: Do this cached
|
||||
const i = entitiyList.findIndex(x => x.id === entity.id)
|
||||
if (i !== -1) {
|
||||
const e = entitiyList[i]
|
||||
if (e.unload) e.unload(entity)
|
||||
entitiyList.splice(i, 1)
|
||||
}
|
||||
}
|
||||
|
||||
export const runGame = (width: number, height: number, title: string, startupCallback: () => void) => {
|
||||
initWindow(width, height, title)
|
||||
setTargetFPS(60)
|
||||
this.load()
|
||||
while(!(this.quit = windowShouldClose())){
|
||||
startupCallback()
|
||||
while(!windowShouldClose()){
|
||||
dispatchPromises()
|
||||
this.update()
|
||||
for (const entity of entitiyList) {
|
||||
if (entity.update) entity.update(entity)
|
||||
}
|
||||
beginDrawing()
|
||||
clearBackground(this.clearColor)
|
||||
this.draw()
|
||||
clearBackground(BLACK)
|
||||
for (const entity of entitiyList) {
|
||||
if (entity.draw) entity.draw(entity)
|
||||
}
|
||||
endDrawing()
|
||||
}
|
||||
this.unload()
|
||||
resourceUnloadAll()
|
||||
closeWindow()
|
||||
}
|
||||
|
||||
abstract draw(): void;
|
||||
abstract update(): void;
|
||||
abstract load(): void;
|
||||
abstract unload(): void;
|
||||
}
|
|
@ -1,47 +1,23 @@
|
|||
import { fadeIn, fadeOut, wait } from "./animation";
|
||||
import { EntityOf } from "./entity";
|
||||
import { Game, makeUpdateablePromise } from "./game";
|
||||
import { entityAdd, entityRemove, runGame } from "./game";
|
||||
import { TextEntity, makeTextEntity } from "./text";
|
||||
|
||||
class MyGame<T> extends Game {
|
||||
entities: EntityOf<T>[] = []
|
||||
|
||||
draw(): void {
|
||||
for (const entity of this.entities) {
|
||||
if(entity.draw) entity.draw(entity)
|
||||
}
|
||||
}
|
||||
update(): void {
|
||||
for (const entity of this.entities) {
|
||||
if(entity.update) entity.update(entity)
|
||||
}
|
||||
}
|
||||
load(): void {
|
||||
}
|
||||
unload(): void {
|
||||
}
|
||||
|
||||
addEntity(entity: EntityOf<T>){
|
||||
this.entities.push(entity)
|
||||
}
|
||||
}
|
||||
|
||||
const game = new MyGame<TextEntity>(800,450,"Typescript Game")
|
||||
|
||||
const main = async () => {
|
||||
runGame(800,400, "Typescript Game", async () => {
|
||||
const text = <TextEntity>{
|
||||
...makeTextEntity("Welcome to rayjs!"),
|
||||
size: 80,
|
||||
position: new Vector2(100, game.height/2 - 40)
|
||||
position: new Vector2(100, getScreenHeight()/2 - 40),
|
||||
font: 'resources/anonymous_pro_bold.ttf'
|
||||
}
|
||||
game.addEntity(text)
|
||||
let quit = false
|
||||
entityAdd(text)
|
||||
while(!quit){
|
||||
await fadeIn(text, 2)
|
||||
await wait(2)
|
||||
await fadeOut(text, 2)
|
||||
text.position.x += 75
|
||||
text.text = "This Summer!"
|
||||
await fadeIn(text, 2)
|
||||
await fadeOut(text, 3)
|
||||
entityRemove(text)
|
||||
}
|
||||
|
||||
main()
|
||||
game.run()
|
||||
})
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
type ResourceType = 'texture' | 'image' | 'shader' | 'font'
|
||||
|
||||
const resourceList = new Map<string, Resource>()
|
||||
|
||||
interface Resource {
|
||||
refcount: number,
|
||||
id: string,
|
||||
resource: any
|
||||
unload: (t: any) => void
|
||||
}
|
||||
|
||||
function loadResourceFunc<T>(loader: (filename: string) => T, unloader: (resource: T) => void){
|
||||
return (filename: string) => {
|
||||
if(resourceList.has(filename)){
|
||||
const res = resourceList.get(filename)
|
||||
res!.refcount++
|
||||
return <T>res?.resource
|
||||
} else {
|
||||
traceLog(LOG_INFO, "here")
|
||||
const resource = loader(filename)
|
||||
traceLog(LOG_INFO, <string>resource)
|
||||
resourceList.set(filename, {
|
||||
refcount: 1,
|
||||
id: filename,
|
||||
resource: resource,
|
||||
unload: unloader
|
||||
})
|
||||
return resource
|
||||
}
|
||||
}
|
||||
}
|
||||
export const resourceUnload = (id: string) => {
|
||||
const res = resourceList.get(id)
|
||||
if(res){
|
||||
res.refcount--
|
||||
if(res.refcount === 0){
|
||||
res.unload(res.resource)
|
||||
}
|
||||
}
|
||||
}
|
||||
export const resourceUnloadAll = () => {
|
||||
for (const res of resourceList.entries()) {
|
||||
res[1].unload(res[1].resource)
|
||||
}
|
||||
}
|
||||
export const textureLoad = loadResourceFunc<Texture>(loadTexture,unloadTexture)
|
||||
export const fontLoad = loadResourceFunc<Font>(loadFont,unloadFont)
|
|
@ -1,34 +1,41 @@
|
|||
import { EntityOf, HasColor, HasPosition, makeColorRgb, makeEntity, makePosition } from "./entity"
|
||||
import { fontLoad, resourceUnload } from "./resource"
|
||||
|
||||
export interface Text extends HasPosition, HasColor {
|
||||
text: string,
|
||||
font: Font,
|
||||
font?: string,
|
||||
size: number,
|
||||
spacing: number
|
||||
}
|
||||
|
||||
export interface TextEntity extends EntityOf<Text> {
|
||||
type: "text"
|
||||
fontResource?: Font
|
||||
}
|
||||
|
||||
export const makeText = (text = "", size = 20, font = null, spacing = 1) => (<Text>{
|
||||
export const makeText = (text = "", size = 20, font: string | null = null, spacing = 1) => (<Text>{
|
||||
...makePosition(),
|
||||
...makeColorRgb(),
|
||||
text: text,
|
||||
font: font === null ? getFontDefault() : loadFont(font),
|
||||
font: font,
|
||||
size: size,
|
||||
spacing: spacing
|
||||
})
|
||||
|
||||
export const textDraw = (t: Text) => {
|
||||
return drawTextEx(t.font, t.text, t.position, t.size, t.spacing, t.color);
|
||||
export const textDrawFn = (t: TextEntity) => drawTextEx(t.fontResource!, t.text, t.position, t.size, t.spacing, t.color);
|
||||
export const textLoadFn = (t: TextEntity) => {
|
||||
t.fontResource = (t.font ? fontLoad(t.font) : getFontDefault())
|
||||
return
|
||||
}
|
||||
export const textUnloadFn = (t: TextEntity) => t.font ? resourceUnload(t.font) : undefined
|
||||
|
||||
export const makeTextEntity = (text: string = "") => (<TextEntity>{
|
||||
...makeEntity(),
|
||||
...makeText(text),
|
||||
type: "text",
|
||||
draw: textDraw,
|
||||
draw: textDrawFn,
|
||||
load: textLoadFn,
|
||||
unload: textUnloadFn
|
||||
})
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue