actor template

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • XML templates should specify default values.

      Rather than make a bunch of actor templates that are all the same except of the position, it might be easier to, say, tweak the actor creation process to take a position vector as input that would override the default position.

      That way whatever system is spawning enemies can have final say over their starting position. So it would be controlled by logic rather then data.

      GCC4 allows you to pass an XMLElement containing override values into the create actor function. So you'll probably want to do something similar.
    • I handled this by allowing instantiated actors to override any of the default values in the template by simply defining the XML that was different from the template.

      So, in your example above, to define an identical actor at a new position, you'd do this:

      XML Source Code

      1. <?xml version="1.0" encoding="utf-8"?>
      2. <Actor type = "Gnome" template="true">
      3. <PositionComponent>
      4. <Position x="1250" y="1250"></Position>
      5. </PositionComponent>
      6. <CollisionComponent>
      7. <Collision type="Enemy"></Collision>
      8. </CollisionComponent>
      9. </Actor>
      10. <Actor type = "Gnome" instance="Gnome_1">
      11. <PositionComponent>
      12. <Position x="1450" y="850"></Position>
      13. </PositionComponent>
      14. </Actor>
      15. <Actor type = "Gnome" instance="Gnome_2">
      16. <PositionComponent>
      17. <Position x="250" y="950"></Position>
      18. </PositionComponent>
      19. </Actor>
      Display All


      You have to define the difference between the template, or "archetype" as this is typically called, with an instantiation of it.

      This took a little extra work in the initialization code - check out the ActorFactory::ModifyActor method to see how it works in the GCC4 source code. The editor chapter also does a great job explaining this in detail.
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • That's correct. Typically what you have are templates and instance. Templates define the default structure and instances define the override data for that specific actor. You usually generate the instance XML from a level editing tool.

      [Edit]
      I'm not sure I like this freelance thing you're doing, Mike. Now you're beating me to the answer. ;)

      -Rez
    • Yeah you pretty much have it.

      The instance attribute is a way to give particular instances of actors unique names. They make it easier to distinguish them in the editor for setting their unique properties, such as hit points. If you didn't do that you have a 1,000 long list of, oh, wooden crates in your level.

      In the game code, when you search for actors you might want to reference a particular instance, say to trigger an AI behavior for one of the gnomes in a group, maybe that you've given a unique instance name so you can easily find it in code.

      In GCC4 creating level actors goes a little like this:

      Source Code

      1. Open actor archetype XML files.
      2. Open level XML file.
      3. For each actor in the level XML file
      4. Parse the archetype and create an actor with all the default property values.
      5. For each overridden property
      6. Read the property attributes, and change the newly created actor
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • Instead of having your actors handle their own events. You should have a given system handle the event for a given actor.

      Just pass the ActorID along with the event data, and have the system apply the logic to that actor.

      Otherwise your going to have like 200 Actors catching an event and running some sort of if statement to see if that even belongs to them.
    • There are many different ways to deal with actor-specific events. A map is certainly one way, though that's not exactly how I'd do it.

      The simplest way is to just have a delegate on BaseGameLogic (or better yet, your game-specific subclass) that handles the event and calls a function on the actor directly. This is what we do in GCC for actor-specific events. For an example, look at BaseGameLogic::MoveActorDelegate(). This calls VMoveActor() which in turn just looks up the actor, finds its transform component, and moves it. Simple. This can get a little messy if you tons of events, but it works reasonably well.

      If you want something a bit more complex, you could create an event class that inherited from BaseEventData called ActorEventData. This would contain an actor ID and an accessor for it. All actor-specific events would be required to inherit from this.

      You would also create a new component called GameplayEventHandlerComponent and attach it to every actor that needs to respond to events. This class would have the mapping, which would look something like this:

      Source Code

      1. std::map<EventType, EventListenerDelegate> m_eventHandlerMap;


      This maps event types to a delegate. You'll also want an API for adding and removing events to specific actors, which is something you'd likely define in XML.

      In BaseGameLogic, you would create an event handler delegate function that grabbed the actor ID from the event, found the appropriate actor, grabbed the GameplayEventHandlerComponent, and sent the event directly to that actor. The component would handle the rest.

      That's the complicated way to do what you're trying to do.

      An even better way is to handle of this on the scripting side. Everything you've explained in your post makes me think this should be in your game script, not on the C++ engine side. This kind of problem is much easier to solve in a language like Lua.

      By now, I've probably confused the issue with my 3am ramblings. ;) So, in conclusion, I would just do the simplest way for now and refactor later if it becomes a problem.

      -Rez
    • The actor system is a bit heavy-weight for tiles. Tiles often don't change once they've entered the world and are usually handled differently than typical actors. For example, if I want to loop through all actors, I probably don't want to include tiles. Likewise, if I want to process all tiles, I don't care about actors. Tiles also tend to be mostly static data where things don't change much at runtime where actors are usually changing a lot. The data is usually very different as well.

      The actor system is not the right solution for tiling. If you want 100,000 tiles, it won't work. In fact, if you want 100,000 actors of any kind, this system won't work well and you should use another method.

      As for the XML size issue, the real problem is data redundancy. This is a problem that should be solved with data templates, not code. On The Sims, we define tuning inside of a schema in the gameplay script. There's a post-process that's run to expot that schema into an XML schema that's consumed by our tuning tool. When a designer makes a change and exports the tuning, only the values that are different from the schema-defined defaults makes it into the resulting XML data. Designers can also create prototypes, which are just tuning files that other tuning files use as defaults. For example, all socials share some common data, so there's a Social prototype. When a specific social interaction tuning file is exported, only the differences from the prototype are written to XML.

      This concept of data prototyping is really powerful and cuts down quite a bit on redundant data.

      -Rez
    • Usually, components themselves don't embody an entire system. For example, you'd never write your physics system entirely inside an actor component nor would you do the same for graphics. This would couple the systems tightly together and prevent non-actors from things like getting rendered. Usually, each of these things gets its own system that is nicely decoupled from the engine and manipulated through a thin interface. Then you'll write an actor component that uses this interface.

      For example, my graphics system has a Scene base class that serves as the main interface for dealing with the graphics system. I have a function on that class that looks like this:

      Source Code

      1. // This function adds an object to the scene for rendering next frame. Ownership of the RenderData object is
      2. // passed to the scene.
      3. virtual void AddObjectToScene(RenderCommand* pObjectData) = 0;


      This function is called by anyone that needs to render an object. This adds it to the scene for the following frame. I just did a search in my whole codebase for it and found it in just a few places. The SpriteRenderSystem class calls it when it loops through all AnimatedSprite components on any actors that have them so they can be rendered. It's called from my UI system to render the UI layer. It's also called from my world system to render tiles.

      You should follow this same methodology with things like collision. You should have a collision system that handles collisions. You should build it in such a way that you could completely test it without hooking up any other part of your game engine. You should be able to write a stand-alone app that does nothing but tests the class and doesn't rely on any other code whatsoever (this is called a unit test). Then, plug it into your engine and write a CollisionComponent. The component should pretty much write itself at that point.

      By the way, you should able to unit test the vast majority of your core systems in this exact way. Unit tests make the whole process take twice as long (sometimes longer), but the resulting code is MUCH better. You seem to consistently find a need to refactor the way you handle collisions, so it's probably worth while to go through this exercise.

      -Rez

    • I would just like to get the junior level where someone is reviewing my code on a regular basis. Being steered away from potential pitfalls is much better than falling in every time.

      I disagree. Falling into the pitfalls provides a much stronger lesson. If I just told you the answer every time, it wouldn't be as effective. It's the give-a-man-a-fish vs teach-a-man-to-fish concept.

      -Rez
    • I have a question about how to deal with persistent entities that related to each other (eg. aircraft starting in flight that has a mission which is assigned to an specific arrival airport). I see two problem to deal with:

      1- Maintain a persistent relationship between actors/instances. At first i thought about saving the id and preserving it (thus save it and load it even between save chain), and there is not doubt that it works but i believe it adds a few unnecessary works like protect from duplicate id (especially for games where the match might last very very long like in a simulator). Here i see the suggestion of using a string as instance id which is not bad since it detach the inner id system from the level/world/campaign definition (basically at the high level of the campaign it ignores the existence of id in the game engine).
      2- Saving the mission that might be owned not by the aircraft itself but rather by the hq (the one who manage all mission and if required abort one and callback the executor) which then assign the mission to the aircraft.

      My question is, is the <Actor type = "Gnome" instance="Gnome_1"> method enough to deal with persistence relationship (that could be applied to other situation like keeping the ownership of an object in the world - like a dropped item - and maintain its persistence between saves).


      Thanks in advance
      NE


      PS: when i was about to submit another problem came to my mind, what if i want some sort of parent/children actor relationship ? Would it be a good idea to create an specific component that keep tracks of that? Maybe use the update method to verify whether the parent still exists or the contrary children still exist and if not send an event of some like that?
    • xXNightEagleXx wrote:

      I have a question about how to deal with persistent entities that related to each other (eg. aircraft starting in flight that has a mission which is assigned to an specific arrival airport). I see two problem to deal with:

      1- Maintain a persistent relationship between actors/instances. At first i thought about saving the id and preserving it (thus save it and load it even between save chain), and there is not doubt that it works but i believe it adds a few unnecessary works like protect from duplicate id (especially for games where the match might last very very long like in a simulator). Here i see the suggestion of using a string as instance id which is not bad since it detach the inner id system from the level/world/campaign definition (basically at the high level of the campaign it ignores the existence of id in the game engine).
      2- Saving the mission that might be owned not by the aircraft itself but rather by the hq (the one who manage all mission and if required abort one and callback the executor) which then assign the mission to the aircraft.

      My question is, is the <Actor type = "Gnome" instance="Gnome_1"> method enough to deal with persistence relationship (that could be applied to other situation like keeping the ownership of an object in the world - like a dropped item - and maintain its persistence between saves).


      Thanks in advance
      NE


      PS: when i was about to submit another problem came to my mind, what if i want some sort of parent/children actor relationship ? Would it be a good idea to create an specific component that keep tracks of that? Maybe use the update method to verify whether the parent still exists or the contrary children still exist and if not send an event of some like that?


      Don't quite see the problem here. If you're tracking a unique entity, just assign it a guid. When you're storing the data, it saves the guid as well. And you recall the data when you're rebuilding the world. If you need to recall the special id, just use stl's multiset.
      You'll have to weight which is faster, recalling all past events to build a game state from generic actors, or restoring every actor and their internal states. There's a few patterns that might help you with the former.