1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.replica.replicaisland; 18 19 import android.content.Context; 20 import android.os.Build; 21 import android.view.KeyEvent; 22 import android.view.MotionEvent; 23 import android.view.WindowManager; 24 import android.widget.Toast; 25 26 /** 27 * High-level setup object for the AndouKun game engine. 28 * This class sets up the core game engine objects and threads. It also passes events to the 29 * game thread from the main UI thread. 30 */ 31 public class Game extends AllocationGuard { 32 private GameThread mGameThread; 33 private Thread mGame; 34 private ObjectManager mGameRoot; 35 36 private GameRenderer mRenderer; 37 private GLSurfaceView mSurfaceView; 38 private boolean mRunning; 39 private boolean mBootstrapComplete; 40 private LevelTree.Level mPendingLevel; 41 private LevelTree.Level mCurrentLevel; 42 private LevelTree.Level mLastLevel; 43 private boolean mGLDataLoaded; 44 private ContextParameters mContextParameters; 45 private TouchFilter mTouchFilter; 46 Game()47 public Game() { 48 super(); 49 mRunning = false; 50 mBootstrapComplete = false; 51 mGLDataLoaded = false; 52 mContextParameters = new ContextParameters(); 53 } 54 55 /** 56 * Creates core game objects and constructs the game engine object graph. Note that the 57 * game does not actually begin running after this function is called (see start() below). 58 * Also note that textures are not loaded from the resource pack by this function, as OpenGl 59 * isn't yet available. 60 * @param context 61 */ bootstrap(Context context, int viewWidth, int viewHeight, int gameWidth, int gameHeight, int difficulty)62 public void bootstrap(Context context, int viewWidth, int viewHeight, int gameWidth, int gameHeight, int difficulty) { 63 if (!mBootstrapComplete) { 64 mRenderer = new GameRenderer(context, this, gameWidth, gameHeight); 65 66 // Create core systems 67 BaseObject.sSystemRegistry.openGLSystem = new OpenGLSystem(null); 68 69 BaseObject.sSystemRegistry.customToastSystem = new CustomToastSystem(context); 70 71 ContextParameters params = mContextParameters; 72 params.viewWidth = viewWidth; 73 params.viewHeight = viewHeight; 74 params.gameWidth = gameWidth; 75 params.gameHeight = gameHeight; 76 params.viewScaleX = (float)viewWidth / gameWidth; 77 params.viewScaleY = (float)viewHeight / gameHeight; 78 params.context = context; 79 params.difficulty = difficulty; 80 BaseObject.sSystemRegistry.contextParameters = params; 81 82 final int sdkVersion = Integer.parseInt(Build.VERSION.SDK); 83 if (sdkVersion < Build.VERSION_CODES.ECLAIR) { 84 mTouchFilter = new SingleTouchFilter(); 85 } else { 86 mTouchFilter = new MultiTouchFilter(); 87 } 88 89 // Short-term textures are cleared between levels. 90 TextureLibrary shortTermTextureLibrary = new TextureLibrary(); 91 BaseObject.sSystemRegistry.shortTermTextureLibrary = shortTermTextureLibrary; 92 93 // Long-term textures persist between levels. 94 TextureLibrary longTermTextureLibrary = new TextureLibrary(); 95 BaseObject.sSystemRegistry.longTermTextureLibrary = longTermTextureLibrary; 96 97 // The buffer library manages hardware VBOs. 98 BaseObject.sSystemRegistry.bufferLibrary = new BufferLibrary(); 99 100 101 102 BaseObject.sSystemRegistry.soundSystem = new SoundSystem(); 103 104 // The root of the game graph. 105 MainLoop gameRoot = new MainLoop(); 106 107 InputSystem input = new InputSystem(); 108 BaseObject.sSystemRegistry.inputSystem = input; 109 BaseObject.sSystemRegistry.registerForReset(input); 110 111 WindowManager windowMgr = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); 112 int rotationIndex = windowMgr.getDefaultDisplay().getOrientation(); 113 input.setScreenRotation(rotationIndex); 114 115 InputGameInterface inputInterface = new InputGameInterface(); 116 gameRoot.add(inputInterface); 117 BaseObject.sSystemRegistry.inputGameInterface = inputInterface; 118 119 LevelSystem level = new LevelSystem(); 120 BaseObject.sSystemRegistry.levelSystem = level; 121 122 CollisionSystem collision = new CollisionSystem(); 123 BaseObject.sSystemRegistry.collisionSystem = collision; 124 BaseObject.sSystemRegistry.hitPointPool = new HitPointPool(); 125 126 GameObjectManager gameManager = new GameObjectManager(params.viewWidth * 2); 127 BaseObject.sSystemRegistry.gameObjectManager = gameManager; 128 129 GameObjectFactory objectFactory = new GameObjectFactory(); 130 BaseObject.sSystemRegistry.gameObjectFactory = objectFactory; 131 132 BaseObject.sSystemRegistry.hotSpotSystem = new HotSpotSystem(); 133 134 BaseObject.sSystemRegistry.levelBuilder = new LevelBuilder(); 135 136 BaseObject.sSystemRegistry.channelSystem = new ChannelSystem(); 137 BaseObject.sSystemRegistry.registerForReset(BaseObject.sSystemRegistry.channelSystem); 138 139 CameraSystem camera = new CameraSystem(); 140 141 142 BaseObject.sSystemRegistry.cameraSystem = camera; 143 BaseObject.sSystemRegistry.registerForReset(camera); 144 145 collision.loadCollisionTiles(context.getResources().openRawResource(R.raw.collision)); 146 147 gameRoot.add(gameManager); 148 149 // Camera must come after the game manager so that the camera target moves before the camera 150 // centers. 151 152 gameRoot.add(camera); 153 154 155 // More basic systems. 156 157 GameObjectCollisionSystem dynamicCollision = new GameObjectCollisionSystem(); 158 gameRoot.add(dynamicCollision); 159 BaseObject.sSystemRegistry.gameObjectCollisionSystem = dynamicCollision; 160 161 162 RenderSystem renderer = new RenderSystem(); 163 BaseObject.sSystemRegistry.renderSystem = renderer; 164 BaseObject.sSystemRegistry.vectorPool = new VectorPool(); 165 BaseObject.sSystemRegistry.drawableFactory = new DrawableFactory(); 166 167 HudSystem hud = new HudSystem(); 168 hud.setFuelDrawable( 169 new DrawableBitmap(longTermTextureLibrary.allocateTexture( 170 R.drawable.ui_bar), 0, 0), 171 new DrawableBitmap(longTermTextureLibrary.allocateTexture( 172 R.drawable.ui_bar_bg), 0, 0)); 173 hud.setFadeTexture(longTermTextureLibrary.allocateTexture(R.drawable.black)); 174 hud.setButtonDrawables( 175 new DrawableBitmap(longTermTextureLibrary.allocateTexture( 176 R.drawable.ui_button_fly_disabled), 0, 0), 177 new DrawableBitmap(longTermTextureLibrary.allocateTexture( 178 R.drawable.ui_button_fly_off), 0, 0), 179 new DrawableBitmap(longTermTextureLibrary.allocateTexture( 180 R.drawable.ui_button_fly_on), 0, 0), 181 new DrawableBitmap(longTermTextureLibrary.allocateTexture( 182 R.drawable.ui_button_stomp_off), 0, 0), 183 new DrawableBitmap(longTermTextureLibrary.allocateTexture( 184 R.drawable.ui_button_stomp_on), 0, 0), 185 new DrawableBitmap(longTermTextureLibrary.allocateTexture( 186 R.drawable.ui_movement_slider_base), 0, 0), 187 new DrawableBitmap(longTermTextureLibrary.allocateTexture( 188 R.drawable.ui_movement_slider_button_off), 0, 0), 189 new DrawableBitmap(longTermTextureLibrary.allocateTexture( 190 R.drawable.ui_movement_slider_button_on), 0, 0)); 191 Texture[] digitTextures = { 192 longTermTextureLibrary.allocateTexture(R.drawable.ui_0), 193 longTermTextureLibrary.allocateTexture(R.drawable.ui_1), 194 longTermTextureLibrary.allocateTexture(R.drawable.ui_2), 195 longTermTextureLibrary.allocateTexture(R.drawable.ui_3), 196 longTermTextureLibrary.allocateTexture(R.drawable.ui_4), 197 longTermTextureLibrary.allocateTexture(R.drawable.ui_5), 198 longTermTextureLibrary.allocateTexture(R.drawable.ui_6), 199 longTermTextureLibrary.allocateTexture(R.drawable.ui_7), 200 longTermTextureLibrary.allocateTexture(R.drawable.ui_8), 201 longTermTextureLibrary.allocateTexture(R.drawable.ui_9) 202 }; 203 DrawableBitmap[] digits = { 204 new DrawableBitmap(digitTextures[0], 0, 0), 205 new DrawableBitmap(digitTextures[1], 0, 0), 206 new DrawableBitmap(digitTextures[2], 0, 0), 207 new DrawableBitmap(digitTextures[3], 0, 0), 208 new DrawableBitmap(digitTextures[4], 0, 0), 209 new DrawableBitmap(digitTextures[5], 0, 0), 210 new DrawableBitmap(digitTextures[6], 0, 0), 211 new DrawableBitmap(digitTextures[7], 0, 0), 212 new DrawableBitmap(digitTextures[8], 0, 0), 213 new DrawableBitmap(digitTextures[9], 0, 0) 214 }; 215 DrawableBitmap xDrawable = new DrawableBitmap( 216 longTermTextureLibrary.allocateTexture(R.drawable.ui_x), 0, 0); 217 218 hud.setDigitDrawables(digits, xDrawable); 219 hud.setCollectableDrawables( 220 new DrawableBitmap( 221 longTermTextureLibrary.allocateTexture(R.drawable.ui_pearl), 0, 0), 222 new DrawableBitmap( 223 longTermTextureLibrary.allocateTexture(R.drawable.ui_gem), 0, 0)); 224 225 BaseObject.sSystemRegistry.hudSystem = hud; 226 if (AndouKun.VERSION < 0) { 227 hud.setShowFPS(true); 228 } 229 gameRoot.add(hud); 230 231 BaseObject.sSystemRegistry.vibrationSystem = new VibrationSystem(); 232 233 EventRecorder eventRecorder = new EventRecorder(); 234 BaseObject.sSystemRegistry.eventRecorder = eventRecorder; 235 BaseObject.sSystemRegistry.registerForReset(eventRecorder); 236 237 gameRoot.add(collision); 238 239 // debug systems 240 //BaseObject.sSystemRegistry.debugSystem = new DebugSystem(longTermTextureLibrary); 241 //dynamicCollision.setDebugPrefs(false, true); 242 243 244 objectFactory.preloadEffects(); 245 246 mGameRoot = gameRoot; 247 248 mGameThread = new GameThread(mRenderer); 249 mGameThread.setGameRoot(mGameRoot); 250 251 252 mCurrentLevel = null; 253 254 mBootstrapComplete = true; 255 } 256 } 257 258 stopLevel()259 protected synchronized void stopLevel() { 260 stop(); 261 GameObjectManager manager = BaseObject.sSystemRegistry.gameObjectManager; 262 manager.destroyAll(); 263 manager.commitUpdates(); 264 265 //TODO: it's not strictly necessary to clear the static data here, but if I don't do it 266 // then two things happen: first, the static data will refer to junk Texture objects, and 267 // second, memory that may not be needed for the next level will hang around. One solution 268 // would be to break up the texture library into static and non-static things, and 269 // then selectively clear static game components based on their usefulness next level, 270 // but this is way simpler. 271 GameObjectFactory factory = BaseObject.sSystemRegistry.gameObjectFactory; 272 factory.clearStaticData(); 273 factory.sanityCheckPools(); 274 275 // Reset the level 276 BaseObject.sSystemRegistry.levelSystem.reset(); 277 278 // Ensure sounds have stopped. 279 BaseObject.sSystemRegistry.soundSystem.stopAll(); 280 281 // Reset systems that need it. 282 BaseObject.sSystemRegistry.reset(); 283 284 // Dump the short-term texture objects only. 285 mSurfaceView.flushTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary); 286 BaseObject.sSystemRegistry.shortTermTextureLibrary.removeAll(); 287 mSurfaceView.flushBuffers(BaseObject.sSystemRegistry.bufferLibrary); 288 BaseObject.sSystemRegistry.bufferLibrary.removeAll(); 289 } 290 requestNewLevel()291 public synchronized void requestNewLevel() { 292 // tell the Renderer to call us back when the 293 // render thread is ready to manage some texture memory. 294 mRenderer.requestCallback(); 295 } 296 restartLevel()297 public synchronized void restartLevel() { 298 DebugLog.d("AndouKun", "Restarting..."); 299 final LevelTree.Level level = mCurrentLevel; 300 stop(); 301 302 // Destroy all game objects and respawn them. No need to destroy other systems. 303 GameObjectManager manager = BaseObject.sSystemRegistry.gameObjectManager; 304 manager.destroyAll(); 305 manager.commitUpdates(); 306 307 // Ensure sounds have stopped. 308 BaseObject.sSystemRegistry.soundSystem.stopAll(); 309 310 // Reset systems that need it. 311 BaseObject.sSystemRegistry.reset(); 312 313 LevelSystem levelSystem = BaseObject.sSystemRegistry.levelSystem; 314 levelSystem.incrementAttemptsCount(); 315 levelSystem.spawnObjects(); 316 317 BaseObject.sSystemRegistry.hudSystem.startFade(true, 0.2f); 318 319 mCurrentLevel = level; 320 mPendingLevel = null; 321 start(); 322 } 323 goToLevel(LevelTree.Level level)324 protected synchronized void goToLevel(LevelTree.Level level) { 325 326 ContextParameters params = BaseObject.sSystemRegistry.contextParameters; 327 BaseObject.sSystemRegistry.levelSystem.loadLevel(level, 328 params.context.getResources().openRawResource(level.resource), mGameRoot); 329 330 Context context = params.context; 331 mRenderer.setContext(context); 332 mSurfaceView.loadTextures(BaseObject.sSystemRegistry.longTermTextureLibrary); 333 mSurfaceView.loadTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary); 334 mSurfaceView.loadBuffers(BaseObject.sSystemRegistry.bufferLibrary); 335 336 mGLDataLoaded = true; 337 338 mCurrentLevel = level; 339 mPendingLevel = null; 340 341 TimeSystem time = BaseObject.sSystemRegistry.timeSystem; 342 time.reset(); 343 344 HudSystem hud = BaseObject.sSystemRegistry.hudSystem; 345 if (hud != null) { 346 hud.startFade(true, 1.0f); 347 } 348 349 350 CustomToastSystem toast = BaseObject.sSystemRegistry.customToastSystem; 351 if (toast != null) { 352 if (level.inThePast) { 353 toast.toast(context.getString(R.string.memory_playback_start), Toast.LENGTH_LONG); 354 } else { 355 if (mLastLevel != null && mLastLevel.inThePast) { 356 toast.toast(context.getString(R.string.memory_playback_complete), Toast.LENGTH_LONG); 357 } 358 } 359 } 360 361 mLastLevel = level; 362 363 start(); 364 } 365 366 /** Starts the game running. */ start()367 public void start() { 368 if (!mRunning) { 369 assert mGame == null; 370 // Now's a good time to run the GC. 371 Runtime r = Runtime.getRuntime(); 372 r.gc(); 373 DebugLog.d("AndouKun", "Start!"); 374 mGame = new Thread(mGameThread); 375 mGame.setName("Game"); 376 mGame.start(); 377 mRunning = true; 378 AllocationGuard.sGuardActive = false; 379 } else { 380 mGameThread.resumeGame(); 381 } 382 } 383 stop()384 public void stop() { 385 if (mRunning) { 386 DebugLog.d("AndouKun", "Stop!"); 387 if (mGameThread.getPaused()) { 388 mGameThread.resumeGame(); 389 } 390 mGameThread.stopGame(); 391 try { 392 mGame.join(); 393 } catch (InterruptedException e) { 394 mGame.interrupt(); 395 } 396 mGame = null; 397 mRunning = false; 398 mCurrentLevel = null; 399 AllocationGuard.sGuardActive = false; 400 } 401 } 402 onTrackballEvent(MotionEvent event)403 public boolean onTrackballEvent(MotionEvent event) { 404 if (mRunning) { 405 if (event.getAction() == MotionEvent.ACTION_MOVE) { 406 BaseObject.sSystemRegistry.inputSystem.roll(event.getRawX(), event.getRawY()); 407 } else if (event.getAction() == MotionEvent.ACTION_DOWN) { 408 onKeyDownEvent(KeyEvent.KEYCODE_DPAD_CENTER); 409 } else if (event.getAction() == MotionEvent.ACTION_UP) { 410 onKeyUpEvent(KeyEvent.KEYCODE_DPAD_CENTER); 411 } 412 } 413 return true; 414 } 415 onOrientationEvent(float x, float y, float z)416 public boolean onOrientationEvent(float x, float y, float z) { 417 if (mRunning) { 418 BaseObject.sSystemRegistry.inputSystem.setOrientation(x, y, z); 419 } 420 return true; 421 } 422 onTouchEvent(MotionEvent event)423 public boolean onTouchEvent(MotionEvent event) { 424 if (mRunning) { 425 mTouchFilter.updateTouch(event); 426 } 427 return true; 428 } 429 430 431 onKeyDownEvent(int keyCode)432 public boolean onKeyDownEvent(int keyCode) { 433 boolean result = false; 434 if (mRunning) { 435 BaseObject.sSystemRegistry.inputSystem.keyDown(keyCode); 436 } 437 return result; 438 } 439 onKeyUpEvent(int keyCode)440 public boolean onKeyUpEvent(int keyCode) { 441 boolean result = false; 442 if (mRunning) { 443 BaseObject.sSystemRegistry.inputSystem.keyUp(keyCode); 444 } 445 return result; 446 } 447 getRenderer()448 public GameRenderer getRenderer() { 449 return mRenderer; 450 } 451 onPause()452 public void onPause() { 453 if (mRunning) { 454 mGameThread.pauseGame(); 455 } 456 } 457 onResume(Context context, boolean force)458 public void onResume(Context context, boolean force) { 459 if (force && mRunning) { 460 mGameThread.resumeGame(); 461 } else { 462 mRenderer.setContext(context); 463 // Don't explicitly resume the game here. We'll do that in 464 // the SurfaceReady() callback, which will prevent the game 465 // starting before the render thread is ready to go. 466 BaseObject.sSystemRegistry.contextParameters.context = context; 467 } 468 } 469 onSurfaceReady()470 public void onSurfaceReady() { 471 DebugLog.d("AndouKun", "Surface Ready"); 472 473 if (mPendingLevel != null && mPendingLevel != mCurrentLevel) { 474 if (mRunning) { 475 stopLevel(); 476 } 477 goToLevel(mPendingLevel); 478 } else if (mGameThread.getPaused() && mRunning) { 479 mGameThread.resumeGame(); 480 } 481 } 482 setSurfaceView(GLSurfaceView view)483 public void setSurfaceView(GLSurfaceView view) { 484 mSurfaceView = view; 485 } 486 onSurfaceLost()487 public void onSurfaceLost() { 488 DebugLog.d("AndouKun", "Surface Lost"); 489 490 BaseObject.sSystemRegistry.shortTermTextureLibrary.invalidateAll(); 491 BaseObject.sSystemRegistry.longTermTextureLibrary.invalidateAll(); 492 BaseObject.sSystemRegistry.bufferLibrary.invalidateHardwareBuffers(); 493 494 mGLDataLoaded = false; 495 } 496 onSurfaceCreated()497 public void onSurfaceCreated() { 498 DebugLog.d("AndouKun", "Surface Created"); 499 500 // TODO: this is dumb. SurfaceView doesn't need to control everything here. 501 // GL should just be passed to this function and then set up directly. 502 503 if (!mGLDataLoaded && mGameThread.getPaused() && mRunning && mPendingLevel == null) { 504 505 mSurfaceView.loadTextures(BaseObject.sSystemRegistry.longTermTextureLibrary); 506 mSurfaceView.loadTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary); 507 mSurfaceView.loadBuffers(BaseObject.sSystemRegistry.bufferLibrary); 508 mGLDataLoaded = true; 509 } 510 } 511 setPendingLevel(LevelTree.Level level)512 public void setPendingLevel(LevelTree.Level level) { 513 mPendingLevel = level; 514 } 515 setSoundEnabled(boolean soundEnabled)516 public void setSoundEnabled(boolean soundEnabled) { 517 BaseObject.sSystemRegistry.soundSystem.setSoundEnabled(soundEnabled); 518 } 519 setControlOptions(boolean clickAttack, boolean tiltControls, int tiltSensitivity, int movementSensitivity, boolean onScreenControls)520 public void setControlOptions(boolean clickAttack, 521 boolean tiltControls, int tiltSensitivity, int movementSensitivity, boolean onScreenControls) { 522 BaseObject.sSystemRegistry.inputGameInterface.setUseClickForAttack(clickAttack); 523 BaseObject.sSystemRegistry.inputGameInterface.setUseOrientationForMovement(tiltControls); 524 BaseObject.sSystemRegistry.inputGameInterface.setOrientationMovementSensitivity((tiltSensitivity / 100.0f)); 525 BaseObject.sSystemRegistry.inputGameInterface.setMovementSensitivity((movementSensitivity / 100.0f)); 526 BaseObject.sSystemRegistry.inputGameInterface.setUseOnScreenControls(onScreenControls); 527 BaseObject.sSystemRegistry.hudSystem.setMovementSliderMode(onScreenControls); 528 } 529 setSafeMode(boolean safe)530 public void setSafeMode(boolean safe) { 531 mSurfaceView.setSafeMode(safe); 532 } 533 getGameTime()534 public float getGameTime() { 535 return BaseObject.sSystemRegistry.timeSystem.getGameTime(); 536 } 537 getLastDeathPosition()538 public Vector2 getLastDeathPosition() { 539 return BaseObject.sSystemRegistry.eventRecorder.getLastDeathPosition(); 540 } 541 setLastEnding(int ending)542 public void setLastEnding(int ending) { 543 BaseObject.sSystemRegistry.eventRecorder.setLastEnding(ending); 544 } 545 getLastEnding()546 public int getLastEnding() { 547 return BaseObject.sSystemRegistry.eventRecorder.getLastEnding(); 548 } 549 isPaused()550 public boolean isPaused() { 551 return (mRunning && mGameThread != null && mGameThread.getPaused()); 552 } 553 setKeyConfig(int leftKey, int rightKey, int jumpKey, int attackKey)554 public void setKeyConfig(int leftKey, int rightKey, int jumpKey, 555 int attackKey) { 556 BaseObject.sSystemRegistry.inputGameInterface.setKeys(leftKey, rightKey, jumpKey, attackKey); 557 } 558 getRobotsDestroyed()559 public int getRobotsDestroyed() { 560 return BaseObject.sSystemRegistry.eventRecorder.getRobotsDestroyed(); 561 } 562 getPearlsCollected()563 public int getPearlsCollected() { 564 return BaseObject.sSystemRegistry.eventRecorder.getPearlsCollected(); 565 } 566 getPearlsTotal()567 public int getPearlsTotal() { 568 return BaseObject.sSystemRegistry.eventRecorder.getPearlsTotal(); 569 } 570 } 571