Jump to content

Subscribing classes to world update?


NewGuy
 Share

Recommended Posts

Hey, people. 

Weird question, I'd like to build a class 'EventController' who will, in the long run, control things like timing, item spawning, special events etc. and I feel like having an update method would be appropriate, however, I don't believe the world picks up the Update method for any old class and I'd like to avoid simply extending a class that has an update. (or is this my only option)

I guess my question is how can I subscribe my classes to the world update OR maybe a suggestion for a more personal timer to the class perhaps? (I've considered just having a timer that periodically goes off and does checks there?)

Any words welcomed!  :)

Link to comment
Share on other sites

So far I've implemented essentially what I wanted. Still feels like a hack in a way

 

    ...    export class EventController {        eventInProgress: boolean;        currentEvent: number;        currentDifficulty: number;        currentProgress: number;        updateFrequency: number;        constructor(private _game) {            this.eventInProgress = false;            this.currentEvent = 0;            this.currentDifficulty = 0;            this.updateFrequency = 1; //Time in seconds between updates            //Whipped up a game loop            _game.time.events.loop(Phaser.Timer.SECOND * this.updateFrequency, this.eventUpdate, this);        }        eventUpdate() {            this.currentProgress = Math.floor(_game.scrollTotal);            console.log(this.currentProgress.toString() + " meters!");        }    ...

It works well enough and it also allows you to adjust how often the update goes off. I'd still like to hear if there is a more proper way of adding the world update to classes? 

Link to comment
Share on other sites

Do you intend to have a single instance of this object, just to help organize your timing-related code in one class, or will there be multiple instances?

 

If just a single instance, then I would just call myEventController.update(); from the game state's update function.

 

If multiple instances, then read on. :)

 

If you're anything like me, then you know that you want your code organized into classes for your own sanity and organization. But now you've created this "timer" class that gets instantiated once and then disappears into the background and just does stuff "magically" on its own. I recently(ish) solved a similar problem in my game and I was happy to extend Phaser.Sprite to take advantage of their inbuilt update functions. The solution I used focused on making the game state a place to "compose" all of my other classes from. Since everything is triggered from the game state (and nothing happens "magically" in the background), it's easier for me to untangle the spaghetti when I need to troubleshoot in the future.

 

Here's a quick overview (JavaScript, not TypeScript, so sorry if the following syntax doesn't match yours):

 

In my game state's create() function, I initialize a bunch of enemy spawners that extend Phaser.Sprite, which I place in a Phaser Group:

// Create Group for Spawners.spawners = new Group(game);// Populate spawners from map data.// Developer's note: for this demo, all you need to know is that this populates my group with Spawners, which extend Phaser.Sprite.spawners = ObjectLayerHelper.createObjectsByType(game, 'spawner', map, 'spawners', Spawner, spawners);// Register events for all of my spawners.spawners.forEach(this.registerSpawnerEvents, this);// Add the whole group of spawners to the world so update() gets called "automatically".game.add.existing(spawners);

Then I have a few other functions defined for handling event registration and the like:

// Register spawner events.registerSpawnerEvents: function (spawner) {    // Register enemy events for all sprites already in the spawner's pool.    spawner.sprites.forEach(this.registerEnemyEvents, this);        // Listen for new enemies this spawner might spawn, so we can register their events eventually, too.    spawner.events.onSpawn.add(this.onSpawnerSpawn, this);},// Register enemy events.registerEnemyEvents: function (enemy) {    // Register basic events that all enemies have    enemy.events.onDeath.add(this.onEnemyDeath, this);    enemy.events.onDrop.add(this.onEnemyDrop, this);    // If onSpawnChild is defined for an enemy, treat it like another spawner.    // Listen for new enemies this spawner might spawn, so we can register their events eventually, too.    if (enemy.events.onSpawnChild) enemy.events.onSpawnChild.add(this.onSpawnerSpawn, this);},// Helper function for scoping event registration on newly spawned enemies.onSpawnerSpawn: function(spawner, sprite) {    this.registerEnemyEvents(sprite);},

So that's all stuff that happens when my game state is created. I'll post a quick follow-up with my Spawner code so you can see that, too.

Link to comment
Share on other sites

As promised, here's the code for my Spawner class.

 

Some notes:

  • I don't have time to go through and add nice comments for you right now, but feel free to ask questions. :)
  • I use RequireJS to manage requirements. That's what the first few lines are all about. You can pretty much ignore that part...
  • The Spawner handles all of the logic for deciding whether it should spawn an enemy on any given update().
define([    'phaser'], function (Phaser) {     'use strict';    // Shortcuts    var game, self;    function Spawner (_game, x, y, key, frame, properties) {        game = _game;        self = this;        // Initialize sprite        Phaser.Sprite.call(this, game, x, y, 'spawner');        this.renderable = false;                // Spawn settings        this.maxSpawned = 1;        this.spawnRate = 500; // Delay to spawn, in ms        this.isFresh = true;                // Sprites spawned        this.sprites = game.add.group();        this.sprites.x = 0;        this.sprites.y = 0;        this.sprites.classType = game.spriteClassTypes[properties.key];        this.sprites.createMultiple(this.maxSpawned, properties.key, 1, true);        this.sprites.setAll('x', this.x);        this.sprites.setAll('y', this.x);        this.sprites.callAll('kill');                // Spawn timer        this.spawnTimer = game.time.create(false);        this.spawnTimer.start();         // Signals        this.events.onSpawn = new Phaser.Signal();                game.physics.enable(this);        this.body.immovable = true;        this.body.allowGravity = false;    }    Spawner.prototype = Object.create(Phaser.Sprite.prototype);    Spawner.prototype.constructor = Spawner;    Spawner.prototype.update = function () {        if (this.inCamera) {            // Attempt to spawn when the spawner is within the camera bounds.            this.spawn();        }        else {            this.isFresh = true;        }        // Call up!        Phaser.Sprite.prototype.update.call(this);    };    function onCooldownComplete () {        // No action necessary?    }    Spawner.prototype.spawn = function () {        var sprite;        if (!this.spawnTimer.duration && this.isFresh) {            sprite = this.sprites.getFirstDead();            if (sprite) {                sprite.revive();                sprite.x = this.x;                sprite.y = this.y;                this.events.onSpawn.dispatch(this, sprite);                if (!this.sprites.getFirstDead()) {                    this.isFresh = false; // fresh out!                }                this.spawnTimer.add(this.spawnRate, onCooldownComplete, this);            }            else{                this.isFresh = false; // fresh out!            }        }    };    return Spawner;});
Link to comment
Share on other sites

If you only need one instance across the lifetime of your game then you should definitely check out Phaser plugins... as in, literally, extending the Phaser.Plugin class and adding it via the plugin manager like so: "game.plugins.add(NameOfMyPluginClass);"

 

The plugin manager will instantiate your plugin and its methods (create, update, whatever) will get called by Phaser.

Link to comment
Share on other sites

On 1/6/2016 at 5:52 PM, zatch said:

Do you intend to have a single instance of this object, just to help organize your timing-related code in one class, or will there be multiple instances?

 

If just a single instance, then I would just call myEventController.update(); from the game state's update function.

 

If multiple instances, then read on. :)

 

If you're anything like me, then you know that you want your code organized into classes for your own sanity and organization. But now you've created this "timer" class that gets instantiated once and then disappears into the background and just does stuff "magically" on its own. I recently(ish) solved a similar problem in my game and I was happy to extend Phaser.Sprite to take advantage of their inbuilt update functions. The solution I used focused on making the game state a place to "compose" all of my other classes from. Since everything is triggered from the game state (and nothing happens "magically" in the background), it's easier for me to untangle the spaghetti when I need to troubleshoot in the future.

 

Here's a quick overview (JavaScript, not TypeScript, so sorry if the following syntax doesn't match yours):

 

In my game state's create() function, I initialize a bunch of enemy spawners that extend Phaser.Sprite, which I place in a Phaser Group:


// Create Group for Spawners.spawners = new Group(game);// Populate spawners from map data.// Developer's note: for this demo, all you need to know is that this populates my group with Spawners, which extend Phaser.Sprite.spawners = ObjectLayerHelper.createObjectsByType(game, 'spawner', map, 'spawners', Spawner, spawners);// Register events for all of my spawners.spawners.forEach(this.registerSpawnerEvents, this);// Add the whole group of spawners to the world so update() gets called "automatically".game.add.existing(spawners);

Then I have a few other functions defined for handling event registration and the like:


// Register spawner events.registerSpawnerEvents: function (spawner) {    // Register enemy events for all sprites already in the spawner's pool.    spawner.sprites.forEach(this.registerEnemyEvents, this);        // Listen for new enemies this spawner might spawn, so we can register their events eventually, too.    spawner.events.onSpawn.add(this.onSpawnerSpawn, this);},// Register enemy events.registerEnemyEvents: function (enemy) {    // Register basic events that all enemies have    enemy.events.onDeath.add(this.onEnemyDeath, this);    enemy.events.onDrop.add(this.onEnemyDrop, this);    // If onSpawnChild is defined for an enemy, treat it like another spawner.    // Listen for new enemies this spawner might spawn, so we can register their events eventually, too.    if (enemy.events.onSpawnChild) enemy.events.onSpawnChild.add(this.onSpawnerSpawn, this);},// Helper function for scoping event registration on newly spawned enemies.onSpawnerSpawn: function(spawner, sprite) {    this.registerEnemyEvents(sprite);},

So that's all stuff that happens when my game state is created. I'll post a quick follow-up with my Spawner code so you can see that, too.

Late response, sorry! This wasn't what I was looking for but ended up being a really cool idea that I essentially implemented.

You guys are all awesome. (:

Link to comment
Share on other sites

On 1/6/2016 at 4:38 AM, drhayes said:

If you only need one instance across the lifetime of your game then you should definitely check out Phaser plugins... as in, literally, extending the Phaser.Plugin class and adding it via the plugin manager like so: "game.plugins.add(NameOfMyPluginClass);"

 

The plugin manager will instantiate your plugin and its methods (create, update, whatever) will get called by Phaser.

This was more to what I was looking for, thanks, drhayes.

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...