Game Architecture (Friday)
Moderated by Dave Weinstein of Red Storm
War/Horror Stories
- Rainbow 6, young company, used "subclassing procedural" design. 3 subclasses of the simulation engine. Had problems with not passing new arguments to parents in class hierarchy. To switch simulations, had to destroy and recreate simulation object (and many things required a simulation object -- caused bugs during the transition).
- Single/multiplayer combo
- Everything derived from MFC classes
- Pass by CString
- Data in UI widgets rather than reflected by UI
- Scoping problems leading to name clashes
- Common names conflict with middleware
- 1600 character line with for, if, etc. -- dubplicated 4 times
- "Premature abstraction" bad; "insufficient abstraction" also bad
- complicated logical architecture
- Duplicated decision points
- #ifdef
- no view/logic separation -- Rainbow 6 won't run without a sound card
- Custom script with preprocessor:
#define foo true
#define foo false
interpreted as #define true false
- custom script language without a debugger
- different tools that duplicate functionality, without sharing code
- no source control
- header dependencies
- poor factoring
- library inter-dependencies
- junk drawer syndrome -- physical sparation of files and directories helps
Good Ideas
- Read & write in the same file; better if next to each other; even better the same function
- Debug: serialize and deserialize a default object as a test
- In debug build, serialize and deserialize are type safe (tag values with types).
Better if debug and release builds are interoperable.
- Similar: 4 character code before each binary blob in debug mode.
- Keep separate game logic and engine
- Game logic can be in a higher level language
- ABC (abstract base classes): no concrete headers included in an interface .h. Went from 40 minute to 5 minute compile times.
- Use directories to enforce abstraction layers.
- Pluggable factory used in particle system -- went from supporting one type to many.
- Read Design Patterns.
- Abstract player input: it helps with debugging and being able to replay.
- Plan for debugging: save all data. Ex: Keep all hresults in a struct.
- Readable data format in debug mode
- Networking mistake: premature optimization. Use text initially then inspect the logs to find out what is using the bandwidth.
- Prototype the finite state machine with a tool
- Automated file format definition transformation into a GUI editting tool
- Configurable logging at runtime; ex: syslog
- Keep a list of all code areas with name, color, max memory, time/frame. Usefull for debugging, logging, profiling.
- Document the system structure.
- Build on another platform -- even when not a cross platform game.
- Build on a PC when making a console title: can share dev kits.
- Write on the console first since architectures are so different -- important to start with "suckier" platform
- Enforce memory polices -- ie. no memory allocation in main loop
- Lock memory manager in section to assert on memory allocation
- For internationalization: hash english text instead of String IDs, lead to readable code. Automated string table management good.
- Translators need context to resolve homynyms. They might translate "back" as in "previous" as "back" as in "dorsal side".
- Using LUA for front end helped with string translation.
- Don't parameterize strings if you can avoid it.