component based game design

34

Upload: akukareka

Post on 24-Nov-2015

28 views

Category:

Documents


2 download

DESCRIPTION

Component based game design

TRANSCRIPT

  • Theory and Practice of Game Object Component ArchitectureMarcin ChadyRadical Entertainment

  • OutlineComponent-Oriented vs Object-Oriented ProgrammingRadicals approachResults from [PROTOTYPE]

  • What are Game Objects?Anything that has a representation in the game worldCharacters, props, vehicles, missiles, cameras, trigger volumes, lights, etc.Need for a standard ontologyClarityUniformityFeature, staff and tool mobilityCode reuseMaintenanceE.g. use of modularity/inheritance reduces duplication

  • A Game Object Class HierarchyDrawable(renderable model)Simulated(rigid body model)Trigger(volume)GameObject(transform, refcount)Prop

  • Adding StuffDrawable(renderable model)Simulated(rigid body model)Trigger(volume)GameObject(transform, refcount)Animated(animation controller)Animated(animation controller)

  • Mix-ins Perhaps?Drawable(renderable model)Simulated(rigid body model)Trigger(volume)GameObject(transform, refcount)AnimatedMixin(animation controller)AnimatedAnimatedWithPhysics

  • ObservationsNot every set of relationships can be described in a directed acyclic graphClass hierarchies are hard to changeFunctionality drifts upwardsSpecialisations pay the memory cost of the functionality in siblings and cousins

  • ChangeYou can ignore itYou can resist itOr you can embrace itBut you cannot stop it

  • Component-Based ApproachRelated to, but not the same as aspect-oriented programmingOne class, a container for:attributes (data)behaviours (logic)Attributes := list of key-value pairsBehaviour := object with OnUpdate() and OnMessage()

  • Components vs HierarchiesPropGameObject GameObjectAttributeBehaviour**

  • Hulk:UD Object Model

  • Prototype Game Objects

  • Data-Driven CreationText or binaryLoaded from pipelineLoad and goDelayed instancingDedicated toolsData-driven inheritanceTOD_BeginObject GameObject 1 "hotdog_concession"{ behaviours { PhysicsBehaviour 1 { physicsObject "hotdog_concession" } , RenderBehaviour 1 { drawableSource "hotdog_concession" } , HealthBehaviour 1 { health 2.000000 } , GrabbableBehaviour 1 { grabbableClass "2hnd" } }}TOD_EndObject

  • AdvantagesEndowing with new properties is easyCreating new types of entities is easyBehaviours are portable and reusableCode that talks to game objects is type-agnosticEverything is packaged and designed to talk to each otherIn short: you can write generic code

  • DisadvantagesIn short: you have to write generic codeGame objects are typeless and opaqueCant ask, e.g.

    if object has AttachableBehaviourthen attach to it

    Code has to treat all objects identicallyThis is wrong!

  • MessagingAttachMessage msg(this);object->OnMessage(&msg);Dispatched immediately to all interested behaviours (synchronous operation)Fast, but not as fast as a function callUse for irregular (unscheduled) processingCollisions, state transitions, event handlingCan be used for returning values

  • Attribute AccessThe game object must be notified if you modify an attributeConst accessorRead-only accessCacheableNon-const accessorPermits writingNot cacheableSends a notification message to the game objectFree access from objects own behaviours

  • An attribute or not an attribute?Attribute ifaccessed by more than one behaviour, oraccessed by external codeOtherwise a private member of the behaviourIf not sure, make it an attribute

  • Game Object UpdateGameObject::OnUpdate(pass, delta) for b in behaviours b.OnUpdate(pass, delta)OnUpdate() and OnMessage() are the only two entry points to a behaviour.

  • HealthBehaviour Examplevoid HealthBehaviour::OnMessage(Message* m){ switch (m.type) { case APPLY_DAMAGE: Attribute* healthAttr = GetAttribute(HEALTH_KEY); healthAttr->value -= m.damage; if (healthAttr->value < 0.f) mGameObject->SetLogicState(DEAD); break;

    case ATTR_UPDATED: if (m.key == HEALTH_KEY) { Attribute* healthAttr = GetAttribute(HEALTH_KEY); if (healthAttr->value < 0.f) mGameObject->SetLogicState(DEAD); } break; }}

  • Components in PracticeBehaviours and Attributesin [PROTOTYPE]

  • AdoptionSome coders were resistant:Too complicatedDont know whats going onToo cumbersomeCalling a function is easier than sending a messageReading a data member is easier than retrieving an attributeDont like typeless objectsOngoing education

  • Post-Mortem Survey

  • Post-Mortem CommentsData-driven creation was the biggest winPrototyping is the biggest win once you have a library of behavioursModularity of behaviours was the biggest winData inheritance was the biggest winComponents are nothing new - no modern game could be built without them

  • PerformanceGameObject::OnUpdate and OnMessage are easy targetsFor the criticFor the optimiserExisting optimisations:Message masksUpdate masksLogic state masksTime-slicingAttribute cachingLeaving the back door open

  • Performance LessonsBest optimisations are algorithmic:Avoid unnecessary messages, e.g.

    object->OnMessage(&message1);if (message1.x)object->OnMessage(&message2);

    Prefer attributes over messagesAvoid unnecessary updatesBetter instrumentationLegalise the back door entrance

  • Future ImprovementsStateless behavioursSubmit batches of objects to stateless behavioursBetter suited for parallel architecturesMessage queuing

  • Prototypes Data Types1544 game object definitions145 unique behaviours335 unique data types156 unique prop types alone

    Chart1

    1

    26

    17

    18

    20

    21

    22

    18

    10

    11

    17

    24

    13

    17

    5

    8

    3

    5

    3

    4

    1

    2

    2

    1

    3

    3

    3

    2

    6

    5

    5

    4

    7

    7

    1

    1

    1

    2

    1

    1

    4

    1

    1

    2

    2

    2

    1

    1

    Number of behaviours

    Number of unique types

    behaviour usage histogram

    011

    12627

    21744

    31862

    42082

    521103

    622125

    718143

    810153

    911164

    1017181

    1124205

    1213218

    1317235

    145240

    158248

    163251

    175256

    183259

    204263

    221264

    232266

    242268

    251269

    263272

    273275

    283278

    292280

    306286

    315291

    325296

    334300

    347307

    357314

    361315

    371316

    381317

    392319

    411320

    421321

    434325

    451326

    461327

    472329

    482331

    492333

    501334

    511335

    behaviour usage histogram

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    0

    Number of behaviours

    Number of unique types

  • Behaviour Usage

    Chart1

    0.716

    0.666

    0.639

    0.576

    0.543

    0.513

    0.457

    0.433

    0.421

    0.415

    0.358

    0.322

    0.299

    0.275

    0.263

    0.26

    0.242

    0.233

    0.227

    0.227

    0.221

    0.215

    0.209

    0.209

    0.194

    0.191

    0.182

    0.179

    0.161

    0.158

    0.155

    0.152

    0.14

    0.137

    0.128

    0.125

    0.119

    0.119

    0.096

    0.09

    0.087

    0.084

    0.081

    0.081

    0.075

    0.069

    0.066

    0.063

    0.063

    0.057

    0.051

    0.045

    0.045

    0.045

    0.042

    0.039

    0.039

    0.039

    0.039

    0.039

    0.039

    0.039

    0.036

    0.036

    0.03

    0.03

    0.03

    0.03

    0.03

    0.027

    0.027

    0.027

    0.027

    0.027

    0.024

    0.024

    0.024

    0.021

    0.021

    0.018

    0.018

    0.018

    0.018

    0.015

    0.015

    0.012

    0.012

    0.012

    0.012

    0.012

    0.012

    0.012

    0.009

    0.009

    0.009

    0.009

    0.009

    0.009

    0.009

    0.006

    0.006

    0.006

    0.006

    0.006

    0.006

    0.006

    0.006

    0.006

    0.006

    0.006

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    behaviour usage histogram

    RenderBehaviour24071.60%

    PhysicsBehaviour22366.60%

    engine::AudioEmitterBehaviour21463.90%

    engine::PlaybackTreeBehaviour19357.60%

    proto::HitReactionBehaviour18254.30%

    engine::PropLogicTreeBehaviour17251.30%

    proto::TargetableBehaviour15345.70%

    MotionTreeBehaviour14543.30%

    GrabbableBehaviour14142.10%

    proto::HitEffectBehaviour13941.50%

    proto::CloudBehaviour12035.80%

    proto::CollisionActionBehaviour10832.20%

    proto::HealthBehaviour10029.90%

    proto::SensesInfoBehaviour9227.50%

    proto::FightVariablesBehaviour8826.30%

    PuppetBehaviour8726.00%

    CharacterIntentionBehaviour8124.20%

    proto::AdvancedHealthBehaviour7823.30%

    GrabBehaviour7622.70%

    proto::FEDisplayBehaviour7622.70%

    TargetSelectionBehaviour7422.10%

    PerceptionBehaviour7221.50%

    proto::MotionStateBehaviour7020.90%

    AiTreeBehaviour7020.90%

    proto::ThreatReceiverBehaviour6519.40%

    SquadMemberBehaviour6419.10%

    proto::LODAIBehaviour6118.20%

    engine::TouchBehaviour6017.90%

    CharacterSolverBehaviour5416.10%

    RagdollBehaviour5315.80%

    proto::GrabAutoSpawnBehaviour5215.50%

    engine::AudioUDOBehaviour5115.20%

    proto::ShoulderConFixupBehaviour4714.00%

    AimBehaviour4613.70%

    ConsumableBehaviour4312.80%

    proto::ChargeBehaviour4212.50%

    proto::ScaryMonsterBehaviour4011.90%

    proto::CharacterMotionBehaviour4011.90%

    proto::ProtoColourVariationBehaviour329.60%

    proto::DriverBehaviour309.00%

    WalkingBehaviour298.70%

    proto::PhysicsLODBehaviour288.40%

    proto::DrivableVehicleHealthModifierBehaviour278.10%

    proto::DeformShaderBehaviour278.10%

    proto::HealthShaderBehaviour257.50%

    proto::NPCRenderBehaviour236.90%

    engine::PoseAnimationBehaviour226.60%

    proto::WeaponConfigurationBehaviour216.30%

    LODRenderBehaviour216.30%

    proto::FreeAimingBehaviour195.70%

    proto::AttachBillboardBehaviour175.10%

    engine::animation::JawFlapBehaviour154.50%

    engine::TriggerBehaviour154.50%

    proto::AlertBehaviour154.50%

    proto::GrabSelectionBehaviour144.20%

    proto::AddToHeightmapBehaviour133.90%

    proto::AIFlyingPatrolBehaviour133.90%

    HelicopterAimBehaviour133.90%

    proto::HelicopterSolverBehaviour133.90%

    engine::MovingTriggerBehaviour133.90%

    proto::HelicopterMovementBehaviour133.90%

    proto::HelicopterIntentionBehaviour133.90%

    proto::HelicopterRotorAnimationBehaviour123.60%

    proto::TankSolverBehaviour123.60%

    proto::TankIntentionBehaviour103.00%

    proto::MobileSpawnerBehaviour103.00%

    proto::MissileSpawnerBehaviour103.00%

    TankMovementBehaviour103.00%

    TankAimBehaviour103.00%

    proto::AdvancedHitReactionBehaviour92.70%

    engine::PropBehaviour92.70%

    proto::VehicleBehaviourAICharacter92.70%

    proto::PoisonReactionBehaviour92.70%

    proto::StayOnRooftopBehaviour92.70%

    proto::VehicleConstraintsBehaviour82.40%

    proto::HunterMovementBehaviour82.40%

    proto::VehicleBehaviourAmbient82.40%

    proto::TargetingBehaviour72.10%

    proto::CharacterPedBehaviour72.10%

    proto::AnchorToGroundBehaviour61.80%

    proto::AutoTargetingBehaviour61.80%

    AttachableBehaviour61.80%

    proto::PropTargetableByAIBehaviour61.80%

    proto::GunBehaviour51.50%

    TagBehaviour51.50%

    proto::CollectibleBehaviour41.20%

    proto::DoorTriggerBehaviour41.20%

    proto::MonsterHMovementBehaviour41.20%

    engine::RagdollLODBehaviour41.20%

    proto::MissileBehaviour41.20%

    proto::TunnelingBehaviour41.20%

    proto::HunterHealthBehaviour41.20%

    proto::DecalBehaviour30.90%

    proto::IKBehaviour30.90%

    proto::GrabbableStaticBehaviour30.90%

    proto::PowerBehaviour30.90%

    engine::AdvertisementBehaviour30.90%

    proto::DiscardedFirearmBehaviour30.90%

    proto::CollisionSqueezeBehaviour30.90%

    proto::BombBehaviour20.60%

    engine::NISPlayerBehaviour20.60%

    proto::TokenBehaviour20.60%

    proto::TendrilBehaviour20.60%

    proto::ExpiryTimeBehaviour20.60%

    proto::LightDecalBehaviour20.60%

    proto::PatrolPathBehaviour20.60%

    proto::BloodtoxBehaviour20.60%

    SquadBlackboardBehaviour20.60%

    proto::MissileJBehaviour20.60%

    proto::WaterTowerBehaviour20.60%

    proto::BaseHealthBehaviour10.30%

    proto::FormationBehaviour10.30%

    ThreatBallBehaviour10.30%

    proto::DisguiseBehaviour10.30%

    proto::MHealthBehaviour10.30%

    SquadCommanderBehaviour10.30%

    proto::ZoneBehaviour10.30%

    proto::MusicTreeBehaviour10.30%

    engine::AudioZoneBehaviour10.30%

    engine::TrackedMotionToExpressionBehaviour10.30%

    proto::BlobShadowBehaviour10.30%

    proto::BridgeCameraBehaviour10.30%

    proto::CameraFloorBehaviour10.30%

    proto::ButtonHintBehaviour10.30%

    proto::GroundSpikeBehaviour10.30%

    proto::ShieldHitReactionBehaviour10.30%

    proto::RandomDeadPedBehaviour10.30%

    proto::PedBehaviour10.30%

    engine::LightBehaviour10.30%

    proto::CoOpBehaviour10.30%

    proto::PowerWheelBehaviour10.30%

    proto::GroundSpikeMasterBehaviour10.30%

    proto::TransformationBehaviour10.30%

    FlyingBehaviour10.30%

    proto::LockOnTargetingBehaviour10.30%

    proto::PrototypeHealthBehaviour10.30%

    proto::InfectedPedBehaviour10.30%

    proto::GroundSpikeShardBehaviour10.30%

    proto::SquadPerceptionBehaviour10.30%

    proto::FadeBehaviour10.30%

    proto::DistrictBehaviour10.30%

    proto::CopPedBehaviour10.30%

    proto::SoldierPedBehaviour10.30%

    proto::ThreatBehaviour10.30%

    proto::StayOnNavmeshBehaviour10.30%

    behaviour usage histogram

    0.716

    0.666

    0.639

    0.576

    0.543

    0.513

    0.457

    0.433

    0.421

    0.415

    0.358

    0.322

    0.299

    0.275

    0.263

    0.26

    0.242

    0.233

    0.227

    0.227

    0.221

    0.215

    0.209

    0.209

    0.194

    0.191

    0.182

    0.179

    0.161

    0.158

    0.155

    0.152

    0.14

    0.137

    0.128

    0.125

    0.119

    0.119

    0.096

    0.09

    0.087

    0.084

    0.081

    0.081

    0.075

    0.069

    0.066

    0.063

    0.063

    0.057

    0.051

    0.045

    0.045

    0.045

    0.042

    0.039

    0.039

    0.039

    0.039

    0.039

    0.039

    0.039

    0.036

    0.036

    0.03

    0.03

    0.03

    0.03

    0.03

    0.027

    0.027

    0.027

    0.027

    0.027

    0.024

    0.024

    0.024

    0.021

    0.021

    0.018

    0.018

    0.018

    0.018

    0.015

    0.015

    0.012

    0.012

    0.012

    0.012

    0.012

    0.012

    0.012

    0.009

    0.009

    0.009

    0.009

    0.009

    0.009

    0.009

    0.006

    0.006

    0.006

    0.006

    0.006

    0.006

    0.006

    0.006

    0.006

    0.006

    0.006

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

    0.003

  • Implicit Class Hierarchy

  • Implicit Class Hierarchy

  • Prop Hierarchy

  • SummaryDesigns changeClass hierarchies dont like changeComponents do, but not without some sacrifices

  • Questions

    As a rule of thumb, if it has a position it can be a game object, although not all game objects have a position.Most games try to devise a universal representation for their objects.

    Probably the most common way of expressing this ontology is in a semantic network, by way of OOD and inheritance. Here is an example.It feels natural an intuitive. All game objects have a transform and a ref count perhaps. Some game objects can be drawn, other cant. Some of those that can be drawn can also simulate. We might pause for a second there to ponder if there could be simulated objects without renderable models, but who needs simulated invisible objects?Further down the hierarchy you will typically see something like the Prop class, used to represent all props in the game.What if we want to add animation to the hierarchy? Some objects will only animate, while others will switch between animation and simulation.Multiple inheritance is one way of solving the problem. But it doesnt scale well.And it doesnt address the ultimate challenge: a change in design/requirements, e.g. one day a designer deciding he needs a trigger volume that can be seen by the player.Some people argue that if only our industry really grew up and started planning things properly then we wouldnt have the issues with overscoping, budget overruns, missed deadlines, tough crunch times, and the type of refactoring issues weve just seen. I doubt thats possible, but suppose that youve somehow managed to design the perfect game upfront and planned and executed the development so that it sailed through without any hiccups. I argue that your competition can still better you by devising a system that lets them change the design half way through in response to changing market conditions and still deliver on time. Does such a system exist? I think so. I believe searching for it is a more worthwhile pursuit than trying to bet everything on perfect planning.The second point is critical: we have only one class to represent all kinds of objects. Their functionality is defined by an aggregation of behaviours.The game object class is just an empty shell. By using it, you dont inherit any game-specific baggage. You can easily recombine existing components, but you can also replace any one of them. Most importantly, you dont have to shoehorn any object into a definite type.How does it work in practice?

    Shift of ontology from a semantic network (nested sets of functionality) to flat aggregation of functionality.Lets look at an example. This is the object model from the Hulk: Ultimate Destruction game, which predates the game object model.The two main classes are the character class and the prop class. The character class had to cater for a number of types of characters: Hulk, soldiers, Hulkbusters, boss characters etc. Therefore, it was a superset of the functionality required by all of them, e.g. it contained the AI and cloud code, which wasnt required by Hulk. As another example, a character could be held or hold another character, prop or a civilian. These 3 cases required different code. Consequently, the file CCharacter.cpp had 11,000 lines of code and the memory footprint of a character object was over 20k. And that was on the last gen hardware.Props were slightly lighter and had different functionality, although they replicated a lot of the code and data. When it came to implementing helicopters, it proved hard to bend either the Character class or the prop class to fit the requirements, so a hybrid was made, by creating both instances and linking one to another, the so called character props.When it came to fleshing out the ambient system, the characters were deemed too expensive to use for pedestrians and cows. So the pedestrians were written from the ground up, and cows were implemented as animated props. Both had their own state machines and AI.Now lets compare it to the object model in the Prototype game. Here, all entities are instances of the same class. However, every instance is taylor-made for its specific role. It has only the behaviours it needs.The main character doesnt have any AI or cloud data.The helicopter doesnt have the character motion, ragdoll or grabbing code.HLOD pedestrians are similar to Alex, but have fewer behaviours or use their lighter versions.LLOD pedestrians have only three behaviours!As you can see, instead of a rigid hierarchy of classes, onto which every new type of entity has to be more or less forcefully grafted, we have a flexible picknmix approach, where every object can be made into exactly what it needs to be, without incurring much overhead.Ground spike example.

    So, is creating objects as easy as picking candy in a store? It depends on your particular implementation, but once you have a component-based framework in place, its relatively little effort to add a data-driven mechanism for constructing them on the fly. And thats when productivity really takes off.At Radical the constitution of a game object is specified in a text file, which forms part of the game content. Here is an example.The loading system takes care of constructing the game object based on this description and placing it in the game, so its ready to go. Alternatively, this definition can be loaded in as a cached, reusable template. This feature is heavily used by the prop spawning system.We have dedicated tools for run-time and batch editing of game object definitions.Last, but not least, we have a form of inheritance supported by the loading system. It is possible to create new types of objects by concatenating the behaviours and attributes from two or more existing types. Inheritance remains a useful tool, as it helps reduce duplication.So clearly, there are some advantages to the component-based approach.Because game objects are dynamically constructed, you cant tell what youre dealing with at compile time. You dont know what behaviours or attributes it has. We even deliberately hide the behaviours from the programmer, so you cant inspect them at runtime either.The reason for that is to keep behaviours and game objects portable and generic. In order to be able to plug any behaviour into any object there must be no code anywhere that makes assumptions about game objects internal composition.You have to treat every object in the same way, whether they have the behaviour or not.How do you do that?We send a message to a game object and let it decide how to deal with it.This will get dispatched to all behaviours that are interested in attach messages. Among them will be the AttachableBehaviour which will do the actual work of attaching an object to another.Messages are not intended for regular processing. If you have something that needs to run every frame, consider putting it in the behaviour update function (discussed shortly).Note that there is nothing in the system to stop you from retrieving data from behaviours using messages. I generally try to avoid it though, as it increases coupling and goes against the grain of the component-based philosophy.To maintain cohesion, game objects or, strictly speaking, their behaviours must be notified of any state changes that are relevant to them. For changes delivered via the messaging system, thats not a problem. If a behaviour wants to know, it can declare interest in the relevant messages. However, behaviours may also want to know if you modify any of the game objects attributes. For this reason attributes are accessed via a proxy class.Behaviours have direct access to the attributes of the game object they belong to, so they can control if the message is sent or not.If you find yourself writing a lot of Get messages, consider using an attribute instead.If you are worried that something will corrupt it when its a public attribute, remember that you will always get a message if that happens, in which case you can either deal with it, or assert.Going forward we want to shift most if not all of data to attributes and leave behaviours stateless.Finally, to complete the picture, every game object and its behaviours receive update passes.This is where regular processing is done.There is no OnUpdate()The survey indicates that the system was ultimately well received by programmers.It goes without saying that these are all purely subjective perceptions. Save for wiping the teams memories and developing the game again without components, theres not way of knowing how it really compares to inheritance-based design.Dont be overzealous allow last minute hacking

    As part of the post-mortem research I also did some data mining of our game object definitions. I found 1544 definitions, of which 335 were unique. Between them, they used 145 different behaviours.Is 335 game object types a lot? Our entire codebase contained 6400 classes (including the 145 behaviours)! However, we are looking at game objects, which represent one of the most active areas of game development, where most of the flux happens. Traditionally, in our previous games this area would be covered by only a handful of C++ classes. For props alone I found 156 different types, compared to just one in Hulk:UD.This histogram shows only the numbers of types, not instances, with the given number of behaviours.Measuring instances would require elaborate instrumentation of the game code.The data includes types which may have not been used in game at all.RenderBehaviour 240 71.6%PhysicsBehaviour 223 66.6%engine::AudioEmitterBehaviour 214 63.9%engine::PlaybackTreeBehaviour 193 57.6%proto::HitReactionBehaviour 182 54.3%engine::PropLogicTreeBehaviour 172 51.3%

    First thing to notice: no behaviour is used in every type. Top 6 behaviours are used in more than half of types. The rest are more or less custom. Roughly 2/3, or about 100 of all behaviours are very specialised (under 10% use).Render and physics behaviours would have a much higher usage rate per number of instances.

    In order to further assess how behaviours are used to implement different types, one can attempt to reconstruct the implicit class hierarchy from subsets of behaviours used in each unique type. This is what I got by constraining the reconstruction algorithm to single-inheritance.This is without the leaf nodes!Interestingly, the algorithm found the same division as you would see in hand-made hierarchies: props vs characters. However, there are more than one independent character and prop trees.

    And this with multiple-inheritance allowed, again without the leaf nodes.This hierarchy-recovering exercise was interesting for me because it brought about the idea that perhaps there is a way to bake this C++ class hierarchy back into source code to recover some of the performance lost by using components.

    The is the hierarchy of props alone, this time with leaves included (156). Compare it with the single CSPHulkPropInstance class from Hulk:UD.