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