mirror of
https://github.com/izzy2lost/Super3.git
synced 2026-07-05 15:18:38 -07:00
Added virtual steering wheel!
This commit is contained in:
@@ -12,6 +12,22 @@ AndroidInputSystem::AndroidInputSystem()
|
||||
std::strncpy(m_mouseDetails.name, "Touchscreen", MAX_NAME_LENGTH);
|
||||
m_mouseDetails.name[MAX_NAME_LENGTH] = '\0';
|
||||
m_mouseDetails.isAbsolute = true;
|
||||
|
||||
std::memset(&m_virtualJoyDetails, 0, sizeof(m_virtualJoyDetails));
|
||||
std::strncpy(m_virtualJoyDetails.name, "Touch Wheel", MAX_NAME_LENGTH);
|
||||
m_virtualJoyDetails.name[MAX_NAME_LENGTH] = '\0';
|
||||
m_virtualJoyDetails.numAxes = 6;
|
||||
m_virtualJoyDetails.numPOVs = 0;
|
||||
m_virtualJoyDetails.numButtons = 0;
|
||||
m_virtualJoyDetails.hasFFeedback = false;
|
||||
for (int a = 0; a < NUM_JOY_AXES; a++)
|
||||
{
|
||||
m_virtualJoyDetails.hasAxis[a] = false;
|
||||
m_virtualJoyDetails.axisHasFF[a] = false;
|
||||
std::strncpy(m_virtualJoyDetails.axisName[a], GetDefaultAxisName(a), MAX_NAME_LENGTH);
|
||||
m_virtualJoyDetails.axisName[a][MAX_NAME_LENGTH] = '\0';
|
||||
}
|
||||
m_virtualJoyDetails.hasAxis[AXIS_X] = true;
|
||||
}
|
||||
|
||||
AndroidInputSystem::~AndroidInputSystem()
|
||||
@@ -33,6 +49,37 @@ void AndroidInputSystem::SetGunTouchEnabled(bool enabled)
|
||||
std::memset(m_mouseButtonPulseUntilMs, 0, sizeof(m_mouseButtonPulseUntilMs));
|
||||
}
|
||||
|
||||
void AndroidInputSystem::SetVirtualWheelEnabled(bool enabled)
|
||||
{
|
||||
if (m_virtualWheelEnabled == enabled)
|
||||
return;
|
||||
m_virtualWheelEnabled = enabled;
|
||||
m_wheelFingerActive = false;
|
||||
m_wheelFinger = 0;
|
||||
m_virtualJoyX = 0;
|
||||
}
|
||||
|
||||
bool AndroidInputSystem::UseVirtualWheel() const
|
||||
{
|
||||
return m_virtualWheelEnabled && m_controllers.empty();
|
||||
}
|
||||
|
||||
void AndroidInputSystem::SetVirtualSteerFromEncoded(float encodedX)
|
||||
{
|
||||
// encodedX comes from Java as [(steer+1)/2], where steer is in [-1,1].
|
||||
const float steer = std::clamp((encodedX - 0.5f) * 2.0f, -1.0f, 1.0f);
|
||||
constexpr float deadzone = 0.08f;
|
||||
if (std::abs(steer) < deadzone)
|
||||
{
|
||||
m_virtualJoyX = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const float scaled = (std::abs(steer) - deadzone) / (1.0f - deadzone);
|
||||
const float signedScaled = (steer < 0.0f) ? -scaled : scaled;
|
||||
m_virtualJoyX = (int)std::lround(std::clamp(signedScaled, -1.0f, 1.0f) * 32767.0f);
|
||||
}
|
||||
|
||||
void AndroidInputSystem::ApplyConfig(const Util::Config::Node& config)
|
||||
{
|
||||
auto get = [&](const char* key, const char* fallback) -> std::string {
|
||||
@@ -81,6 +128,9 @@ bool AndroidInputSystem::InitializeSystem()
|
||||
std::memset(m_mouseButtonPulseUntilMs, 0, sizeof(m_mouseButtonPulseUntilMs));
|
||||
m_gunFingerActive = false;
|
||||
m_gunFinger = 0;
|
||||
m_wheelFingerActive = false;
|
||||
m_wheelFinger = 0;
|
||||
m_virtualJoyX = 0;
|
||||
|
||||
SDL_GameControllerEventState(SDL_ENABLE);
|
||||
RefreshControllers();
|
||||
@@ -215,6 +265,32 @@ void AndroidInputSystem::HandleTouch(const SDL_TouchFingerEvent& tf, bool down)
|
||||
const float x = tf.x;
|
||||
const float y = tf.y;
|
||||
|
||||
// Virtual steering wheel (racing games): encoded in tf.x from Java and keyed by a fixed fingerId.
|
||||
if (UseVirtualWheel())
|
||||
{
|
||||
constexpr SDL_FingerID kWheelFingerId = 1107;
|
||||
if (down)
|
||||
{
|
||||
if (!m_wheelFingerActive && tf.fingerId == kWheelFingerId)
|
||||
{
|
||||
m_wheelFingerActive = true;
|
||||
m_wheelFinger = tf.fingerId;
|
||||
SetVirtualSteerFromEncoded(x);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_wheelFingerActive && tf.fingerId == m_wheelFinger)
|
||||
{
|
||||
SetVirtualSteerFromEncoded(0.5f);
|
||||
m_wheelFingerActive = false;
|
||||
m_wheelFinger = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tap zones (momentary):
|
||||
// - Bottom-left: Coin (KEY_5)
|
||||
// - Bottom-middle: Start (KEY_1)
|
||||
@@ -320,6 +396,15 @@ void AndroidInputSystem::HandleTouch(const SDL_TouchFingerEvent& tf, bool down)
|
||||
|
||||
void AndroidInputSystem::HandleTouchMotion(const SDL_TouchFingerEvent& tf)
|
||||
{
|
||||
if (UseVirtualWheel())
|
||||
{
|
||||
if (m_wheelFingerActive && tf.fingerId == m_wheelFinger)
|
||||
{
|
||||
SetVirtualSteerFromEncoded(tf.x);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_gunTouchEnabled)
|
||||
{
|
||||
if (m_gunFingerActive && tf.fingerId == m_gunFinger)
|
||||
@@ -500,11 +585,20 @@ void AndroidInputSystem::RefreshControllers()
|
||||
|
||||
int AndroidInputSystem::GetNumJoysticks()
|
||||
{
|
||||
if (UseVirtualWheel())
|
||||
return 1;
|
||||
return static_cast<int>(m_controllers.size());
|
||||
}
|
||||
|
||||
const JoyDetails* AndroidInputSystem::GetJoyDetails(int joyNum)
|
||||
{
|
||||
if (UseVirtualWheel())
|
||||
{
|
||||
if (joyNum == ANY_JOYSTICK || joyNum == 0)
|
||||
return &m_virtualJoyDetails;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (joyNum < 0 || joyNum >= static_cast<int>(m_controllers.size()))
|
||||
return nullptr;
|
||||
return &m_controllers[static_cast<size_t>(joyNum)].details;
|
||||
@@ -573,6 +667,13 @@ bool AndroidInputSystem::ButtonPressedFor(const ControllerState& c, int butNum)
|
||||
|
||||
int AndroidInputSystem::GetJoyAxisValue(int joyNum, int axisNum)
|
||||
{
|
||||
if (UseVirtualWheel())
|
||||
{
|
||||
if (axisNum == AXIS_X)
|
||||
return m_virtualJoyX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m_controllers.empty())
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ public:
|
||||
~AndroidInputSystem() override;
|
||||
|
||||
void SetGunTouchEnabled(bool enabled);
|
||||
void SetVirtualWheelEnabled(bool enabled);
|
||||
|
||||
// Called from the SDL event loop (main thread).
|
||||
void HandleEvent(const SDL_Event& ev);
|
||||
@@ -73,6 +74,9 @@ private:
|
||||
void RefreshControllers();
|
||||
void CloseControllers();
|
||||
|
||||
bool UseVirtualWheel() const;
|
||||
void SetVirtualSteerFromEncoded(float encodedX);
|
||||
|
||||
void SetKey(SDL_Scancode sc, bool down);
|
||||
void PulseKey(SDL_Scancode sc, uint32_t durationMs);
|
||||
void SetKeys(const DualScancode& sc, bool down);
|
||||
@@ -114,6 +118,12 @@ private:
|
||||
SDL_FingerID m_gunFinger = 0;
|
||||
bool m_gunFingerActive = false;
|
||||
|
||||
bool m_virtualWheelEnabled = false;
|
||||
SDL_FingerID m_wheelFinger = 0;
|
||||
bool m_wheelFingerActive = false;
|
||||
int m_virtualJoyX = 0;
|
||||
JoyDetails m_virtualJoyDetails{};
|
||||
|
||||
MouseDetails m_mouseDetails{};
|
||||
int m_mouseX = 0;
|
||||
int m_mouseY = 0;
|
||||
|
||||
@@ -316,6 +316,9 @@ struct Super3Host {
|
||||
(game.inputs & (Game::INPUT_GUN1 | Game::INPUT_GUN2 | Game::INPUT_ANALOG_GUN1 | Game::INPUT_ANALOG_GUN2)) != 0;
|
||||
inputSystem.SetGunTouchEnabled(gunGame);
|
||||
|
||||
const bool vehicleGame = (game.inputs & (Game::INPUT_VEHICLE | Game::INPUT_HARLEY)) != 0;
|
||||
inputSystem.SetVirtualWheelEnabled(vehicleGame);
|
||||
|
||||
// Apply Supermodel.ini overrides (Global + [ game ]) after the loader has determined game->name.
|
||||
ApplyIniOverrides(game.name);
|
||||
ApplyAndroidHardOverrides();
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import java.io.File
|
||||
@@ -64,6 +65,9 @@ class Super3Activity : SDLActivity() {
|
||||
overlay.findViewById<LinearLayout>(R.id.overlay_pedals)?.visibility =
|
||||
if (isRacing) View.VISIBLE else View.GONE
|
||||
|
||||
overlay.findViewById<ImageButton>(R.id.overlay_wheel)?.visibility =
|
||||
if (isRacing) View.VISIBLE else View.GONE
|
||||
|
||||
fun nativeTouch(action: Int, fingerId: Int, x: Float, y: Float, p: Float = 1.0f) {
|
||||
SDLActivity.onNativeTouch(0, fingerId, action, x, y, p)
|
||||
}
|
||||
@@ -126,6 +130,44 @@ class Super3Activity : SDLActivity() {
|
||||
// Match the native pedal zone (right-middle), independent of UI placement.
|
||||
bindHeld(R.id.overlay_gas, fingerId = 1103, x = 0.85f, y = 0.35f)
|
||||
bindHeld(R.id.overlay_brake, fingerId = 1104, x = 0.85f, y = 0.80f)
|
||||
|
||||
val wheel = overlay.findViewById<ImageButton>(R.id.overlay_wheel)
|
||||
wheel?.setOnTouchListener { v, ev ->
|
||||
val w = v.width.toFloat().coerceAtLeast(1f)
|
||||
val cx = w / 2f
|
||||
val dx = (ev.x - cx) / cx
|
||||
val steer = dx.coerceIn(-1f, 1f)
|
||||
|
||||
v.rotation = steer * 75f
|
||||
|
||||
val encodedX = ((steer + 1f) / 2f).coerceIn(0f, 1f)
|
||||
val encodedY = 0.5f
|
||||
|
||||
when (ev.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
v.alpha = 0.75f
|
||||
nativeTouch(MotionEvent.ACTION_DOWN, 1107, encodedX, encodedY)
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
nativeTouch(MotionEvent.ACTION_MOVE, 1107, encodedX, encodedY)
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
v.alpha = 1.0f
|
||||
v.rotation = 0f
|
||||
nativeTouch(MotionEvent.ACTION_UP, 1107, 0.5f, encodedY)
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_CANCEL -> {
|
||||
v.alpha = 1.0f
|
||||
v.rotation = 0f
|
||||
nativeTouch(MotionEvent.ACTION_UP, 1107, 0.5f, encodedY)
|
||||
true
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -21,6 +21,20 @@
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/coin" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/overlay_wheel"
|
||||
android:layout_width="190dp"
|
||||
android:layout_height="190dp"
|
||||
android:layout_gravity="bottom|start"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginBottom="64dp"
|
||||
android:background="@drawable/overlay_ripple_circle"
|
||||
android:contentDescription="Steering"
|
||||
android:padding="12dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/wheel"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/overlay_service"
|
||||
style="?attr/materialButtonOutlinedStyle"
|
||||
@@ -62,7 +76,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_marginBottom="18dp"
|
||||
android:minWidth="140dp"
|
||||
android:minHeight="0dp"
|
||||
android:minWidth="0dp"
|
||||
android:text="START"
|
||||
android:textAllCaps="true"
|
||||
app:rippleColor="@color/overlay_ripple"
|
||||
|
||||
Reference in New Issue
Block a user