Home | History | Annotate | Download | only in replicaisland
      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 
     61 	public InputGameInterface() {
     62 		super();
     63 		reset();
     64 	}
     65 
     66 	@Override
     67 	public void reset() {
     68 		mJumpButton.release();
     69 		mAttackButton.release();
     70 		mDirectionalPad.release();
     71 		mTilt.release();
     72 	}
     73 
     74 
     75 
     76 	@Override
     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 
    259 	private float filterOrientationForMovement(float magnitude) {
    260 		float scaledMagnitude = magnitude * mOrientationSensitivityFactor;
    261 
    262 		return deadZoneFilter(scaledMagnitude, mOrientationDeadZoneMin, mOrientationDeadZoneMax, mOrientationDeadZoneScale);
    263 	}
    264 
    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 
    277 	public final InputXY getDirectionalPad() {
    278 		return mDirectionalPad;
    279 	}
    280 
    281 	public final InputXY getTilt() {
    282 		return mTilt;
    283 	}
    284 
    285 	public final InputButton getJumpButton() {
    286 		return mJumpButton;
    287 	}
    288 
    289 	public final InputButton getAttackButton() {
    290 		return mAttackButton;
    291 	}
    292 
    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 
    300 	public void setUseClickForAttack(boolean click) {
    301 		mUseClickButtonForAttack = click;
    302 	}
    303 
    304 	public void setUseOrientationForMovement(boolean orientation) {
    305 		mUseOrientationForMovement = orientation;
    306 	}
    307 
    308 	public void setOrientationMovementSensitivity(float sensitivity) {
    309 		mOrientationSensitivity = sensitivity;
    310 		mOrientationSensitivityFactor = 2.9f * sensitivity + 0.1f;
    311 	}
    312 
    313 	public void setMovementSensitivity(float sensitivity) {
    314 		mMovementSensitivity  = sensitivity;
    315 	}
    316 
    317 	public void setUseOnScreenControls(boolean onscreen) {
    318 		mUseOnScreenControls = onscreen;
    319 	}
    320 
    321 }
    322