1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import pygame
17 from time import time
18 from GameEngineConsole import GameEngineConsole
19 from GameInspect import GameInspect
20 from DrawableFontObject import DrawableFontObject
21 from Scene import Scene
22
23
25 """
26 The Fortune Engine GameEngine is a main loop wrapper around pygame.
27 It manages the event and drawing loops allowing the user to just
28 register for user events and drawing time in the draw loop.
29 """
30 instance = None
31
32 - def __init__(self, width=1200, height=900, always_draw=False,
33 fps_cap=15, version=False, title="FortuneEngine"):
34 """
35 Constructor for the game engine.
36
37 @param width: Window width
38 @param height: Window height
39 @param always_draw: Boolean to set the animation mode to always
40 draw vs draw when set_dirty is called
41 @param fps_cap: Sets the framerate cap. Set to 0 to disable
42 the cap. Warning: setting the cap to 0 when
43 always draw = False will cause cpu 100% when
44 not driving.
45 @param version: If true, use new rendering system, false uses
46 only the draw system
47 @param title: Window Title
48 """
49 GameEngine.instance = self
50 pygame.init()
51 pygame.mouse.set_visible(False)
52 self.__version = version
53
54
55 self.width = width
56 self.height = height
57 size = width, height
58 self.screen = pygame.display.set_mode(size)
59 pygame.display.set_caption(title)
60 self.__fps = DrawableFontObject("", pygame.font.Font(None, 17))
61 self.__fps.setPosition(0, 0)
62 self.__scene = Scene(self.__fps)
63
64
65 self.__fps_cap = fps_cap
66 self.__showfps = False
67 self.__dirty = True
68 self.__always_draw = always_draw
69 self.__font = pygame.font.Font(None, 17)
70 self.__run_event = False
71
72
73 self.__event_cb = []
74 self.__draw_lst = []
75 self.__object_hold = {}
76
77
78
79 self.__active_event_timers = []
80 self.__active_event_timers_tick = []
81
82
83 self.clock = pygame.time.Clock()
84 self.__tick_time = 0
85
86
87 self._inspector = GameInspect(self.__object_hold)
88
89
90 self.__draw_time = {}
91 self.__draw_calls = {}
92 self.__event_time = {}
93 self.__event_calls = {}
94 self.__timer_time = {}
95 self.__timer_calls = {}
96
97
98 self.console = GameEngineConsole(self, (0, 0, width, height / 2))
99
100
101
102 pygame.event.set_blocked(pygame.MOUSEMOTION)
103
105 """
106 Sets the dirty flag to force the engine to draw the next time
107 it enters the draw flag.
108 """
109 self.__dirty = True
110
112 """
113 Returns the scene object
114
115 @return: Returns the scene object held by the game engine
116 """
117 return self.__scene
118
120 """
121 Starts a timer that fires a user event into the queue every "time"
122 milliseconds
123
124 @param function_cb: The function to call when timer fires
125 @param time: Milliseconds between fires
126 """
127 avail_timer = len(self.__active_event_timers)
128
129 if avail_timer + pygame.USEREVENT < pygame.NUMEVENTS:
130 if function_cb not in self.__active_event_timers:
131 self.__timer_time[str(function_cb)] = 0
132 self.__timer_calls[str(function_cb)] = 0
133
134 self.__active_event_timers.append(function_cb)
135 self.__active_event_timers_tick.append(time)
136 pygame.time.set_timer(pygame.USEREVENT + avail_timer, time)
137 else:
138 print "ERROR TIMER IN LIST"
139 else:
140 print "Ran out of timers :("
141 self.stop_event_loop()
142
144 """
145 Stops the timer that has id from firing
146
147 @param function_cb: The function registered with the timer that
148 should be canceled
149 """
150 try:
151 timer_id = self.__active_event_timers.index(function_cb)
152 except ValueError:
153 return
154
155 pygame.time.set_timer(pygame.USEREVENT + timer_id, 0)
156 del self.__active_event_timers[timer_id]
157 del self.__active_event_timers_tick[timer_id]
158
159
160
161 pygame.event.clear(pygame.USEREVENT + timer_id)
162
164 """
165 returns a list of configured timers, if the timers has a time of 0 the
166 timer is disabled
167 """
168 timer_list = "Event Timers:\n"
169 i = 0
170 for timer_item in self.__active_event_timers:
171 timer_list += "\t%d: %d\n" % (timer_item,
172 self.__active_event_timers_tick[i])
173 i = i + 1
174
175 return timer_list
176
178 """
179 Returns a string representation of the time the game spends
180 in each timer callback.
181 """
182 mystr = "Timer Times:\n\tName\tCalls\tTotal Time\tAvg"
183 for key in self.__timer_time:
184 timer_times = self.__timer_time[key]
185 timer_calls = self.__timer_calls[key]
186 if timer_calls == 0:
187 avg = 0
188 else:
189 avg = timer_times / timer_calls
190
191 mystr = "%s\n\t%s\n\t\t%d\t%f\t%f" % \
192 (mystr, key, timer_calls, timer_times, avg)
193 return mystr
194
195 - def start_main_loop(self):
196 """
197 Starts the game loop.
198
199 This function does not return until after the game loop exits
200 """
201 self.__run_event = True
202 self._event_loop()
203
204 - def _draw(self, tick_time):
205 """
206 Draws all elements in draw callback to the screen
207
208 @param tick_time: The amount of time passed since last
209 draw cycle. (should be produced by
210 pygamme.clock.tick method)
211 """
212 screen = self.screen
213
214
215
216
217 if self.__version:
218 if self.console.active:
219 self.console.draw()
220 pygame.display.flip()
221 else:
222 for fnc in self.__draw_lst:
223 start = time()
224 fnc()
225 self.__draw_time[str(fnc)] += time() - start
226 self.__draw_calls[str(fnc)] += 1
227
228 if self.__showfps:
229 self.__fps.changeText('FPS: %d' % self.clock.get_fps(),
230 (255, 255, 255))
231 else:
232 self.__fps.changeText('')
233 self.__scene.update(tick_time)
234 pygame.display.update(self.__scene.draw(screen))
235 else:
236 if self.console.active:
237 self.console.draw()
238 pygame.display.flip()
239 else:
240 for fnc in self.__draw_lst:
241 start = time()
242 fnc(screen, tick_time)
243 self.__draw_time[str(fnc)] += time() - start
244 self.__draw_calls[str(fnc)] += 1
245
246 if self.__showfps:
247 text = self.__font.render('FPS: %d' % \
248 self.clock.get_fps(), False, (255, 255, 255),
249 (159, 182, 205))
250 screen.blit(text, (0, 0))
251 pygame.display.flip()
252
254 """
255 The main event loop.
256 """
257 while self.__run_event:
258
259 event = pygame.event.poll()
260
261
262 if event.type == pygame.QUIT:
263 self.__run_event = False
264
265
266 elif event.type == pygame.NOEVENT:
267
268
269
270
271 self.__tick_time += self.clock.tick(self.__fps_cap)
272 if self.__always_draw or self.__dirty:
273 self.__dirty = False
274 self._draw(self.__tick_time)
275 self.__tick_time = 0
276
277
278
279 elif event.type >= pygame.USEREVENT and \
280 event.type < pygame.NUMEVENTS:
281
282 timer_id = event.type - pygame.USEREVENT
283
284
285 str_rep = str(self.__active_event_timers[timer_id])
286 start = time()
287 self.__active_event_timers[timer_id]()
288 self.__timer_time[str_rep] += time() - start
289 self.__timer_calls[str_rep] += 1
290
291
292 elif event.type == pygame.KEYDOWN and event.key == pygame.K_w \
293 and pygame.key.get_mods() & pygame.KMOD_CTRL:
294 self.console.set_active()
295 self.set_dirty()
296
297
298 elif self.console.process_input(event):
299 self.set_dirty()
300
301
302 else:
303
304
305 list_cp = self.__event_cb[:]
306
307
308
309 list_cp.reverse()
310
311 for cb in list_cp:
312
313
314 start = time()
315 retur_val = cb(event)
316 self.__event_time[str(cb)] += time() - start
317 self.__event_calls[str(cb)] += 1
318
319 if retur_val:
320 break
321
323 """
324 Sends a pygame.QUIT event into the event queue which
325 exits the event loop
326 """
327 pygame.event.post(pygame.event.Event(pygame.QUIT))
328
330 """
331 Adds event callback to the event callback stack
332
333 @param cb: Callback to be added to the stack when events are fired.
334 """
335 self.__event_time[str(cb)] = 0
336 self.__event_calls[str(cb)] = 0
337 self.__event_cb.append(cb)
338
340 """
341 Removes an event from the event callback stack
342
343 @param cb: The callback to remove from the event callback stack
344 @return: Returns true if successful in removing callback
345 """
346 try:
347 self.__event_cb.remove(cb)
348 return True
349 except:
350 return False
351
353 """
354 Returns a string representation of all events registered with the game
355 engine
356 """
357 event_callbacks = "Event Listeners:\n"
358 for eventlst in self.__event_cb:
359 event_callbacks = "%s\t%s\n" % (event_callbacks, str(eventlst))
360 return event_callbacks
361
363 """
364 Returns a string representation of the time the game spends
365 in each event callback.
366 """
367 mystr = "Event Times:\n\tName\tCalls\tTotal Time\tAvg"
368 for key in self.__event_time:
369 event_times = self.__event_time[key]
370 event_calls = self.__event_calls[key]
371 if event_calls == 0:
372 avg = 0
373 else:
374 avg = event_times / event_calls
375
376 mystr = "%s\n\t%s\n\t\t%d\t%f\t%f" % \
377 (mystr, key, event_calls, event_times, avg)
378 return mystr
379
381 """
382 Adds a callback to the draw list. Function will be passed the
383 game screen it should draw too.
384
385 @param fnc: The function to call when system is drawing
386 """
387
388 self.__draw_time[str(fnc)] = 0
389 self.__draw_calls[str(fnc)] = 0
390 self.__draw_lst.append(fnc)
391
393 """
394 Removes top of draw stack and returns it
395
396 @return: Returns the top callback function that was removed
397 """
398 return self.__draw_lst.pop()
399
401 """
402 Empties draw callback stack
403 """
404 self.__draw_lst = []
405
407 """
408 Removes a draw callback from the game engine draw function
409
410 @param fnc: The callback function to remove
411 @return: Returns true if successful removal of the function
412 """
413 try:
414 self.__draw_lst.remove(fnc)
415 return True
416 except:
417 return False
418
420 """
421 Lists all the draw callbacks currently registered with the game engine
422 """
423
424 callbacks = "Draw Callbacks:\n"
425 for eventlst in self.__draw_lst:
426 callbacks += "\t%s\n" % str(eventlst)
427 return callbacks
428
430 """
431 Returns a string representation of the time the game spends
432 in each drawing callback.
433 """
434 mystr = "Drawing Times:\n\tName\tCalls\tTotal Time\tAvg"
435 for key in self.__draw_time:
436 draw_times = self.__draw_time[key]
437 draw_calls = self.__draw_calls[key]
438 if draw_calls == 0:
439 avg = 0
440 else:
441 avg = draw_times / draw_calls
442
443 mystr = "%s\n\t%s\n\t\t%d\t%f\t%f" % \
444 (mystr, key, draw_calls, draw_times, avg)
445 return mystr
446
448 """
449 Returns true if object is stored in game engine
450
451 @param name: Name of the object to check if exists
452 @return: Returns true if object found
453 """
454 return name in self.__object_hold
455
457 """
458 Adds an object to the game engine datastore
459
460 @param name: The name used to store the object
461 @param obj: The object to store
462 """
463 self.__object_hold[name] = obj
464
466 """
467 Returns an object from the game engine datastore
468
469 @param name: The name of object to return
470 @return: Returns the object
471 """
472 return self.__object_hold[name]
473
475 """
476 Removes an object from the game engine datastore
477
478 @param name: The name of the object to remove
479 """
480 del self.__object_hold[name]
481
483 """
484 Returns a sting of registered objects
485 """
486 objlist = "Objects Registered:\n"
487 for eventlst in self.__object_hold:
488 objlist += "\t%s\n" % str(eventlst)
489 return objlist
490
492 """
493 Toggles fps display
494 """
495 self.__showfps = not self.__showfps
496
497 - def art_scale(self, original, expected, width=True):
498 if width:
499 return int(self.width / float(expected) * float(original))
500 else:
501
502 return int(self.height / float(expected) * float(original))
503