2D Game Engine

Sceny

Cykl życia sceny, SceneManager, dane startowe, fabryki obiektów i automatyczne sprzątanie przy shutdown.

Cykl życia sceny#

text
preload() → create() → update(dt) / fixedUpdate(dt) → shutdown()
FazaKiedyPo co
preload()Przed createDeklaracja i ładowanie assetów (declareAssets)
create()Raz, po załadowaniuBudowanie świata gry
update(dt)Co klatkęLogika gry
fixedUpdate(dt)Co stały krokFizyka i deterministyczna logika
shutdown()Przy zmianie scenySprzątanie (silnik robi większość za Ciebie)

Definiowanie sceny#

ts
import { Scene, type AssetDescriptor } from '@engine'

export class GameScene extends Scene {
  constructor() {
    super('game')
  }

  declareAssets(assets: AssetDescriptor[]) {
    super.declareAssets([
      { id: 'hero', type: 'spritesheet', src: '/assets/hero.png', frameWidth: 32, frameHeight: 32 },
      { id: 'coin', type: 'image', src: '/assets/coin.png' },
      { id: 'pickup', type: 'audio', src: '/assets/pickup.ogg' }
    ])
  }

  async preload() {
    await this.getAssets().load()
  }

  create() {
    this.createSprite('hero', { x: 100, y: 300, tags: ['player'] })
    this.createText('score', { x: 16, y: 16, text: '0', fontSize: '18px', color: '#fff' })
  }

  update(dt: number) {
    const player = this.findByTag('player')[0]
    if (this.getInput().isActionDown('move-right')) {
      player.translate(220 * dt, 0)
    }
  }
}

Rejestracja i przełączanie#

ts
engine.registerScene(new MenuScene())
engine.registerScene(new GameScene())
engine.start('menu')

engine.startScene('game', { level: 3 })
engine.stopScene('menu')

SceneManager pilnuje unikalności id — duplikat rzuca błąd przy rejestracji.

ts
const scenes = engine.getScenes()
scenes.has('game')
scenes.getActiveSceneId()
scenes.switch('menu', 'game', { level: 1 })
scenes.list()

Dane startowe#

Scena odbiera dane przez getData<T>():

ts
class GameScene extends Scene {
  create() {
    const { level } = this.getData<{ level: number }>()
    this.createText('level', { x: 16, y: 16, text: `Poziom ${level}` })
  }
}

engine.startScene('game', { level: 5 })

Fabryki obiektów#

ts
createObject(options?): GameObject
createSprite(textureId: string, options?): GameObject
createText(text: string, options?): GameObject
createRect(width, height, options): GameObject
createEllipse(radius, options): GameObject

Wyszukiwanie i grupy#

ts
getObject<T>(id: string): T | undefined
find(predicate); findAll(predicate)
findByName(name); findByTag(tag); findByTags(tags, mode?)
group(name: string): Group
getGroupMembers(name); getGroups()
each(fn: (object: GameObject) => void): void
ts
const enemies = this.findByTag('enemy')
const bosses = this.findByTags(['enemy', 'boss'], 'all')

this.group('projectiles')
for (const bullet of this.getGroupMembers('projectiles')) {
  bullet.translate(400 * dt, 0)
}

Sprzątanie przy shutdown#

shutdown() sceny automatycznie czyści:

  • rejestr obiektów i indeks tagów,
  • grupy,
  • subskrypcje zdarzeń przypisane do sceny,
  • tweeny (stopAll),
  • dźwięki właściciela (stopOwner),
  • uchwyty assetów (releaseOwner).
ts
class GameScene extends Scene {
  shutdown() {
    this.saveProgress()
  }
}

Informacja

Nie musisz ręcznie usuwać obiektów przy zmianie sceny — silnik zrobi to za Ciebie. Własny shutdown() służy tylko do logiki biznesowej (zapis, statystyki, analytics).

Animacje klatkowe#

ts
this.playAnimation(player, 'hero:run')
this.pauseAnimation(player, true)
this.setFrame(player, 3)
this.getAnimationState(player)
this.stopAnimation(player)

Klucz animacji ma format <assetId>:<key> i jest rejestrowany przez backend podczas ładowania spritesheeta.