Bear knight is going to be a single line defense game, where you are a bear and a knight or a warrior of some sort, defending "something" right behind you, as you can read I am a programmer, not a real game designer, I will figure things out as I go.
Just like Light Bubble, I will be making use of my Entity System Engine, so why not some code:
Let's take a look at the Entity class, first the fields:
public class Entity : Poolable { public EntityType Type; public Entity[] Children; public int ChildrenCount; public bool Awaken; public BehaviorComponent BehaviorComponent; public GraphicsComponent GraphicsComponent; public PhysicsComponent PhysicsComponent; public StatusComponent StatusComponent; public bool DoesDraw; public bool DoesAct; public bool DoesCollide;
From top to bottom, the Entity is a "Poolable" item, this was explained not so long ago in another blog entry, regardless, this means that the Entity belongs in a pool, and that is recyclable, then we go to the Enum "EntityType" this is to differentiate or organize entities, we can say that the type if "Enemy" or something general, or even specific like "player". The children is just if the Entity requires to have control over other entities, this is usually for grouping or recycling things in groups. the children count is used to know how many children are active in the array. Awaken is a bool used to know if the Entity is sleeping, this can be toggled by some actions, explained later. The components, I would have to write a single entry for every type of component, so I am going to give you a general idea, the BehaviorComponent holds all the "Actions" and decisions for the entity to execute, the GraphicsComponent holds all the visual representation of the entity, the PhysicsComponent holds all the information about it's physical body, such as where it is, how much it weights, how big, etc. And finally the StatusComponent, this component is the most special component of all, it is basically what changes from Entity, it holds data specific to the game, such as life points if you are a player, and other stats.
Then we have 3 bools, this bools are basically all I need to know what this Entity is capable of, if it draws it will be true, etc. By making use of a flag I do not require to bind the entity to some "Manager" or remove it from it when the Entity requires to recycle, I can simply say "False" and whatever checks the entities will ignore this one.
Moving on we have the constructor which is only called at load time or if the pool requires more entities to supply for the game:
public Entity() { Awaken = true; BehaviorComponent = new BehaviorComponent(); GraphicsComponent = new GraphicsComponent(); PhysicsComponent = new PhysicsComponent(); StatusComponent = new StatusComponent(); DoesDraw = false; DoesAct = false; DoesCollide = false; Children = new Entity[20]; ChildrenCount = 0; }
There's not much to explain besides that the default number of Children is 20, the reason why I do not use a list instead of an array is because an array is faster in this case.
Then we have a self clean up call, used when we want the Entity to recycle:
public void SelfDestruct() { if (InUse) { BehaviorComponent.Reset(); GraphicsComponent.Reset(); PhysicsComponent.Reset(); StatusComponent.Reset(); DoesDraw = false; DoesAct = false; DoesCollide = false; EntityFactory.EntityPool.ReturnPoolable(this); for (int i = 0; i < ChildrenCount; i++) { Children[i].SelfDestruct(); Children[i] = null; } ChildrenCount = 0; } }"InUse" is part of the poolable base class, we do not want to recycle an entity if it has already been recycled (this caused much grief once, but it was quickly identified), right after we proceed to reset all the components, this will make all the values to be set back to the original form, we "unsubscribe" from all the "Managers" and then return the Entity to the pool (where "InUse" gets set to false), making use of the "ChildrenCount" we get every single child in the array and recycle them as well, then set the reference to null (Cleaning up all ties is extremely crucial when working with a reusable entity system), finally we set the count back to 0, recycling has been done. The next function in the Entity class allows us to add children to the array.
public void AddChildren(Entity Child) { if (ChildrenCount >= Children.Length) { //todo: expand Entity[] NewPool = new Entity[ChildrenCount + 10]; Children.CopyTo(NewPool, 0); Children = NewPool; } Children[ChildrenCount] = Child; ChildrenCount++; }The procedure is pretty standard, it acts almost like a list, by copying and adding another 10 (not double) Entities slots to the array if the array runs out of space, at the end the children gets added at the last position available. The use of the children pattern here has to be cautious, I would not allow a child to deactivate on it's own, since the reference to that Entity would remain on the parent, and if the reference gets used by another entity before the parent gets recycled, that new entity would inexplicably (well, not inexplicably) disappear. Loading and saving has always been important, specially in the Windows phone, I could have gone the xml way or data contract way, but I like to suffer, and manually save everything in structs, the reason for this is simply because it allows me more control on what to save:
public void Save() { EntityPackage Data = new EntityPackage(); Data.Index = Index; Data.Children = new int[ChildrenCount]; Data.Awaken = Awaken; Data.ChildrenCount = ChildrenCount; Data.DoesAct = DoesAct; Data.DoesCollide = DoesCollide; Data.DoesDraw = DoesDraw; Data.Type = (int)Type; Data.Children = new int[ChildrenCount]; for (int i = 0; i < ChildrenCount; i++) { Data.Children[i] = Children[i].Index; } Data.Behavior = BehaviorComponent.Save(); Data.Physics = PhysicsComponent.Save(); Data.Status = StatusComponent.Save(); Data.Graphics = GraphicsComponent.Save(); IsolatedStorageSettings.ApplicationSettings.Add(Index.ToString(), Data); }the "IsolatedStorageSettings" is not the best place to store your data for loading and saving, but it suffices my intentions and quite well, the "EntityPackage" is a struct that contains all sort of primitives, making saving native to the IsolatedStorage. Loading is done at the "EntityFactory" level, not native to the Entity itself, since the Entity doesn't really know what it is until it is, someone has to tell it. Pre Update makes sure that everything that requires "time adjustment" or a slight "clean up" happen before the actions start to go:
public void PreUpdate() { if (DoesAct) { BehaviorComponent.Upkeep(); } if (DoesDraw) { GraphicsComponent.Update(StatusComponent.Speed); } if (DoesCollide) { PhysicsComponent.PreUpdate(); QuadTree.RunTree(this); } }It will only pre update what it needs to by checking what are you subscribed to. Finally we have the updates, which are the actions:
public void Update() { int RootCount = BehaviorComponent.RootConditionCount; for (int a = 0; a < RootCount; a++) { BaseCondition CurrentRoot = BehaviorComponent.RootConditions[a]; CurrentRoot.RunBehaviourTree(); } } }
This basically concludes my Entity class, I will be posting code for the components on another entries, I hope you can understand a bit of the methodology I am using here, keep in mind that almost absolutely everything is an Entity in my engine, a particle is a full entity, to that extent, sounds heavy? well it is not really heavy, but if it ever gets heavy, it compensates with it's really fast processing time. Thanks for reading.
No comments:
Post a Comment