Tải bản đầy đủ - 0 (trang)
Chapter 2.  An Animation Framework

Chapter 2.  An Animation Framework

Tải bản đầy đủ - 0trang

Startingandterminatingananimation

Doublebuffering

Userinteraction

Activerendering

Animationcontrolbasedonauser'srequestedFPS

Themanagementofinaccuraciesinthetimerandsleep

operations

CombiningFPSandgamestateupdatespersecond(UPS)

Gamepausingandresumption

ThoughmostofthischapterisabouttheGamePanelanimation

loop,Iwillconsidertwootherpopularapproachesto

implementinganimation:usingtheSwingtimerandtheutility

timerinjava.util.timer.



Theexampleprogramsusedinthischaptercanbefoundinthe

Timings/directory.Allthecodedirectoriesmentionedinthechapters

canbedownloadedfromthebook'swebsiteat

http://fivedots.coe.psu.ac.th/tildad/jg.



InChapters3and4,Idevelopapplet,windowed,andfullscreenapplicationsforaWormChasegameusingthefinalversion

ofGamePanel(withminorvariations).Asasideeffectofthegame

play,statisticsaregathered,includingtheaverageFPSand

UPS,toshowthatGamePanelsupportsconsistentlyhigh-speed



animation.



AnimationasaThreadedCanvas

AJPanelisemployedasadrawingsurface,andananimation

loopisembeddedinsideathreadlocaltothepanel.Theloop

consistsofthreestages:gameupdate,rendering,andashort

sleep.

ThecodeinExample2-1showsthemainelementsofGamePanel,

includingtherun()methodcontainingtheanimationloop.As

thechapterprogresses,additionalmethodsandglobalvariables

willbeaddedtoGamePanel,andsomeoftheexistingmethods

(especiallyrun())willbechangedandextended.



Example2-1.TheGamePanelclass(initial

version)

publicclassGamePanelextendsJPanelimplementsRunnable

{

privatestaticfinalintPWIDTH=500;//sizeofpanel

privatestaticfinalintPHEIGHT=400;



privateThreadanimator;//fortheanimation

privatevolatilebooleanrunning=false;//stopstheani



privatevolatilebooleangameOver=false;//forgameterm

//morevariables,explainedlater

//:

publicGamePanel()

{setBackground(Color.white);//whitebackground

setPreferredSize(newDimension(PWIDTH,PHEIGHT));

//creategamecomponents



//...

}//endofGamePanel()



publicvoidaddNotify()

/*WaitfortheJPaneltobeaddedtothe

JFrame/JAppletbeforestarting.*/

{

super.addNotify();//createsthepeer

startGame();//startthethread

}



privatevoidstartGame()

//initialiseandstartthethread

{

if(animator==null||!running){

animator=newThread(this);

animator.start();

}

}//endofstartGame()

publicvoidstopGame()

//calledbytheusertostopexecution

{running=false;}



publicvoidrun()

/*Repeatedlyupdate,render,sleep*/

{

running=true;

while(running){

gameUpdate();//gamestateisupdated

gameRender();//rendertoabuffer

repaint();//paintwiththebuffer

try{



Thread.sleep(20);//sleepabit

}

catch(InterruptedExceptionex){}

}

System.exit(0);//soenclosingJFrame/JAppletexits

}//endofrun()



privatevoidgameUpdate()

{if(!gameOver)//updategamestate...

}

//moremethods,explainedlater...

}//endofGamePanelclass



GamePanelactsasafixedsizewhitecanvas,whichwillbe



embeddedinsideaJFrameinapplicationsandinsideJAppletin

applets.Theembeddingwillonlyrequireminorchanges,except

whenGamePanelisusedinapplicationsusingfull-screenexclusive

mode(FSEM).Eveninthatcase,theanimationloopwillstay

essentiallythesame.

addNotify()iscalledautomaticallyasGamePanelisbeingaddedto



itsenclosingGUIcomponent(e.g.,aJFrameorJApplet),soitisa

goodplacetoinitiatetheanimationthread(animator).stopGame()

willbecalledfromtheenclosingJFrame/JAppletwhentheuser

wantstheprogramtoterminate;itsetsaglobalBoolean,

running,tofalse.



JustStopIt

SomeauthorssuggestusingTHRead'sstop()method,atechniquedeprecatedby

Sun.stop()causesathreadtoterminateimmediately,perhapswhileitis

changingdatastructuresormanipulatingexternalresources,leavingtheminan

inconsistentstate.TherunningBooleanisabettersolutionbecauseitallowsthe

programmertodecidehowtheanimationloopshouldfinish.Thedrawbackis

thatthecodemustincludeteststodetecttheterminationflag.



SynchronizationConcerns

TheexecutingGamePanelobjecthastwomainthreads:the

animatorthreadforgameupdatesandrendering,andaGUI

eventprocessingthread,whichrespondstosuchthingsaskey

pressesandmousemovements.Whentheuserpressesakey

tostopthegame,thiseventdispatchthreadwillexecute

stopGame().Itwillsetrunningtofalseatthesametimethe

animationthreadisexecuting.

Onceaprogramcontainstwoormorethreadsutilizingashared

variable,datastructure,orresource,thenthorny

synchronizationproblemsmayappear.Forexample,whatwill

happenifashareditemischangedbyonethreadatthesame

momentthattheotheronereadsit?TheJavaMemoryModel

(JMM)statesthataccessesandupdatestoallvariables,other

thanlongsordoubles,areatomic,i.e.,theJMMsupports32-bit

atomicity.Forexample,anassignmenttoaBooleancannotbe

interleavedwitharead.Thismeansthatthechangingofthe

runningflagbystopGame()cannotoccuratthesamemomentthat

theanimationthreadisreadingit.

TheatomicityofreadandwritestoBooleansisauseful



property.However,thepossibilityofsynchronizationproblems

formorecomplexdatastructurescannotbeignored,asyou'll

seeinChapter3.



ApplicationandGameTermination

AcommonpitfallistouseaBoolean,suchasrunning,todenote

applicationterminationandgametermination.Theendofa

gameoccurswhentheplayerwins(orloses),butthisis

typicallynotthesameasstoppingtheapplication.Forinstance,

theendofthegamemaybefollowedbytheuserentering

detailsintoahighscorestableorbytheuserbeinggiventhe

optiontoplayagain.Consequently,Irepresentgameendingby

aseparateBoolean,gameOver.ItcanbeseeningameUpdate(),

controllingthegamestatechange.



WhyUseVolatile?

TheJMMletseachthreadhaveitsownlocalmemory(e.g.,

registers)whereitcanstorecopiesofvariables,thereby

improvingperformancesincethevariablescanbemanipulated

morequickly.Thedrawbackisthataccessestothesevariables

byotherthreadsseetheoriginalversionsinmainmemoryand

notthelocalcopies.

TherunningandgameOvervariablesarecandidatesforcopyingto

localmemoryintheGamePaneltHRead.Thiswillcauseproblems

sinceotherthreadsusethesevariables.runningissettofalseby

stopGame()calledfromtheGUIthread(gameOverissettotrueby

theGUIthreadaswell,asI'llexplainlater).Sincerunningand

gameOveraremanipulatedbytheGUIthreadandnotthe

animationthread,theoriginalversionsinmainmemoryare

alteredandthelocalcopiesusedbytheanimationthreadare

unaffected.Oneconsequenceisthattheanimationthreadwill



neverstopsinceitslocalversionofrunningwillneverbecome

false!

Thisproblemisavoidedbyaffixingthevolatilekeywordto

runningandgameOver.volatileprohibitsavariablefrombeing

copiedtolocalmemory;thevariablestaysinmainmemory.

Thus,changestothatvariablebyotherthreadswillbeseenby

theanimationthread.



WhySleep?

Theanimationloopincludesanarbitrary20msofsleeptime:

while(running){

gameUpdate();//gamestateisupdated

gameRender();//rendertoabuffer

repaint();//paintwiththebuffer

try{

Thread.sleep(20);//sleepabit}

catch(InterruptedExceptionex){}

}



Whyisthisnecessary?Therearethreemainreasons.

Thefirstisthatsleep()causestheanimationthreadtostop

executing,whichfreesuptheCPUforothertasks,suchas

garbagecollectionbytheJVM.Withoutaperiodofsleep,the

GamePanelthreadcouldhogalltheCPUtime.However,the20-ms

sleeptimeissomewhatexcessive,especiallywhentheloopis

executing50or100timespersecond.

Thesecondreasonforthesleep()callistogivethepreceding

repaint()timetobeprocessed.Thecalltorepaint()placesa



repaintrequestintheJVM'seventqueueandthenreturns.

Exactlyhowlongtherequestwillbeheldinthequeuebefore

triggeringarepaintisbeyondmycontrol;thesleep()call

makesthethreadwaitbeforestartingthenext

update/renderingcycle,togivetheJVMtimetoact.Therepaint

requestwillbeprocessed,percolatingdownthroughthe

componentsoftheapplicationuntilGamePanel'spaintComponent()is

called.Anobviousquestioniswhether20msissufficienttime

fortherequesttobecarriedout.Perhapsit'soverlygenerous?

ItmayseemthatIshouldchooseasmallersleeptime,5ms

perhaps.However,anyfixedsleeptimemaybetoolongortoo

short,dependingonthecurrentgameactivityandthespeedof

theparticularmachine.

Finally,thesleep()callreducesthechanceofevent

coalescence:IftheJVMisoverloadedbyrepaintrequests,it

maychoosetocombinerequests.Thismeansthatsomeofthe

renderingrequestwillbeskipped,causingtheanimationto

"jump"asframesarelost.



DoubleBufferingDrawing

gameRender()drawsintoitsownGraphicsobject(dbg),which



representsanimagethesamesizeasthescreen(dbImage).

//globalvariablesforoff-screenrendering

privateGraphicsdbg;

privateImagedbImage=null;

privatevoidgameRender()

//drawthecurrentframetoanimagebuffer

{

if(dbImage==null){//createthebuffer

dbImage=createImage(PWIDTH,PHEIGHT);

if(dbImage==null){



System.out.println("dbImageisnull");

return;

}

else

dbg=dbImage.getGraphics();

}

//clearthebackground

dbg.setColor(Color.white);

dbg.fillRect(0,0,PWIDTH,PHEIGHT);

//drawgameelements

//...

if(gameOver)

gameOverMessage(dbg);

}//endofgameRender()



privatevoidgameOverMessage(Graphicsg)

//centerthegame-overmessage

{//codetocalculatexandy...

g.drawString(msg,x,y);

}//endofgameOverMessage()



Thistechniqueisknownasdoublebufferingsincethe(usually

complex)drawingoperationsrequiredforrenderingarenot

applieddirectlytothescreenbuttoasecondaryimage.

ThedbImageimageisplacedonscreenbypaintComponent()asa

resultoftherepaintrequestintherun()loop.Thiscallisonly

madeaftertherenderingstephasbeencompleted:

publicvoidpaintComponent(Graphicsg)

{



super.paintComponent(g);

if(dbImage!=null)

g.drawImage(dbImage,0,0,null);

}



Theprincipaladvantageofdoublebufferingistoreduceonscreenflicker.Ifextensivedrawingisdonedirectlytothe

screen,theprocessmaytakelongenoughtobecomenoticeable

bytheuser.ThecalltodrawImage()inpaintComponent()isfast

enoughthatthechangefromoneframetothenextisperceived

asinstantaneous.

AnotherreasonforkeepingpaintComponent()simpleisthatitmay

becalledbytheJVMindependentlyoftheanimationthread.For

example,thiswilloccurwhentheapplication(orapplet)window

hasbeenobscuredbyanotherwindowandthenbroughtback

tothefront.



TheplacingofgamebehaviorinsidepaintComponent()isacommon

mistake.Thisresultsintheanimationbeingdrivenforwardbyits

animationloopandbytheJVMrepaintingthewindow.



AddingUserInteraction

Infull-screenapplications,therewillbenoadditionalGUI

elements,suchastextfieldsorSwingbuttons.Eveninapplets

orwindowedapplications,theuserwillprobablywantto

interactdirectlywiththegamecanvasasmuchasispossible.

ThismeansthatGamePanelmustmonitorkeypressesandmouse

activity.



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Chapter 2.  An Animation Framework

Tải bản đầy đủ ngay(0 tr)

×