From b5a4986ab36b66aee3da66f4dda6ee58df7fc113 Mon Sep 17 00:00:00 2001 From: Waterdish Date: Fri, 6 Dec 2024 21:07:15 -0800 Subject: [PATCH] Touch Controls --- app/build.gradle | 4 +- app/jni/src/libultraship | 2 +- app/jni/src/soh/src/code/z_camera.c | 28 ++ app/src/main/AndroidManifest.xml | 2 +- .../com/dishii/soh/ControllerButtons.java | 26 ++ .../java/com/dishii/soh/MainActivity.java | 295 +++++++++++++++++- app/src/main/res/drawable/ic_button.xml | 22 ++ app/src/main/res/drawable/ic_cross.xml | 19 ++ .../res/drawable/ic_rectangular_button.xml | 38 +++ app/src/main/res/drawable/ic_show.xml | 10 + app/src/main/res/drawable/ic_stick.xml | 27 ++ .../res/drawable/ic_trigger_button_left.xml | 46 +++ .../res/drawable/ic_trigger_button_right.xml | 46 +++ .../main/res/layout/touchcontrol_overlay.xml | 213 +++++++++++++ 14 files changed, 773 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/com/dishii/soh/ControllerButtons.java create mode 100644 app/src/main/res/drawable/ic_button.xml create mode 100644 app/src/main/res/drawable/ic_cross.xml create mode 100644 app/src/main/res/drawable/ic_rectangular_button.xml create mode 100644 app/src/main/res/drawable/ic_show.xml create mode 100644 app/src/main/res/drawable/ic_stick.xml create mode 100644 app/src/main/res/drawable/ic_trigger_button_left.xml create mode 100644 app/src/main/res/drawable/ic_trigger_button_right.xml create mode 100644 app/src/main/res/layout/touchcontrol_overlay.xml diff --git a/app/build.gradle b/app/build.gradle index 826d34c..feadf82 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,7 +17,7 @@ android { minSdkVersion 18 targetSdkVersion 31 versionCode 6 - versionName "1.2.2" + versionName "1.3.0" externalNativeBuild { //ndkBuild { // arguments "APP_PLATFORM=android-23" @@ -75,7 +75,7 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'androidx.core:core:1.7.0' // Use the latest version - + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' } task wrapper(type: Wrapper) { diff --git a/app/jni/src/libultraship b/app/jni/src/libultraship index 43adc0d..03e683e 160000 --- a/app/jni/src/libultraship +++ b/app/jni/src/libultraship @@ -1 +1 @@ -Subproject commit 43adc0d5a08df6e0ca183e4b3216df52540b8601 +Subproject commit 03e683e1d1be96ed714f0459f603e40ea1e82444 diff --git a/app/jni/src/soh/src/code/z_camera.c b/app/jni/src/soh/src/code/z_camera.c index 4353d39..a57a565 100644 --- a/app/jni/src/soh/src/code/z_camera.c +++ b/app/jni/src/soh/src/code/z_camera.c @@ -36,6 +36,24 @@ s32 Camera_UpdateWater(Camera* camera); #include "z_camera_data.inc" +#ifdef __ANDROID__ +#include "jni.h" + +float touchCameraYaw=0; +float touchCameraPitch=0; + +extern void JNICALL Java_com_dishii_soh_MainActivity_setCameraState(JNIEnv *env, jobject jobj, jint axis, jfloat value) { + switch(axis){ + case 0: + touchCameraYaw=value; + break; + case 1: + touchCameraPitch=value; + break; + } +} +#endif + /*===============================================================*/ /** @@ -1422,6 +1440,11 @@ s32 SetCameraManual(Camera* camera) { f32 newCamX = -D_8015BD7C->state.input[0].cur.right_stick_x * 10.0f; f32 newCamY = D_8015BD7C->state.input[0].cur.right_stick_y * 10.0f; +#ifdef __ANDROID__ + newCamX += -touchCameraYaw*10.0f; + newCamY += touchCameraPitch*10.0f; +#endif + if ((fabsf(newCamX) >= 15.0f || fabsf(newCamY) >= 15.0f) && camera->play->manualCamera == false) { camera->play->manualCamera = true; @@ -1489,6 +1512,11 @@ s32 Camera_Free(Camera* camera) { f32 newCamY = D_8015BD7C->state.input[0].cur.right_stick_y * 10.0f * (CVarGetFloat("gThirdPersonCameraSensitivityY", 1.0f)); bool invertXAxis = (CVarGetInteger("gInvertXAxis", 0) && !CVarGetInteger("gMirroredWorld", 0)) || (!CVarGetInteger("gInvertXAxis", 0) && CVarGetInteger("gMirroredWorld", 0)); +#ifdef __ANDROID__ + newCamX += -touchCameraYaw*10.0f; + newCamY += touchCameraPitch*10.0f; +#endif + camera->play->camX += newCamX * (invertXAxis ? -1 : 1); camera->play->camY += newCamY * (CVarGetInteger("gInvertYAxis", 1) ? 1 : -1); diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2309948..2a9ad2c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,7 @@ diff --git a/app/src/main/java/com/dishii/soh/ControllerButtons.java b/app/src/main/java/com/dishii/soh/ControllerButtons.java new file mode 100644 index 0000000..40fcb36 --- /dev/null +++ b/app/src/main/java/com/dishii/soh/ControllerButtons.java @@ -0,0 +1,26 @@ +package com.dishii.soh; + +public class ControllerButtons { + // Xbox Buttons + 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_LB = 9; // Left Bumper + public static final int BUTTON_RB = -5; // Right Bumper + public static final int BUTTON_BACK = 4; + public static final int BUTTON_START = 6; + public static final int BUTTON_DPAD_UP = 11; + public static final int BUTTON_DPAD_DOWN = 12; + public static final int BUTTON_DPAD_LEFT = 13; + public static final int BUTTON_DPAD_RIGHT = 14; + + // Xbox Axis (Joysticks and Triggers) + public static final int AXIS_LX = 0; // Left stick X + public static final int AXIS_LY = 1; // Left stick Y + public static final int AXIS_RX = 2; // Right stick X + public static final int AXIS_RY = 3; // Right stick Y + public static final int AXIS_LT = -2; // Left Trigger (negative axis) + public static final int AXIS_RT = -4; // Right Trigger (negative axis) +} + diff --git a/app/src/main/java/com/dishii/soh/MainActivity.java b/app/src/main/java/com/dishii/soh/MainActivity.java index aa9551a..046c80c 100644 --- a/app/src/main/java/com/dishii/soh/MainActivity.java +++ b/app/src/main/java/com/dishii/soh/MainActivity.java @@ -18,6 +18,13 @@ import android.Manifest; import android.content.pm.PackageManager; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.FrameLayout; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ImageView; import android.util.Log; //This class is the main SDLActivity and just sets up a bunch of default files @@ -25,10 +32,15 @@ public class MainActivity extends SDLActivity{ private static final int STORAGE_PERMISSION_REQUEST_CODE = 1; + SharedPreferences preferences; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + preferences = getSharedPreferences("com.dishii.soh.prefs",Context.MODE_PRIVATE); + + setupControllerOverlay(); // Check if storage permissions are granted if (hasStoragePermission()) { doVersionCheck(); @@ -36,10 +48,10 @@ public class MainActivity extends SDLActivity{ } else { requestStoragePermission(); } + attachController(); } private void doVersionCheck(){ - SharedPreferences preferences = getSharedPreferences("com.dishii.soh.prefs",Context.MODE_PRIVATE); int currentVersion = BuildConfig.VERSION_CODE; // Use your app's version code int storedVersion = preferences.getInt("appVersion", 1); @@ -186,4 +198,285 @@ public class MainActivity extends SDLActivity{ return Environment.MEDIA_MOUNTED.equals(state); } + + + public native void attachController(); + public native void detachController(); + // Native method for setting button state + public native void setButton(int button, boolean value); + public native void setCameraState(int axis, float value); + + // Native method for setting joystick axis value + public native void setAxis(int axis, short value); + + private Button button1, button2, button3, button4; + private Button buttonA, buttonB, buttonX, buttonY; + private Button buttonDpadUp, buttonDpadDown, buttonDpadLeft, buttonDpadRight; + private Button buttonLB, buttonRB, buttonZ, buttonStart, buttonBack; + private Button buttonToggle; + private FrameLayout leftJoystick; + private ImageView leftJoystickKnob; + private View overlayView; + + // Function to set up the controller overlay (inflate layout and initialize buttons) + private void setupControllerOverlay() { + // Inflate the touchcontrol_overlay layout + LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); + overlayView = inflater.inflate(R.layout.touchcontrol_overlay, null); + + // Set layout params for overlayView to control positioning and sizing + FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT + ); + overlayView.setLayoutParams(layoutParams); + // Add overlay view to the main layout (you may need to add it to a container like FrameLayout) + ViewGroup view = (ViewGroup) getContentView(); + view.addView(overlayView); + + final ViewGroup buttonGroup = overlayView.findViewById(R.id.button_group); + + buttonA = overlayView.findViewById(R.id.buttonA); + buttonB = overlayView.findViewById(R.id.buttonB); + buttonX = overlayView.findViewById(R.id.buttonX); + buttonY = overlayView.findViewById(R.id.buttonY); + + buttonDpadUp = overlayView.findViewById(R.id.buttonDpadUp); + buttonDpadDown = overlayView.findViewById(R.id.buttonDpadDown); + buttonDpadLeft = overlayView.findViewById(R.id.buttonDpadLeft); + buttonDpadRight = overlayView.findViewById(R.id.buttonDpadRight); + + buttonLB = overlayView.findViewById(R.id.buttonLB); + buttonRB = overlayView.findViewById(R.id.buttonRB); + buttonZ = overlayView.findViewById(R.id.buttonZ); + + buttonStart = overlayView.findViewById(R.id.buttonStart); + buttonBack = overlayView.findViewById(R.id.buttonBack); + + buttonToggle = overlayView.findViewById(R.id.buttonToggle); + + // Initialize joysticks and joystick knobs from the inflated layout + leftJoystick = overlayView.findViewById(R.id.left_joystick); + leftJoystickKnob = overlayView.findViewById(R.id.left_joystick_knob); + + FrameLayout rightScreenArea = overlayView.findViewById(R.id.right_screen_area); + + // Set OnTouchListeners for the Xbox controller buttons + addTouchListener(buttonA, ControllerButtons.BUTTON_A); // SDL Button 0 (A) + addTouchListener(buttonB, ControllerButtons.BUTTON_B); // SDL Button 1 (B) + addTouchListener(buttonX, ControllerButtons.BUTTON_X); // SDL Button 2 (X) + addTouchListener(buttonY, ControllerButtons.BUTTON_Y); // SDL Button 3 (Y) + + setupCButtons(buttonDpadUp, ControllerButtons.AXIS_RY, 1); // SDL Button 10 (D-Pad Up) + setupCButtons(buttonDpadDown, ControllerButtons.AXIS_RY , -1); // SDL Button 11 (D-Pad Down) + setupCButtons(buttonDpadLeft, ControllerButtons.AXIS_RX, 1); // SDL Button 12 (D-Pad Left) + setupCButtons(buttonDpadRight, ControllerButtons.AXIS_RX, -1); // SDL Button 13 (D-Pad Right) + + addTouchListener(buttonLB, ControllerButtons.BUTTON_LB); // SDL Button 4 (LB) + addTouchListener(buttonRB, ControllerButtons.BUTTON_RB); // SDL Button 5 (RB) + addTouchListener(buttonZ, ControllerButtons.AXIS_RT); // SDL Button 5 (Z) + + addTouchListener(buttonStart, ControllerButtons.BUTTON_START); // SDL Button 7 (Start) + addTouchListener(buttonBack, ControllerButtons.BUTTON_BACK); // SDL Button 6 (Back) + + + // Setup joystick movement + setupJoystick(leftJoystick, leftJoystickKnob, true); // Left joystick + + setupLookAround(rightScreenArea); + + setupToggleButton(buttonToggle,buttonGroup); + + } + + private void setupToggleButton(Button button, ViewGroup uiGroup){ + boolean isHidden = preferences.getBoolean("controlsVisible", false); // Default to 'false' (visible) + uiGroup.setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE); // Set the initial visibility based on the saved state + /*if(isHidden){ + detachController(); + }*/ + button.setOnClickListener(new View.OnClickListener() { + boolean isHidden = false; + @Override + public void onClick(View v) { + if (isHidden) { + uiGroup.setVisibility(View.VISIBLE); // Show UI elements + //attachController(); + } else { + uiGroup.setVisibility(View.INVISIBLE); // Hide UI elements + //detachController(); + } + preferences.edit().putBoolean("controlsVisible", !isHidden).apply(); + isHidden = !isHidden; // Toggle state + } + }); + } + + // Function to set a touch listener for each button + private void addTouchListener(Button button, int buttonNum) { + button.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + setButton(buttonNum, true); + button.setPressed(true); + return true; + case MotionEvent.ACTION_UP: + setButton(buttonNum, false); + button.setPressed(false); + return true; + case MotionEvent.ACTION_CANCEL: + setButton(buttonNum, false); + return true; + } + return false; + } + }); + } + + private void setupCButtons(Button button, int buttonNum, int direction) { + button.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + setAxis(buttonNum, direction<0 ? Short.MAX_VALUE : Short.MIN_VALUE); + button.setPressed(true); + return true; + case MotionEvent.ACTION_UP: + setAxis(buttonNum, (short) 0); + button.setPressed(false); + return true; + case MotionEvent.ACTION_CANCEL: + setAxis(buttonNum, (short) 0); + return true; + } + return false; + } + }); + } + + boolean TouchAreaEnabled = true; + + void DisableTouchArea(){ + TouchAreaEnabled = false; + } + void EnableTouchArea(){ + TouchAreaEnabled = true; + } + + private void setupLookAround(FrameLayout rightScreenArea) { + rightScreenArea.setOnTouchListener(new View.OnTouchListener() { + private float lastX = 0; + private float lastY = 0; + private boolean isTouching = false; + + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + // Start tracking the finger's position + lastX = event.getX(); + lastY = event.getY(); + isTouching = true; + break; + + case MotionEvent.ACTION_MOVE: + if (isTouching) { + // Calculate the change in position (delta) + float deltaX = event.getX() - lastX; + float deltaY = event.getY() - lastY; + + // Update the last position + lastX = event.getX(); + lastY = event.getY(); + + // Increase sensitivity by using a larger multiplier + // Adjust these multipliers to suit your needs + float sensitivityMultiplier = 15; // Higher value for more sensitivity + float rx = (deltaX * sensitivityMultiplier); + float ry = (deltaY * sensitivityMultiplier); + + // Send the mapped values to the joystick axes + setCameraState(0, rx); // Right stick X axis + setCameraState(1, ry); // Right stick Y axis + } + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + // Stop tracking the finger's position and reset joystick input + isTouching = false; + setCameraState(0, 0.0f); // Reset right stick X axis + setCameraState(1, 0.0f); // Reset right stick Y axis + break; + } + return TouchAreaEnabled; // Event full handled + } + }); + } + + + + + + // Function to set joystick movement with reset to center when not touched + private void setupJoystick(FrameLayout joystickLayout, ImageView joystickKnob, boolean isLeft) { + joystickLayout.post(() -> { + // Calculate the joystick center once, before any events + final float joystickCenterX = joystickLayout.getWidth() / 2f; + final float joystickCenterY = joystickLayout.getHeight() / 2f; + + joystickLayout.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_MOVE: + // Calculate the joystick movement and move the knob + float deltaX = event.getX() - joystickCenterX; + float deltaY = event.getY() - joystickCenterY; + + // Clamp the joystick movement to prevent it from going outside the area + float maxRadius = joystickLayout.getWidth() / 2f - joystickKnob.getWidth() / 2f; + float distance = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY); + if (distance > maxRadius) { + float scale = maxRadius / distance; + deltaX *= scale; + deltaY *= scale; + } + + joystickKnob.setX(joystickCenterX + deltaX - joystickKnob.getWidth() / 2f); + joystickKnob.setY(joystickCenterY + deltaY - joystickKnob.getHeight() / 2f); + + // Send joystick values to native C code + short x = (short) (deltaX / maxRadius * Short.MAX_VALUE); + short y = (short) (deltaY / maxRadius * Short.MAX_VALUE); + + // Send X-axis and Y-axis values + setAxis(isLeft ? ControllerButtons.AXIS_LX : ControllerButtons.AXIS_RX, x); // X-axis + setAxis(isLeft ? ControllerButtons.AXIS_LY : ControllerButtons.AXIS_RY, y); // Y-axis + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + // Reset joystick knob to the center position (ensure it's placed correctly) + joystickKnob.setX(joystickCenterX - joystickKnob.getWidth() / 2f); + joystickKnob.setY(joystickCenterY - joystickKnob.getHeight() / 2f); + + // Reset joystick values to 0 when released or canceled + setAxis(isLeft ? ControllerButtons.AXIS_LX : ControllerButtons.AXIS_RX, (short) 0); // X-axis + setAxis(isLeft ? ControllerButtons.AXIS_LY : ControllerButtons.AXIS_RY, (short) 0); // Y-axis + break; + } + return true; + } + }); + }); + + + + } + } diff --git a/app/src/main/res/drawable/ic_button.xml b/app/src/main/res/drawable/ic_button.xml new file mode 100644 index 0000000..f949910 --- /dev/null +++ b/app/src/main/res/drawable/ic_button.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_cross.xml b/app/src/main/res/drawable/ic_cross.xml new file mode 100644 index 0000000..4ba63a2 --- /dev/null +++ b/app/src/main/res/drawable/ic_cross.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_rectangular_button.xml b/app/src/main/res/drawable/ic_rectangular_button.xml new file mode 100644 index 0000000..9d83fb4 --- /dev/null +++ b/app/src/main/res/drawable/ic_rectangular_button.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_show.xml b/app/src/main/res/drawable/ic_show.xml new file mode 100644 index 0000000..418a456 --- /dev/null +++ b/app/src/main/res/drawable/ic_show.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_stick.xml b/app/src/main/res/drawable/ic_stick.xml new file mode 100644 index 0000000..486b2b0 --- /dev/null +++ b/app/src/main/res/drawable/ic_stick.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_trigger_button_left.xml b/app/src/main/res/drawable/ic_trigger_button_left.xml new file mode 100644 index 0000000..3b7840b --- /dev/null +++ b/app/src/main/res/drawable/ic_trigger_button_left.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_trigger_button_right.xml b/app/src/main/res/drawable/ic_trigger_button_right.xml new file mode 100644 index 0000000..4c21d6b --- /dev/null +++ b/app/src/main/res/drawable/ic_trigger_button_right.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/touchcontrol_overlay.xml b/app/src/main/res/layout/touchcontrol_overlay.xml new file mode 100644 index 0000000..e11345a --- /dev/null +++ b/app/src/main/res/layout/touchcontrol_overlay.xml @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + +