Improve touch UX in 3rd person camera

This commit is contained in:
Christian Semmler 2026-03-10 21:09:24 -07:00
parent 33436ef00f
commit 3bcd8cc908
No known key found for this signature in database
GPG Key ID: 086DAA1360BEEE5C
5 changed files with 110 additions and 36 deletions

View File

@ -638,7 +638,7 @@ void LegoInputManager::RemoveJoystick(SDL_JoystickID p_joystickID)
MxBool LegoInputManager::HandleTouchEvent(SDL_Event* p_event, TouchScheme p_touchScheme)
{
if (Extension<MultiplayerExt>::Call(HandleTouchInput).value_or(FALSE)) {
if (Extension<MultiplayerExt>::Call(HandleTouchInput, p_event).value_or(FALSE)) {
return FALSE;
}

View File

@ -71,9 +71,9 @@ class MultiplayerExt {
// Returns TRUE when the third-person camera is active.
static MxBool IsThirdPersonCameraActive();
// Suppresses touch input when a multi-touch camera gesture is active.
// Returns TRUE if the caller should return early.
static MxBool HandleTouchInput();
// Routes touch events by screen zone: right half → camera, left half → movement.
// Returns TRUE if the event was consumed by the camera (caller should skip movement).
static MxBool HandleTouchInput(SDL_Event* p_event);
// Overrides nav controller movement for camera-relative 3rd person controls.
// Returns TRUE if the hook handled movement (caller should return early).

View File

@ -68,7 +68,13 @@ class ThirdPersonCamera {
// Free camera input handling
void HandleSDLEvent(SDL_Event* p_event);
bool IsTouchGestureActive() const { return m_touchGestureActive; }
// Finger-claiming API for split-screen touch zones (left=movement, right=camera)
bool TryClaimFinger(const SDL_TouchFingerEvent& event);
bool TryReleaseFinger(SDL_FingerID id);
bool IsFingerTracked(SDL_FingerID id) const;
static constexpr float CAMERA_ZONE_X = 0.5f;
private:
// Orbit camera helpers
@ -114,7 +120,6 @@ class ThirdPersonCamera {
float m_smoothedSpeed; // Extension-managed velocity for smooth acceleration/deceleration
// Touch gesture tracking
bool m_touchGestureActive = false;
struct TouchState {
SDL_FingerID id[2];
float x[2], y[2];

View File

@ -11,7 +11,6 @@
#include "legoentity.h"
#include "legoeventnotificationparam.h"
#include "legogamestate.h"
#include "legoinputmanager.h"
#include "legonavcontroller.h"
#include "legopathactor.h"
#include "misc.h"
@ -310,18 +309,37 @@ MxBool MultiplayerExt::IsThirdPersonCameraActive()
return FALSE;
}
MxBool MultiplayerExt::HandleTouchInput()
MxBool MultiplayerExt::HandleTouchInput(SDL_Event* p_event)
{
if (s_networkManager && s_networkManager->GetThirdPersonCamera().IsActive() &&
s_networkManager->GetThirdPersonCamera().IsTouchGestureActive()) {
LegoInputManager* im = InputManager();
im->m_touchFinger = 0;
im->m_touchVirtualThumb = {0, 0};
im->m_touchFlags.clear();
return TRUE;
if (!s_networkManager || !s_networkManager->GetThirdPersonCamera().IsActive()) {
return FALSE;
}
Multiplayer::ThirdPersonCamera& cam = s_networkManager->GetThirdPersonCamera();
switch (p_event->type) {
case SDL_EVENT_FINGER_DOWN:
if (cam.TryClaimFinger(p_event->tfinger)) {
return TRUE;
}
return FALSE;
case SDL_EVENT_FINGER_MOTION:
if (cam.IsFingerTracked(p_event->tfinger.fingerID)) {
return TRUE;
}
return FALSE;
case SDL_EVENT_FINGER_UP:
case SDL_EVENT_FINGER_CANCELED:
if (cam.TryReleaseFinger(p_event->tfinger.fingerID)) {
return TRUE;
}
return FALSE;
default:
return FALSE;
}
}
MxBool MultiplayerExt::HandleNavOverride(

View File

@ -758,6 +758,55 @@ MxBool ThirdPersonCamera::HandleCameraRelativeMovement(
return TRUE;
}
bool ThirdPersonCamera::TryClaimFinger(const SDL_TouchFingerEvent& event)
{
if (!m_active || m_touch.count >= 2 || event.x < CAMERA_ZONE_X) {
return false;
}
int idx = m_touch.count;
m_touch.id[idx] = event.fingerID;
m_touch.x[idx] = event.x;
m_touch.y[idx] = event.y;
m_touch.count++;
if (m_touch.count == 2) {
float dx = m_touch.x[1] - m_touch.x[0];
float dy = m_touch.y[1] - m_touch.y[0];
m_touch.initialPinchDist = SDL_sqrtf(dx * dx + dy * dy);
}
return true;
}
bool ThirdPersonCamera::TryReleaseFinger(SDL_FingerID id)
{
for (int i = 0; i < m_touch.count; i++) {
if (m_touch.id[i] == id) {
// Shift remaining finger down
if (i == 0 && m_touch.count == 2) {
m_touch.id[0] = m_touch.id[1];
m_touch.x[0] = m_touch.x[1];
m_touch.y[0] = m_touch.y[1];
}
m_touch.count--;
m_touch.initialPinchDist = 0.0f;
return true;
}
}
return false;
}
bool ThirdPersonCamera::IsFingerTracked(SDL_FingerID id) const
{
for (int i = 0; i < m_touch.count; i++) {
if (m_touch.id[i] == id) {
return true;
}
}
return false;
}
void ThirdPersonCamera::HandleSDLEvent(SDL_Event* p_event)
{
switch (p_event->type) {
@ -784,7 +833,10 @@ void ThirdPersonCamera::HandleSDLEvent(SDL_Event* p_event)
}
case SDL_EVENT_FINGER_DOWN: {
if (m_touch.count < 2) {
// Finger may already be claimed via TryClaimFinger (called from HandleTouchInput).
// Only register if not already tracked and in the camera zone.
if (!IsFingerTracked(p_event->tfinger.fingerID) && m_touch.count < 2 &&
p_event->tfinger.x >= CAMERA_ZONE_X) {
int idx = m_touch.count;
m_touch.id[idx] = p_event->tfinger.fingerID;
m_touch.x[idx] = p_event->tfinger.x;
@ -792,7 +844,6 @@ void ThirdPersonCamera::HandleSDLEvent(SDL_Event* p_event)
m_touch.count++;
if (m_touch.count == 2) {
m_touchGestureActive = true;
float dx = m_touch.x[1] - m_touch.x[0];
float dy = m_touch.y[1] - m_touch.y[0];
m_touch.initialPinchDist = SDL_sqrtf(dx * dx + dy * dy);
@ -802,7 +853,22 @@ void ThirdPersonCamera::HandleSDLEvent(SDL_Event* p_event)
}
case SDL_EVENT_FINGER_MOTION: {
if (m_touch.count == 2) {
if (m_touch.count == 1) {
// Single-finger drag: apply yaw/pitch rotation
if (m_touch.id[0] == p_event->tfinger.fingerID) {
float oldX = m_touch.x[0];
float oldY = m_touch.y[0];
m_touch.x[0] = p_event->tfinger.x;
m_touch.y[0] = p_event->tfinger.y;
float moveX = m_touch.x[0] - oldX;
float moveY = m_touch.y[0] - oldY;
m_absoluteYaw -= moveX * 2.0f;
m_orbitPitch += moveY * 2.0f;
ClampPitch();
}
}
else if (m_touch.count == 2) {
// Find which finger moved
int idx = -1;
for (int i = 0; i < 2; i++) {
@ -835,7 +901,7 @@ void ThirdPersonCamera::HandleSDLEvent(SDL_Event* p_event)
// Two-finger drag for orbit
float moveX = m_touch.x[idx] - oldX;
float moveY = m_touch.y[idx] - oldY;
m_absoluteYaw += moveX * 2.0f;
m_absoluteYaw -= moveX * 2.0f;
m_orbitPitch += moveY * 2.0f;
ClampPitch();
}
@ -844,22 +910,7 @@ void ThirdPersonCamera::HandleSDLEvent(SDL_Event* p_event)
case SDL_EVENT_FINGER_UP:
case SDL_EVENT_FINGER_CANCELED: {
for (int i = 0; i < m_touch.count; i++) {
if (m_touch.id[i] == p_event->tfinger.fingerID) {
// Shift remaining finger down
if (i == 0 && m_touch.count == 2) {
m_touch.id[0] = m_touch.id[1];
m_touch.x[0] = m_touch.x[1];
m_touch.y[0] = m_touch.y[1];
}
m_touch.count--;
if (m_touch.count == 0) {
m_touchGestureActive = false;
}
m_touch.initialPinchDist = 0.0f;
break;
}
}
TryReleaseFinger(p_event->tfinger.fingerID);
break;
}