Wiley.Games.on.Symbian.OS.A.Handbook.for.Mobile.Development.Apr.2008 (779888), страница 63
Текст из файла (страница 63)
You can even leverage thisso that when the user next starts the MIDlet, it offers them the option toresume their last game which is a nice feature to have.It’s also a good idea to give the player a few seconds to re-orientatethemselves in the game world before resuming the game loop. Not onlydoes this give them time to position their fingers on the keypad andmentally prepare for the event, but this time can be used behind thescenes to re-acquire resources and re-initialize the game state.Most games need to track time – whether this is for the game AI,the simulation based on your physics model (say fading out sprites ina particle system), or for more mundane tasks like ensuring a steadyframe rate.
Sometimes it makes sense to run your AI at a different rate toyour simulation, depending on what you’re doing. In any case, elapsedtime is how we make our game worlds dynamic, independent of userinput.This is where the pause/resume process can trip you up if you’renot careful. Mostly developers use System.currentTimeMillis()to monitor time in a game loop and ensure a steady tick rate.
This is fine,but your game needs to work in virtual time (i.e., time should proceedsmoothly as far as the game is concerned). It can get a bit daunting unlessyou’re aware of this beforehand but, again, planning for the game pausemakes this trivial. Simply reset your clocks by the amount of real time thathas elapsed during the pause, and as far as the game world is concerned,it never happened.9.7 Living it UpWhen you start designing a mobile game, the very first thing you shouldbe thinking about is how to leverage the MIDlet life cycle correctly.
Mosttext book examples get this wrong and that’s not all that surprising asthere are some common misconceptions in the industry, mainly to dowith delegation of responsibilities between the AMS and a MIDlet.278MIDP GAMES ON SYMBIAN OSThe AMS governs the life cycle of a MIDlet and is therefore responsiblefor affecting transitions on a MIDlet’s state as well as tracking it. It movesa MIDlet between the active, paused and destroyed states by invokingthe startApp(), pauseApp() and destroyApp() methods of theMIDlet base class respectively.
These methods are callback methods forthe AMS and it is the responsibility of the developer to provide non-trivialimplementations of them.MIDlets can initiate state changes to the paused or destroyed statesthemselves and inform the AMS of this by calling either the notifyPaused() or notifyDestroyed() methods. A paused MIDlet canalso let the AMS know that it would like to re-enter the active state bycalling the resumeRequest() method.The thing to remember is that your code should never call the AMScall back methods.
It’s very common to see code that does this like thatshown below:private void shutDown(){try{destroyApp(true);notifyDestroyed();}catch(MIDletStateChangeException e){// whatever}}While this will work, it assumes that the cleanup code in a MIDletinitiated shutdown is the same as that in an AMS initiated one. Further, thiscan lead to inconsistencies – what if the OS directs the AMS to destroy theMIDlet while the above method is executing? Then the destroyApp()method gets called twice as does any cleanup code it contains – whichis a sub-optimal state of affairs to say the least.
An analogous situationexists with AMS versus MIDlet initiated pausing.The general solution here is to recognize that the MIDlet needs totrack its own state internally to prevent duplicate code executions andseparate out start, stop, pause, and resume code from the six life cyclemethods. The other benefit of doing this is that it then becomes trivialto correctly handle automatic pause/resume functionality for the gamewhen the MIDlet is moved in or out of the background for any reason.However, a word of caution is in order here – the state transition codeneeds to be synchronized, since it can be called by two different threads(the AMS and the MIDlet).
However it’s generally not good practice tomake the AMS wait on an object lock as it may need to act immediatelyin response to a directive issued by the operating system (such as a lowmemory condition for example). Symbian has put a lot of thought intothis problem though, so it won’t cause any issues here.LIVING IT UP279Furthermore, this is done using the same centralized methods used inhandling the life cycle state transitions, as the following code snippetsillustrate (adapted from Programming the MIDP Lifecycle on SymbianOS, a 2005 paper by Martin de Jode which can be found on developer.symbian.com).Managing the life cycle state transitions:public class GameMIDlet extends MIDlet {...private final static int PAUSED = 0;private final static int ACTIVE = 1;private final static int DESTROYED = 2;private int iState = PAUSED;...public void startApp(){start();}public void startMIDlet(){resumeRequest(); // request startApp()}private synchronized void start(){if(iState == PAUSED){controller.startApplication();iState = ACTIVE;}}public void pauseMIDlet(){pause();notifyPaused();}public void pauseApp() {pause();}private synchronized void pause(){if(iState == ACTIVE){controller.pauseApplication();iState = PAUSED;}}public void exitMIDlet(){cleanup();notifyDestroyed();}public void destroyApp(boolean unconditional) {cleanup();}280MIDP GAMES ON SYMBIAN OSprivate synchronized void cleanup(){if(iState != DESTROYED){controller.stopApplication();iState = DESTROYED;}}}Handling background and foreground events:public class GameScreen extends GameCanvasimplements Runnable{...public void showNotify(){if(controller.gamePaused()){reactivateMIDlet();}}public void hideNotify(){if(controller.gameRunning()){pauseMIDlet();}}...}These concepts are summarized in Table 9.2.
For a more detailedanalysis, please refer to the de Jode paper, Programming the MIDPLifecycle on Symbian OS, mentioned earlier.9.8 Game ArchitectureSoftware architecture is a contentious topic and generally a highly subjective one – after all if you put three computer scientists in a room you’llgenerally get six opinions on how to get out (and one of them will haveat least three ways in mind). When you add in the large disparity of skillsets and handsets available in the mobile game industry, you find thatwell, not everyone agrees.However, given that Java ME broke new ground in MIDP 1.0 days, it’sfairly common to adopt design patterns commonly used in mainstreamJava development (standard and desktop) such as the Model-View (MV)and Model-View-Controller (MVC) patterns.Since most Java ME game development occurred before MIDP 2.0was released, companies created their own custom game packages underMIDP 1.0 to promote re-use of their code base.
In general, MIDP 1.0devices had quite severe constraints on core application parameters suchGAME ARCHITECTURE281Table 9.2 The six life cycle methodsMETHODCalled ByConditionsstartAppAMSMIDlet first startedstartAppAMSA previouslybackgroundedMIDlet in thePAUSED state ismoved into theforegroundstartAppAMSWhen the AMSfinds that it canservice aresumeRequestcall from theMIDletpauseAppAMSLow batteryconditions on abackgroundedMIDlet that is notin the PAUSEDstatedestroyAppAMSUser terminatesthe MIDletmanuallyNotesCan occurmultiple times soneed to flag if anyone-offinitializationoccursAutomaticallyinvoked whenbackgrounded,depending on UIplatform (S60,UIQ)Low memoryconditions orpower downdestroyAppnotifyPausedMIDletMIDlet voluntarilypausing itselfTells AMS toupdate it’s copy ofthe MIDlet statenotifyDestroyedMIDletMIDlet voluntarilydestroying itselfTells AMS toupdate it’s copy ofthe MIDlet stateresumeRequestMIDletInforms the AMSthat the MIDletwould like tore-enter theACTIVE state.The AMS may notbe able toimmediatelyservice thisrequest due tocurrent conditions282MIDP GAMES ON SYMBIAN OSas JAR size, available heap memory, and small amounts of RAM.
Part ofthis process included having to determine what architectures worked andwhat didn’t under these conditions, and in some respects, these modestbeginnings still guide (and to some extent even restrict) developmentprocesses even today.However, it’s now 2008 and given the short average lifetime of a phonehandset, the point to remember here is that these restrictions generallydon’t exist in the new generation of smartphones – and in particularSymbian smartphones.
So, many of the more cautious approaches, whilevalid, need to be re-assessed for relevance in the current market.In many cases, we can now accept a larger JAR size for the sake ofbuilding a more structured class hierarchy than was previously viable.Rather than avoiding interfaces, directly exposing member variables, andoptimizing string references to minimize class sizes, we can shift thedesign focus away from (necessary) minimalism across to consistency,re-usability and maintenance.9.9 Case Study: Third DegreeIn this section I’m going to run through the main points of the samplegame, Third Degree, that is supplied with this chapter. The game’s splashscreen and a screenshot from the gameplay are shown in Figures 9.1 and9.2 respectively. You can download the code from this book’s main pageon the Symbian Developer Network at developer.symbian.com.