NewGuy Posted January 5, 2016 Share Posted January 5, 2016 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 More sharing options...
NewGuy Posted January 6, 2016 Author Share Posted January 6, 2016 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 More sharing options...
zatch Posted January 6, 2016 Share Posted January 6, 2016 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. NewGuy 1 Link to comment Share on other sites More sharing options...
zatch Posted January 6, 2016 Share Posted January 6, 2016 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 More sharing options...
drhayes Posted January 6, 2016 Share Posted January 6, 2016 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. NewGuy 1 Link to comment Share on other sites More sharing options...
NewGuy Posted January 12, 2016 Author Share Posted January 12, 2016 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 More sharing options...
NewGuy Posted January 12, 2016 Author Share Posted January 12, 2016 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 More sharing options...
Recommended Posts