1 / 30

Announcements

Announcements. Public playtesting starts next week! Look at the following 195n lecture for tips regarding good public playtesting : http ://cs.brown.edu/courses/cs195n/.archive/2012/lectures/10.pdf#page=34. Lecture 11. C++ Tip of the Week. Beyond class hierarchies!. Playtesting.

vinny
Download Presentation

Announcements

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Announcements • Public playtesting starts next week! • Look at the following 195n lecture for tips regarding good public playtesting:http://cs.brown.edu/courses/cs195n/.archive/2012/lectures/10.pdf#page=34

  2. Lecture 11 C++ Tip of the Week Beyond class hierarchies! Playtesting Game Object Systems

  3. Introduction • So far we've explored many subsystems • Collision detection/response, AI, networking, etc. • Today we'll explore game objects • Central to the architecture of a game engine • Difficult to implement in a large system • Lots of case studies from real games

  4. Game objects • Piece of logical interactive content • Monster, tree, level, door, item, trigger, camera sequence, etc. • Diverse set of behaviors • Pathfinding, networking, animating, rendering, triggering, persisting • What are goals of a game object system? • Flexibility • Reusability • Ease of use (even for non-programmers) • Modularity • Is a class hierarchy the best technique?

  5. The problem with class hierarchies • Renderable class has a position and sprite • Moveable class has position and velocity • Works for non-moveable renderables • But what about non-renderablemoveables?

  6. The problem with class hierarchies • Try composition over inheritance • Entity has renderable and moveable as members • Now there are two copies of position

  7. The problem with class hierarchies • Try multiple inheritance • Virtual inheritance solves duplication (one position) • C# and Java don't have it (not portable) • Entities are still defined at compile-time, artists can't change entity definitions

  8. The problem with class hierarchies • Single inheritance hierarchies don't scale • Functionality drifts upwards • Hierarchy stops making sense • System "hardens", refactoring becomes expensive • A DAG can't describe every set of relationships

  9. Case study: Hulk: Ultimate Destruction • Single inheritance game object system • Monolithic CCharacter class • Common behaviors all boiled up to this superclass • 11,000 lines of code • 20k memory footprint • CCivilian implemented from scratch • Too memory intensive to use CCharacter • Other overarching problems • Have to decide how to categorize each new entity • Difficult to add new behaviors • Difficult for designer to tweak system

  10. Case study: Hulk: Ultimate Destruction • Everything migrated up into CCharacter:

  11. Component-based development • Alternative to class hierarchies • One entity class • List of behaviors (logic) • List of attributes (shared data) • Separation of concerns • Components each handle a small, specific thing • Promotes modularity and cohesiveness • Substitutable • Swap in one component for another

  12. Component-based development

  13. Case study: Prototype (2009) • First component-based system for Radical Entertainment • Lots of specialized components • Entities only hold relevant components • Avoids monolithic superclass problem from Hulk • Much more memory efficient • Easy to create new behaviors

  14. Case study: Prototype (2009)

  15. Case study: Prototype (2009) • Survey givens to engineers on Prototype • Compares component system with old class system

  16. Implementing components • Entities: attributes and behaviors • Concerns • What if processing order is important? • What if components need to share state? • What if components need to communicate directly?

  17. Components: Messages • Broadcast information between behaviors • Sent to GameObject, relayed to behaviors • Only notifies behaviors that are interested in the sent message type • Used for unscheduled processing • Anything that doesn't happen every frame • e.g. collisions, state transitions, event handling • Slower than regular function calls

  18. Components: Example behavior voidHealthBehavior::onMessage(GameObject* obj, Message* m) { switch (m->type) { caseAPPLY_DAMAGE: obj->attr<float>(HEALTH_KEY)->value -= m->arg<float>(0); obj->send(newMessage(ATTR_CHANGED, HEALTH_KEY)); break; caseATTR_CHANGED: if (m->arg<int>(0) == HEALTH_KEY) { if (obj->attr<float>(HEALTH_KEY)->value <= 0) { obj->attr<bool>(DEAD_KEY)->value = true; obj->send(newMessage(ATTR_CHANGED, DEAD_KEY)); } } break; } }

  19. Pure component model • All logic split amongst components • Don't even need game object classes! • Represent game objects by integer IDs • Not without its problems... • Harder to instantiate correct collection of components • No logical grouping of behaviors • Performance

  20. Case study: Thief (1998) • Goals • Maximize designer flexibility and control • All scripting • Allow systems to iterate over relevant data without indirection • Game objects are only an ID • Properties contain data for all game objects • Attributes vs. properties: Array-of-structs vs. struct-of-arrays • int health = GetProperty(HEALTH)->GetIntValue(objId);

  21. Thief example property: Physics • Goal: fast random access for all physics objects while in physics loop • Physics behaviors stored in array • Owned by physics subsystem • Supplementary hash map from object ID into array • Physics loop operates entirely on its internal array, no extra indirection • Other systems can get physics data using the supplementary hash map • Hash object ID and return physics data • Still O(1), but extra level of indirection

  22. Thief: Problems • Object creation is slow • Each property needs to create data for each new ID • Property lookups are relatively slow • Compared to direct variable accesses • Programmer complexity • Need debugging tools • Show why a property value is on a game object

  23. Components via multiple inheritance // Layer 1: Base structEntity { virtualvoid draw(); virtualvoidupdate(float seconds); }; // Layer 2: Attributes structPosition : virtualEntity { Vector3 position; }; structVelocity : virtualEntity { Vector3 velocity; }; structHealth : virtualEntity { int health; }; structTarget : virtualEntity { Position *target; }; // Layer 3: Behaviors structRenders : virtualPosition { void draw(); }; structMoves : virtualPosition, virtualVelocity { void update(float); }; structSeeks : virtualMoves, virtualTarget { void update(float); }; // Layer 4: Definitions structRocket : virtualHealth, virtualRenders, virtualSeeks {};

  24. Components: Conclusion • Pros • Maintainability • Easy reuse • Usability for non-programmers • Easy to integrate with editor / tools • Scriptability • Cons • Extra glue code to set up • Debugging • Annoying to manipulate without editor / tools • Performance

  25. Lecture 11 C++ Tip of the Week Yodawg! Playtesting Game Object Systems

  26. C++ tip of the week • Template metaprogramming • The C++ type system is Turing complete (i.e. can be used for computation) • Discovered by accident during C++ standardization • Compile-time programming: programs generating programs • Abuses template specialization • C++ templates are a functional language • Recursion instead of iteration • Immutable variables • Create a variable that holds a type via typedef • Create a variable that holds an int via enum

  27. C++ tip of the week • Simple example: compile-time factorial // Recursive template for general case template <int N> structfactorial { enum { value = N * factorial<N - 1>::value }; }; // Use template specialization for base case template <> structfactorial<0> { enum { value = 1 }; }; int result = factorial<5>::value; // == 5*4*3*2*1 == 120

  28. C++ tip of the week • Another example: compile-time linked list // Compile-time list of integers template <int A, typenameB> structnode { enum { num= A }; typedefBnext; }; structend {}; // Compile-time sum function template <typenameL> structsum { enum { value = L::num + sum<typenameL::next>::value }; }; template <> structsum<end> { enum { value = 0 }; }; typedefnode<1, node<2, node<3, end> > > list123; int total = sum<list123>::value; // == 1 + 2 + 3 == 6

  29. C++ tip of the week /usr/local/lib/gcc/i686-pc-linux-gnu/3.4.2/../../../../include/c++/3.4.2/bits/stl_algo.h:In function `_OutputIteratorstd::transform(_InputIterator,_InputIterator, _OutputIterator, _UnaryOperation) [with _InputIterator=std::_List_iterator<std::string>, _OutputIterator =std::back_insert_iterator<std::list<std::string,std::allocator<std::string> > >, _UnaryOperation =std::binder1st<std::pointer_to_binary_function<std ::list<std::string,std::allocator<std::string> >, std::string, std::list<std::string,std::allocator<std::string> > > >]': test3.cpp:24: instantiated fromhere/usr/local/lib/gcc/i686-pc-linux-gnu/3.4.2/../../../../include/c++/3.4.2/bits/stl_algo.h:789:error: no match for 'operator=' in'(&__result)->std::back_insert_iterator<_Container>::operator * [with_Container = std::list<std::string, std::allocator<std::string> >]() =std::binder1st<_Operation>::operator()(typename_Operation::second_argument_type&) const [with _Operation =std::pointer_to_binary_function<std::list<std::string,std::allocator<std::string> >, std::string, std::list<std::string,std::allocator<std::string> >](((std::string&)(+(&__first)->std::_List_iterator<_Tp>::operator*[with _Tp = std::string]())))'/usr/local/lib/gcc/i686-pc-linux-gnu/3.4.2/../../../../include/c++/3.4.2/bits/stl_iterator.h:363:note: candidates are: std::back_insert_iterator<_Container>&std::back_insert_iterator<_Container>::operator=(t ypename_Container::const_reference) [with _Container = std::list<std::string,std::allocator<std::string> >]/usr/local/lib/gcc/i686-pc-linux-gnu/3.4.2/../../../../include/c++/3.4.2/bits/stl_iterator.h:338:note: std::back_insert_iterator<std::list<std::string,std::allocator<std::string> > >&std::back_insert_iterator<std::list<std::string,std::allocator<std::string> > >::operator=(conststd::back_insert_iterator<std::list<std::string,std::allocator<std::string> > >&) • Drawbacks • Much longer compile times (computation via template instantiation is inefficient) • No debugger, only page-long error messages • Turing completeness brings the halting problem // This code will infinite-loop the compiler template <typenameT> structloop { loop<T*> operator->(); }; loop<int> i, j = i->fail;

  30. Lecture 11 C++ Tip of the Week Every week from now on! Playtesting Game Object Systems

More Related