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.
|
@ -1,19 +1,19 @@
|
||||||
|
|
||||||
export interface HasIdentity {
|
export interface HasIdentity {
|
||||||
id: number
|
id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Drawable<T> {
|
export interface Drawable<T> {
|
||||||
draw: (entity: T) => void
|
draw: (entity: T) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Updatable<T> {
|
export interface Updatable<T> {
|
||||||
update: (entity: T) => void
|
update: (entity: T) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HasResources<T> {
|
export interface HasResources<T> {
|
||||||
load: (entity: T) => void
|
load: (entity: T) => void
|
||||||
unload: (entity: T) => void
|
unload: (entity: T) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HasPosition {
|
export interface HasPosition {
|
||||||
|
@ -24,7 +24,7 @@ export interface HasColor {
|
||||||
color: Color
|
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
|
let ID = 0
|
||||||
export const makeEntity = () => ({
|
export const makeEntity = () => ({
|
||||||
|
@ -32,13 +32,16 @@ export const makeEntity = () => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
export const makePosition = (x = 0, y = 0) => ({
|
export const makePosition = (x = 0, y = 0) => ({
|
||||||
position: new Vector2(x,y)
|
position: new Vector2(x, y)
|
||||||
})
|
})
|
||||||
|
|
||||||
export const makeColorRgb = (r = 255, g = 255, b = 255, a = 255) => ({
|
export const makeColorRgb = (r = 255, g = 255, b = 255, a = 255) => ({
|
||||||
color: new Color(r,g,b,a)
|
color: new Color(r, g, b, a)
|
||||||
})
|
})
|
||||||
|
|
||||||
export const makeColorClone = (c = WHITE) => ({
|
export const makeColorClone = (c = WHITE) => ({
|
||||||
color: new Color(c.r,c.g,c.b,c.a)
|
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 promiseUpdateList: (()=>boolean)[] = []
|
||||||
|
const entitiyList: EntityOf<any>[] = []
|
||||||
|
|
||||||
|
|
||||||
const dispatchPromises = () => {
|
const dispatchPromises = () => {
|
||||||
for (var i = promiseUpdateList.length - 1; i >= 0; i--) {
|
for (var i = promiseUpdateList.length - 1; i >= 0; i--) {
|
||||||
|
@ -23,34 +28,37 @@ export const makeUpdateablePromise = (updateFn: () => boolean) => {
|
||||||
return promise
|
return promise
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const entityAdd = (entity: EntityOf<any>) => {
|
||||||
|
if (entity.load) entity.load(entity)
|
||||||
|
entitiyList.push(entity)
|
||||||
|
}
|
||||||
|
|
||||||
export abstract class Game {
|
export const entityRemove = (entity: EntityOf<any>) => {
|
||||||
public clearColor = BLACK
|
// TODO: Do this cached
|
||||||
private quit = false
|
const i = entitiyList.findIndex(x => x.id === entity.id)
|
||||||
|
if (i !== -1) {
|
||||||
constructor(public readonly width: number,
|
const e = entitiyList[i]
|
||||||
public readonly height: number,
|
if (e.unload) e.unload(entity)
|
||||||
public readonly title: string){
|
entitiyList.splice(i, 1)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public run(){
|
export const runGame = (width: number, height: number, title: string, startupCallback: () => void) => {
|
||||||
initWindow(this.width,this.height,this.title)
|
initWindow(width, height, title)
|
||||||
setTargetFPS(60)
|
setTargetFPS(60)
|
||||||
this.load()
|
startupCallback()
|
||||||
while(!(this.quit = windowShouldClose())){
|
while(!windowShouldClose()){
|
||||||
dispatchPromises()
|
dispatchPromises()
|
||||||
this.update()
|
for (const entity of entitiyList) {
|
||||||
beginDrawing()
|
if (entity.update) entity.update(entity)
|
||||||
clearBackground(this.clearColor)
|
|
||||||
this.draw()
|
|
||||||
endDrawing()
|
|
||||||
}
|
}
|
||||||
this.unload()
|
beginDrawing()
|
||||||
closeWindow()
|
clearBackground(BLACK)
|
||||||
|
for (const entity of entitiyList) {
|
||||||
|
if (entity.draw) entity.draw(entity)
|
||||||
|
}
|
||||||
|
endDrawing()
|
||||||
}
|
}
|
||||||
|
resourceUnloadAll()
|
||||||
abstract draw(): void;
|
closeWindow()
|
||||||
abstract update(): void;
|
|
||||||
abstract load(): void;
|
|
||||||
abstract unload(): void;
|
|
||||||
}
|
}
|
|
@ -1,47 +1,23 @@
|
||||||
import { fadeIn, fadeOut, wait } from "./animation";
|
import { fadeIn, fadeOut, wait } from "./animation";
|
||||||
import { EntityOf } from "./entity";
|
import { entityAdd, entityRemove, runGame } from "./game";
|
||||||
import { Game, makeUpdateablePromise } from "./game";
|
|
||||||
import { TextEntity, makeTextEntity } from "./text";
|
import { TextEntity, makeTextEntity } from "./text";
|
||||||
|
|
||||||
class MyGame<T> extends Game {
|
runGame(800,400, "Typescript Game", async () => {
|
||||||
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 () => {
|
|
||||||
const text = <TextEntity>{
|
const text = <TextEntity>{
|
||||||
...makeTextEntity("Welcome to rayjs!"),
|
...makeTextEntity("Welcome to rayjs!"),
|
||||||
size: 80,
|
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
|
||||||
await fadeIn(text, 2)
|
entityAdd(text)
|
||||||
await wait(2)
|
while(!quit){
|
||||||
await fadeOut(text, 2)
|
await fadeIn(text, 2)
|
||||||
text.position.x += 75
|
await wait(2)
|
||||||
text.text = "This Summer!"
|
await fadeOut(text, 2)
|
||||||
await fadeIn(text, 2)
|
text.text = "This Summer!"
|
||||||
}
|
await fadeIn(text, 2)
|
||||||
|
await fadeOut(text, 3)
|
||||||
main()
|
entityRemove(text)
|
||||||
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 { EntityOf, HasColor, HasPosition, makeColorRgb, makeEntity, makePosition } from "./entity"
|
||||||
|
import { fontLoad, resourceUnload } from "./resource"
|
||||||
|
|
||||||
export interface Text extends HasPosition, HasColor {
|
export interface Text extends HasPosition, HasColor {
|
||||||
text: string,
|
text: string,
|
||||||
font: Font,
|
font?: string,
|
||||||
size: number,
|
size: number,
|
||||||
spacing: number
|
spacing: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TextEntity extends EntityOf<Text> {
|
export interface TextEntity extends EntityOf<Text> {
|
||||||
type: "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(),
|
...makePosition(),
|
||||||
...makeColorRgb(),
|
...makeColorRgb(),
|
||||||
text: text,
|
text: text,
|
||||||
font: font === null ? getFontDefault() : loadFont(font),
|
font: font,
|
||||||
size: size,
|
size: size,
|
||||||
spacing: spacing
|
spacing: spacing
|
||||||
})
|
})
|
||||||
|
|
||||||
export const textDraw = (t: Text) => {
|
export const textDrawFn = (t: TextEntity) => drawTextEx(t.fontResource!, t.text, t.position, t.size, t.spacing, t.color);
|
||||||
return drawTextEx(t.font, 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>{
|
export const makeTextEntity = (text: string = "") => (<TextEntity>{
|
||||||
...makeEntity(),
|
...makeEntity(),
|
||||||
...makeText(text),
|
...makeText(text),
|
||||||
type: "text",
|
type: "text",
|
||||||
draw: textDraw,
|
draw: textDrawFn,
|
||||||
|
load: textLoadFn,
|
||||||
|
unload: textUnloadFn
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue