View Full Version : Complex Game Structure?
grandpaw broon
February 3rd, 2009, 08:28 AM
Hi all,
I have quite a complex game in development at the moment and i am struggling with the architecture.
I might have a few questions, and id appreciate if you could give me advice. Some of them might seem basic but i need to get rid of the doubts, often i read books, forums and blogs and get conflicting advice.
Firstly, in terms of larger games, do you use ineritance or composition for display objects?
public class GridDisplay extends Sprite
{
public function GridDisplay()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(): void
{
//draw your grid here
}
}Usage:
var grid:GridDisplay = new GridDisplay();
addChild(grid);Or is it best to use composition?
public class GridDisplay
{
private var _sprite:Sprite;
public function GridDisplay(gridSprite:Sprite)
{
init()
}
private function init(): void
{
_sprite.? //draw your grid here
}
public function moveToX(number:Number): void
{
_sprite.x = number;
}
}Usage:
var gridSprite:Sprite = new Sprite();
gridSprite.x = 400;
gridSprite.y = 400;
addEventListener(gridSprite);
var grid:GridDisplay = new GridDisplay(gridSprite);
grid.moveToX(100);
What is the best approach on this?
Thanks!
substance
February 3rd, 2009, 08:54 AM
Personally, I think it's better to approach your classes as abstract objects, not at images or pictures. I like to keep my display different from action.
public class Entity {
protected var _position:Vector2D;
protected var _graphic:Sprite;
public function Entity(_desiredGraphic:Sprite, _x:Number = 0, _y:Number = 0){
_position = new Vector2D(_x,_y);
_graphic = _desiredGraphic;
}
public function draw():void{
_graphic.x = _position.x;
_graphic.y = _position.y;
}
grandpaw broon
February 3rd, 2009, 09:53 AM
Personally, I think it's better to approach your classes as abstract objects, not at images or pictures. I like to keep my display different from action.
public class Entity {
protected var _position:Vector2D;
protected var _graphic:Sprite;
public function Entity(_desiredGraphic:Sprite, _x:Number = 0, _y:Number = 0){
_position = new Vector2D(_x,_y);
_graphic = _desiredGraphic;
}
public function draw():void{
_graphic.x = _position.x;
_graphic.y = _position.y;
}
Thanks for your reply.
See _desiredGraphic? Where does this come from?
If i have an EntityHolder Class which does not extend sprite then i am left with this:
Main > EntityHolder > Entity
Main is the only valid reference to the display list?
so
EntityHolder takes reference to Main (or stage), EntitlyHolder then uses this reference to create a new sprite before an Entity object is created?
private var _referenceGraphic:Sprite; //in this case, Main/stage
private var _entitySprite:Sprite; //a newly created sprite
private var _entity:Entity; //to use with the entity
public function EntityHolder(referenceGraphic:Sprite)
{
_referenceGraphic = referenceGraphic;
init();
}
private function init(): void
{
_entitySprite = new Sprite();
_referenceGraphic.addChild(_entitySprite);
_entity = new Entity(_referenceGraphic, 100, 100);
}
substance
February 3rd, 2009, 09:30 PM
Sure, that's one way to do it.
Another method is the 'Object Factory' method, and Object Factory is basically a class that creates all objects for you, giving them an ID and setting any initial parameters, it also passes a reference to the stage to any object it creates.
So instead of trying to create a class directly, you tell the factory object what kind of object you want to create, it creates it and fills in any variables you want it to initially setup and then gives you back a reference to the object it created.
grandpaw broon
February 4th, 2009, 05:23 AM
Hey mate,
Thanks for clearing this up for me, you have been most helpful and for once im not lost!
Cheers!
dandylion13
February 5th, 2009, 01:03 AM
Hi,
You might just want to take a short break from your coding and spend a bit of time flipping through AS3.0 Design Patterns by Sanders and Cumaranatunge. It can be really helpful. Between Singletons, Factory Pattern and AS3.0's event model, that should cover you well for most games. A good look at MVC pattern and Strategy would also help in many situations. I went through a lot of architecture hand-wringing about a year ago starting to learn design patterns really helped. The Joey Lott book is excellent too and has some excellent examples.
Good luck.. sounds like a fun project!
grandpaw broon
February 9th, 2009, 01:31 PM
Alright pal,
Yeah you hit the nail on the head. When my projects get more complicated then i get lost in code so i stopped coding and read both the Actionscript 3.0 design pattern books i got from amazon.
However there is still many basic things i do not understand about the language.
The display list is still bothering me.
I decided over the weekend to go for a Singleton DisplayManager class whose job it is during instantiation to build the display list.
So whereas last week i was used to:
public class Foo extends Sprite
{
public function Foo(): void
{
//do stuff
}
}Now i do this
public class Foo extends DisplayEntity
{
protected var _sprite:Sprite; //this is in the superclass
public function Foo(sprite:Sprite)
{
_sprite = sprite;
//do stuff to sprite
}
}And to use the above class i am doing:
var foo:Foo = new Foo(DisplayManager.getInstance().FooDisplayObject //returns sprite)If i can successfully understand the seperation of display objects from my classes then i might appreciate the books more!
Also it feels like i am going crazy on the singletons. Both my sandbox and my UI for example need to know a width and height amongst colors and other things. So i dont know if its best to create a Settings Singleton to hold this information or to put it the constructor.
ie:
public class Foo extends DisplayEntity
{
protected var _sprite:Sprite; //this is in the superclass
public function Foo(sprite:Sprite, width:Number, height:Number)
{
_sprite = sprite;
_sprite.drawASquareHere(width, height);
//do stuff to sprite
}
}
or
public class Foo extends DisplayEntity
{
protected var _sprite:Sprite; //this is in the superclass
public function Foo(sprite:Sprite)
{
_sprite = sprite;
_sprite.drawASquareHere(Settings.getInstance().foo Width, Settings.getInstance().fooHeight;
//do stuff to sprite
}
}
dandylion13
February 10th, 2009, 04:05 PM
Hi,
I must say I'm not entirely sure what you're trying to accomplish (my problem, not yours!) but here is something I've found helpful working with singletons and game design that isn't directly covered in any of the design pattern books:
- You can create a GameManager singleton.
- Create an instance of the GameManager in every class of objects that will be used in the game. You create this instance inside the object classes using compostion.
- Have the object classes register themselves with an array in the GameManager which stores a list of all game items. (You can break this down into different arrays to seperate different types of objects if you need to.)
- Once you've done this, you can access all of the properties of the objects in the GameManager singleton. (Usually the GameManager needs access to the stage too, so you can add it to the stage using addChild() in the document class.)
- Code all your game logic in the GameManager.
- When something important happens in the game, flag it by triggering an event.
- Have all your game objects add listeners to the GameManager to listen for events that are relevent to them, and then build the code for reacting to those events in the object classes. AS3.0's Event system is analogous to the Obsever design pattern.
So in general, you build your game objects using inheretence, but link them to the GameManager using composition (you instantiate the manager in the object classes, not the other way around). This is just a very general model, but works well for games. You can implement more specific design patterns to solve particular problems you might have later once this model is in place. I'm working through some of these same problems myself at the moment and will post some more specifics when take I look back at the code I'm using :)
Regarding the display list, it's an array of instances of classes that have been added to the stage. Anything on the stage is on the display list. You can access this list, but usually you don't need to build a game. It's not more complex than that.
You're also right that most of the books on Design Patterns (and AS3.0 coding in general) don't specifically address the practical ways of working with objects on the stage that most game developers routinely do. For example, Essential AS.30, most through book on the subject, pushes it to a chapter right at the back and doesn't cover common pitfalls that otherwise will take a lot of trial and error to figure out. Working with display list objects is easier than many of the books lead us to believe.. don't worry though, just keep asking questions here on Kirupa :)
pradvan
February 22nd, 2009, 11:39 PM
Interesting discussion. I like dandylion13 (http://www.kirupa.com/forum/member.php?u=133682) approach out of many different possibilities. I'll try to find a flowchart i made loosely based on that concept.
:beer2:
dandylion13
February 23rd, 2009, 01:53 AM
Funny how sometimes you can miss the forest for the trees. I should have also mentioned that using events wise can vastly simplyfy architecture
http://www.kirupa.com/forum/showthread.php?t=320375
grandpaw broon
February 23rd, 2009, 06:07 AM
Hello!
Regarding question #2
The best way for classes to communicate without middleware and maintaining complete encapsulation is by using events.
The general idea is that when something of note happens, you can dispatch an event, like this:
ClassA:
ActionScript Code:
dispatchEvent(new Event("eventName", true));
Any class that wants to listen to that event can add a listerner to the stage to do so:
ClassB:
ActionScript Code:
stage.addEventListener("eventName", onEvent);
The class that's listening can then use the onEvent handler to do something based on whatever that event is, like this:
ClassB:
ActionScript Code:
private function onEvent(event:Event):void
{
//.. any directives that should occur when the event happens
//To find out the object that triggered the event, use: event.target
//For example, to find the name of the object:
trace(event.target.name);
//To access a property in the object (accessible through a public getter)
event.target.propertyName
}
To maintain encapuslation, the golden rule is that you don't want classes to change the properties of other classes if you can avoid it. Using events like I've described above means that object can find out what is going on in the program, and if it's relevant to them, they can change their own properties internally.
The great thing about this system is that classes communicate without middleware through AS3.0's event dispatcher and they're completely encapsulated. You don't need to use public methods or even public setters if you design your classes carefully.
This makes sense apart from one thing.
When we are talking about events, i thought i had to listen to the object who dispatches those events?
My "anchor" class merely contains a sprite with button capability. If the mouse is clicked on the sprite i catch the click event and dispatch an AnchorEvent for someone else to use.
//this is a line from the constuctor of the Anchor class
sprite.addEventListener(MouseEvent.CLICK, onClick);
private function onClick(e:MouseEvent): void
{
dispatchEvent(new AnchorEvent(AnchorEvent.AnchorClick));
}
I thought the only class out there who can listen out for those events was the class that uses anchor instances?:
var anchor:Anchor = new Anchor();
anchor.addEventListener(AnchorEvent.AnchorClick, onAnchorClick);
Either way i am a bit hazy on this, what i ended up doing was this.
//this is a line from the constuctor of the Anchor class
sprite.addEventListener(MouseEvent.CLICK, onClick);
private function onClick(e:MouseEvent): void
{
GameManager.getInstance().dispatchEvent(new AnchorEvent(AnchorEvent.AnchorClick));
}
So that means anyone can now listen out for AnchorEvents on GameManager. Problem is, this cant be right because my e.target property now becomes GameManager. I worked around this by adding a "dispatcher" property to the AnchorEvent.
So
e.target = GameManager (which seems wrong)
and
e.dispatcher = Anchor (the actual anchor which was clicked)
Am i approaching this incorrectly?
You say you can listen out for events on stage?
I also noticed that if i used inheritance, by where my Anchor extends Sprite, then i could catch events on stage using bubbling. ie: stage.addEventListener but i thought that because i now use composition and my objects are no longer DisplayObjects themselfs, this wasnt possible?
There is no method to the madness here by the way, I just believe that if i am to listen to a dispatched event, i need to have reference to that event dispatcher. I would like to believe an object in Class structure A>B>C>D can be picked up by a class in F>G>H>I>J. D and J have no relation to each other what so ever and that for me is a nightmare, hence why i now use the GameManager for dispatching events. I know that every object in my application is an Entity which contains reference to the GameManager(singleton extending eventDispatcher) and therefor all objects can listen out for events dispatched by any other class.
So im either on the road to discovery or completely off the mark and i dont know :P
dandylion13
February 23rd, 2009, 06:56 AM
Just a quick thought..
It makes sense to me that your objects couldn't listen to those events because you were using composition, and they weren't part of the display list. Also, I don't think any object that wasn't on the stage would be able to hear them.
Using a GameManager singleton changes things a bit because you use it as an intermediary and it's also not part of the display list. You can dispatch events in the game manager class, and listen to those events in the game object classes, like this:
gameManagerInstance.addEventListener("eventName", onEvent)
So the idea is, the GameManager handles the game logic based on properties of the object classes. When something important happens, it dispatches an event. Any objects that have registered to listen to that event then call their event handlers.
I think this is basically what you're doing.. so your strategy makes sense to me :)
grandpaw broon
February 23rd, 2009, 07:02 AM
Yeah what your saying makes sense and i think im along those lines!
Thanks Dan.
Powered by vBulletin® Version 4.1.10 Copyright © 2012 vBulletin Solutions, Inc. All rights reserved.