Jump to content

Trying to wrap my head around project structure


Recommended Posts

Hi guys,


I'm not unfamiliar with Javascript, but I come from a very procedural AS3 background so I'm having a hard time wrapping my head around how everything ties together. Would appreciate some help : ). To give a bit of background behind my project, I've always been a fan of Diablo II's character, stats, and inventory system. Not sure what direction I want this experiment to go, but I thought I'd emulate it as closely as possibly for as long as I can. To start, I grabbed the basic game template from the phaser/wip folder. In order, it goes Boot.js to Preloader.js to MainMenu.js to Game.js. I'm jumping straight to Game.js and starting from there (code pasted below).


- I've created the hero object outside of the create function to make it globally accessible. Is this bad practice?

- Text on screen is not important at this point, but I wanted to play around with text and style. I thought I could create different styles up front and call upon them later on to provide some consistency.

- Below the text vars, I have each of the possible character classes as objects with starting stats

- Then, the hero object is created with name and character class passed as arguments. The hero is then given base stats.


I'm not sure how to structure the code from here.


- Would I be creating the inventory management system within create() as well? Monster generation? Item drops? Level generation?

- Should I be using the default update() function to track life/mana/stat changes? I'm worried that tracking changes in hundreds of variables might be too resource heavy.

- On the quitGame() function, I've set hero to null before deleting it and returning to the main menu. Not sure if this is correct.


So many other questions, but a little help with these would give me some much needed direction. Apologies on my coding, I'm working on it! Thanks!

_Game.Game = function (game) {    this.game;		//	a reference to the currently running game    this.add;		//	used to add sprites, text, groups, etc    this.camera;	//	a reference to the game camera    this.cache;		//	the game cache    this.input;		//	the global input manager    this.load;		//	for preloading assets    this.math;		//	lots of useful common math operations    this.sound;		//	the sound manager - add a sound, play one, set-up markers, etc    this.stage;		//	the game stage    this.time;		//	the clock    this.tweens;        //      the tween manager    this.state;	        //	the state manager    this.world;		//	the game world    this.particles;	//	the particle manager    this.physics;	//	the physics manager    this.rnd;		//	the repeatable random number generator    var hero;    _Game.Game.prototype = {	create: function () {        	    var gameTitle = '2D-iablo';	    var gameVersion = '0.0.1';            var h1_style = {font:'16px Arial', fill:'#fff', align:'left'};	    var h2_style = {font:'10px Arial', fill:'#666', align:'left'};    	    var p_style = {font:'10px Arial', fill:'#fff', align:'left'};			    this.add.text(10, 9, gameTitle, h1_style);	    this.add.text(10, 27, 'VERSION ' + gameVersion, h2_style);	 	            var amazon = { charclass:'amazon', life:50, mana:15, str:20, dex:25, vit:20, ene:15, vitalityLifeBonus:3, energyManaBonus:1.5 };            var assassin = { charclass:'assassin', life:50, mana:25, str:20, dex:20, vit:20, ene:25, vitalityLifeBonus:3, energyManaBonus:1.75 };            var necromancer = { charclass:'necromancer', life:45, mana:25, str:15, dex:25, vit:15, ene:25, vitalityLifeBonus:2, energyManaBonus:2 };            var barbarian = { charclass:'barbarian', life:55, mana:10, str:30, dex:20, vit:25, ene:10, vitalityLifeBonus:4, energyManaBonus:1 };            var sorceress = { charclass:'sorceress', life:40, mana:35, str:10, dex:25, vit:10, ene:35, vitalityLifeBonus:2, energyManaBonus:2 };            var druid = { charclass:'druid', life:55, mana:20, str:15, dex:20, vit:25, ene:20, vitalityLifeBonus:2, energyManaBonus:2 };            var paladin = { charclass:'paladin', life:55, mana:15, str:25, dex:20, vit:25, ene:15, vitalityLifeBonus:3, energyManaBonus:1.5 };                    function Hero(charName, charclass) {                this.charName = charName;                this.charclass = charclass;                this.str = this.charclass.str;                this.dex = this.charclass.dex;                this.vit = this.charclass.vit;                this.ene = this.charclass.ene;                this.life = Math.round(this.charclass.life + this.charclass.vit * this.charclass.vitLifeBonus);                this.mana = Math.round(this.charclass.mana + this.charclass.nrg * this.charclass.energyManaBonus);                this.fireRes = 0;                this.coldRes = 0;                this.lightRes = 0;                this.poisonRes = 0;                this.block = 0;                this.dodge = 0;                this.damageReduction = 0;                this.attackSpeed = 0;                this.castSpeed = 0;                this.moveSpeed = 0;                this.goldFind = 0;                this.magicFind = 0;                            console.log(this);            }                    hero = new Hero('Leang', assassin);        	},	update: function () {        	},	quitGame: function (pointer) {	    //	Here you should destroy anything you no longer need.            hero = null;            delete hero;        	    this.state.start('MainMenu');	}};
Link to comment
Share on other sites

Before you progress, you should make some distinctions about some parts of your program.
1. Right now, it looks like there's nothing "Phaser-y" inside the Hero class. If that will always be the case, it would be best to move the Hero function into a new javascript file, Hero.js, that can be loaded before or after phaser.js, but definitely before Game.js.

A. Would also not be a bad idea to make Hero a "class" instead of a function. JavaScript doesn't have true classes, but it does have inheritence: https://alexsexton.com/blog/2013/04/understanding-javascript-inheritance/ You could then make all your character classes into subclasses of Hero so that they can all have individual functionality. If you go this route it would be worth looking into the Factory pattern as well: http://addyosmani.com/resources/essentialjsdesignpatterns/book/#factorypatternjavascript

B. Your "hero" instance of your Hero class isn't technically global, it's confined to the _Game.Game scope, which is where it needs to be in order to be used in both create and update for instance. The only other way would be to make hero a property on game, and that would be almost as bad as a true global anyway.

2. Setting up your text styles up front is a good idea, however, where you've declared them right now, they're only available to the create method of Game.js. You could even pull those out to a file called TextStyles.js and have it look like:

var TextStyles = {    h1_style : {font:'16px Arial', fill:'#fff', align:'left'};    h2_style : {font:'10px Arial', fill:'#666', align:'left'};    p_style : {font:'10px Arial', fill:'#fff', align:'left'};}

Then later: this.add.text(10, 27, 'VERSION ' + gameVersion, TextStyles.h2_style);


3. If your inventory management system is going to be vanilla javascript and have nothing "phaser-y", do the same as I mentioned with the Hero class. Don't clutter your game code if you can avoid it. Remember that the create function is not the only place where you can create sprites etc. You can create them in update as well, just use create to initialize your level. But it's best to only CREATE in create(), not necessarily DEFINE. You could actually move the definition of a monster to an outside file, and just create it in create(). Item drops available to the level should probably be defined here, but you may not want to define the actual items themselves. You can do that in an outside file. And you don't have to create the item instances. You could pass the available items to all spawned monsters in the level, and then when they die, they spawn an item from the available items.


4. There's no way to avoid having tons of update stuff run. Remember that simple things like math are super fast. Drawing is what is slow. I prefer to leave update() itself pretty bare though. If your characters all eventually get represented as Sprites, Sprite.update can be assigned a function that does updates specific to that sprite. That is where I would put most of my code logic. Your game works best if your Sprites can handle logic independent from your update call.


5. Setting "hero" to null is not a bad idea, but deleting might have unintended consequences, I can't tell without playing with it. You might run into issues if you start the game again after quitting as technically the line that declares hero won't get called again (I don't think).


I hope this all makes sense and is helpful. I'm sure there are people on the forum that will disagree, but I think separating your code into many files is crucial for it to be readable and truly modular. If you're worried about loading times, you can always combine and condense before release.

Link to comment
Share on other sites

That clears up a lot! Thanks for the reply, Adam! I'll take a look at the links you provided about inheritance and factory pattern, and separate the hero creation into a separate class that will also manage leveling up and skills, as well as plan for classes for monsters, loot, inventory management, and questing. I'll keep a js file to handle declaring things like text styles in one location. Maybe include strings for dialogue as well. One potentially major problem I've been wondering about is Javascript's ability to handle saving the progress of a game like this. Any ideas on this possibility?


Edit: Since there will be only one hero object, I think the factory pattern is too complex a solution, but inheritance sounds good (I may use the factory pattern on a monsterFactory). To clarify, the new Hero.js file will have a makeHero() function and use Object.create to give var hero the individual traits of specific character classes. If I leave var hero declaration where it is now, at the top of Game.js where it will be fully accessible to Game.js and not global, can I pass hero var to Hero.js using makeHero(hero)? Or will that not work because var hero is confined to _Game.Game?

Link to comment
Share on other sites

Not sure on the first one, would be interested in hearing how other people approached this. Would be a very good thing to have in phaser-examples.
This library looks promising: https://github.com/zefhemel/persistencejs

On your second point, passing in hero would work, because you are passing it in, not referencing it externally. A more conventional looking approach would be to have makeHero actually construct the object so your function call would look like "hero = makeHero()" instead.

Link to comment
Share on other sites

Completely unrelated question now! I'm trying to create key captures for the 10 key pad, which have keycodes 96 and up, but I can't seem to get the syntax right. Googling, forums, and doc have provided no solutions, but it must be possible, right?


Edit: Nevermind, found it in the source files! The 10 keypad seems to work fine, except the Enter key. Odd.

keyEnt = game.input.keyboard.addKey(Phaser.Keyboard.NUMPAD_ENTER );  
Link to comment
Share on other sites


  • Recently Browsing   0 members

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