I came across this article: http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/ and thought it looked like an interesting way to handle game entities and what not. I don't really understand it all 100%, and just knocked out the following code which seems to be along the same kind of lines. It's basically a component based entity system where each entity can have a different set of components that you kind of plug in your game objects according to your needs (article explains it all).
There doesn't seem to be many decent code examples floating around so I had a go:
Blitzmax code:
a,s,d,w are the controls if you run it.
It's pretty basic but would be interested to hear some different views on how you approach handling your game objects aside from the usual object hierarchy.
There doesn't seem to be many decent code examples floating around so I had a go:
Blitzmax code:
SuperStrict 'A world type to contain the game objects Type tWorld 'our world needs a few lists to contain our objects 'firstly a list to contain our cameras Field cameras:TList = CreateList() 'and a list to contain all the entities Field entities:TList = CreateList() 'Any types that extend tWorld must have an init method to set the world up Method init() Abstract Method addCamera(c:tcamera) cameras.AddLast(c) End Method Method addEntity(e:tEntity) entities.AddLast(e) End Method 'update all entities and cameras in the lists Method update() For Local c:tCamera = EachIn cameras c.update() Next For Local e:tEntity = EachIn entities e.update() Next End Method 'The render method will draw the world and all the objects it contains Method render() Cls 'iterate through all the cameras in the world For Local c:tCamera = EachIn cameras 'iterate through all the entities in the world For Local e:tEntity = EachIn entities 'set the scale of the entity depending on its z and the camera z 'draw the entity on the screen relative to where it is in the world and which camera is looking at it 'the calculations also keep things relative to the current zoom level of the camera 'also, obviously it only needs to be drawn if it has a shape component If e.shape SetScale e.position.z * c.position.z, e.position.z * c.position.z SetRotation e.movement.direction SetColor e.shape.red, e.shape.green, e.shape.blue DrawRect (e.position.x * c.position.z) + c.screenx + (- c.position.x * c.position.z), .. (e.position.y * c.position.z) + c.screeny + (- c.position.y * c.position.z), .. e.shape.w, e.shape.h End If Next Next Flip End Method 'The main game loop that runs the world Method run() While Not KeyDown(KEY_ESCAPE) update() render() Wend End Method End Type 'Components. these are the components that can be used to make our entities Type tController 'controller type is use to define how the entities are controlled in some way, eg player input 'a link to the movement component Field Movement:tMovement Method setmovement(v:tMovement) movement = v End Method Method getmovement:tMovement() Return Movement End Method 'every controller must have an update method to handle however it's controlled, receive input, do ai etc. Method Update() Abstract End Type Type tPosition 'The position component stores where in the world the entity is located Field x:Float = 0 Field y:Float = 0 Field z:Float = 1 'A link a movement component that will update the location of the entity Field mover:tMovement Method Create:tPosition() Local p:tPosition = New tPosition Return p End Method Method setmover(v:tMovement) mover = v End Method Method getmover:tMovement() Return mover End Method Method update() 'use the movement component to update the position 'I guess I could have put this in the movement type instead... x:+mover.speed * Sin(mover.direction) y:-mover.speed * Cos(mover.direction) End Method Method setx(v:Float) x = v End Method Method sety(v:Float) y = v End Method Method setz(v:Float) z = v End Method Method getx:Float() Return x End Method Method gety:Float() Return y End Method Method getz:Float() Return z End Method End Type Type tMovement 'The movement component that stores speed direction etc. Field direction:Float Field speed:Float Field accelleration:Float Method Create:tMovement() Local m:tMovement = New tMovement Return m End Method Method setaccelleration(v:Float) accelleration = v End Method Method setspeed(v:Float) speed = v End Method Method setdirection(v:Float) direction = v End Method Method getaccelleration:Float() Return accelleration End Method Method getspeed:Float() Return speed End Method Method getection:Float() Return direction End Method End Type Type tShape 'shape component that defines what the entity looks like and how it should be rendered Field w:Float 'Width Field h:Float 'Height Field red:Int Field green:Int Field blue:Int Method Create:tShape() Return New tShape End Method Method setw(v:Float) w = v End Method Method seth(v:Float) h = v End Method Method getw:Float() Return w End Method Method geth:Float() Return h End Method Method setcolour(r:Int, g:Int, b:Int) red = r green = g blue = b End Method Method getcolour(r:Int Var, g:Int Var, b:Int Var) r = red g = green b = blue End Method End Type 'Base Objects Type tEntity 'The base entity object 'links to the components that will make up the entity Field position:tPosition Field movement:tMovement Field controller:tController Field shape:tShape 'The init method is used to "plug in" which ever components we need for the entities we make Method init() Abstract 'the update method to run each update method of the components (see example below) Method update() Abstract Method setposition(v:tPosition) position = v End Method Method setmovement(v:tmovement) movement = v End Method Method setcontroller(v:tcontroller) controller = v End Method Method setshape(v:tshape) shape = v End Method Method getposition:tPosition() Return position End Method Method getmovement:tmovement() Return movement End Method Method getcontroller:tcontroller() Return controller End Method Method getshape:tshape() Return shape End Method Method kill() position = Null movement = Null controller = Null shape = Null End Method End Type Type tCamera 'camera type to make rendering more managable 'can have a focus so the camera will follow a particular entity Field focus:tEntity 'the camera requires the position component to give it a location in the world Field position:tPosition 'we need to store the center of the screen so that objects can be drawn in the proper place. 'for example, when the camera is located at coordinates 0,0 and an entity is at 0,0, it will 'be drawn in the center of the screen. Field screenx:Float Field screeny:Float Method Create:tCamera() Local c:tCamera = New tCamera c.init() Return c End Method Method init() 'assign a position component position = New tPosition.Create() screenx = GraphicsWidth() / 2 screeny = GraphicsHeight() / 2 End Method Method update() 'stay focused on the chosen entity If focus position.x = focus.position.x position.y = focus.position.y End If End Method Method setfocus(v:Player) focus = v End Method Method getfocus:tentity() Return focus End Method End Type 'Example---------------------- 'we make a controller to control the player based on tController Type KeyInput Extends tController Field moveup:Int = KEY_W Field movedown:Int = KEY_S Field turnleft:Int = KEY_A Field turnright:Int = KEY_D Method Create:KeyInput() Local c:KeyInput = New KeyInput Return c End Method Method Update() 'simple movement controlls If KeyDown(moveup) movement.speed:+movement.accelleration End If If KeyDown(movedown) movement.speed:-movement.accelleration End If If KeyDown(turnleft) movement.direction:-3 End If If KeyDown(turnright) movement.direction:+3 End If End Method End Type 'make a player type based on entity that will use all 4 components Type Player Extends tEntity Method Create:Player() Local e:Player = New Player e.init() Return e End Method Method init() 'Plugin the components and set a few attributes for our player 'need to make some components aware of the other components, eg, the controller needs 'to know what its moving, and position what its being moved by movement = New tMovement.Create() movement.setaccelleration(0.1) position = New tPosition.Create() position.setmover(movement) controller = New KeyInput.Create() controller.setmovement(movement) shape = New tShape.Create() shape.setw(20) shape.seth(20) shape.setcolour(255, 255, 255) End Method Method update() 'Controller updates the movement which in turn updates the position controller.update() position.update() End Method End Type 'Now a basic Tile type also based on entity (most game objects should be i guess) 'this only needs the position and shape components, as they won't be moving anywhere Type tile Extends tEntity Method Create:tile() Local t:tile = New tile t.init() Return t End Method Method init() 'plugin the position and shape components position:tPosition = New tPosition.Create() shape:tShape = New tShape.Create() shape.setw(40) shape.seth(40) shape.setcolour(255, 0, 0) End Method Method update() 'nothing to update in this basic example! End Method End Type 'Now to create our world! Just set up some tiles and a player. Type myWorld Extends tWorld Field p:Player Field c:tCamera Method Create:myworld() Local m:myWorld = New myWorld m.init() Return m End Method Method init() c = New tCamera.Create() p = New Player.Create() 'create the environment and add to the lists For Local c:Int = 1 To 25 Local t:tile = New tile.Create() t.position.setx(Rnd(- 500, 500)) t.position.sety(Rnd(- 500, 500)) addentity(t) Next 'focus the camera on the player and add them to the world lists c.SetFocus(p) addCamera(c) addEntity(p) End Method End Type Graphics 640, 480 'pretty much it, just create an instance of the world and run it! Local w:myWorld = New myWorld.Create() w.run()
a,s,d,w are the controls if you run it.
It's pretty basic but would be interested to hear some different views on how you approach handling your game objects aside from the usual object hierarchy.