Sie sind auf Seite 1von 23

ImplementingGameplayElementsUsingGamebryo

Chapter1ASimpleFrameworkforaGameApplication
Motivation
Novicegamedevelopers,inspiredbythelatestgamedevelopedbyCrytekorLionheadorEA,may wanttobeginprogramminggameplaywithoutthinkingaboutthedesignandarchitectureofthe game application. They want to work on code that makes the game fun! You might be able to create a simple game without considering the software design; however, if you try to build a complex game by adding features to adhoc code, you will find that the cost to develop a new featureincreasesovertime1.Further,thecodewilllikelybecomemorefragile,whichcanleadto costoverrunsandprojectfailure.WewanttoteachyouhowtousetheGamebryosystem,while atthesametimeillustratingsomeofthedesignelementsthatyoushouldconsiderwhenbuilding yourgame. You may be interested in constructing gameplay prototypes, developing a game as part of an academicproject,ordevelopinghighqualitycommercialgames.Inanyofthesecases,whenyou choosetobuildyourgameusingGamebryo,youarenotchoosingtomodanexistingshrinkwrap gamebyaddingyourowncontentandtweakingAI.Youwillbebuildingyourowncustomgame enginefromhighquality,robustcomponentsthathavebeendesignedtoallowyoutheflexibility thatthemodcommunitydoesnotenjoy.Thegoalofthistutorialseriesistoteachyoutodevelop gameplay features using Gamebryo, the centerpiece of your custom game engine. Each chapter will illustrate some Gamebryo feature, showing you how to integrate it with thirdparty open sourceprojectsandyourowncode. Chapter 1 will begin to walk you through the development of some of the software needed to assemble a simple game engine using Gamebryo and other available components. The text will describeconceptsanddocumentthedevelopmentofasmallfragmentofagamethatimplements the concepts. The code developed throughout this series is meant only to be illustrative, fairly easytoreadandcomprehend.Itisnotintendedtorepresentthecurrentbestofbreedpractices inthegameindustry.Wewillprovidewitheachchapterasetofrecommendedreferenceswhere you can explore other ideas. The game development community is quite open, and it is easy to

1 Notethatthegameplayfirstapproachtogamedevelopmentcanplayanimportantroleinthe successofalargeproject,forexamplewhenusedtobuildprototypesasapartofpreproduction. SporedeveloperMaxishasincorporatedgameplayprototypingintotheirdevelopmentprocessfor manyyears,especiallyintheearlyphasesofdevelopment,andthepopularityoftheirpriorgames isevidencethattheirapproachissuccessful.Themodcommunitiesthatgrowoutofthefanbase ofmanyfirstpersonshootersarealsoabletoquicklybeginworkingongameplaywithoutconcern for software architecture, In this case, somebody else created the architecture for them, and thoughthisenablesgameproductiontobeginquickly,theabilitytoaddnewfeaturesislimited.

Chapter1:ASimpleFrameworkforaGameApplication1

ImplementingGameplayElementsUsingGamebryo learnfromotherdevelopersby readingtheproceedingsoftheGameDevelopersConferenceor otherindustryconferencesthatoccureachyeararoundtheworld.So,withthat,letusbegin!

GameasaStateMachine
Theconceptofafinitestatemachineappearsinmosteverygamedesign.Itisperhapsmostoften associated with artificial intelligence (AI), though developers do not implement AI using state machines exclusively and often use state machines for algorithms other than AI. The most fundamentaluseofthestatemachineinagamehasnothingtodowithgameAI.TheWikipedia pageforafinitestatemachinestatesthatAfinitestatemachine(FSM)isamodelofbehavior composed of a finite number of states, transitions between those states, and actions. This definitionmightseemratherabstract,butitiseasytomakesenseofthis.Considerthefollowing characteristics,gamescreensinatypicalgame: TheopeningsplashscreenthatappearswhenyoufirstputthegameCDintotheconsole andturntheconsoleon:thegamesbootscreen(StartupSplash) The main game user interface screen that lets you select a number of options such as beginanewgame,loadasavedgame,visitanonlinegaminglobby,browseaninventory ofmagicspellsgatheredwhileplayingthegame,etc.(Home) Thescreentobeginanewgamethatletsyouselectacharactertoplay,aleveltoload,and thedifficultylevel(StartNewGame) An attract mode in which the game plays back a cinematic sequence, originally developedforarcadegamestoattractplayerswhomightbestandingnearby(Attract) The loading levelplease wait screen that appears after you have selected a level to play(Loading) Apopupwindowthatdisplayswhenthegameispaused(GamePaused) Themaingameplaywindow,whichdisplaysa3Dgameworld,theplayerscharacterand NPCs, perhaps a small inset overhead map of the level, player statistics, inventory, and selectedweaponry,etc.(MainGamePlay)

Allofthesegamescreensdisplaysomethingdifferenttotheplayer.Eachisindependent,andonly one can be in control at any instant in time (though more than one might be actively doing something).Eachofthesegamescreensisagamestate,andsoitbeginstobeeasytoimaginea finite state machine that represents the overall flow of the major game screens of a game. Lets lookatadiagramofthestatesdescribedabove.

Chapter1:ASimpleFrameworkforaGameApplication2

ImplementingGameplayElementsUsingGamebryo
Attract

StartupSplash

Home

StartNewGame

Loading

MainGamePlay

GamePaused

Here,anarrowhasbeendrawnbetweentheStartupSplashstatetotheHomestate.Thisindicates aratherobvioustransition.TheStartupSplashstateisactiveonlyuntilsufficientgameassetsare loadedtodisplaytheHomestate,themainopeninggameuserinterface.TheStartupSplashstate isthefaceofthegameasitboots.Letsaddanothersetoftransitions.Ifnoplayerinteractswith thegameforsomeperiodoftime,say60seconds,theHomestatewillinitiateatransitiontothe Attract state. After some time, or if the game detects that a player is doing something with a gamepad,theAttractstatewillinitiateatransitionbacktotheHomestate.Thefollowingfigure showsthisnewpairoftransitions.
Attract

StartupSplash

Home

StartNewGame

Loading

MainGamePlay

GamePaused

Without much difficulty, if you are familiar with games, you should be able to fill in the remainingbasictransitions.Thefollowingfigureillustratesacompletedstatemachinetransition diagram. (Though actually, there are other transitions that make sense here. For example, the player might be able to cancel out of the StartNewGame state, causing that state to transition backtotheHomestate.)

Chapter1:ASimpleFrameworkforaGameApplication3

ImplementingGameplayElementsUsingGamebryo
Attract

StartupSplash

Home

StartNewGame

Loading

MainGamePlay

GamePaused

Thisverybasicintroductiontotheconceptofstatemachinesshouldconvinceyouthatatypical gamereally,anygamethatgivestheplayeranyoptionscanbebuiltaroundahighlevelfinite statemachine.Withalittlebitofplanning,itisntdifficulttobuildthesetypesofstatesintoeven thesmallestofgames,fromthebeginning.

Implementing a Simple Game Application using Gamebryo and a Simple Finite State Machine
Theremainderofthischapterwillassembleasimplegameapplicationframework,implementing asubsetofthestatesdescribedabove.
StartupSplash StartNewGame

MainGamePlay

GamePaused

The way our initial set of 4 states will work is extremely simple. It resembles a traditional application that displays a main window, but can change the way the user interacts by display popupdialogboxes,addingandremovingpanesandcontrols,etc.Onekeydifferencehereisthat, thoughonlyonestatecanbeincontrolofthegameapplication,anynumberofsuspendedstates has the opportunity to perform actions interactively in time. These suspended states are not respondingtocontrollerinput,butareplayinganimationsorotherspecialeffects,decidingwhen toautosavealevelinplay,etc.Thesimplicityofthisinitialdemowillmakeiteasierforyouto seehowthisworks,howthingsfittogether,andhowyoucanexploitcertainGamebryofeatures

Chapter1:ASimpleFrameworkforaGameApplication4

ImplementingGameplayElementsUsingGamebryo withinthesegamestates.Hereisadescriptionofthespecificbehaviorswewillimplementinthe 4states. StartupSplash 1. Displayastaticfullscreenimageshowingthegametitleandinformationthatthe gameisloading 2. Forourchapter1tutorialcode,simplywaitforafewsecondstomimicthedelaya playerwillexperiencewhilegameassetsloadingfromdisk.Thetutorialwillload assets, but for now the assets arent so significant that the player would be botheredbythedelay. 3. At the end of the waiting period, remove the fullscreen image and force a state changetoStartNewGame StartNewGame 1. Displayalistboxwiththenamesofallavailableplayablecharacters,andallowthe usertoselectfromthelistboxwiththemouse 2. Displayalistboxwiththenamesofalltheavailablegamelevels.Forthischapter, onlyonegamelevelwillbeavailable,andtheuserwillnotbeabletochangethe selection 3. Display a button titled Begin Game, which when clicked with the left mouse button will force a state change to MainGamePlay, thus beginning a new game usingthecurrentlyselectedcharacterandlevel 4. Displaya3Danimatedviewofthecurrentlyselectedcharacteronaportionofthe screen.Thecharactershouldactivateanidleanimation. 5. Displaya3Dviewofthecurrentlyselectedgamelevelonaportionofthescreen. 6. While waiting for the user to begin a game, respond to selection changes in the characterlistboxbychangingthe3Dcharacterviewtoreflectchangedselections 7. Updatetheactiveanimationofthecurrentlyselectedcharacterovertime MainGamePlay 1. Loadtheselectedlevelandselectedcharacter.Placethecharacteratthelocation of a named spawn point node located in the game levels scene graph. Make a cameranamedDefaultCameratheactivecamera.Thiscameraiscontainedinthe levelscenegraph 2. Animatethecharacterinitsidlestateastimepasses 3. Monitorthekeyboard.WhentheuserpressestheQkey,quittheapplicationwith nomessagestotheplayer 4. WhentheuserpressesthePkey,temporarilyentertheGamePausedstate.Note thatthiscausesMainGamePlaytobecomesuspended. 5. Whilesuspended,donotrespondtokeyboardclicksorotheruserinterfaceactions 6. While suspended, continue to update the characters idle animation, so that the gamelevelstillhaslifeeventhoughgameplayisnotcontinuing 7. Whenthestateisresumed,resumerespondingtothekeyboard GamePaused

Chapter1:ASimpleFrameworkforaGameApplication5

ImplementingGameplayElementsUsingGamebryo Display a popup window UI over a portion of the screen that contains a single buttonlabeledResumeGame 2. Respond to button clicks, and when the user selects the Resume Game button, removethepopupwindowandremoveGamePausedfromthestatestack,causing thegametoreentertheMainGamePlaystate. StandardandOpenSourceComponents ThistutorialserieswillusetheNiApplicationframeworkthatispartofGamebryo.Usingthis framework enables us to focus on integrating a simple finite state machine that is suitable for managing high level game state transitions and implementing the four game states we have selected.ThetutorialcodecreatesanewapplicationclasscalledGameObject,whichextendsthe NiApplicationclassasshownintheclassdiagrambelow. 1.

NiApplication

GameObject

WearegoingtointegrateanopensourcestatemachinesystemcontributedbyJamesBoertothe book Game Programming Gems 5, titled LargeScale StackBased State Machines. When you browsethesourcecode,takenoteofJamescopyrightandusagerightsthatareassignedwithin hissourcecode.Thissimpleframeworkwasdesignedforthingslikehighlevelgamestate,andis quite easy to use. It is the perfect choice for getting the initial game code up and running. The tutorialusesBoersimplementationnearlyverbatimfromtheCDdistributedwiththebook.Only thefollowingchangeswereapplied.CommentsintheStateManager.handStateMachine.cppfiles documentthesechanges.LookforthelabelEGTalongwithdescriptivetext. The pure virtual state interface class, IBaseState, was updated to be a Gamebryo referencecountedobject,e.g.,itisnowderivedfromtheNiRefObjectbaseclass. The current simulation time was added as a parameter to the IBaseState::Update function,toenableconcretestateclassestoupdateanimations,timestatechanges,etc. TheStateManagerclasswasupdatedtostoresmartpointerstoreferencecountedstate instancesratherthanrawpointers,toaidinmemorycleanup. TheStateManagerclasswasmodifiedtoincludethecurrentsimulationtimeincallsto IBaseState::Updateontheactiveandsuspendedstates.

Wewillbeusingoneadditionalexistingopensourcecomponent.Sincemostofthestatesrequire a graphical user interface, we need classes that can implement widgets such as buttons and list

Chapter1:ASimpleFrameworkforaGameApplication6

ImplementingGameplayElementsUsingGamebryo boxes.EmergentdoesprovideaspartoftheGamebryodistributionasimpleuserinterfacelibrary, theNiUI*classes;however,theprovidedGamebryolibraryincludesonlyaverylimitedselection ofcontrols.WewillbeusingCrazyEddiesGUISystem(CEGUI).OneofEmergentssupportstaff hastakenthetimetointegrateCEGUIwithGamebryo,andwewillexploitthisintegration.You can find the original demo integration in the Gamebryo support forum thread titled "CEGUI Gamebryo integration" located in the "Community Code Swap" folder, currently part of the Download forum.This codetooisusedinnearlypureform.Theonlychangeistoensurethe CEGUIsystemhasbeeninitializedatthetimeaCEGUIrenderclickisconstructed.Thesechanges aredocumentedintheCEGUIRenderClick.cppfilewiththelabelEGTanddescriptivetext. Do keep in mind that these open source components may not be the only options available to you. For commercial projects, you may wish to consider using libraries provided by Emergents commercialpartners,sincetoolsandsupportforcommercialoptionsareoftenmorecosteffective androbustthanopensourcecode. AnatomyofaState Above,wepresentedthebasicconceptofagamestate,andtheideaoftransitionsbetweenthem. Here, we will look at the anatomy of a single state, how the state has the opportunity to implementgameplay,andhowstatetransitionsareinitiated. Anygivenstatecanbeeitheractiveorinactive.Allstatesthathavebeenregisteredwiththestate managerareinactivebydefault,anddonothingastimepasses.Youcanthinkofthemasalibrary ofstatesthatareavailableforusewhenyouneedthem.Theyarenotonthestatestack.Active statesarethosethatareonthestatestack,andallofthesehavetheopportunitytodoavarietyof tasks,including: Loadsceneobjects Modifytheframerenderingsystemtodisplaydifferentoverlaysanduserinterfaces Modifyscenegraphstospawnnewobjects,respondtotriggers,etc. Displaystatespecificscreenoverlaysandcontrols Performartificialintelligenceorphysicscalculations Readfromuserinterfacedevices(suchasgamepads)toimplementplayerinteraction Updateanimationsandrenderingeffectssothatingamevisualsareproperlydisplayed

Note that a state that is made active will become inactive once again if the game removes the statefromthestatestack. States typically do not do any rendering. They can change the frame rendering configuration, adding, deleting, and modifying items. But the actual rendering is triggered within the NiApplicationframeworkafterstateupdateshavecompletedforeachframe. Anactivestatecanbesuspendedtemporarily,allowinganotherstatetotakeoverforawhile.An exampleofthisistheMainGamePlaystate,whichissuspendedwhilethegameispaused.

Chapter1:ASimpleFrameworkforaGameApplication7

ImplementingGameplayElementsUsingGamebryo Once constructed and registered with a state manager, each state object has 5 primary entry points,whichoverridethefollowingbaseclassentrypoints: IBaseState::OnEnter: the state manager calls this member function whenever the gameisenteringthestate,andbecomingactive.Itisherethatastatecanmodifyascene graph, create a thread to do background loads, create a statespecific graphical user interface(e.g.,apopupdialogbox)toberendered,etc. IBaseState::OnExit: the state manager calls this member function whenever the gameisexitingthestate,andthestateisbecominginactive.Thefunctionshouldperform cleanuptaskshere,freeingmemoryandunloadinganystatespecificscenegraphelements orotherresources. IBaseState::OnOverride: the state manager calls this member function whenever another state is being pushed onto the state stack. The state remains active, but is suspended. It is allowed to do updates over time, but it is not the highest priority state. For example, the state manager will call this entry point on MainGamePlay whenever GamePausedbecomestheactivestateatthetopofthestatestack. IBaseState::OnResume:thestatemanagercallsthismemberfunctionwheneverthis statemovestothetopofthestack,becomingthehighestprioritystate.Forexample,the statemanagerwillcallthisentrypointonMainGamePlaywheneverGamePausedismade inactive. IBaseState::Update: Once per frame, the state manager calls this member function onallactivestates.Thisenablestheactivestatestoupdatethemselvesovertime.

Thecurrentgamestatecanbechangedmostanywhereinthegamecode,buttypicallythisoccurs somewherewithinthehighestpriorityactivestate.Forexample,theStartupSplashstatewillforce atransitiontotheStartNewGamestateinsideStartupSplash::Update,onceitisdonewith bootstrap processing. The StartNewGame state will transition to MainGamePlay when the user pressesabutton,andMainGamePlaysuspendsitselfbypushingGamePausedontothestatestack whentheuserperformssomeuserinterfaceactiontopausethegame. State transitions are activated via the StateManager class, using the following member functions.ThegameapplicationmuststoreasingleinstanceofStateManager,andmustmake itavailabletoanygamestatethatneedstotransition. StateManager::ChangeState: Call this function to change the current highest priorityactivestate.Thisdoesnotchangeanylowerprioritystatesinthestatestack. StateManager::PushState:Callthisfunctiontosuspendthecurrenthighestpriority state,pushanewstateontothestack,andenterthenewstate. StateManager::PopState:Callthisfunctiontoexitthehighestprioritystate,popit fromthestack,andresumethenexthigherprioritystate.

Chapter1:ASimpleFrameworkforaGameApplication8

ImplementingGameplayElementsUsingGamebryo NiApplicationandGameInitialization Withthosepreliminarydesignnotesoutoftheway,letsuslookattheassemblyofcodeforthis chapter.OurgameisbuiltaroundtheNiApplicationframeworkthatisusedbythetutorials thatarepartoftheGamebryodistribution.Mostoftheinternalsofgameapplicationstartupare handled deep within NiApplication. You can find a detailed description of that class in the Gamebryodocumentation.OurGameObjectclassderivesfromNiApplicationandoverrides severaloftheNiApplicationmethodstosupportCEGUIintegrationandtosupportthestate machineframework.TheGameObjectclassincludestwomembervariables: GameObject::m_StateManager.Thestatemachinemanagerinstancethatcontrolsall stateupdates,thestatestack,andtransitionsbetweenstates. GameObject::m_spGUIRenderClick.Asmartpointertotherenderclickobjectthat willdisplayallCEGUIuserinterfaceelements.

MuchoftheGameObjectclassisboilerplate.ItsimplyoverridesvariousNiApplicationbase classmethodstoaddsupportfortheCEGUIsystemandthestatemachinesystem: GameObject::Initialize: This member function overrides the base class function, adding initialization of the state manager by calling StateManager::Init and registeringallofthegamestatesbycallingGameObject::RegisterBaseStates. GameObject::Terminate.Thismemberfunctiondisablesthestatemanagerbycalling theStateManager::Termmemberfunction,thendestroystheCEGUIrenderclick,and finallycallsthebaseclassmethod. GameObject::CreateRenderer. This member function is largely a copy of the base class method. It has been modified to force the renderer to be windowed when the applicationisaDEBUGbuild,andfullscreenwhentheapplicationisnotaDEBUGbuild. GameObject::UpdateFrame. This member function calls the base class method, and thenupdatestheactivestatesbycallingStateManager::Update.Thestatemanagerin turn calls the Update member function on all active states, allowing them to perform transientoperationssuchasstatespecificanimationsandtomakedecisionsasafunction oftime. GameObject::CreateFrame. This member function calls the base class method to createtherenderframefortheGamebryoframerenderingsystem,andthencallsanother function, GameObject::InitializeGUIFramework, which initializes CEGUI and integratesitintotheGamebryoframerenderingsystem.

TwopiecesoftheGameObjectclassrequireabitmoreexplanation. First, the GameObject::InitializeGUIFramework member function, called from GameObject::CreateScene,isresponsibleforcreatingtheCEGUIrenderclick,whichisthe objectthatactuallyrenderstheuserinterfaceelementsforthegame.Fordetailedinformationon renderclicks,seeNiRenderClickorFrameRenderingSystemintheGamebryodocumentation. ThismethodalsoconfiguresthecoreCEGUIframework,tellingitwhereresourcesarecontained Chapter1:ASimpleFrameworkforaGameApplication9

ImplementingGameplayElementsUsingGamebryo relative to the game application working folder. All runtime assets for the game are contained under the assets folder, and all user interface elements specifically are contained under the assets/UI folder. The method configures CEGUI so that it can locate 5 standard CEGUI asset types:schemes,imagesets,fonts,layouts,andlooknfeels.Finally,thismethodloadsafewCEGUI assets that are generally required for user interface elements within the game. It then adds the CEGUI render click to the screen space render step, so that GUI elements will be drawn, and creates a small, completely transparent background CEGUI window to be a container for any CEGUIelementsagamestatemightneedtocreate.IfyouuseCEGUIinyourgame,youwillneed tohavesimilarcodetoconfigureCEGUI.Theimportantdifferencesforyourowngamewouldbe inthespecificcontrolschemes,fonts,etc.,thatyoudecidetopreloadduringgameinitialization, and perhaps the asset folder configuration. For the most part, this method too becomes boilerplate.(ItisnotthepurposeofthistutorialtoteachyouaboutCEGUI.Ifyouwishtolearn moreabout theseassettypes, visittheCEGUIhomepage.Ifyouareonlyinterestedincreating newuserinterfacelayoutsforsmallprojects,andarentinterestedincustomart,youcanusethe CELayoutEditortooltographicallydesignnewlayouts.) The game initialization code becomes a bit more interesting within the GameObject::RegisterBaseStates member function. It is here that you will begin to see thedesignofthegamecometogether.Thismethodisquitesimple,butsetsupall4ofthegame states,bycreatinginstancesofthemandregisteringthemwiththestatemanager.Notethatthe firstregisteredclassisStartupSplash,theonlyactivestatewhenthegamebegins.Thestate manager will immediately transition into this state during the first GameObject ::UpdateFramecallmadeonceNiApplicationstartsthegamesimulationloop. ClassificationoftheStates With the application initialized and states registered, let us now take a closer look at the individual state objects. We can classify states, where appropriate, in order to reuse some code acrossstates. Allstatesmayneedtoknowwhetherornottheyhavebeensuspended.Thebasestateinterface, IBaseState,doesnotdirectlyprovidethisinformation,andtheStateManagerclassalsodoes not directly provide this information. So, for convenience, the code for this chapter derives an intermediate base class, GameBaseState, that overrides the OnOverride and OnResume memberfunctionstosetamembervariablethattrackswhetherthestateissuspendedornot.It alsoprovidesanemptyimplementationofGameBaseState::Update,justincaseanyderived statedoesnotneedtoperformtransienttasks(e.g.,somestatesmightmerelyexisttorespondto buttonclicks). Threeofthestatesinourinitialdesignwillincludeuserinterfaceelementssuchasbuttons,list boxes, and screen overlays: GamePaused, StartNewGame, and StartupSplash. All of these states willexploitCEGUI,andwilluseCEGUIwindowlayoutstoloadtheuserinterfacecontrolsforthe state. It makes sense then to implement a base class for user interface classes. This class,

Chapter1:ASimpleFrameworkforaGameApplication10

ImplementingGameplayElementsUsingGamebryo GameUIState, is derived from GameBaseState. The classes for the three states listed above thenderivefromGameUIState,asshownintheclassdiagrambelow. Ouroneremainingstateforthischapter,MainGamePlay,doesnotimplementanyuserinterface featuresotherthana3Drenderviewofthegamescene.InsteadofderivingfromGameUIState, itderivesdirectlyfromGameBaseState.

NiRefObject

IBaseState

GameBaseState

GameUIState

MainGamePlay

StartNewGame

StartupSplash

GamePaused

ImplementationoftheBaseUserInterfaceState Let us first look at the user interface base state class, GameUIState. The constructor for the GameUIState,andallderivedstates,containstwoparameters,pcGUIImageSetFilenameand pcGUILayoutFilename. The first parameter is the name of a CEGUI asset file with the extension.imageset.ThisfileisanXMLfilethatpointstoanimagefile(*.jpg,*.tga,etc.)and defines named rectangular areas within that image. The user interface states of our application use the named imageset as a decorative image to be displayed on the screen when the state is active.ThesecondparameteristhenameofaCEGUIassetfilewiththeextension.layout.This isanXMLfilethatdefinesalayoutofuserinterfacecontrolsbuttons,listboxes,etc.,aswellasa backgroundpanebasedonthedecorativeimageset.Thevisualappearanceofthecontrolsusedin the layout is defined via CEGUI schemes and looknfeel objects, which reference imagesets and definewhichnamedrectangularareasareassociatedwithaparticulartypeofcontrol,e.g.,which rectangular subimages represent a buttons up and down states. The scheme and looknfeel also definehowaparticularcontrolismappedtoCEGUIlibrarycodethatrepresentsthebehaviorof thecontrol.

Chapter1:ASimpleFrameworkforaGameApplication11

ImplementingGameplayElementsUsingGamebryo During GameObject initialization, a number of schemes were loaded, and these schemes, togetherwiththeimagesetandlooknfeelobjectsthattheyimplicitlyloaded,arealwaysavailable for any states layout to use. If you are using custom art, then your art team is responsible for creating all of the necessary CEGUI assets. The images used by the decorative background and controlsalikecancontainalphachannels,enablingyourartiststopaintinterestinguserinterfaces witharbitraryshapesandborders.YourartistsshouldtakenotethatimagesreferencedbyCEGUI imagesetsmusthavedimensionsthatarepowersoftwo. TheconstructorforGameUIStatedoesverylittle.Itsimplycachesthepassedinimagesetand layout names into instance member variables. The creation of the CEGUI user interface is implemented within the GameUIState::OnEnter and GameUIState::OnExit member functions. Whenever a user interface state is entered, and the state manager calls the states OnEnter member function, the base class will first initialize the state (e.g., load the decorative imageset and create the layout from file), if it is not initialized, and then it will append the statespecific user interface layout as a child of the transparent background window that was created by the GameObject::InitializeGUIFramework member function. There is a convenient artifact that arises from appending the statespecific user interface layout. The statespecific layout becomesthelastiteminalistoflayoutstoberendered,andsoitisrenderedontopofeverything else. This is desirable, since wed expect all suspended states to be obscured by the new state. Imagine that the MainGamePlay state contains user interface elements, and that the player pressesagamepadbuttontocausethegametopause.Thepausestatewilladditsownlayoutthat willberenderedontopoftheMainGamePlaylayoutjustasyouwouldexpectinapopupdialog box. Wheneverauserinterfacestateisexited,andthestatemanagercallsastatesOnExitmember function, the base class will first remove the statespecific layout from the CEGUI background window(thusrevealinganyotheractivestatelayoutsthatwerehiddenbytheoneexiting),and thenwilldestroythestatespecificlayoutandremoveitsimageset.Thecleanupcodeisawayof reducingmemoryusageforstatesthatarenotactive.Caremustbetakenforstatesthatsharea decorativeimageset.Thesamplecodedoesnothandlethiscase. ImplementationoftheStartupSplashState This is the simplest of all the game states. Its layout displays a decorative background image calledStartupSplashBackground.Therearenocontrolsatall. Thebehaviorthatthisstateimplementsissimplytowaitforawhile,andthenforceatransition to StartNewGame. The behavior is entirely implemented within the StartupSplashBackground::Updatememberfunction,usingthecodeshownbelow:
if (fCurrentTime >= 1.0) { StateManager &rStateManager = GameObject::GetStateManager();

Chapter1:ASimpleFrameworkforaGameApplication12

ImplementingGameplayElementsUsingGamebryo
rStateManager.ChangeState("StartNewGame"); }

Nothingcouldbesimpler.Here,fCurrentTimeisthecurrentgamesimulationtime,which is passedintothemethodasaparameter.Afterthesimulationtimereachesonesecond,thestate uses the state manager to explicitly change the state to StartNewGame. The actual state change willoccurduringthenextframe,whenthestatemanagerprocessesitsinternaleventqueue.(Ina realgame,ofcourse,thesplashstatewouldtypicallydomorethansimplywaitforsomeperiodof timebeforeenteringthenextstate.) ImplementationoftheStartNewGameState ThisstateisabitmorecomplexthanStartupSplash.Thetwolistboxesandthebuttonarebuilt intotheCEGUIlayoutnamedStartNewGame.layout.Thedecorativebackgroundimagethatis usedbythelayoutiscalledStartNewGameBackground.imageset.Thedecorativebackgroundin thiscasecontainstwotransparentrectangularareaswherethe3Dviewsoftheselectedcharacter andlevelaretobedisplayed.Thecontrolitemsinthelayoutarecreatedandpreparedfordisplay asdescribedabove,bythebaseclass,GameUIState. WiththisstatewecanlookintotheGamebryoframerenderingsysteminalittlemoredetail.The NiApplicationboilerplateconfigurationcodecreatesanumberofrendersteps(inGamebryo terms), including a shadow render step, a main render step intended to render 3D gameplay views,andascreenspacerenderstep,intendedtorender2Doverlaysanduserinterfacecontrols. In this state, we will use the main and screen space render steps. The main render step will displaytworenderclicks.Thefirstrenderclickthatwewillcreaterenderstoasmallportionof thegamewindow,aviewportthathasbeenallocatedspecificallytoshowananimatedviewofthe currentlyselected3Dcharacter.Thischaracterviewisthefirstrenderclickthatwillberendered tothescreen,asshownbelow.Thenumberintheupperleftcorneroftherenderclickindicates the order in which that render click is drawn to the screen. The large grey rectangle represents thefullgameapplicationwindow.

Chapter1:ASimpleFrameworkforaGameApplication13

ImplementingGameplayElementsUsingGamebryo

1 3DCharacter ViewRender Click

MainGameObject RenderStep

The second render click in the main render step will render a view of a second 3D scene that shows the selected level using a different camera. Again, this render click renders into a small viewportwithinthegamewindowthathasbeenallocatedforthe3Dlevelview.

1 3DCharacter ViewRender Click

2 3DLevel ViewRender Click

MainGameObject RenderStep

Chapter1:ASimpleFrameworkforaGameApplication14

ImplementingGameplayElementsUsingGamebryo There are no more render clicks in the main render step. The next render step created by NiApplication is the screen space render step. In the GameObject initialization code, we attachedtheCEGUIuserinterfacerenderclicktothisstep,andsotheuserinterfacerenderclick is drawn on top of the two 3D view render clicks. The user interface render click draws to the entire game window, and so if its scene contains opaque objects that cover the viewport areas drawn by the 3D views, the user interface will occlude the 3D views. The following image illustratesthedisplayoftheuserinterfacerenderclickontopofthepriorrenderclicks.Here,the renderclickrectanglehasbeendrawnslightly smallerthantheoutlineshownforthefullgame window,simplytotryandillustratethatitisbeingdrawnontop.

3 1

CEGUIRenderClick 2 3DLevel ViewRender Click

3DCharacter ViewRender Click

ScreenSpaceRenderStep

The view above does not show any of the contents of the user interface render click. It merely illustratestheviewportandthefactthatitisdrawnontopofthepriorrenderclicksalayering effect. Below, the figure illustrates the elements of the StartNewGame states user interface: the decorativebackgroundimage,thelistboxforcharacterselection,thelistboxforlevelselection, andtheBeginGamebutton.Theseareillustratedasthoughtheyweredrawnatthesametime, but in fact they too are drawn in sequence, as defined by the CEGUI layout. See the CEGUI documentationformoreinformationonlayeringofCEGUIelements.

Chapter1:ASimpleFrameworkforaGameApplication15

ImplementingGameplayElementsUsingGamebryo

3 1

CEGUIRenderClick 2 3DLevel ViewRender Click

3DCharacter ViewRender Click

Character ListBox

Level ListBox BeginGame

ScreenSpaceRenderStep

Notethat,inthefigureabove,the3Dviewsshowthroughtheuserinterface.Thisillustratesthe fact that the background imageset used by StartNewGame has transparent regions in the alpha channelofitstexture,designedtorevealwhateverwasrenderedunderneath. Now, lets look at how this configuration of the frame rendering system is accomplished. The StartNewGame class overrides the GameUIState::OnEnter method in order to do state specificinitialization: Register a callback function, StartNewGame::ButtonClick, that will be called whenevertheuserclickstheBeginGamebutton. Generate a 3D view of the selected character, and populate the list box containing the namesoftheselectablecharacters.Thisisaccomplishedinaseparatememberfunction, StartNewGame::ConfigureSelectedCharacterView. Generate a 3D view of the selected level, and populate the level name list box. This is accomplished in a separate method, StartNewGame::ConfigureSelectedLevelView.

Creationofthe3Dviewoftheselectedcharacterandlevelaresomewhatinvolved.Itisnecessary toloadGamebryoassetsandcreatenewrenderviewstodisplaythe3Dobjects.Themethodsto createthecharacterviewandlevelviewareverysimilar,sosimilarthatthisdiscussionwillonly describethecharacterviewsetup. The StartNewGame::ConfigureSelectedCharacterView member function is the entry pointusedtocreatethecharacterview.Itaccomplishesseveralthings:

Chapter1:ASimpleFrameworkforaGameApplication16

ImplementingGameplayElementsUsingGamebryo UsestheGamebryoobjectstreamingsystemtoloadaGamebryoNiSceneasset(a*.gsa file created by the Gamebryo SceneDesigner application) using the NiEntity library. Thischaracterselectionscenecontainsalloftheselectablecharactersandacamerathat looksateachofthem Configuresacameraviewporttofitintherectangularareaassociatedwiththecharacter view Calls another member function, StartNewGame::ConfigureAvailableCharacters,toparsethecharacterNiSceneanddiscoverthenamesofalltheplayable characters Calls another method, StartNewGame::SelectCharacterImpl, to activate the camera for the initially selected character and to activate that camera within the appropriateviewport Updatestheinitialcameraviewportandaspectratiotomatchtherectangularareawhere charactersarebeingdisplayed Creates a new render view and render click to use the camera to render the character sceneintheappropriateplaceonscreen

Within this method, for chapter 1, the Gamebryo scene file name is hardcoded. Similarly, the rectangular area allocated for characters is hardcoded based on pixel coordinates that are built intothedecorativeimagesetforthisstate.Inlaterchapters,wewilllookatwaystousemoredata drivenconfigurations,usingruntimescripts. The StartNewGame::ConfigureAvailableCharacters function iterates through the entitiescontainedinthecharacterselectionscene,searchingforentitiesthathaveamasterentity thathavethetext[Characters]inthename.Itistheresponsibilityofyourcodeandartteamsto agreeonnamingconventionsandstandardsforassets,toensurethatgamelogicisabletoproperly parse assets. In this case, for each discovered selectable character entity, the method creates a newentryinthecharacterselectionlistbox,whichiscalled"StartNewGame/CharacterSel"inthe CEGUIlayoutfileforthisstate.Thecodepopulatesalistboxwiththenamesofthecharactersit finds, and initially selects the first character in the list. The method does one more thing. It registers a callback function, StartNewGame::SelectCharacter, which will be called whenevertheplayerselectsadifferentcharacter. TheStartNewGame::SelectCharacterImplfunctionfindsthecurrentlyselectedcharacter intheStartNewGame/CharacterSellistandlocatesthecameraassociatedwiththatcharacterin thescene.Thecodeassumesthatforeverycharacter,thereisacamerathathasthesamenameas the character but with the text Cam appended. So, for the Dragonman character, the code expectstofindacameranamedDragonmanCamthathasbeenarrangedwithinSceneDesigner to look at the character named Dragonman. Here again is an example of code that requires coordination between code and art teams. If a camera is found, the code adjusts the cameras viewportandaspectratiotomatchtheuserinterfaceareawheretheselectedcharacterisbeing displayed.

Chapter1:ASimpleFrameworkforaGameApplication17

ImplementingGameplayElementsUsingGamebryo TheStartNewGame::SelectCharactercallbackfunction,whichiscalledwhenevertheuser clicks a different character name in the list box, simply calls through to StartNewGame:: SelectCharacterImpltochangethecameraview. Thatcompletesconfigurationoftheselectedcharacterview.Configurationfortheselectedlevel issimilar;however,forthischapteronlyonelevelisavailableandthecodeisabitsimplerthan for charactersno iteration though available levels and no callback to handle a change in level selection. The remaining code for this class concerns updating the state over time and responding to the buttonclickthatbeginsthegame. TheStartNewGame::Updatefunctioniscalledbythestatemanageronceperframe.Thecode israthersimple:itcallstheUpdatefunctionontheNiSceneobjectsassociatedwithcharacters andlevels.Forthecharacters,thesecallsplaywhateveranimationcyclewasselectedwithinScene Designer.If thecamera associatedwiththecharacterwasanimated,thiscall would also update thecameraalongitspath.Similarly,anyanimationsthatexistforthelevelselectscenearealso played here. Your art team is free to develop interesting effects that they would like to be be displayedandanimatedwithinthecharacterandlevelselectscreen. TheStartNewGame::ButtonClickfunctionrespondswhentheuserclickstheBeginGame button.TheonlyresponseistochangethestatetoMainGamePlay.Thestatemanagerwillcallthe statesOnExitmethodduringthenextupdate,justpriortocallingMainGamePlay::OnEnter. The StartNewGame::OnExit function performs cleanup tasks, first removing the custom render clicks associated with the character and level 3D views, then calling the base class to performstandarduserinterfacecleanup.Thismethoddoesoneotherimportanttask:itretrieves apointertotheMainGamePlaystatefromthestatemanager,andcallsamethodinthatstateto tellthestatewhichcharacterandwhichleveltouseforthegame. ImplementingtheMainGamePlayState Forthischapter,theMainGamePlaystateissimple,atleastwhencomparedwithStartNewGame. Ultimately, it does little more than display the selected level, create an instance of the selected characterataprescribedspawnpointthatislocatedinthelevelscenegraph,thenviewthescene graph using a prescribed camera. As with StartNewGame, the code and art/design teams must agreeonexactlyhowthescenewillbepresentedduringgameplay. Prior to the game entering the MainGamePlay state, the StartNewGame state will call MainGamePlay::BeginLevel on the inactive instance of MainGamePlay, which caches the NiSceneobjectfortheselectedlevelandapointertoacopyoftheselectedcharacterentity.The character copy is created using MainGamePlay::SpawnCharacter, a function that instantiatesacloneofagivencharacter,andplacesitatanamedspawnpoint.(Inlaterchapters, theStartNewGamestatemightloadasimplifiedversionofthelevelfordisplaypriortothegame start, and in this case the MainGamePlay state would need to be delayed while the full level is

Chapter1:ASimpleFrameworkforaGameApplication18

ImplementingGameplayElementsUsingGamebryo loaded. An intermediate state, such as the Loading state described near the beginning of this chapter,wouldbeappropriatehere.) The MainGamePlay::OnEnter function appends a render click to the GameObjects main renderstep.Sincetherearenootherscenesbeingrendered,andbecausethereis(currently)no userinterface,thisrenderclickistheonlyonethatwillrenderanythingwhileMainGamePlayis at the top of the state stack. The new render click fills the application window with a single viewportthatdisplaystheselectedlevel.Thisisthegameplaywindow.Thefunctionalsosearches forandactivatesacamerainthescenenamedDefaultCamera. When the player begins the game, the StartNewGame state calls the MainGamePlay::SpawnCharacter function, which clones the character that the player selected.Bycloningthecharacter,whichwasloadedintomemoryforthecharacterselection,the game can avoid accessing the model from disk. If you study the function carefully, you will see that it clones the master entity of the players selected character, rather than the selected character itself. The reason why it is inappropriate to clone the selected character is that the selected character may have a custom translation and orientation that pose and animate the character as an artist intended it to be viewed during character selection, and this may be different from the ingame pose. The ingame character needs to be based on a canonical characterstatethatplacesthecharacteratthelevelorigin,facingthenegativeydirection,with feetontheground.Gamelogiccan,therefore,veryeasilyplacetheclonewhereitneedstobein the game level. The master entity has the canonical state. Once the master entity has been cloned, the method changes the value of the Translation property to place the clone at the spawnpointlocation,setstheappropriateactiveanimationsequence,andaddstheclonetothe listofentitiesaffectedbythelightinthescene.Finally,thecloneisaddedtothescene. The remaining code for this state deals with updating the state over time, and transitioning to otherstates. TheMainGamePlaystateclassimplementationoftheIBaseState::Updatevirtualmember function is a bit more complex than the implementation in states. As with the StartNewGame state, it calls the Update function on the appropriate NiScene object, which in turn updates renderingeffectsandanimations.Beyondthis,ifthestateisnotsuspended,itreadsthekeyboard andhandlestwokeyboardcommands: The Q key will cause the state to issue a standard Windows message to shut down the application, to quite the game immediately. More sophisticated behavior would be to transitiontoanotherstateallowingtheusertheoptionofsavingthegame,resumingthe game,orexitingtothemainmenu. ThePkeywillcausethegametosuspendthisstate,pushingtheGamePausedstateonto thestatestack.

Themethodalsocontainsplaceholdercommentsforavarietyofotherthingsthatcanhappenon everyframeduringgameplay,including: Chapter1:ASimpleFrameworkforaGameApplication19

ImplementingGameplayElementsUsingGamebryo artificialintelligencetaskssuchaspathfinding physicstasksthatmightnotbehandledautomatically(withinanyanimationupdates) scriptingtasks andotherstatespecifictasks

Wewillfillintheseotherperframetasksaswebuildthisgameupoverthechapterstocome. Aswiththeotherstates,theMainGamePlay::OnExitfunctionperformscleanuptasks.Inthis case,theexplicitcleanupcodeissimplytoremovetherenderclickthatwasaddedtorenderthe level. The method also dereferences the levels NiScene object, which might cause it to be destructed (if it is not referenced elsewhere). The scene owns the cloned character, and so the nonsmart pointer to the character is set to zero to avoid potential use of an object that might havebeendestructed. ImplementingtheGamePausedState GamePausedisthelaststatethatweareimplementingforchapter1. It is a user interface state, and so its class derives from GameUIState and utilizes a CEGUI *.layoutfiletoobtainitscontrols.Thisstatehasasinglecontrol,abuttonthattheusercanpress to resume the game. Its GamePaused::Initialize function registers a callback function, GamePaused::ButtonClick,thattheCEGUIsystemwillcallwheneverthebuttonisclicked. Thatcallbackfunctionsimplypopsthestateoffofthestatemanagersstack,causingthestateto exit,andcausingthepriorstatetoresume.Inthiscase,theonlystatethatcanenterGamePaused is MainGamePlay. So, when GamePaused responds to the button click, at the next GameObject::UpdateFrame call performed by NiApplication, the game will resume the game.

FinalRemarksandLessonstobeLearnedfromtheChapter1DemoCode
Nowthatthe4gamestatesarefullyimplemented,thegameisreadytorun!Althoughsomeofthe configurationcodecanbeabittedious,itislargelyboilerplateandeasilywrittenonceinabase class,thenreusedforever.Forexample,ourGameUIStatebaseclassmanagesalluserinterface layouts for display, and manages cleanup when a state goes inactive. The derived classes only need to register statespecific callbacks for control interactions, and then respond to those controls.Thecodeforthischapterisrathersimple,andpresentsonlyoneapproachtomanaging highlevelstatesinagame.Hopefully,asyouobservedthecodedevelopedhere,andlearnedhow the pieces work together, you began to better understand how to think about software architectureandstructureasyoudesignyourowngame. Werecommendthatyounowtakethetimetorunthecodeindebugmode,withbreakpointsset at the top of the OnEnter, OnExit, OnOverride, and OnResume functions of each state, as well as on lines of code that call StateManager::ChangeState, StateManager::PushState, and StateManager::PopState. This will enable you to experience the state diagram transitions directly, and may deepen your appreciation for the

Chapter1:ASimpleFrameworkforaGameApplication20

ImplementingGameplayElementsUsingGamebryo simplicityofthesystem.Also,youmaybeinterestedinbrowsingtheHTMLdocumentationfor thechapterscode.OpentheHTMLfile,index.html,locatedintheChapter1/docs/htmlfolder,to viewthedocumentation. Oneimportantlessonyoushouldlearnfromthischapteristhatitisvitallyimportantfortheart team(includingdesigners)andcodeandscriptdeveloperstocometoanagreementonnaming conventions and organizational conventions for all art assets. Establishing these conventions must be one of the first things you do during preproduction, regardless of whether you are workingonanAAAtitleorastudentorhobbygameproject!Ifyoufailtoestablishconventions, andusethem,youwilleventuallywasteasignificantamountoftimecorrelatingcode,script,and assets.Sotakeheed!

FileDirectoryStructurefortheTutorialCode
TheclassesdescribedaboveloadvariousCEGUIandGamebryoassets,fromtimetotime.Allof theassetsarelocatedinahierarchyofsubfoldersundertheassetsfolder,whichmustbedirectly underneath the application working folder. Following is a summary of the hierarchy of asset foldersusedbythischapterscode.
CEGUI_Integration Chapter1 Objects that integrate CEGUI into Gamebryo Root folder for the Chapter 1 demo code The configuration file for doxygen, used to generate source code documentation, is contained here. The Visual Studio project builds the game executable files into this folder, and this is the working folder when running the game in debug mode, since the assets are directly underneath Source files for main game app and base classes The project solution file and project file for Visual Studio 2005 .NET are here Source files for concrete game state classes Source files for various utility functions Output folder for doxygen, this folder contains formatted documentation for the source code Root folder for game runtime assets Root folder for game character assets Folder containing the Dragonman character and animations Folder containing the Phoenix character and animations Folder containing general game models Folder containing game scene files, created with the Gamebryo SceneDesigner. These scenes reference items in the characters and models folders Root folder for all CEGUI user interface assets CEGUI config schema files

SourceWin32

States Utilities Docs Assets Characters Dragonman Phoenix Models Scenes

UI Configs

Chapter1:ASimpleFrameworkforaGameApplication21

ImplementingGameplayElementsUsingGamebryo
Fonts Imagesets Layouts CEGUI font files CEGUI image sets CEGUI layouts. Layouts define the look and layout of user interface controls for those states that display user interfaces. The layouts can be created using the CEGUI tool, CELayoutEditor. CEGUI look-n-feel files CEGUI scheme files

Looknfeel Schemes

OtherResourcesandReferences
Emergent Game Technologies, Introduction to the Frame Rendering System in the Gamebryo Documentation EmergentGameTechnologies,IntroductiontoNiApplicationintheGamebryoDocumentation EmergentGameTechnologies,NiEntityIntroductionintheGamebryoDocumentation CrazyEddiesGUISystemWebsite:http://www.cegui.org.uk/wiki/index.php/Main_Page Boer,James,LargeScaleStackBasedStateMachines,GameProgrammingGems5,CharlesRiver Media,2005.

Exercises
Howwouldyoumodifythehighlevelgamestatemachineillustratedabovetoincludean onlinegamelobby? 2. How would you extend the example application to represent the full collection of states illustratedabove? 3. Howwouldyouaddaningameminigametothestatemachine? 1.

Chapter1:ASimpleFrameworkforaGameApplication22

Das könnte Ihnen auch gefallen