/* * 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.view.KeyEvent; public class InputGameInterface extends BaseObject { private static final float ORIENTATION_DEAD_ZONE_MIN = 0.03f; private static final float ORIENTATION_DEAD_ZONE_MAX = 0.1f; private static final float ORIENTATION_DEAD_ZONE_SCALE = 0.75f; private final static float ROLL_TIMEOUT = 0.1f; private final static float ROLL_RESET_DELAY = 0.075f; // Raw trackball input is filtered by this value. Increasing it will // make the control more twitchy, while decreasing it will make the control more precise. private final static float ROLL_FILTER = 0.4f; private final static float ROLL_DECAY = 8.0f; private final static float KEY_FILTER = 0.25f; private final static float SLIDER_FILTER = 0.25f; private InputButton mJumpButton = new InputButton(); private InputButton mAttackButton = new InputButton(); private InputXY mDirectionalPad = new InputXY(); private InputXY mTilt = new InputXY(); private int mLeftKeyCode = KeyEvent.KEYCODE_DPAD_LEFT; private int mRightKeyCode = KeyEvent.KEYCODE_DPAD_RIGHT; private int mJumpKeyCode = KeyEvent.KEYCODE_SPACE; private int mAttackKeyCode = KeyEvent.KEYCODE_SHIFT_LEFT; private float mOrientationDeadZoneMin = ORIENTATION_DEAD_ZONE_MIN; private float mOrientationDeadZoneMax = ORIENTATION_DEAD_ZONE_MAX; private float mOrientationDeadZoneScale = ORIENTATION_DEAD_ZONE_SCALE; private float mOrientationSensitivity = 1.0f; private float mOrientationSensitivityFactor = 1.0f; private float mMovementSensitivity = 1.0f; private boolean mUseClickButtonForAttack = true; private boolean mUseOrientationForMovement = false; private boolean mUseOnScreenControls = false; private float mLastRollTime; public InputGameInterface() { super(); reset(); } @Override public void reset() { mJumpButton.release(); mAttackButton.release(); mDirectionalPad.release(); mTilt.release(); } @Override public void update(float timeDelta, BaseObject parent) { InputSystem input = sSystemRegistry.inputSystem; final InputButton[] keys = input.getKeyboard().getKeys(); final InputXY orientation = input.getOrientationSensor(); // tilt is easy mTilt.clone(orientation); final InputTouchScreen touch = input.getTouchScreen(); final float gameTime = sSystemRegistry.timeSystem.getGameTime(); float sliderOffset = 0; // update movement inputs if (mUseOnScreenControls) { final InputXY sliderTouch = touch.findPointerInRegion( ButtonConstants.MOVEMENT_SLIDER_REGION_X, ButtonConstants.MOVEMENT_SLIDER_REGION_Y, ButtonConstants.MOVEMENT_SLIDER_REGION_WIDTH, ButtonConstants.MOVEMENT_SLIDER_REGION_HEIGHT); if (sliderTouch != null) { final float halfWidth = ButtonConstants.MOVEMENT_SLIDER_BAR_WIDTH / 2.0f; final float center = ButtonConstants.MOVEMENT_SLIDER_X + halfWidth; final float offset = sliderTouch.getX() - center; float magnitudeRamp = Math.abs(offset) > halfWidth ? 1.0f : (Math.abs(offset) / halfWidth); final float magnitude = magnitudeRamp * Utils.sign(offset) * SLIDER_FILTER * mMovementSensitivity; sliderOffset = magnitudeRamp * Utils.sign(offset); mDirectionalPad.press(gameTime, magnitude, 0.0f); } else { mDirectionalPad.release(); } } else if (mUseOrientationForMovement) { mDirectionalPad.clone(orientation); mDirectionalPad.setMagnitude( filterOrientationForMovement(orientation.getX()), filterOrientationForMovement(orientation.getY())); } else { // keys or trackball final InputXY trackball = input.getTrackball(); final InputButton left = keys[mLeftKeyCode]; final InputButton right = keys[mRightKeyCode]; final float leftPressedTime = left.getLastPressedTime(); final float rightPressedTime = right.getLastPressedTime(); if (trackball.getLastPressedTime() > Math.max(leftPressedTime, rightPressedTime)) { // The trackball never goes "up", so force it to turn off if it wasn't triggered in the last frame. // What follows is a bunch of code to filter trackball events into something like a dpad event. // The goals here are: // - For roll events that occur in quick succession to accumulate. // - For roll events that occur with more time between them, lessen the impact of older events // - In the absence of roll events, fade the roll out over time. if (gameTime - trackball.getLastPressedTime() < ROLL_TIMEOUT) { float newX; float newY; final float delay = Math.max(ROLL_RESET_DELAY, timeDelta); if (gameTime - mLastRollTime <= delay) { newX = mDirectionalPad.getX() + (trackball.getX() * ROLL_FILTER * mMovementSensitivity); newY = mDirectionalPad.getY() + (trackball.getY() * ROLL_FILTER * mMovementSensitivity); } else { float oldX = mDirectionalPad.getX() != 0.0f ? mDirectionalPad.getX() / 2.0f : 0.0f; float oldY = mDirectionalPad.getX() != 0.0f ? mDirectionalPad.getX() / 2.0f : 0.0f; newX = oldX + (trackball.getX() * ROLL_FILTER * mMovementSensitivity); newY = oldY + (trackball.getX() * ROLL_FILTER * mMovementSensitivity); } mDirectionalPad.press(gameTime, newX, newY); mLastRollTime = gameTime; trackball.release(); } else { float x = mDirectionalPad.getX(); float y = mDirectionalPad.getY(); if (x != 0.0f) { int sign = Utils.sign(x); x = x - (sign * ROLL_DECAY * timeDelta); if (Utils.sign(x) != sign) { x = 0.0f; } } if (y != 0.0f) { int sign = Utils.sign(y); y = y - (sign * ROLL_DECAY * timeDelta); if (Utils.sign(x) != sign) { y = 0.0f; } } if (x == 0 && y == 0) { mDirectionalPad.release(); } else { mDirectionalPad.setMagnitude(x, y); } } } else { float xMagnitude = 0.0f; float yMagnitude = 0.0f; float pressTime = 0.0f; // left and right are mutually exclusive if (leftPressedTime > rightPressedTime) { xMagnitude = -left.getMagnitude() * KEY_FILTER * mMovementSensitivity; pressTime = leftPressedTime; } else { xMagnitude = right.getMagnitude() * KEY_FILTER * mMovementSensitivity; pressTime = rightPressedTime; } if (xMagnitude != 0.0f) { mDirectionalPad.press(pressTime, xMagnitude, yMagnitude); } else { mDirectionalPad.release(); } } } // update other buttons final InputButton jumpKey = keys[mJumpKeyCode]; // when on-screen movement controls are on, the fly and attack buttons are flipped. float flyButtonRegionX = ButtonConstants.FLY_BUTTON_REGION_X; float stompButtonRegionX = ButtonConstants.STOMP_BUTTON_REGION_X; if (mUseOnScreenControls) { ContextParameters params = sSystemRegistry.contextParameters; flyButtonRegionX = params.gameWidth - ButtonConstants.FLY_BUTTON_REGION_WIDTH - ButtonConstants.FLY_BUTTON_REGION_X; stompButtonRegionX = params.gameWidth - ButtonConstants.STOMP_BUTTON_REGION_WIDTH - ButtonConstants.STOMP_BUTTON_REGION_X; } final InputXY jumpTouch = touch.findPointerInRegion( flyButtonRegionX, ButtonConstants.FLY_BUTTON_REGION_Y, ButtonConstants.FLY_BUTTON_REGION_WIDTH, ButtonConstants.FLY_BUTTON_REGION_HEIGHT); if (jumpKey.getPressed()) { mJumpButton.press(jumpKey.getLastPressedTime(), jumpKey.getMagnitude()); } else if (jumpTouch != null) { if (!mJumpButton.getPressed()) { mJumpButton.press(jumpTouch.getLastPressedTime(), 1.0f); } } else { mJumpButton.release(); } final InputButton attackKey = keys[mAttackKeyCode]; final InputButton clickButton = keys[KeyEvent.KEYCODE_DPAD_CENTER]; // special case final InputXY stompTouch = touch.findPointerInRegion( stompButtonRegionX, ButtonConstants.STOMP_BUTTON_REGION_Y, ButtonConstants.STOMP_BUTTON_REGION_WIDTH, ButtonConstants.STOMP_BUTTON_REGION_HEIGHT); if (mUseClickButtonForAttack && clickButton.getPressed()) { mAttackButton.press(clickButton.getLastPressedTime(), clickButton.getMagnitude()); } else if (attackKey.getPressed()) { mAttackButton.press(attackKey.getLastPressedTime(), attackKey.getMagnitude()); } else if (stompTouch != null) { // Since touch events come in constantly, we only want to press the attack button // here if it's not already down. That makes it act like the other buttons (down once then up). if (!mAttackButton.getPressed()) { mAttackButton.press(stompTouch.getLastPressedTime(), 1.0f); } } else { mAttackButton.release(); } // This doesn't seem like exactly the right place to write to the HUD, but on the other hand, // putting this code elsewhere causes dependencies between exact HUD content and physics, which // we sometimes wish to avoid. final HudSystem hud = sSystemRegistry.hudSystem; if (hud != null) { hud.setButtonState(mJumpButton.getPressed(), mAttackButton.getPressed(), mDirectionalPad.getPressed()); hud.setMovementSliderOffset(sliderOffset); } } private float filterOrientationForMovement(float magnitude) { float scaledMagnitude = magnitude * mOrientationSensitivityFactor; return deadZoneFilter(scaledMagnitude, mOrientationDeadZoneMin, mOrientationDeadZoneMax, mOrientationDeadZoneScale); } private float deadZoneFilter(float magnitude, float min, float max, float scale) { float smoothedMagnatude = magnitude; if (Math.abs(magnitude) < min) { smoothedMagnatude = 0.0f; // dead zone } else if (Math.abs(magnitude) < max) { smoothedMagnatude *= scale; } return smoothedMagnatude; } public final InputXY getDirectionalPad() { return mDirectionalPad; } public final InputXY getTilt() { return mTilt; } public final InputButton getJumpButton() { return mJumpButton; } public final InputButton getAttackButton() { return mAttackButton; } public void setKeys(int left, int right, int jump, int attack) { mLeftKeyCode = left; mRightKeyCode = right; mJumpKeyCode = jump; mAttackKeyCode = attack; } public void setUseClickForAttack(boolean click) { mUseClickButtonForAttack = click; } public void setUseOrientationForMovement(boolean orientation) { mUseOrientationForMovement = orientation; } public void setOrientationMovementSensitivity(float sensitivity) { mOrientationSensitivity = sensitivity; mOrientationSensitivityFactor = 2.9f * sensitivity + 0.1f; } public void setMovementSensitivity(float sensitivity) { mMovementSensitivity = sensitivity; } public void setUseOnScreenControls(boolean onscreen) { mUseOnScreenControls = onscreen; } }