Ideally, we would like to be able to know in advance which tasks and systems we need to work on first and which can wait a while.. 9.2.2 Internal and external milestones We can see that
Trang 1class evt_Event{
class evt_StateEvent : public evt_Event{
class evt_Scheduler{
public:
void AddTimedEvent( evt_TimedEvent * pEvent );void AddStateEvent( evt_StateEvent * pEvent );};
and so we get to keep our encapsulation-preserving interface:// evt_IsEventDriven.hpp
class evt_EventScheduler;
class evt_IsEventDriven{
public:
Trang 2const char cToken[ MAX_TOKEN_NAME ];
while( aStream.ReadToken( cToken ) )
{
if ( !strcmp( cToken, "set_position" ) ){
// …}
// …else{Warning( "Unknown command ‘%s’, cToken );
}}
then I’d have enough for a cappuccino and a skinny breakfast muffin at my
favourite multinational coffee bar Leaving aside the horrors of case-sensitive
comparisons and the hideous inefficiency of a large number of
per-object-per-game-loop string comparisons, the main problem with this sort of scripting
is the lack of grammar
Trang 3For example, the above set_position command can fall flat on its face ifpresented with
set_position 10 15 + 2*sin( t * 6.28 ) 1.0E-2
for a multitude of reasons There are two alternatives for handling grammar:
● An escalating series of special-case bodges in the scripting system
● Use of a grammar definition language to generate the script toolset
The latter is, in theory, more appealing than the former In practice, mers have to wrestle with tools such as lex and yacc or flex and bison, or – morerecently – antlr (all these are available, free of charge, over the Internet) Often,
program-it looks as if the cure is worse than the disease, as specifying, modifying anddebugging a complete grammar can be a sizeable task
The tools mentioned above can generate code for any platform, withinlimits defined mainly by the core systems that they insert into the project codebase For example, antlr uses STL, therefore your target platform would need tosupport that to use it at run time They may make assumptions about thingssuch as IO, as well, for example reading from streams (cin) rather than stdio(stdin) So you will inevitably have to ask yourself: ‘Is it really worth it?’
Suggested answer: ‘Yes’ Remember that the goal is to write a script languagethat can support high- and intermediate-level behaviour Programmers are going
to get quite frustrated with a grammar-lite language if they need to implementmoderately sophisticated behaviour with it Even designers will get annoyedwith lack of reasonable semantic and grammatical power One project I knew ofhad a bespoke scripting language that allowed ‘IF’ statements but not an ‘ELSE’clause, much to the chagrin of the scriptwriters
If the game would be served better by writing a GUI to implement and-drop scripting, then much ugliness and syntactic limitations can be hidden,providing that no one will be required to debug the script that the GUI gener-ates Chance would be a fine thing! If the designers don’t understand thescripting language (they do everything with point-and-click, so why shouldthey?), then it will fall to programmers to do the debugging Recall that anothergoal of writing a scripting system was to spread the load between disciplines,and it is clear that a GUI can have the opposite effect to that intended There isalso a question mark over how flexible a WIMP-based scripting environmentreally can be, and it is going to be difficult to write a generic GUI to wrap ascripting system Writing a bespoke – per-project – GUI makes more sense intheory, but in practice it can be time-consuming It depends on the complexity
drag-of the toolset to determine feasibility, as well as on having an existing library drag-oftool building components that can speed up the process of developing gameediting systems
Trang 48.5 Summary
● Games that are data-driven need no or minimal recompilation between data
changes, speeding up the development cycle …
● … but if overused they can obfuscate the way the application works, slowing
down development
● Game designers need to be able to control the game in a legible, high-level
fash-ion without having to worry about nasty technical issues Data-driven systemsare ideal for this
● Scripting systems are, to a large extent, functionally equivalent Though many
exe-cution paradigms exist, you can usually achieve the same results independent ofthe implemented methodology Therefore, simplicity and clarity are paramount
● Make the effort to create a familiar grammar for your scripting system, and don’t
waste time writing fancy graphical drag-and-drop interfaces
Trang 69.1 Introduction
In this chapter, we discuss two important questions in development and
pro-vide a single answer for both They turn out to be fundamental not only to the
logical structure of the code-development process but also to the production
methodology Here are the questions:
● What order should tasks be performed in?
● When is the game finished?
9.1.1 Prioritising tasks
The worst thing in the world as far as development is concerned is to be writing
system-critical code towards the end of a project Yet this is such a common
occurrence you would think someone would have spotted it and put a stop to it
Not only will (and does) it induce huge amounts of stress in the team, but it is
absolutely guaranteed to introduce all sorts of problems in other systems that
were previously considered stable
Ideally, we would like to pick an order in which to perform tasks that doesnot lead to this horror Ideally, we would like to be able to know in advance
which tasks and systems we need to work on first and which can wait a while If
we cannot attain this ideal state – and I would be foolhardy to suggest we can –
we can certainly do better than writing critical-path code during alpha or beta
phases in a project
9.1.2 How long is a piece of virtual string?
Although a game is a finite piece of software, it is rather tricky to describe
crite-ria for completion It is almost universally true that the functionality and
features of games that we see on store shelves are only some percentage of the
development team’s ambitions More will have been designed than
imple-mented, and not all that was implemented will have been used Given, then,
that games rarely reach the all-done mark, how are we to decide whether a game
Iterative development
351
Trang 7is releasable? What metrics are available to inform us how much is actuallydone and dusted?
Consider also a problem of scheduling subtasks Say a programmer (let’s callher Jo) has said it’ll take ten days to write the ‘exploding trap’ object, and thatshe’s four days into this time Is her task 40% complete? It’s very hard to tell,especially since we cannot see the trap exploding till maybe day nine or ten Butlet’s be optimistic and suggest that Jo works hard and gets the job done in eightdays Usually there is a profit of plus two days marked up, the task is marked ascomplete, and everything looks hunky dory for the project
Later on, it turns out that the trap needs to be modified since (say) it needs
to be able to trap larger objects It’s another four days of work for our Jo, andnow we have a deficit of minus two days, and suddenly the project starts to looklike it’s slipping
The point is this: most objects in a game rarely get written just once We’llrevisit them over the course of a project to fix bugs, add and remove features,optimise and maybe even rewrite them entirely This isn’t a pathological behav-iour: almost all significant software systems grow and evolve over the course oftime How naive then does the ‘four days in, 40% complete’ metric look? Prettydamn naive, to put it politely What we really need is a system that allows timeand space for evolution without driving projects into schedule loss and theresulting state of semi-panic that characterises most development processes
9.2 Incremental delivery 9.2.1 Milestones around my neckAlmost all software development (outside of research, which by its nature it open-ended) is driven by some kind of milestone system Let me state unequivocallynow that this is a good thing, and that the days of anarchic commercial softwaredevelopment should be buried and remain so Nevertheless, just by virtue of itbeing a good thing does not mean that it doesn’t come with its own particular set
of pros and cons In particular, if we accept (for all the pro reasons) that stone-driven development is the way to go, then we must also pay attention tothe con side, which will inevitably frustrate our attempts to make the processwork with the efficiency we require for delivery on time and within budget.One of the most difficult cons that games developers have to deal with isthe different way that milestones and the associated schedules are interpreted
mile-by production teams and management As most of those who have worked withnon-trivial software products (or, in fact, any large project that requires multiplebespoke interacting component parts spanning a variety of disciplines) havecome to realise, schedules represent a team’s best guess at how the product willevolve over time
On the other hand, management – perhaps unused to the way that ules are produced, perhaps because it requires correlation of studio funding with
Trang 8sched-progress – often read the document completely differently They see the
docu-ment almost as a contract between themselves and developers, promising
certain things at certain times
This disparity between seeing a schedule as a framework for project tion to facilitate tracking, and as a binding agreement to deliver particular
evolu-features at particular times, causes much angst for both developers and
man-agers The former often have to work ridiculous hours under pressure to get
promised features out The latter have responsibility for financial balances that
depend on the features being in place
9.2.2 Internal and external milestones
We can see that there are some basic premises about milestones that need to
be addressed:
● Teams that do not work to milestones that mark important features
becom-ing available in the game will not be able to deliver on time
● Teams that are held to unrealistic milestones will not be able to deliver on
time, irrespective of how financially important or lucrative that may be
● Managers need to know how long the team thinks development will be and
what the important markers are along the way Without this, there can be
no business plan and therefore no project
Clearly, the sort of milestones that managers need to be aware of are cruder or at
a lower granularity than the milestones that developers need to pace the
evolu-tion of the product We can therefore distinguish between external milestones,
which are broad-brush descriptions of high-level features with granularity of
weeks (maybe even months), and internal milestones, which are medium- and
fine-level features scheduled in weeks and days
Managers therefore never need to know the internal mechanisms that erate the software To adopt a programming metaphor, the team can be viewed
gen-as a black-box type of object with the producer gen-as its interface There are two
types of question (public methods, to extend the analogy) that a manager can
ask of a producer:
● Give me the latest version of the game
● Give me the latest (high-level) schedule
This is an unrealistically simple example of interaction between production and
management The latter will want to know issues of team dynamics, why things are
running late (as they inevitably seem to) and a whole host of other project-related
information However, it draws a fuzzy – but distinguishable – line in the sand
between the scheduling of features and accountability for their development
Trang 99.2.3 The breaking-wheel of progressThere is one other important sense in which management and developmentperceive milestones differently It is based on the concept of visibility and is,without doubt, the biggest millstone (half-pun intended) around developers’necks this side of Alpha Centauri.
Almost ubiquitously in the industry, management refuse to regard featuresthat they cannot see (or perhaps hear) within a short time of picking up thegame as importantly as those obviously visible (or audible) ones For those of usthat work on AI, physics, memory managers, scripting systems, maths, optimisa-tion, bug fixing and all those other vital areas of a game’s innards that are notopen to visual inspection, this is particularly galling To spend weeks and monthsworking on hidden functionality only to have the team’s work dismissed as inad-equate because there was no new eye candy is an all too common occurrence.The education of managers in the realities of development is a slow on-going and painful process Meanwhile, we developers have to work with what
we are given, therefore it remains important to – somehow – build ongoingvisible/audible progress into the development of the project
There is an intimate relationship between the concept of visibility and that
of completeness Many tasks may not become tangibly present until they arecomplete Saying that something is 40% complete, even if that was a rigorouslyobtained metric, might still amount to 0% visible So, we’ll only be able toaddress the issue of progress fully when we deal later with determining com-pleteness for a task
9.2.4 Always stay a step aheadDespite our best – though sometimes a little less – efforts, we will slip We willdeliver a feature late or perhaps not even at all, and if the management is in aparticularly fussy mood, then there may be much pounding of fists and redfaces Worse than showing no visible progress would be to show retrogradeprogress – fewer features apparent than a previous milestone Nevertheless, it is
a common and required ability for projects to arbitrarily disable and re-enableparticular functionality within the code base With the advent of version con-trol systems, we are now able to store a complete history of source code anddata, so in theory it is always possible to roll back to a previous version of thegame that had the feature enabled
Just because it’s possible, does that make it desirable? In this case, yes.Indeed, I would argue that working versions of the game should be built fre-quently – if not daily, then at least weekly – and archived in some sensiblefashion When management asks production for the latest version of the game(one of their two allowed questions from the previous section), then the pro-ducer will return not the current (working) build but the one previous to that.Why not the current working build? Because it is important to showprogress, and development must ensure that to the best of their ability the gamehas improved visibly from one iteration to the next If it becomes necessary –
Trang 10and it usually does – to spend time maintaining, upgrading, optimising or
rewriting parts of the code base, then releasing the next-but-one working
ver-sion gives another release with visible improvements before we hit the calm
spot with no apparent progress
From one point of view, this is a sneaky manoeuvre It’s no more sneakythan (say) insuring your house against flood.1Publishers and managers always
want to see the latest version, and a development team itching to impress may
well be tempted to show them it Resist this urge! Remember: development
should be opaque to management inspection other than through the supplied
interface Anything else is just development suicide
9.3 Iterated delivery
So we’ve decided that rather than work specifically to release code at external
milestones, we’ll supply work-in-progress builds at these times Internally, we’ll
be working to our own schedule How should we organise this schedule?
I’ll start by assuming that there is a reasonably comprehensive design ment for the game (but believe me, you’d be surprised at the number of times
docu-there isn’t) This document should describe, in brief, what the game is about –
characters (if any), storyline (if any), situations and rules Step one to producing
an internal schedule is to produce the object-oriented design diagram for the
game We are not interested here in the diagram specifying interrelationships
between the objects; the end goal is simply to produce a big list of all classes
that map directly to concepts in the game Auxiliary classes such as containers
and mathematical objects need not apply – we are looking only for classes that
map to game-level concepts
Once we have produced this list, it needs to be given back to the designteam, as step two is really its call It needs to classify all the objects in the list
(I’ll use the terms ‘objects’ and ‘features’ interchangeably in this section) into
the following three groups:
● required
● desired
Core features form the basis for the game Without them, there is only a basic
executable shell consisting of (some of) start-up code, rendering, memory
man-agement, sound, controller support, scripting support, resource manman-agement,
etc Should any of these non-game systems require engineering, then they
should be added to the core group, which will otherwise contain the most
fun-1 As I write this section, areas of Britain have been submerged as rain falls near continually and rivers
Trang 11damental objects For definiteness, consider a soccer game; the most tal objects are:
fundamen-● player (and subclasses)
● stats (determining player abilities)
Required features expand the core functionality into what makes this game
playable and unique Often, these features are more abstract than core features.They will embody concepts such as NPC behaviour, scoring systems and rules.Also, they will pay some homage to the particular genre the game will fit into,because rival products will dictate that we implement features in order to com-pete effectively To continue the soccer example, we might place the followingfeatures in this group:
● AI for player subclasses
● referee (either a visible or invisible one that enforces rules)
● crowd (with context-dependent sounds and graphics)
● knockout, league and cup competitions
A game consisting of core and required features will be playable and releasable.Nevertheless, it should be considered the minimal amount of content that will bereleasable, and it still requires work if the game is to be near the top of the genre
Desired features provide the polish for the game This will include such
things as visual and audio effects, hidden features and levels, and cheats.Features in this group will not alter game play in significant ways, though theywill enhance the breadth and depth of the playing experience and (as withrequired features) the competition may dictate their inclusion
Depending on the type of game, they may be game-related objects Forexample, in the soccer game, having assistant referees would be a desired fea-ture, as the game will function just fine without them
The end result is a list of features that is effectively sorted in terms of tance to the product It is tempting to say that the optimal order of tasks is then
impor-to start at the impor-top – the most important core tasks – and work our way down
We carry on completing tasks until we run out of time
Well it’s close, but there’s no cigar for that method There are fundamentalproblems in organising work this way There is little evidence of anything thatresembles continual progress In the pathological case, the game is in bits forthe entire development cycle until just before the end, when the bits are pulledtogether and – hopefully – fit This is guaranteed to have producers and man-
Trang 12agement biting their knuckles with stress Furthermore, the most likely outcome
is that components do not work together or have unforeseen side effects that
may involve radical redesign very late on in the project
Clearly, it is correct to do the most important tasks first and the superficialtasks last (time allowing) But if we wish to show continual improvement of the
product, then we shall need to be a little smarter So we will progress to the
third phase of the iterated delivery method (the actual ‘iterated’ part) We’ll start
again with the list of features, which, because an object-oriented design process
has generated them, map directly to classes
Consider just one of these classes How does it start off its life? Usuallysomething like this:
Over the course of the product development, much will be added and much will
also be removed, but generally the object evolves This evolution can occur in
one of two ways First, it can start with zero functionality and end up fully
implemented This is possible, but not very common More realistically, the
object is rewritten either fully or partially to have more complex, or more
robust, or more efficient behaviour over the duration
So far, so obvious But consider the formalisation of the principle thatobjects evolve: instead of evolving the feature from zero functionality at the
start to full functionality at the end, consider writing versions of the full object
functionality We define the following four versions of the feature:
Trang 131 The null version: this is the initial version of the object interface with no
imple-mentation (empty functions) Note that this is a complete project that can becompiled and linked and that will run, albeit not doing anything useful
2 The base version: this has a working interface and shows placeholder
func-tionality Some of the required properties may be empty or have minimalimplementation For example, a shadow may be represented by a singlegrey sprite, and a human character may be represented by a stick person or
a set of flat-shaded boxes The intent is that the object shows the most basicbehaviour required by the design without proceeding to full implementa-tion, and therefore integration problems at the object level will show upsooner rather than later
3 The nominal version: this iteration of the feature represents a commercially
viable object that has fully implemented and tested behaviour and is ally acceptable For example, the shadow may now be implemented as aseries of textured alpha-blended polygons
visu-4 The optimal version: this is the ultimate singing and dancing version,
visu-ally state of the art, and then some To continue the shadow example, wemay be computing shadow volumes or using projective texture methods.We’ll refer to the particular phase that an object is in at any point in the project
as the level of the class: a level 1 object has a null implementation, whereas a
level 4 object is optimal
Some points to note: first of all, some objects will not fit naturally into thisscheme Some may be so simple that they go straight from null to optimal.Conversely, some may be so complex that they require more than four itera-tions Neither of these scenarios presents a problem for us, since we aren’t really
counting iterations per se We’re effectively tracking implementation quality In
the case of an apparently simple object, we can test it effectively only in thecontext of any associated object at whatever level it’s at In other words, systemsand subsystems have a level, which we can define slightly informally as:
L(subsystem) = minjL(objectj)L(system) = miniL(subsystemi)with L() denoting the level of an object, system or subsystem Applying this idea
to the application as a whole,L(application) = minkL(systemk)
or, in simple terms, the application’s level is the smallest of its constituentobject levels
Now we need to put together the ideas of level and priority to get someuseful definitions, which form the basis of iterated delivery
Trang 14An application is defined as of release quality if, and only if, its required
fea-tures are at the nominal level
An application is referred to as complete if, and only if, its desired features
are at the optimal level
From these definitions, we see that there is a sliding scale that starts from a
barely releasable product all the way up to implementing and polishing every
feature that the design specifies The product just gets better and better, and –
provided that the tasks have been undertaken in a sensible order – can be
released at any time after it becomes of release quality
The second point to note is that object-oriented development is perfectlysuited to a level-based scheme (and, conversely, procedural development does
not adapt as easily) For example, consider our shadow code An object that has
a shadow may declare:
Trang 15Sprite * m_pSprite;
};
// File ShadowPolygonal.hppclass ShadowPolygonal : public Shadow{
public:
// Nominal implementation
virtual void Compute( SCENE::Frame * pScene );
virtual void Render( REND::Target * pTarget );
private:
Polygon * m_pPolygons;
};
// File ShadowProjected.hppclass ShadowProjected : public Shadow{
public:
// Optimal version
virtual void Compute( SCENE::Frame * pScene ) = 0;
virtual void Render( REND::Target * pTarget ) = 0;
m_pShadow = new ShadowProjected();
We can even use a so-called factory pattern to create our shadow objects:// AnObject.cpp
#define SHADOW_LEVEL level_NOMINAL// …
m_pShadow = Shadow::CreateShadow( SHADOW_LEVEL );
// Shadow.cpp/*static*/
Shadow * Shadow::CreateShadow( int iLevel )