Package fortuneengine :: Module GameEngine
[hide private]
[frames] | no frames]

Source Code for Module fortuneengine.GameEngine

  1  #    FortuneEngine is free software: you can redistribute it and/or modify 
  2  #    it under the terms of the GNU General Public License as published by 
  3  #    the Free Software Foundation, either version 3 of the License, or 
  4  #    (at your option) any later version. 
  5  # 
  6  #    FortuneEngine is distributed in the hope that it will be useful, 
  7  #    but WITHOUT ANY WARRANTY; without even the implied warranty of 
  8  #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  9  #    GNU General Public License for more details. 
 10  # 
 11  #    You should have received a copy of the GNU General Public License 
 12  #    along with the FortuneEngine.  If not, see <http://www.gnu.org/licenses/>. 
 13  # 
 14  #    Author: Justin Lewis  <jlew.blackout@gmail.com> 
 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   
24 -class GameEngine(object):
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 #true is new, false is old 53 54 # Window Settings 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 # Engine Internal Variables 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 # Variables to hold game engine elements and callbacks 73 self.__event_cb = [] 74 self.__draw_lst = [] 75 self.__object_hold = {} 76 77 78 # Game Timers 79 self.__active_event_timers = [] 80 self.__active_event_timers_tick = [] 81 82 # Game Clock 83 self.clock = pygame.time.Clock() 84 self.__tick_time = 0 85 86 # Inspector 87 self._inspector = GameInspect(self.__object_hold) 88 89 # Time Profiler Timers 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 # Initialize Py Console 98 self.console = GameEngineConsole(self, (0, 0, width, height / 2)) 99 100 # Disable Mouse Usage 101 # TODO Allow mouse motion on request 102 pygame.event.set_blocked(pygame.MOUSEMOTION)
103
104 - def set_dirty(self):
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
111 - def get_scene(self):
112 """ 113 Returns the scene object 114 115 @return: Returns the scene object held by the game engine 116 """ 117 return self.__scene
118
119 - def start_event_timer(self, function_cb, time):
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
143 - def stop_event_timer(self, function_cb):
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 # Timers have been removed, now need to clear any events 160 # already fired and in the queue 161 pygame.event.clear(pygame.USEREVENT + timer_id)
162
163 - def list_event_timers(self):
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
177 - def list_timer_time(self):
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 # If console is active, we want to draw console, pausing 215 # game drawing (events are still being fired, just no 216 # draw updates. 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 # Print Frame Rate 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 # Print Frame Rate 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
253 - def _event_loop(self):
254 """ 255 The main event loop. 256 """ 257 while self.__run_event: 258 259 event = pygame.event.poll() 260 261 # Handle Game Quit Message 262 if event.type == pygame.QUIT: 263 self.__run_event = False 264 265 # No-Op sent, draw if set to always draw 266 elif event.type == pygame.NOEVENT: 267 # Tick even if not drawing 268 # We want to pause the cpu from getting into a 269 # 100% usage looping on the poll until something 270 # becomes dirty 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 # Handle User event Timers 279 elif event.type >= pygame.USEREVENT and \ 280 event.type < pygame.NUMEVENTS: 281 282 timer_id = event.type - pygame.USEREVENT 283 284 # Call timer 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 # Check if we should activate the console 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 # Pass event to console 298 elif self.console.process_input(event): 299 self.set_dirty() 300 301 # Pass events to all others 302 else: 303 # Make a copy first so that adding events don't get fired 304 # right away 305 list_cp = self.__event_cb[:] 306 307 # Reverse list so that newest stuff is on top 308 # TODO: cache this list 309 list_cp.reverse() 310 311 for cb in list_cp: 312 # Fire the event for all in cb and stop 313 # if the callback returns True 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
322 - def stop_event_loop(self):
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
329 - def add_event_callback(self, cb):
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
339 - def remove_event_callback(self, cb):
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
352 - def list_event_callbacks(self):
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
362 - def list_event_time(self):
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
380 - def add_draw_callback(self, fnc):
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
392 - def pop_draw_callback(self):
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
400 - def clear_draw_callback(self):
401 """ 402 Empties draw callback stack 403 """ 404 self.__draw_lst = []
405
406 - def remove_draw_callback(self, fnc):
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
419 - def list_draw_callbacks(self):
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
429 - def list_draw_time(self):
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
447 - def has_object(self, name):
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
456 - def add_object(self, name, obj):
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
465 - def get_object(self, name):
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
474 - def remove_object(self, name):
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
482 - def list_objects(self):
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
491 - def toggle_fps(self):
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