committed by
bunnei
2 changed files with 205 additions and 243 deletions
-
243src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.java
-
205src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
@ -1,243 +0,0 @@ |
|||||
/** |
|
||||
* Copyright 2013 Dolphin Emulator Project |
|
||||
* Licensed under GPLv2+ |
|
||||
* Refer to the license.txt file included. |
|
||||
*/ |
|
||||
|
|
||||
package org.yuzu.yuzu_emu.overlay; |
|
||||
|
|
||||
import android.content.res.Resources; |
|
||||
import android.graphics.Bitmap; |
|
||||
import android.graphics.Canvas; |
|
||||
import android.graphics.Rect; |
|
||||
import android.graphics.drawable.BitmapDrawable; |
|
||||
import android.view.MotionEvent; |
|
||||
|
|
||||
import org.yuzu.yuzu_emu.NativeLibrary; |
|
||||
import org.yuzu.yuzu_emu.NativeLibrary.ButtonType; |
|
||||
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings; |
|
||||
|
|
||||
/** |
|
||||
* Custom {@link BitmapDrawable} that is capable |
|
||||
* of storing it's own ID. |
|
||||
*/ |
|
||||
public final class InputOverlayDrawableJoystick { |
|
||||
// The ID value what type of joystick this Drawable represents. |
|
||||
private int mJoystickId; |
|
||||
// The ID value what type of button this Drawable represents. |
|
||||
private int mButtonId; |
|
||||
// The ID value what motion event is tracking |
|
||||
private int mTrackId = -1; |
|
||||
private float mXAxis; |
|
||||
private float mYAxis; |
|
||||
private int mControlPositionX, mControlPositionY; |
|
||||
private int mWidth; |
|
||||
private int mHeight; |
|
||||
private Rect mVirtBounds; |
|
||||
private Rect mOrigBounds; |
|
||||
private BitmapDrawable mOuterBitmap; |
|
||||
private BitmapDrawable mDefaultStateInnerBitmap; |
|
||||
private BitmapDrawable mPressedStateInnerBitmap; |
|
||||
private BitmapDrawable mBoundsBoxBitmap; |
|
||||
private boolean mPressedState = false; |
|
||||
|
|
||||
/** |
|
||||
* Constructor |
|
||||
* |
|
||||
* @param res {@link Resources} instance. |
|
||||
* @param bitmapOuter {@link Bitmap} which represents the outer non-movable part of the joystick. |
|
||||
* @param bitmapInnerDefault {@link Bitmap} which represents the default inner movable part of the joystick. |
|
||||
* @param bitmapInnerPressed {@link Bitmap} which represents the pressed inner movable part of the joystick. |
|
||||
* @param rectOuter {@link Rect} which represents the outer joystick bounds. |
|
||||
* @param rectInner {@link Rect} which represents the inner joystick bounds. |
|
||||
* @param joystick Identifier for which joystick this is. |
|
||||
*/ |
|
||||
public InputOverlayDrawableJoystick(Resources res, Bitmap bitmapOuter, |
|
||||
Bitmap bitmapInnerDefault, Bitmap bitmapInnerPressed, |
|
||||
Rect rectOuter, Rect rectInner, int joystick, int button) { |
|
||||
mJoystickId = joystick; |
|
||||
mButtonId = button; |
|
||||
|
|
||||
mOuterBitmap = new BitmapDrawable(res, bitmapOuter); |
|
||||
mDefaultStateInnerBitmap = new BitmapDrawable(res, bitmapInnerDefault); |
|
||||
mPressedStateInnerBitmap = new BitmapDrawable(res, bitmapInnerPressed); |
|
||||
mBoundsBoxBitmap = new BitmapDrawable(res, bitmapOuter); |
|
||||
mWidth = bitmapOuter.getWidth(); |
|
||||
mHeight = bitmapOuter.getHeight(); |
|
||||
|
|
||||
setBounds(rectOuter); |
|
||||
mDefaultStateInnerBitmap.setBounds(rectInner); |
|
||||
mPressedStateInnerBitmap.setBounds(rectInner); |
|
||||
mVirtBounds = getBounds(); |
|
||||
mOrigBounds = mOuterBitmap.copyBounds(); |
|
||||
mBoundsBoxBitmap.setAlpha(0); |
|
||||
mBoundsBoxBitmap.setBounds(getVirtBounds()); |
|
||||
SetInnerBounds(); |
|
||||
} |
|
||||
|
|
||||
public void draw(Canvas canvas) { |
|
||||
mOuterBitmap.draw(canvas); |
|
||||
getCurrentStateBitmapDrawable().draw(canvas); |
|
||||
mBoundsBoxBitmap.draw(canvas); |
|
||||
} |
|
||||
|
|
||||
public boolean updateStatus(MotionEvent event) { |
|
||||
int pointerIndex = event.getActionIndex(); |
|
||||
int xPosition = (int) event.getX(pointerIndex); |
|
||||
int yPosition = (int) event.getY(pointerIndex); |
|
||||
int pointerId = event.getPointerId(pointerIndex); |
|
||||
int motion_event = event.getAction() & MotionEvent.ACTION_MASK; |
|
||||
boolean isActionDown = motion_event == MotionEvent.ACTION_DOWN || motion_event == MotionEvent.ACTION_POINTER_DOWN; |
|
||||
boolean isActionUp = motion_event == MotionEvent.ACTION_UP || motion_event == MotionEvent.ACTION_POINTER_UP; |
|
||||
|
|
||||
if (isActionDown) { |
|
||||
if (!getBounds().contains(xPosition, yPosition)) { |
|
||||
return false; |
|
||||
} |
|
||||
mPressedState = true; |
|
||||
mOuterBitmap.setAlpha(0); |
|
||||
mBoundsBoxBitmap.setAlpha(255); |
|
||||
if (EmulationMenuSettings.getJoystickRelCenter()) { |
|
||||
getVirtBounds().offset(xPosition - getVirtBounds().centerX(), |
|
||||
yPosition - getVirtBounds().centerY()); |
|
||||
} |
|
||||
mBoundsBoxBitmap.setBounds(getVirtBounds()); |
|
||||
mTrackId = pointerId; |
|
||||
} |
|
||||
|
|
||||
if (isActionUp) { |
|
||||
if (mTrackId != pointerId) { |
|
||||
return false; |
|
||||
} |
|
||||
mPressedState = false; |
|
||||
mXAxis = 0.0f; |
|
||||
mYAxis = 0.0f; |
|
||||
mOuterBitmap.setAlpha(255); |
|
||||
mBoundsBoxBitmap.setAlpha(0); |
|
||||
setVirtBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right, |
|
||||
mOrigBounds.bottom)); |
|
||||
setBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right, |
|
||||
mOrigBounds.bottom)); |
|
||||
SetInnerBounds(); |
|
||||
mTrackId = -1; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
if (mTrackId == -1) |
|
||||
return false; |
|
||||
|
|
||||
for (int i = 0; i < event.getPointerCount(); i++) { |
|
||||
if (mTrackId != event.getPointerId(i)) { |
|
||||
continue; |
|
||||
} |
|
||||
float touchX = event.getX(i); |
|
||||
float touchY = event.getY(i); |
|
||||
float maxY = getVirtBounds().bottom; |
|
||||
float maxX = getVirtBounds().right; |
|
||||
touchX -= getVirtBounds().centerX(); |
|
||||
maxX -= getVirtBounds().centerX(); |
|
||||
touchY -= getVirtBounds().centerY(); |
|
||||
maxY -= getVirtBounds().centerY(); |
|
||||
final float AxisX = touchX / maxX; |
|
||||
final float AxisY = touchY / maxY; |
|
||||
final float oldXAxis = mXAxis; |
|
||||
final float oldYAxis = mYAxis; |
|
||||
|
|
||||
// Clamp the circle pad input to a circle |
|
||||
final float angle = (float) Math.atan2(AxisY, AxisX); |
|
||||
float radius = (float) Math.sqrt(AxisX * AxisX + AxisY * AxisY); |
|
||||
if (radius > 1.0f) { |
|
||||
radius = 1.0f; |
|
||||
} |
|
||||
mXAxis = ((float) Math.cos(angle) * radius); |
|
||||
mYAxis = ((float) Math.sin(angle) * radius); |
|
||||
SetInnerBounds(); |
|
||||
return oldXAxis != mXAxis && oldYAxis != mYAxis; |
|
||||
} |
|
||||
|
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
private void SetInnerBounds() { |
|
||||
int X = getVirtBounds().centerX() + (int) ((mXAxis) * (getVirtBounds().width() / 2)); |
|
||||
int Y = getVirtBounds().centerY() + (int) ((mYAxis) * (getVirtBounds().height() / 2)); |
|
||||
|
|
||||
if (X > getVirtBounds().centerX() + (getVirtBounds().width() / 2)) |
|
||||
X = getVirtBounds().centerX() + (getVirtBounds().width() / 2); |
|
||||
if (X < getVirtBounds().centerX() - (getVirtBounds().width() / 2)) |
|
||||
X = getVirtBounds().centerX() - (getVirtBounds().width() / 2); |
|
||||
if (Y > getVirtBounds().centerY() + (getVirtBounds().height() / 2)) |
|
||||
Y = getVirtBounds().centerY() + (getVirtBounds().height() / 2); |
|
||||
if (Y < getVirtBounds().centerY() - (getVirtBounds().height() / 2)) |
|
||||
Y = getVirtBounds().centerY() - (getVirtBounds().height() / 2); |
|
||||
|
|
||||
int width = mPressedStateInnerBitmap.getBounds().width() / 2; |
|
||||
int height = mPressedStateInnerBitmap.getBounds().height() / 2; |
|
||||
mDefaultStateInnerBitmap.setBounds(X - width, Y - height, X + width, Y + height); |
|
||||
mPressedStateInnerBitmap.setBounds(mDefaultStateInnerBitmap.getBounds()); |
|
||||
} |
|
||||
|
|
||||
public void setPosition(int x, int y) { |
|
||||
mControlPositionX = x; |
|
||||
mControlPositionY = y; |
|
||||
} |
|
||||
|
|
||||
private BitmapDrawable getCurrentStateBitmapDrawable() { |
|
||||
return mPressedState ? mPressedStateInnerBitmap : mDefaultStateInnerBitmap; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Gets this InputOverlayDrawableJoystick's button ID. |
|
||||
* |
|
||||
* @return this InputOverlayDrawableJoystick's button ID. |
|
||||
*/ |
|
||||
public int getJoystickId() { |
|
||||
return mJoystickId; |
|
||||
} |
|
||||
|
|
||||
public float getXAxis() { |
|
||||
return mXAxis; |
|
||||
} |
|
||||
|
|
||||
public float getYAxis() { |
|
||||
// Nintendo joysticks have y axis inverted |
|
||||
return -mYAxis; |
|
||||
} |
|
||||
|
|
||||
public int getButtonId() { |
|
||||
return mButtonId; |
|
||||
} |
|
||||
|
|
||||
public int getTrackId() { |
|
||||
return mTrackId; |
|
||||
} |
|
||||
|
|
||||
public int getButtonStatus() { |
|
||||
// TODO: Add button support |
|
||||
return NativeLibrary.ButtonState.RELEASED; |
|
||||
} |
|
||||
|
|
||||
public Rect getBounds() { |
|
||||
return mOuterBitmap.getBounds(); |
|
||||
} |
|
||||
|
|
||||
public void setBounds(Rect bounds) { |
|
||||
mOuterBitmap.setBounds(bounds); |
|
||||
} |
|
||||
|
|
||||
private Rect getVirtBounds() { |
|
||||
return mVirtBounds; |
|
||||
} |
|
||||
|
|
||||
private void setVirtBounds(Rect bounds) { |
|
||||
mVirtBounds = bounds; |
|
||||
} |
|
||||
|
|
||||
public int getWidth() { |
|
||||
return mWidth; |
|
||||
} |
|
||||
|
|
||||
public int getHeight() { |
|
||||
return mHeight; |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,205 @@ |
|||||
|
package org.yuzu.yuzu_emu.overlay |
||||
|
|
||||
|
import android.content.res.Resources |
||||
|
import android.graphics.Bitmap |
||||
|
import android.graphics.Canvas |
||||
|
import android.graphics.Rect |
||||
|
import android.graphics.drawable.BitmapDrawable |
||||
|
import android.view.MotionEvent |
||||
|
import org.yuzu.yuzu_emu.NativeLibrary |
||||
|
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings |
||||
|
import kotlin.math.atan2 |
||||
|
import kotlin.math.cos |
||||
|
import kotlin.math.sin |
||||
|
import kotlin.math.sqrt |
||||
|
|
||||
|
/** |
||||
|
* Custom [BitmapDrawable] that is capable |
||||
|
* of storing it's own ID. |
||||
|
* |
||||
|
* @param res [Resources] instance. |
||||
|
* @param bitmapOuter [Bitmap] which represents the outer non-movable part of the joystick. |
||||
|
* @param bitmapInnerDefault [Bitmap] which represents the default inner movable part of the joystick. |
||||
|
* @param bitmapInnerPressed [Bitmap] which represents the pressed inner movable part of the joystick. |
||||
|
* @param rectOuter [Rect] which represents the outer joystick bounds. |
||||
|
* @param rectInner [Rect] which represents the inner joystick bounds. |
||||
|
* @param joystickId The ID value what type of joystick this Drawable represents. |
||||
|
* @param buttonId The ID value what type of button this Drawable represents. |
||||
|
*/ |
||||
|
class InputOverlayDrawableJoystick( |
||||
|
res: Resources, |
||||
|
bitmapOuter: Bitmap, |
||||
|
bitmapInnerDefault: Bitmap, |
||||
|
bitmapInnerPressed: Bitmap, |
||||
|
rectOuter: Rect, |
||||
|
rectInner: Rect, |
||||
|
val joystickId: Int, |
||||
|
val buttonId: Int |
||||
|
) { |
||||
|
|
||||
|
// The ID value what motion event is tracking |
||||
|
var trackId = -1 |
||||
|
var xAxis = 0f |
||||
|
private var yAxis = 0f |
||||
|
private var controlPositionX = 0 |
||||
|
private var controlPositionY = 0 |
||||
|
val width: Int |
||||
|
val height: Int |
||||
|
private var virtBounds: Rect |
||||
|
private val origBounds: Rect |
||||
|
private val outerBitmap: BitmapDrawable |
||||
|
private val defaultStateInnerBitmap: BitmapDrawable |
||||
|
private val pressedStateInnerBitmap: BitmapDrawable |
||||
|
private val boundsBoxBitmap: BitmapDrawable |
||||
|
private var pressedState = false |
||||
|
|
||||
|
// TODO: Add button support |
||||
|
val buttonStatus: Int |
||||
|
get() = |
||||
|
NativeLibrary.ButtonState.RELEASED |
||||
|
var bounds: Rect? |
||||
|
get() = outerBitmap.bounds |
||||
|
set(bounds) { |
||||
|
outerBitmap.bounds = bounds!! |
||||
|
} |
||||
|
|
||||
|
// Nintendo joysticks have y axis inverted |
||||
|
val realYAxis: Float |
||||
|
get() = -yAxis |
||||
|
|
||||
|
private val currentStateBitmapDrawable: BitmapDrawable |
||||
|
get() = if (pressedState) pressedStateInnerBitmap else defaultStateInnerBitmap |
||||
|
|
||||
|
init { |
||||
|
outerBitmap = BitmapDrawable(res, bitmapOuter) |
||||
|
defaultStateInnerBitmap = BitmapDrawable(res, bitmapInnerDefault) |
||||
|
pressedStateInnerBitmap = BitmapDrawable(res, bitmapInnerPressed) |
||||
|
boundsBoxBitmap = BitmapDrawable(res, bitmapOuter) |
||||
|
width = bitmapOuter.width |
||||
|
height = bitmapOuter.height |
||||
|
bounds = rectOuter |
||||
|
defaultStateInnerBitmap.bounds = rectInner |
||||
|
pressedStateInnerBitmap.bounds = rectInner |
||||
|
virtBounds = bounds!! |
||||
|
origBounds = outerBitmap.copyBounds() |
||||
|
boundsBoxBitmap.alpha = 0 |
||||
|
boundsBoxBitmap.bounds = virtBounds |
||||
|
setInnerBounds() |
||||
|
} |
||||
|
|
||||
|
fun draw(canvas: Canvas?) { |
||||
|
outerBitmap.draw(canvas!!) |
||||
|
currentStateBitmapDrawable.draw(canvas) |
||||
|
boundsBoxBitmap.draw(canvas) |
||||
|
} |
||||
|
|
||||
|
fun updateStatus(event: MotionEvent): Boolean { |
||||
|
val pointerIndex = event.actionIndex |
||||
|
val xPosition = event.getX(pointerIndex).toInt() |
||||
|
val yPosition = event.getY(pointerIndex).toInt() |
||||
|
val pointerId = event.getPointerId(pointerIndex) |
||||
|
val motionEvent = event.action and MotionEvent.ACTION_MASK |
||||
|
val isActionDown = |
||||
|
motionEvent == MotionEvent.ACTION_DOWN || motionEvent == MotionEvent.ACTION_POINTER_DOWN |
||||
|
val isActionUp = |
||||
|
motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP |
||||
|
if (isActionDown) { |
||||
|
if (!bounds!!.contains(xPosition, yPosition)) { |
||||
|
return false |
||||
|
} |
||||
|
pressedState = true |
||||
|
outerBitmap.alpha = 0 |
||||
|
boundsBoxBitmap.alpha = 255 |
||||
|
if (EmulationMenuSettings.joystickRelCenter) { |
||||
|
virtBounds.offset( |
||||
|
xPosition - virtBounds.centerX(), |
||||
|
yPosition - virtBounds.centerY() |
||||
|
) |
||||
|
} |
||||
|
boundsBoxBitmap.bounds = virtBounds |
||||
|
trackId = pointerId |
||||
|
} |
||||
|
if (isActionUp) { |
||||
|
if (trackId != pointerId) { |
||||
|
return false |
||||
|
} |
||||
|
pressedState = false |
||||
|
xAxis = 0.0f |
||||
|
yAxis = 0.0f |
||||
|
outerBitmap.alpha = 255 |
||||
|
boundsBoxBitmap.alpha = 0 |
||||
|
virtBounds = Rect( |
||||
|
origBounds.left, |
||||
|
origBounds.top, |
||||
|
origBounds.right, |
||||
|
origBounds.bottom |
||||
|
) |
||||
|
bounds = Rect( |
||||
|
origBounds.left, |
||||
|
origBounds.top, |
||||
|
origBounds.right, |
||||
|
origBounds.bottom |
||||
|
) |
||||
|
setInnerBounds() |
||||
|
trackId = -1 |
||||
|
return true |
||||
|
} |
||||
|
if (trackId == -1) return false |
||||
|
for (i in 0 until event.pointerCount) { |
||||
|
if (trackId != event.getPointerId(i)) { |
||||
|
continue |
||||
|
} |
||||
|
var touchX = event.getX(i) |
||||
|
var touchY = event.getY(i) |
||||
|
var maxY = virtBounds.bottom.toFloat() |
||||
|
var maxX = virtBounds.right.toFloat() |
||||
|
touchX -= virtBounds.centerX().toFloat() |
||||
|
maxX -= virtBounds.centerX().toFloat() |
||||
|
touchY -= virtBounds.centerY().toFloat() |
||||
|
maxY -= virtBounds.centerY().toFloat() |
||||
|
val axisX = touchX / maxX |
||||
|
val axisY = touchY / maxY |
||||
|
val oldXAxis = xAxis |
||||
|
val oldYAxis = yAxis |
||||
|
|
||||
|
// Clamp the circle pad input to a circle |
||||
|
val angle = atan2(axisY.toDouble(), axisX.toDouble()).toFloat() |
||||
|
var radius = sqrt((axisX * axisX + axisY * axisY).toDouble()).toFloat() |
||||
|
if (radius > 1.0f) { |
||||
|
radius = 1.0f |
||||
|
} |
||||
|
xAxis = cos(angle.toDouble()).toFloat() * radius |
||||
|
yAxis = sin(angle.toDouble()).toFloat() * radius |
||||
|
setInnerBounds() |
||||
|
return oldXAxis != xAxis && oldYAxis != yAxis |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
private fun setInnerBounds() { |
||||
|
var x = virtBounds.centerX() + (xAxis * (virtBounds.width() / 2)).toInt() |
||||
|
var y = virtBounds.centerY() + (yAxis * (virtBounds.height() / 2)).toInt() |
||||
|
if (x > virtBounds.centerX() + virtBounds.width() / 2) x = |
||||
|
virtBounds.centerX() + virtBounds.width() / 2 |
||||
|
if (x < virtBounds.centerX() - virtBounds.width() / 2) x = |
||||
|
virtBounds.centerX() - virtBounds.width() / 2 |
||||
|
if (y > virtBounds.centerY() + virtBounds.height() / 2) y = |
||||
|
virtBounds.centerY() + virtBounds.height() / 2 |
||||
|
if (y < virtBounds.centerY() - virtBounds.height() / 2) y = |
||||
|
virtBounds.centerY() - virtBounds.height() / 2 |
||||
|
val width = pressedStateInnerBitmap.bounds.width() / 2 |
||||
|
val height = pressedStateInnerBitmap.bounds.height() / 2 |
||||
|
defaultStateInnerBitmap.setBounds( |
||||
|
x - width, |
||||
|
y - height, |
||||
|
x + width, |
||||
|
y + height |
||||
|
) |
||||
|
pressedStateInnerBitmap.bounds = defaultStateInnerBitmap.bounds |
||||
|
} |
||||
|
|
||||
|
fun setPosition(x: Int, y: Int) { |
||||
|
controlPositionX = x |
||||
|
controlPositionY = y |
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue