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.view.KeyEvent; 20 21 public class InputGameInterface extends BaseObject { 22 private static final float ORIENTATION_DEAD_ZONE_MIN = 0.03f; 23 private static final float ORIENTATION_DEAD_ZONE_MAX = 0.1f; 24 private static final float ORIENTATION_DEAD_ZONE_SCALE = 0.75f; 25 26 private final static float ROLL_TIMEOUT = 0.1f; 27 private final static float ROLL_RESET_DELAY = 0.075f; 28 29 // Raw trackball input is filtered by this value. Increasing it will 30 // make the control more twitchy, while decreasing it will make the control more precise. 31 private final static float ROLL_FILTER = 0.4f; 32 private final static float ROLL_DECAY = 8.0f; 33 34 private final static float KEY_FILTER = 0.25f; 35 private final static float SLIDER_FILTER = 0.25f; 36 37 38 private InputButton mJumpButton = new InputButton(); 39 private InputButton mAttackButton = new InputButton(); 40 private InputXY mDirectionalPad = new InputXY(); 41 private InputXY mTilt = new InputXY(); 42 43 private int mLeftKeyCode = KeyEvent.KEYCODE_DPAD_LEFT; 44 private int mRightKeyCode = KeyEvent.KEYCODE_DPAD_RIGHT; 45 private int mJumpKeyCode = KeyEvent.KEYCODE_SPACE; 46 private int mAttackKeyCode = KeyEvent.KEYCODE_SHIFT_LEFT; 47 48 private float mOrientationDeadZoneMin = ORIENTATION_DEAD_ZONE_MIN; 49 private float mOrientationDeadZoneMax = ORIENTATION_DEAD_ZONE_MAX; 50 private float mOrientationDeadZoneScale = ORIENTATION_DEAD_ZONE_SCALE; 51 private float mOrientationSensitivity = 1.0f; 52 private float mOrientationSensitivityFactor = 1.0f; 53 private float mMovementSensitivity = 1.0f; 54 55 private boolean mUseClickButtonForAttack = true; 56 private boolean mUseOrientationForMovement = false; 57 private boolean mUseOnScreenControls = false; 58 59 private float mLastRollTime; 60 InputGameInterface()61 public InputGameInterface() { 62 super(); 63 reset(); 64 } 65 66 @Override reset()67 public void reset() { 68 mJumpButton.release(); 69 mAttackButton.release(); 70 mDirectionalPad.release(); 71 mTilt.release(); 72 } 73 74 75 76 @Override update(float timeDelta, BaseObject parent)77 public void update(float timeDelta, BaseObject parent) { 78 InputSystem input = sSystemRegistry.inputSystem; 79 final InputButton[] keys = input.getKeyboard().getKeys(); 80 final InputXY orientation = input.getOrientationSensor(); 81 82 // tilt is easy 83 mTilt.clone(orientation); 84 85 final InputTouchScreen touch = input.getTouchScreen(); 86 final float gameTime = sSystemRegistry.timeSystem.getGameTime(); 87 88 float sliderOffset = 0; 89 90 // update movement inputs 91 if (mUseOnScreenControls) { 92 final InputXY sliderTouch = touch.findPointerInRegion( 93 ButtonConstants.MOVEMENT_SLIDER_REGION_X, 94 ButtonConstants.MOVEMENT_SLIDER_REGION_Y, 95 ButtonConstants.MOVEMENT_SLIDER_REGION_WIDTH, 96 ButtonConstants.MOVEMENT_SLIDER_REGION_HEIGHT); 97 98 if (sliderTouch != null) { 99 final float halfWidth = ButtonConstants.MOVEMENT_SLIDER_BAR_WIDTH / 2.0f; 100 final float center = ButtonConstants.MOVEMENT_SLIDER_X + halfWidth; 101 final float offset = sliderTouch.getX() - center; 102 float magnitudeRamp = Math.abs(offset) > halfWidth ? 1.0f : (Math.abs(offset) / halfWidth); 103 104 final float magnitude = magnitudeRamp * Utils.sign(offset) * SLIDER_FILTER * mMovementSensitivity; 105 sliderOffset = magnitudeRamp * Utils.sign(offset); 106 mDirectionalPad.press(gameTime, magnitude, 0.0f); 107 } else { 108 mDirectionalPad.release(); 109 } 110 } else if (mUseOrientationForMovement) { 111 mDirectionalPad.clone(orientation); 112 mDirectionalPad.setMagnitude( 113 filterOrientationForMovement(orientation.getX()), 114 filterOrientationForMovement(orientation.getY())); 115 } else { 116 // keys or trackball 117 final InputXY trackball = input.getTrackball(); 118 final InputButton left = keys[mLeftKeyCode]; 119 final InputButton right = keys[mRightKeyCode]; 120 final float leftPressedTime = left.getLastPressedTime(); 121 final float rightPressedTime = right.getLastPressedTime(); 122 123 124 if (trackball.getLastPressedTime() > Math.max(leftPressedTime, rightPressedTime)) { 125 // The trackball never goes "up", so force it to turn off if it wasn't triggered in the last frame. 126 // What follows is a bunch of code to filter trackball events into something like a dpad event. 127 // The goals here are: 128 // - For roll events that occur in quick succession to accumulate. 129 // - For roll events that occur with more time between them, lessen the impact of older events 130 // - In the absence of roll events, fade the roll out over time. 131 if (gameTime - trackball.getLastPressedTime() < ROLL_TIMEOUT) { 132 float newX; 133 float newY; 134 final float delay = Math.max(ROLL_RESET_DELAY, timeDelta); 135 if (gameTime - mLastRollTime <= delay) { 136 newX = mDirectionalPad.getX() + (trackball.getX() * ROLL_FILTER * mMovementSensitivity); 137 newY = mDirectionalPad.getY() + (trackball.getY() * ROLL_FILTER * mMovementSensitivity); 138 } else { 139 float oldX = mDirectionalPad.getX() != 0.0f ? mDirectionalPad.getX() / 2.0f : 0.0f; 140 float oldY = mDirectionalPad.getX() != 0.0f ? mDirectionalPad.getX() / 2.0f : 0.0f; 141 newX = oldX + (trackball.getX() * ROLL_FILTER * mMovementSensitivity); 142 newY = oldY + (trackball.getX() * ROLL_FILTER * mMovementSensitivity); 143 } 144 145 mDirectionalPad.press(gameTime, newX, newY); 146 mLastRollTime = gameTime; 147 trackball.release(); 148 } else { 149 float x = mDirectionalPad.getX(); 150 float y = mDirectionalPad.getY(); 151 if (x != 0.0f) { 152 int sign = Utils.sign(x); 153 x = x - (sign * ROLL_DECAY * timeDelta); 154 if (Utils.sign(x) != sign) { 155 x = 0.0f; 156 } 157 } 158 159 if (y != 0.0f) { 160 int sign = Utils.sign(y); 161 y = y - (sign * ROLL_DECAY * timeDelta); 162 if (Utils.sign(x) != sign) { 163 y = 0.0f; 164 } 165 } 166 167 168 if (x == 0 && y == 0) { 169 mDirectionalPad.release(); 170 } else { 171 mDirectionalPad.setMagnitude(x, y); 172 } 173 } 174 175 } else { 176 float xMagnitude = 0.0f; 177 float yMagnitude = 0.0f; 178 float pressTime = 0.0f; 179 // left and right are mutually exclusive 180 if (leftPressedTime > rightPressedTime) { 181 xMagnitude = -left.getMagnitude() * KEY_FILTER * mMovementSensitivity; 182 pressTime = leftPressedTime; 183 } else { 184 xMagnitude = right.getMagnitude() * KEY_FILTER * mMovementSensitivity; 185 pressTime = rightPressedTime; 186 } 187 188 if (xMagnitude != 0.0f) { 189 mDirectionalPad.press(pressTime, xMagnitude, yMagnitude); 190 } else { 191 mDirectionalPad.release(); 192 } 193 } 194 } 195 196 // update other buttons 197 final InputButton jumpKey = keys[mJumpKeyCode]; 198 199 // when on-screen movement controls are on, the fly and attack buttons are flipped. 200 float flyButtonRegionX = ButtonConstants.FLY_BUTTON_REGION_X; 201 float stompButtonRegionX = ButtonConstants.STOMP_BUTTON_REGION_X; 202 203 if (mUseOnScreenControls) { 204 ContextParameters params = sSystemRegistry.contextParameters; 205 flyButtonRegionX = params.gameWidth - ButtonConstants.FLY_BUTTON_REGION_WIDTH - ButtonConstants.FLY_BUTTON_REGION_X; 206 stompButtonRegionX = params.gameWidth - ButtonConstants.STOMP_BUTTON_REGION_WIDTH - ButtonConstants.STOMP_BUTTON_REGION_X; 207 } 208 209 final InputXY jumpTouch = touch.findPointerInRegion( 210 flyButtonRegionX, 211 ButtonConstants.FLY_BUTTON_REGION_Y, 212 ButtonConstants.FLY_BUTTON_REGION_WIDTH, 213 ButtonConstants.FLY_BUTTON_REGION_HEIGHT); 214 215 if (jumpKey.getPressed()) { 216 mJumpButton.press(jumpKey.getLastPressedTime(), jumpKey.getMagnitude()); 217 } else if (jumpTouch != null) { 218 if (!mJumpButton.getPressed()) { 219 mJumpButton.press(jumpTouch.getLastPressedTime(), 1.0f); 220 } 221 } else { 222 mJumpButton.release(); 223 } 224 225 final InputButton attackKey = keys[mAttackKeyCode]; 226 final InputButton clickButton = keys[KeyEvent.KEYCODE_DPAD_CENTER]; // special case 227 228 final InputXY stompTouch = touch.findPointerInRegion( 229 stompButtonRegionX, 230 ButtonConstants.STOMP_BUTTON_REGION_Y, 231 ButtonConstants.STOMP_BUTTON_REGION_WIDTH, 232 ButtonConstants.STOMP_BUTTON_REGION_HEIGHT); 233 234 if (mUseClickButtonForAttack && clickButton.getPressed()) { 235 mAttackButton.press(clickButton.getLastPressedTime(), clickButton.getMagnitude()); 236 } else if (attackKey.getPressed()) { 237 mAttackButton.press(attackKey.getLastPressedTime(), attackKey.getMagnitude()); 238 } else if (stompTouch != null) { 239 // Since touch events come in constantly, we only want to press the attack button 240 // here if it's not already down. That makes it act like the other buttons (down once then up). 241 if (!mAttackButton.getPressed()) { 242 mAttackButton.press(stompTouch.getLastPressedTime(), 1.0f); 243 } 244 } else { 245 mAttackButton.release(); 246 } 247 248 // This doesn't seem like exactly the right place to write to the HUD, but on the other hand, 249 // putting this code elsewhere causes dependencies between exact HUD content and physics, which 250 // we sometimes wish to avoid. 251 final HudSystem hud = sSystemRegistry.hudSystem; 252 if (hud != null) { 253 hud.setButtonState(mJumpButton.getPressed(), mAttackButton.getPressed(), mDirectionalPad.getPressed()); 254 hud.setMovementSliderOffset(sliderOffset); 255 } 256 } 257 258 filterOrientationForMovement(float magnitude)259 private float filterOrientationForMovement(float magnitude) { 260 float scaledMagnitude = magnitude * mOrientationSensitivityFactor; 261 262 return deadZoneFilter(scaledMagnitude, mOrientationDeadZoneMin, mOrientationDeadZoneMax, mOrientationDeadZoneScale); 263 } 264 deadZoneFilter(float magnitude, float min, float max, float scale)265 private float deadZoneFilter(float magnitude, float min, float max, float scale) { 266 float smoothedMagnatude = magnitude; 267 if (Math.abs(magnitude) < min) { 268 smoothedMagnatude = 0.0f; // dead zone 269 } else if (Math.abs(magnitude) < max) { 270 smoothedMagnatude *= scale; 271 } 272 273 return smoothedMagnatude; 274 } 275 276 getDirectionalPad()277 public final InputXY getDirectionalPad() { 278 return mDirectionalPad; 279 } 280 getTilt()281 public final InputXY getTilt() { 282 return mTilt; 283 } 284 getJumpButton()285 public final InputButton getJumpButton() { 286 return mJumpButton; 287 } 288 getAttackButton()289 public final InputButton getAttackButton() { 290 return mAttackButton; 291 } 292 setKeys(int left, int right, int jump, int attack)293 public void setKeys(int left, int right, int jump, int attack) { 294 mLeftKeyCode = left; 295 mRightKeyCode = right; 296 mJumpKeyCode = jump; 297 mAttackKeyCode = attack; 298 } 299 setUseClickForAttack(boolean click)300 public void setUseClickForAttack(boolean click) { 301 mUseClickButtonForAttack = click; 302 } 303 setUseOrientationForMovement(boolean orientation)304 public void setUseOrientationForMovement(boolean orientation) { 305 mUseOrientationForMovement = orientation; 306 } 307 setOrientationMovementSensitivity(float sensitivity)308 public void setOrientationMovementSensitivity(float sensitivity) { 309 mOrientationSensitivity = sensitivity; 310 mOrientationSensitivityFactor = 2.9f * sensitivity + 0.1f; 311 } 312 setMovementSensitivity(float sensitivity)313 public void setMovementSensitivity(float sensitivity) { 314 mMovementSensitivity = sensitivity; 315 } 316 setUseOnScreenControls(boolean onscreen)317 public void setUseOnScreenControls(boolean onscreen) { 318 mUseOnScreenControls = onscreen; 319 } 320 321 } 322