Chimera Posted July 19, 2014 Share Posted July 19, 2014 I know there are a few threads and tutorials on this already, but I cannot get my head around some of these concepts. I am just working on some small games, actually decided to create my own version of Lessmilks coin grabber since it has some addicting gameplay. What I want to do now is create classes for my player, enemies, and level to organize the code, but I am not sure how to organize/nest the classes in Phaser and call them from some sort of working class to pull it all together. Can anyone take a look and recommend a tutorial or provide some insight? Current work flowEach step is a separate file and state, and I am switching states to load the next classPhaser --> Boot --> Load --> Menu --> Play (game) --> gameover --> menu (repeat) What I want is some way to separate the different components of the game and call them from some external class. I have never done this kind of programming so it is kinda of hard for me to follow. I was reusing a lot of code with the enemies because I was creating different types of enemies which all have the same properties. I wanted to create a base enemy class and create new instances of that enemy, but using my current method I cant find a way for it to work.PhaserPlayer, Enemy, LevelWorker --> player/enemy/level Challenges, Using this new abstract method in my head, I am not sure how to change game states since the classes which contain player, enemy, and level are all required by each state. Would I have to manage the states myself and create some sort of destroy function in each class that is called when moving from one level to another? Link to comment Share on other sites More sharing options...
Red Spark Posted July 20, 2014 Share Posted July 20, 2014 There are lots of different schools of organizing game code, and none of them are objectively right or wrong. Over the years I've come with my own framework that is basically multi-agent system that can be built upon any other framework or engine (and Phaser too). It consists of a two basic classes: GameObject and GameSystem, and a mediating environment for event flow: Event. In terms of phaser GameObject extends Phaser.Group and is completely proactive, which means it completely manages its state, creates images and sprites inside itself, updates itself, adds itself to the world and removes from it. GameSystem extends Phaser.Plugin and is proactive too, but it's not a display object, but rather a static class with a collection of methods. All the game objects in the game like players, weapons, monsters and such are extended from the GameObject, and all the managers like Physics, Sound, etc. are extended from GameSystem. Custom Event class is basically a global class to manage even flow throughout the entire object pool: you can use its static methods like Event.register (E.SomethingHappened, this.onSomethingHappened, this) and Event.dispatch (E.SomethingHappened, {x:2, txt: 'hehe', b:true}) inside your objects and systems to setup effective communication between them. E is just a class with predefined events with float values assigned to them. The key feature of this framework is unprecedented encapsulation, which I find very pleasing for my workflow. Objects and systems know practically nothing about each other - all they do is:1) subscribe to some events with internal callbacks, usually at initialization state2) when some event fires, callback changes something in this object or system3) when something important happens in the object, it dispatches an event along with data for others to receive4) event is dispatched to the 'outside', and this object/system doesn't know what objects/systems have received it Other than that, I'm using all major game pieces as static classes, and utilize factories inside them, so I could always do things like Player.current.doSomething(), Bullet.instance(n).destroy(), Missile.createNew (), etc, without storing private variables of class A inside class B. That's practically it. Link to comment Share on other sites More sharing options...
Chimera Posted July 20, 2014 Author Share Posted July 20, 2014 I'll be honest, I don't fully grasp your method. That is not because you haven't explained it well, just my lack of programming experience I would say. I understand aspects of it, but I have trouble visualizing how I would be able to apply it to my own project. Your intention was likely just to show me one method of how the code can be organized, not necessarily to do it your way, which is appreciated, just wish some of these concepts 'clicked' a bit fast . Using your example, where would you store a tween animation for your player or level? Is the event class declared outside of the phaser groups to make it global? Link to comment Share on other sites More sharing options...
Danny00014 Posted July 20, 2014 Share Posted July 20, 2014 When I looked at the code from end3r's Monster Wants Candy it changed how I did things. http://www.html5gamedevs.com/topic/6893-monster-wants-candy/ I'm not a Javascript super wizard, this is my first large undertaking with Phaser, I don't 100% understand .prototype and why it does what it does but using the same organizational methods end3r uses has really made it quite easy to keep things clean and modular. He has it on GitHub so I suggest you take a look. https://github.com/EnclaveGames/Monster-Wants-Candy end3r 1 Link to comment Share on other sites More sharing options...
Chimera Posted July 20, 2014 Author Share Posted July 20, 2014 Thank you Danny for the reply, it looks like the github link does not exist though, is there an alternate link? Link to comment Share on other sites More sharing options...
Red Spark Posted July 20, 2014 Share Posted July 20, 2014 Using your example, where would you store a tween animation for your player or level? Is the event class declared outside of the phaser groups to make it global? Phaser.Game already has a tweens variable that is intended for that. You just pass a game variable to every new object or system you create and then use all phaser goods internally, when reacting to events, like this.game.sound.volume = 0.5, this.game.time.now, this.game.world.add (this). etc. I'm using TypeScript so on top of that I extend Phaser.Game as Game and Phaser.State as LoaderState, MenuState, PlayState, etc. In JS you can do it with extending prototype chain. All high-order game logics always happens inside states anyway, even when not using my framework. Link to comment Share on other sites More sharing options...
Chimera Posted July 22, 2014 Author Share Posted July 22, 2014 Alright, so I have been trying to rework my code into separate classes that make sense and I am still getting stuck on how to make the code better, more object oriented. Can someone take a look at the code below, this is the single class I am trying to separate up. var playState = { create: function() { // Create player sprite/object this.player = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, 'player'); // Set player animations from spritesheet this.player.animations.add('static', [0], 1, true); this.player.animations.add('left', [1], 1, true); this.player.animations.add('right', [2], 1, true); this.player.animations.add('jump', [3], 1, true); this.player.animations.add('down', [4], 1, true); // Set anchor point this.player.anchor.setTo(0.5, 0.5); // Enable physics for player object this.game.physics.arcade.enable(this.player); // Set gravity to player object this.player.body.gravity.y = 500; // Initialize game sounds this.bgmLoop = this.game.add.audio('bgmLoop'); this.enemyDie = this.game.add.audio('enemyDie'); this.uniqueEnemySpawn = this.game.add.audio('uniqueEnemySpawn'); this.pickupCoin = this.game.add.audio('pickupCoin'); this.playerDie = this.game.add.audio('playerDie'); this.playerJump = this.game.add.audio('playerJump'); // Start bgm loop this.bgmLoop.loop = true; this.bgmLoop.play(); // Creating an emitter for pixel partical effect this.emitter = this.game.add.emitter(0, 0, 15); this.emitter.makeParticles('pixel'); this.emitter.setYSpeed(-150, 150); this.emitter.setXSpeed(-150, 150); this.emitter.gravity = 0; // Setup controller object this.cursor = this.game.input.keyboard.createCursorKeys(); // Assigns these keys to the game object to prevent the browser from utilizing them this.game.input.keyboard.addKeyCapture([Phaser.Keyboard.UP, Phaser.Keyboard.DOWN, Phaser.Keyboard.LEFT, Phaser.Keyboard.RIGHT]); // Setup WASD as alternate keys this.wasd = { up: this.game.input.keyboard.addKey(Phaser.Keyboard.W), left: this.game.input.keyboard.addKey(Phaser.Keyboard.A), right: this.game.input.keyboard.addKey(Phaser.Keyboard.D), down: this.game.input.keyboard.addKey(Phaser.Keyboard.S), }; // Create level this.createWorld(); // Create coins this.coin = this.game.add.sprite(60, 140, 'coin'); this.game.physics.arcade.enable(this.coin); this.coin.anchor.setTo(0.5, 0.5); // Add score text this.scoreLabel = this.game.add.text(30, 30, 'score: 0', { font: '18px Arial', fill: '#ffffff' }); this.score = 0; // Add Enemy this.enemies = this.game.add.group(); this.enemies.enableBody = true; this.enemies.createMultiple(10, 'enemy'); this.game.time.events.loop(2200, this.addEnemy, this); }, createWorld: function() { // Create our wall group with Arcade physics this.walls = this.game.add.group(); this.walls.enableBody = true; // Create the 10 walls this.game.add.sprite(0, 0, 'wallV', 0, this.walls); // Left this.game.add.sprite(480, 0, 'wallV', 0, this.walls); // Right this.game.add.sprite(0, 0, 'wallH', 0, this.walls); // Top left this.game.add.sprite(300, 0, 'wallH', 0, this.walls); // Top right this.game.add.sprite(0, 330, 'wallH', 0, this.walls); // Bottom left this.game.add.sprite(300, 330, 'wallH', 0, this.walls); // Bottom right this.game.add.sprite(-100, 160, 'wallH', 0, this.walls); // Middle left this.game.add.sprite(400, 160, 'wallH', 0, this.walls); // Middle right var middleTop = this.game.add.sprite(100, 80, 'wallH', 0, this.walls); middleTop.scale.setTo(1.5, 1); var middleBottom = this.game.add.sprite(100, 240, 'wallH', 0, this.walls); middleBottom.scale.setTo(1.5, 1); // Set all the walls to be immovable this.walls.setAll('body.immovable', true); }, movePlayer: function() { // Left movement if (this.cursor.left.isDown || this.wasd.left.isDown) { this.player.body.velocity.x = -200; this.player.animations.play('left'); } // Right movement else if (this.cursor.right.isDown || this.wasd.right.isDown) { this.player.body.velocity.x = 200; this.player.animations.play('right'); } // Velocity set to 0 if no key pressed else { this.player.body.velocity.x = 0; this.player.animations.play('static'); } // Jump if up key is pressed if (this.cursor.up.isDown || this.wasd.up.isDown) { this.player.animations.play('jump'); if (this.player.body.touching.down) { this.player.body.velocity.y = -320; this.playerJump.play(); } } else if (this.cursor.down.isDown || this.wasd.down.isDown) { this.player.animations.play('down'); } }, playerDeath: function() { this.game.global.score = this.score; this.player.kill(); this.playerDie.play(); this.emitter.x = this.player.x; this.emitter.y = this.player.y; this.emitter.start(true, 1000, null, 20); this.game.time.events.add(1000, this.gameOver, this); }, gameOver: function() { this.game.state.start('gameover'); this.bgmLoop.stop(); }, updateCoinPosition: function() { // Store all the possible coin positions in an array var coinPosition = [ {x: 140, y: 60}, {x: 360, y: 60}, // Top row {x: 60, y: 140}, {x: 440, y: 140}, // Middle row {x: 130, y: 310}, {x: 370, y: 310} // Bottom row ]; // Remove the current coin position from the array // Otherwise the coin could appear at the same spot twice in a row for (var i = 0; i < coinPosition.length; i++) { if (coinPosition[i].x === this.coin.x) { coinPosition.splice(i, 1); } } // Randomly select a position from the array var newPosition = coinPosition[ this.game.rnd.integerInRange(0, coinPosition.length-1)]; // Set the new position of the coin this.coin.reset(newPosition.x, newPosition.y); }, takeCoin: function(player, coin) { // Play coin sound this.pickupCoin.play(); // Update score by 5 points this.score += 5; // Update score label on screen this.scoreLabel.text = 'score: ' + this.score; // Change coin position this.updateCoinPosition(); }, addEnemy: function() { // Create new enemy if one exists var enemy = this.enemies.getFirstDead(); // If no enemies exists execute a return ending the function if(!enemy) { return; } // If the function has not ended then proceed to create new enemy with below properties enemy.anchor.setTo(0.5, 1); enemy.reset(this.game.world.centerX, 0); enemy.body.gravity.y = 500; enemy.body.velocity.x = 100 * Phaser.Math.randomSign(); enemy.body.bounce.x = 1; enemy.body.bounce.y = 0; enemy.scale.setTo(1, 1); enemy.checkWorldBounds = true; enemy.outOfBoundsKill = true; var num = this.game.rnd.integerInRange(0, 100); if (this.score > 40 && num < 15) { enemy.body.velocity.x = 60 * Phaser.Math.randomSign(); enemy.body.gravity.y = 250; enemy.scale.setTo(1.5, 1.5); console.log('+++ Big Boy +++'); this.uniqueEnemySpawn.play(); } else if (this.score > 120 && num > 15 && num < 30) { enemy.body.velocity.x = 200 * Phaser.Math.randomSign(); enemy.scale.setTo(1, 1); console.log('+++ Speedy +++'); this.uniqueEnemySpawn.play(); } else if (this.score > 80 && num > 30 && num < 45) { enemy.body.velocity.x = 100 * Phaser.Math.randomSign(); enemy.scale.setTo(.8, .8); enemy.body.bounce.y = .70; enemy.body.gravity.y = 500; console.log('+++ Bouncy +++'); this.uniqueEnemySpawn.play(); } console.log('Enemy Integer: ' + num); console.log('Enemy Velocity: ' + enemy.body.velocity.x); console.log('Enemy BounceX: ' + enemy.body.bounce.x); console.log('Enemy BounceY: ' + enemy.body.bounce.y); console.log('Enemy Gravity: ' + enemy.body.gravity.y); console.log('Enemy ScaleX: ' + enemy.scale.x); console.log('Enemy ScaleY: ' + enemy.scale.y); console.log('\n'); }, update: function() { // Adds collision between player and the level ground/walls this.game.physics.arcade.collide(this.player, this.walls); // Calls the movePlayer() function every frame checking if any keys have been pressed this.movePlayer(); // Checks if player is outside of the world boundry and calls playerDeath() function if true if (!this.player.inWorld) { this.playerDeath(); } // Checks if the player and coin objects are overlapping and calls takeCoin if true this.game.physics.arcade.overlap(this.player, this.coin, this.takeCoin, null, this); // Make enemies collide with ground/walls this.game.physics.arcade.collide(this.enemies, this.walls); // Call the playerDeath() function when the player and an enemy overlap this.game.physics.arcade.overlap(this.player, this.enemies, this.playerDeath, null, this); },}; Link to comment Share on other sites More sharing options...
SignalSin Posted July 22, 2014 Share Posted July 22, 2014 I can't really write the class for you at the moment, but I'm working on something very similar at the moment. You can look at it on my github. I currently have something similar to what you're after with a main class, plus a player and enemy class. It's very basic at the moment and can/will be refactored over time (hopefully ). It might not be particularly great code, but it works and does what you're trying to do. Also look at the best answer for this thread here. I found it very useful when building my other classes. Link to comment Share on other sites More sharing options...
end3r Posted July 22, 2014 Share Posted July 22, 2014 Thank you Danny for the reply, it looks like the github link does not exist though, is there an alternate link? Due to some people who took the source code of the game, rip off the links and logos and was selling it on CodeCanyon, also put adverts and was selling it across various app stores and portals and destroying my business I had to take the full sources of some of the games down from GitHub.You can still see the commented source code of the demo on GitHub with the structure and basic functions, you can also read the long tutorial about it on Tuts+ Game Development website. Link to comment Share on other sites More sharing options...
Chimera Posted July 23, 2014 Author Share Posted July 23, 2014 Thank you Signalsin and End3r, I will take a look at the links and see if that helps. I started building empty classes to help define them a bit better, and that seems to be helping as well. Easier to visualize what each class will be doing when you don't clutter it up with the actual code . I figure I can create a player, enemy, and maybe a level class, then create a worker class which actually has the logic and game update method etc. I will take a look at your links before going further to see if that changes how I approach writing this, and will probably post the results after for critique . Link to comment Share on other sites More sharing options...
Danny00014 Posted July 24, 2014 Share Posted July 24, 2014 Due to some people who took the source code of the game, rip off the links and logos and was selling it on CodeCanyon, also put adverts and was selling it across various app stores and portals and destroying my business I had to take the full sources of some of the games down from GitHub.You can still see the commented source code of the demo on GitHub with the structure and basic functions, you can also read the long tutorial about it on Tuts+ Game Development website. Wow end3r that is crazy. Sorry to hear it. I suppose you have to be careful here. Link to comment Share on other sites More sharing options...
Recommended Posts