Wiley.Mobile.Python.Rapid.prototyping.of.applications.on.the.mobile.platform.Dec.2007 (779889), страница 20
Текст из файла (страница 20)
Note thatthe photo is typically taken in 3:4 picture ratio, which is not the same asthe canvas size, usually. The photo is too big to fit the canvas and we donot want to resize it to fill the canvas in full, since this would break theaspect ratio.
Instead, we define the target area to which the photo shouldbe copied. The target area has the aspect ratio of 3:4, which the photo is104SOUND, INTERACTIVE GRAPHICS AND CAMERAthen resized to fit. These actions are handled by two optional parametersto the blit() function, namely target and scale. you can use thephone’s Gallery application to see the newly taken photo or you cancopy it to your PC for viewing.You can change the exposure adjustment and the white balancesettings as well as using digital zoom and flash.
All these parameters aredescribed in detail in the PyS60 API reference documentation.5.5 Mobile Game: UFO ZapperThe concluding example of this chapter is a game. In contrast to theHangman server that was presented in Section 4.5, this is a classic singleplayer action game with stunning graphics – just see Figure 5.9. Your jobis to save the world from a squadron of invading UFOs with a movingpad that shoots laser beams (ahem).Figure 5.9UFO ZapperYou must shoot down as many UFOs as possible within a given timelimit. You get points for each hit: the smaller the UFO you hit the morepoints you get.
Try to beat our highest score of 2304 points!5.5.1 Structure of a Game ApplicationThe example presents several useful concepts that are often used in gameapplications:MOBILE GAME: UFO ZAPPER•an event loop to control the application•dynamic time•double buffering•module random.105Using an event loop to control an applicationAt the beginning of Chapter 4, we noted that we have to use thee32.Ao lock object to stop the execution and start waiting for theuser input.
However, in an action game something should be happeningall the time, even if the user does nothing. Thus, instead of a lock, thegame application is controlled by an event loop.An event loop is typically a simple while loop which takes careof advancing time step by step. Like Ao lock, the loop prevents theapplication from exiting instantly.Whereas an animation proceeds from start to end without any userinteraction, a game must react to user input.
This is handled in a similarway to any other application, using event callbacks, which may changevalues of global variables and thus change the game state. Overall, thestructure of an event loop is typically as follows:Initializewhile <endUpdateRedrawPauseevent callbackscondition>:game statescreenThe last command in the event loop instructs the execution to pausefor a while. We want the event loop to iterate as fast as possible, but wewant to control its speed.
We need to give the user some time to perceivewhat is happening in the game, as human perception is extremely slowcompared to the 200–300 MHz CPU of a modern mobile phone. Wealso want to give the application some time to react to user events.For the above reasons, we pause the event loop for a short while aftereach iteration. The e32.ao sleep() function pauses the execution forthe given time, which can be a fraction of a second. In this case, wepause for a hundredth of a second, e32.ao sleep(0.01), after eachiteration. This means that the game is capable of showing 100 frames persecond (FPS) at best.Dynamic timeIn some cases we want to give the user more time to observe the situation.In this game, we increase the length of the pause to a tenth of a secondwhen a laser beam is fired, so that the user can see how many hits she106SOUND, INTERACTIVE GRAPHICS AND CAMERAscored. The ability to change the length of the pause in the event loop iscalled dynamic time.In an extreme case, we might want to pause for the minimum possibleamount of time, 0 seconds, but let the application handle any user inputanyway.
For this purpose, a special function called e32.ao yield()is provided. By calling this function, you ensure that the user interfacestays responsive although your application may perform computation ina tight loop at full speed.Double bufferingDouble buffering is a traditional method to prevent flickering whenmoving graphics are shown. This method was used to handle redrawingof the Canvas object.As in the previous examples, instead of drawing the game elementsto the Canvas object directly, we have a separate image object, buf,to which all elements are drawn at first.
Only when everything has beendrawn to the image buffer are the contents of the second buffer shownon the canvas using canvas.blit(buf). This way the user does nothave to see every individual drawing operation, but only the final resultfor each frame, which also reduces flickering on the screen.Module randomLast but not least, many games need a random ingredient to stayinteresting.
Python provides a standard module called random thatcontains methods to generate random numbers. Here, the functionrandom.randint() is used to generate integer values between thegiven minimum and maximum values. Another often used function,random.random(), returns a floating point value between 0 and 1.0.5.5.2 The Game Application CodeThe actual game code is divided into three parts that should be combinedto make the final game. In the following discussion, we go through thelistings one by one.Example 37: UFO Zapper (1/3)import key_codes, appuifw, e32, graphics, randomMAX_UFO_SIZE = 50MAX_UFOS = 7UFO_TIME = 100.0PAD_W = 40MOBILE GAME: UFO ZAPPER107PAD_H = 15PAD_COLOR = (12, 116, 204)PAD_SPEED = 7LASER_COLOR = (255, 0, 204)TIME = 1000def handle_redraw(rect):global shoot, sleep, ufos, timerbuf.clear((0, 0, 0))buf.rectangle((pad_x, H - PAD_H, pad_x + PAD_W, H), fill = PAD_COLOR)if shoot:x = pad_x + PAD_W / 2buf.line((x, H - PAD_H, x, 0), width = 2, outline = LASER_COLOR)shoot = Falsesleep = 0.1check_hits(x)else:sleep = 0.01for x, y, s, t, hit in ufos:f = 1.0 - (timer - t) / UFO_TIMEif hit:c = (255, 0, 0)else:c = (0, f * 255, 0)buf.ellipse((x, y, x + s, y + s), fill = c)buf.text((10, 40), u"%d" % score, fill = LASER_COLOR, font = "title")buf.text((W - 70, 40), u"%d" % (TIME - timer),fill = LASER_COLOR, font = "title")canvas.blit(buf)The first part of the game code in Example 37 declares the constantsneeded in the game:•MAX UFO SIZE gives the maximum size of a UFO in pixels.• MAX UFOS controls how many UFOs are shown at once on thescreen.• UFO TIME controls for how long a UFO stays on screen.• PAD W and PAD H determine the pad size in pixels.• PAD COLOR determines color of the pad.• PAD SPEED determines how many pixels the pad advances in oneframe.• LASER COLOR is the color of the laser beam.•TIME specifies for how many frames the game is played.
This isapproximately TIME * 0.05 seconds.108SOUND, INTERACTIVE GRAPHICS AND CAMERAThe function handle redraw() is responsible for drawing the gameon screen based on the internal state of the game. First, the previousframe is cleared and the pad is drawn to its current location on the X axis.If the player has activated the laser beam with the Select key, the beam isdrawn and the function check hits() is called to determine whetherany UFOs have been hit.
Then the laser beam is switched off againwith shoot = False and the length of the pause (sleep) is temporarilyincreased to a tenth of a second so the beam and possible hits can beseen easily.After this, the UFOs are drawn based on the list ufos, which containsthe coordinates, size and lifetime of each UFO. The shade of a UFO isdetermined according to its age: the closer the age is to its maximumlifetime UFO TIME, the darker shade of green is selected.
However, if theUFO has been hit, as determined by the check hits function above,it is painted in glowing red. Finally, the current score and time are drawnon screen.Example 38: UFO Zapper (2/3)def check_hits(laser_x):global ufos, scorei = 0ok_ufos = []for x, y, s, t, hit in ufos:if laser_x > x and laser_x < x + s:ok_ufos.append((x, y, s, t, True))score += MAX_UFO_SIZE - (s - 1)else:ok_ufos.append((x, y, s, t, False))ufos = ok_ufosdef update_ufos():global ufos, timerok_ufos = []for x, y, s, t, hit in ufos:if not hit and timer < t + UFO_TIME:ok_ufos.append((x, y, s, t, False))ufos = ok_ufosif len(ufos) < MAX_UFOS:s = random.randint(10, MAX_UFO_SIZE)x = random.randint(0, W - s)y = random.randint(0, H - PAD_H * 3)t = random.randint(0, UFO_TIME)ufos.append((x, y, s, timer + t, False))Example 38 lists the two functions related to the maintenance of thearmada of UFOs. The check hits() function checks which of theUFOs, if any, have been hit by the laser beam at the X coordinate,laser x.
This is accomplished by checking whether any of the UFOMOBILE GAME: UFO ZAPPER109circles intersect with the beam. If the beam touches a UFO, we mark thehit entry in the corresponding UFO tuple as True.The function update ufos is responsible for cleaning up any UFOsthat have been shot down (that is, hit == True) or which have becometoo old. If the number of UFOs on screen is less than MAX UFOS afterthis process, a new UFO of random size is created at a random location.Example 39: UFO Zapper (3/3)def handle_event(event):global direction, shootif event['keycode'] == key_codes.EKeyLeftArrow:direction = -PAD_SPEEDelif event['keycode'] == key_codes.EKeyRightArrow:direction = PAD_SPEEDelif event['keycode'] == key_codes.EKeySelect:shoot = Truedef quit():global timertimer = TIMEufos = []shoot = Falsedirection = pad_x = score = timer = 0appuifw.app.exit_key_handler = quitappuifw.app.screen = "large"canvas = appuifw.Canvas(event_callback = handle_event,\redraw_callback = handle_redraw)W, H = canvas.sizebuf = graphics.Image.new((W, H))appuifw.app.body = canvaswhile timer < TIME:pad_x += directionpad_x = min(pad_x, W - PAD_W)pad_x = max(pad_x, 0)update_ufos()handle_redraw((W, H))e32.ao_sleep(sleep)timer += 1print "Your final score was %d!" % scoreThe final part of the game, listed in Example 39, includes the simplefunction handle event() which registers any left or right key pressesand changes the direction of the pad accordingly.