| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.replica.replicaisland; |
| |
| import android.os.SystemClock; |
| import android.view.KeyEvent; |
| |
| |
| /** |
| * The GameThread contains the main loop for the game engine logic. It invokes the game graph, |
| * manages synchronization of input events, and handles the draw queue swap with the rendering |
| * thread. |
| */ |
| public class GameThread implements Runnable { |
| private long mLastTime; |
| private float mLastMotionX; |
| private float mLastMotionY; |
| private int mLastTouchX; |
| private int mLastTouchY; |
| private boolean mTouchReleased; |
| private boolean mClickUp; |
| private boolean mClickDown; |
| private float mOrientationX; |
| private float mOrientationY; |
| private float mOrientationZ; |
| private boolean mOrientationChanged; |
| private boolean mKeyLeft; |
| private boolean mKeyRight; |
| private boolean mKeyDown; |
| private boolean mKeyUp; |
| private boolean mKeyTouch; |
| private boolean mKeyClick; |
| private boolean mKeyInputReceived; |
| private boolean mKeyLeftUp; |
| private boolean mKeyRightUp; |
| private boolean mKeyDownUp; |
| private boolean mKeyUpUp; |
| private boolean mKeyTouchUp; |
| private boolean mKeyClickUp; |
| |
| private boolean mClickActive; |
| |
| // Configurable key codes |
| private int mLeftKey = KeyEvent.KEYCODE_DPAD_LEFT; |
| private int mRightKey = KeyEvent.KEYCODE_DPAD_RIGHT; |
| private int mJumpKey = KeyEvent.KEYCODE_SPACE; |
| private int mAttackKey = KeyEvent.KEYCODE_SHIFT_LEFT; |
| |
| private ObjectManager mGameRoot; |
| private GameRenderer mRenderer; |
| private Object mInputLock; |
| private Object mPauseLock; |
| private boolean mFinished; |
| private boolean mPaused = false; |
| private int mProfileFrames; |
| private long mProfileTime; |
| |
| private static final float PROFILE_REPORT_DELAY = 3.0f; |
| |
| public GameThread(GameRenderer renderer) { |
| mLastTime = SystemClock.uptimeMillis(); |
| mRenderer = renderer; |
| mInputLock = new Object(); |
| mPauseLock = new Object(); |
| mFinished = false; |
| mPaused = false; |
| } |
| |
| public void run() { |
| mLastTime = SystemClock.uptimeMillis(); |
| mFinished = false; |
| while (!mFinished) { |
| if (mGameRoot != null) { |
| mRenderer.waitDrawingComplete(); |
| |
| final long time = SystemClock.uptimeMillis(); |
| final long timeDelta = time - mLastTime; |
| long finalDelta = timeDelta; |
| if (timeDelta > 12) { |
| float secondsDelta = (time - mLastTime) * 0.001f; |
| if (secondsDelta > 0.1f) { |
| secondsDelta = 0.1f; |
| } |
| mLastTime = time; |
| |
| synchronized (mInputLock) { |
| |
| |
| if (mKeyInputReceived) { |
| BaseObject.sSystemRegistry.inputSystem.keyUp( |
| mKeyLeftUp, |
| mKeyRightUp, |
| mKeyUpUp, |
| mKeyDownUp, |
| mKeyTouchUp, |
| mKeyClickUp); |
| BaseObject.sSystemRegistry.inputSystem.keyDown( |
| mKeyLeft, |
| mKeyRight, |
| mKeyUp, |
| mKeyDown, |
| mKeyTouch, |
| mKeyClick); |
| mKeyInputReceived = false; |
| mKeyLeft = false; |
| mKeyRight = false; |
| mKeyUp = false; |
| mKeyDown = false; |
| mKeyTouch = false; |
| mKeyClick = false; |
| mKeyLeftUp = false; |
| mKeyRightUp = false; |
| mKeyUpUp = false; |
| mKeyDownUp = false; |
| mKeyTouchUp = false; |
| mKeyClickUp = false; |
| } |
| |
| if (mLastMotionX != 0 || mLastMotionY != 0) { |
| BaseObject.sSystemRegistry.inputSystem |
| .roll(mLastMotionX, mLastMotionY); |
| mLastMotionX = 0; |
| mLastMotionY = 0; |
| } |
| if (mLastTouchX != 0 || mLastTouchY != 0) { |
| BaseObject.sSystemRegistry.inputSystem |
| .touch(mLastTouchX, mLastTouchY, mTouchReleased); |
| mLastTouchX = 0; |
| mLastTouchY = 0; |
| mTouchReleased = false; |
| } |
| |
| if (mClickDown) { |
| BaseObject.sSystemRegistry.inputSystem.clickDown(); |
| mClickDown = false; |
| } |
| if (mClickUp) { |
| BaseObject.sSystemRegistry.inputSystem.clickUp(); |
| mClickUp = false; |
| } |
| |
| if (mOrientationChanged) { |
| BaseObject.sSystemRegistry.inputSystem.setOrientation( |
| mOrientationX, |
| mOrientationY, |
| mOrientationZ); |
| mOrientationChanged = false; |
| } |
| |
| |
| } |
| |
| mGameRoot.update(secondsDelta, null); |
| |
| CameraSystem camera = mGameRoot.sSystemRegistry.cameraSystem; |
| float x = 0.0f; |
| float y = 0.0f; |
| if (camera != null) { |
| x = camera.getFocusPositionX(); |
| y = camera.getFocusPositionY(); |
| } |
| BaseObject.sSystemRegistry.renderSystem.swap(mRenderer, x, y); |
| |
| final long endTime = SystemClock.uptimeMillis(); |
| |
| finalDelta = endTime - time; |
| |
| mProfileTime += finalDelta; |
| mProfileFrames++; |
| if (mProfileTime > PROFILE_REPORT_DELAY * 1000) { |
| final long averageFrameTime = mProfileTime / mProfileFrames; |
| DebugLog.d("Game Profile", "Average: " + averageFrameTime); |
| mProfileTime = 0; |
| mProfileFrames = 0; |
| } |
| } |
| // If the game logic completed in less than 16ms, that means it's running |
| // faster than 60fps, which is our target frame rate. In that case we should |
| // yield to the rendering thread, at least for the remaining frame. |
| |
| if (finalDelta < 16) { |
| try { |
| Thread.sleep(16 - finalDelta); |
| } catch (InterruptedException e) { |
| // Interruptions here are no big deal. |
| } |
| } |
| |
| synchronized(mPauseLock) { |
| if (mPaused) { |
| SoundSystem sound = BaseObject.sSystemRegistry.soundSystem; |
| if (sound != null) { |
| sound.pauseAll(); |
| BaseObject.sSystemRegistry.inputSystem.releaseAllKeys(); |
| } |
| while (mPaused) { |
| try { |
| mPauseLock.wait(); |
| } catch (InterruptedException e) { |
| // No big deal if this wait is interrupted. |
| } |
| } |
| } |
| } |
| } |
| } |
| // Make sure our dependence on the render system is cleaned up. |
| BaseObject.sSystemRegistry.renderSystem.emptyQueues(mRenderer); |
| } |
| |
| public void stopGame() { |
| synchronized (mPauseLock) { |
| mPaused = false; |
| mFinished = true; |
| mPauseLock.notifyAll(); |
| } |
| } |
| |
| public void pauseGame() { |
| synchronized (mPauseLock) { |
| mPaused = true; |
| } |
| } |
| |
| public void resumeGame() { |
| synchronized (mPauseLock) { |
| mPaused = false; |
| mPauseLock.notifyAll(); |
| } |
| } |
| |
| public boolean getPaused() { |
| return mPaused; |
| } |
| |
| public void setGameRoot(ObjectManager gameRoot) { |
| mGameRoot = gameRoot; |
| } |
| |
| public void rollEvent(float f, float g) { |
| synchronized (mInputLock) { |
| mLastMotionX += f; |
| mLastMotionY += g; |
| } |
| } |
| |
| public void clickEvent(boolean down) { |
| synchronized (mInputLock) { |
| if (down) { |
| mClickDown = true; |
| } else { |
| mClickUp = true; |
| } |
| } |
| } |
| |
| public void orientationEvent(float x, float y, float z) { |
| synchronized (mInputLock) { |
| mOrientationX = x; |
| mOrientationY = y; |
| mOrientationZ = z; |
| mOrientationChanged = true; |
| } |
| } |
| |
| public void touchDownEvent(float x, float y) { |
| synchronized (mInputLock) { |
| mTouchReleased = false; |
| mLastTouchX = (int)x; |
| mLastTouchY = (int)y; |
| } |
| } |
| |
| public void touchUpEvent(float x, float y) { |
| synchronized (mInputLock) { |
| mTouchReleased = true; |
| mLastTouchX = (int)x; |
| mLastTouchY = (int)y; |
| } |
| } |
| |
| public boolean keydownEvent(int keycode) { |
| boolean ateKey = true; |
| synchronized (mInputLock) { |
| if (keycode == mLeftKey) { |
| mKeyLeft = true; |
| } else if (keycode == mRightKey) { |
| mKeyRight = true; |
| } else if (keycode == mJumpKey) { |
| mKeyTouch = true; |
| } else if (keycode == mAttackKey) { |
| mKeyClick = true; |
| } else if (mClickActive && keycode == KeyEvent.KEYCODE_DPAD_CENTER) { |
| mKeyClick = true; |
| } else { |
| ateKey = false; |
| } |
| |
| mKeyInputReceived = ateKey; |
| } |
| return ateKey; |
| } |
| |
| public boolean keyupEvent(int keycode) { |
| boolean ateKey = true; |
| synchronized (mInputLock) { |
| if (keycode == mLeftKey) { |
| mKeyLeftUp = true; |
| } else if (keycode == mRightKey) { |
| mKeyRightUp = true; |
| } else if (keycode == mJumpKey) { |
| mKeyTouchUp = true; |
| } else if (keycode == mAttackKey) { |
| mKeyClickUp = true; |
| } else if (mClickActive && keycode == KeyEvent.KEYCODE_DPAD_CENTER) { |
| mKeyClickUp = true; |
| } else { |
| ateKey = false; |
| } |
| mKeyInputReceived = ateKey; |
| } |
| return ateKey; |
| } |
| |
| public void setKeyConfig(int leftKey, int rightKey, int jumpKey, |
| int attackKey) { |
| synchronized (mInputLock) { |
| mLeftKey = leftKey; |
| mRightKey = rightKey; |
| mJumpKey = jumpKey; |
| mAttackKey = attackKey; |
| } |
| } |
| |
| public void setClickActive(boolean clickAttack) { |
| mClickActive = clickAttack; |
| } |
| |
| } |