/*
* Copyright 2014 Google Inc. All rights reserved.
*
* 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.google.fpl.gamecontroller;
import android.view.KeyEvent;
import android.view.MotionEvent;
/**
* Handles input events from game pad controllers (includes joystick and button inputs).
*/
public class GamepadController {
// The buttons on the game pad.
public static final int BUTTON_A = 0;
public static final int BUTTON_B = 1;
public static final int BUTTON_X = 2;
public static final int BUTTON_Y = 3;
public static final int BUTTON_COUNT = 4;
// The axes for joystick movement.
public static final int AXIS_X = 0;
public static final int AXIS_Y = 1;
public static final int AXIS_COUNT = 2;
// Game pads usually have 2 joysticks.
public static final int JOYSTICK_1 = 0;
public static final int JOYSTICK_2 = 1;
public static final int JOYSTICK_COUNT = 2;
// Keep track of button states for the current and previous frames.
protected static final int FRAME_INDEX_CURRENT = 0;
protected static final int FRAME_INDEX_PREVIOUS = 1;
protected static final int FRAME_INDEX_COUNT = 2;
// Positions of the two joysticks.
private final float mJoystickPositions[][];
// The button states for the current and previous frames.
private final boolean mButtonState[][];
// The device that we are tuned to.
private int mDeviceId = -1;
public GamepadController() {
mButtonState = new boolean[BUTTON_COUNT][FRAME_INDEX_COUNT];
mJoystickPositions = new float[JOYSTICK_COUNT][AXIS_COUNT];
resetState();
}
/**
* Returns the controller to its default state.
*
* The histories for all joystick movements and button presses is also reset.
*/
private void resetState() {
for (int button = 0; button < BUTTON_COUNT; ++button) {
for (int frame = 0; frame < FRAME_INDEX_COUNT; ++frame) {
mButtonState[button][frame] = false;
}
}
for (int joystick = 0; joystick < JOYSTICK_COUNT; ++joystick) {
for (int axis = 0; axis < AXIS_COUNT; ++axis) {
mJoystickPositions[joystick][axis] = 0.0f;
}
}
}
/**
* @return The id of the associated controller device, or -1 if not assigned a device.
*/
public int getDeviceId() {
return mDeviceId;
}
/**
* Sets the physical device id for this controller.
*
* @param newId The physical device id, or -1 to indicate no assigned physical device.
*/
public void setDeviceId(int newId) {
if (newId != mDeviceId) {
mDeviceId = newId;
if (newId != -1) {
// Reset our button and axis state when a new physical device is attached.
resetState();
}
}
}
/**
* @return true if this controller is assigned a physical device id.
*/
public boolean isActive() {
return mDeviceId != -1;
}
/**
* Returns the position of a joystick along a single axis.
*
* @param joystickIndex One of: JOYSTICK_1 or JOYSTICK_2.
* @param axis One of: AXIS_X or AXIS_Y.
* @return A value in the range -1 to 1, inclusive, where 0 represents the joystick's
* center position.
*/
public float getJoystickPosition(int joystickIndex, int axis) {
return mJoystickPositions[joystickIndex][axis];
}
/**
* Returns true if the given button is currently pressed.
*
* @param buttonId One of: BUTTON_A, BUTTON_B, BUTTON_X, or BUTTON_Y.
* @return true if the given button is currently pressed.
*/
public boolean isButtonDown(int buttonId) {
return mButtonState[buttonId][FRAME_INDEX_CURRENT];
}
/**
* Returns true if a button is down now, but wasn't last frame.
*
* @param buttonId One of: BUTTON_A, BUTTON_B, BUTTON_X, or BUTTON_Y.
* @return true if a button is down now, but wasn't last frame.
*/
public boolean wasButtonPressed(int buttonId) {
// Returns true if it's down now, but wasn't last frame.
return mButtonState[buttonId][FRAME_INDEX_CURRENT]
&& !mButtonState[buttonId][FRAME_INDEX_PREVIOUS];
}
/**
* Returns true if it's up now, but wasn't last frame.
*
* @param buttonId One of: BUTTON_A, BUTTON_B, BUTTON_X, or BUTTON_Y.
* @return true if it's up now, but wasn't last frame.
*/
public boolean wasButtonReleased(int buttonId) {
return !mButtonState[buttonId][FRAME_INDEX_CURRENT]
&& mButtonState[buttonId][FRAME_INDEX_PREVIOUS];
}
/**
* Tells the controller to start tracking events for the next frame.
*/
public void advanceFrame() {
// Copy the current button state to the previous frame.
// We can't just toggle between both buffers because the buttons only update
// when an event occurs (press or release), and not every frame.
for (int i = 0; i < BUTTON_COUNT; i++) {
mButtonState[i][FRAME_INDEX_PREVIOUS] = mButtonState[i][FRAME_INDEX_CURRENT];
}
}
/**
* Updates the tracked state values of this controller in response to a motion input event.
*/
public void handleMotionEvent(MotionEvent motionEvent) {
mJoystickPositions[JOYSTICK_1][AXIS_X] = motionEvent.getAxisValue(MotionEvent.AXIS_X);
mJoystickPositions[JOYSTICK_1][AXIS_Y] = motionEvent.getAxisValue(MotionEvent.AXIS_Y);
// The X and Y axes of the second joystick on a controller are mapped to the
// MotionEvent AXIS_Z and AXIS_RZ values, respectively.
mJoystickPositions[JOYSTICK_2][AXIS_X] = motionEvent.getAxisValue(MotionEvent.AXIS_Z);
mJoystickPositions[JOYSTICK_2][AXIS_Y] = motionEvent.getAxisValue(MotionEvent.AXIS_RZ);
}
/**
* Updates the tracked state values of this controller in response to a key input event.
*/
public void handleKeyEvent(KeyEvent keyEvent) {
boolean keyIsDown = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BUTTON_A) {
mButtonState[BUTTON_A][FRAME_INDEX_CURRENT] = keyIsDown;
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BUTTON_B) {
mButtonState[BUTTON_B][FRAME_INDEX_CURRENT] = keyIsDown;
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BUTTON_X) {
mButtonState[BUTTON_X][FRAME_INDEX_CURRENT] = keyIsDown;
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BUTTON_Y) {
mButtonState[BUTTON_Y][FRAME_INDEX_CURRENT] = keyIsDown;
}
}
}