feat: add reusable virtual hud input

This commit is contained in:
ud18010
2026-07-30 11:14:56 +08:00
parent 3546d16632
commit 5853c5bd83
26 changed files with 6403 additions and 31 deletions
@@ -5,6 +5,7 @@ using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Rendering;
using UnityEngine.UI;
using XGame.VirtualInput;
using XWorld.Framework.Protocol;
using UObject = UnityEngine.Object;
@@ -29,7 +30,7 @@ namespace XGame
public bool IsPlaceholder; // Obj 当前是胶囊占位(真实角色预设还在异步加载),加载完成后会原地升级
}
private const string JoystickPrefabPath = "Assets/Game/Art/UI/Prefab/UI_LobbyJoystick.prefab";
private const string JoystickPrefabPath = "Assets/Game/Art/UI/Prefab/UI_VirtualJoystick.prefab";
private const string CharacterConfigResourcePath = "Config/CharacterConfig";
private const string SelectedCharacterKey = "CharacterSwitch.SelectedCharacterId";
private const float MoveSpeed = 1f;
@@ -39,7 +40,6 @@ namespace XGame
private const float DirectionChangeMinInterval = 0.1f;
private const float DirectionChangeDot = 0.98f;
private const float JoystickRadius = 74f;
private const float JoystickKnobRadius = 28f;
private const float JoystickPadding = 100.0f;
private const float JoystickDeadZone = 0.18f;
private const float JoystickAcquireRadiusMultiplier = 1.6f;
@@ -83,6 +83,7 @@ namespace XGame
private float lastPinchDistance;
private RectTransform joystickRoot;
private RectTransform joystickWidget; // 视觉容器:新预设里的 "Joystick" 子节点(自带定位);找不到则回退为 joystickRoot
private VirtualJoystick virtualJoystick;
private RectTransform joystickKnob;
private bool joystickMouseCaptured;
private int joystickTouchFingerId = -1;
@@ -151,6 +152,7 @@ namespace XGame
{
joystickRoot.gameObject.SetActive(false);
}
OnVirtualJoystickEnded();
}
public void RefreshSelectedFigure()
@@ -218,6 +220,11 @@ namespace XGame
UpdateCamera();
}
private void OnDestroy()
{
ReleaseJoystickUI(true);
}
private void ApplyState(LobbyStateMsg state)
{
ReconcileSelfPlayerId(state);
@@ -382,7 +389,6 @@ namespace XGame
return;
}
UpdateJoystick();
Vector3 dir = GetMoveDirection();
bool moving = dir.sqrMagnitude > 0.001f;
if (moving)
@@ -418,17 +424,36 @@ namespace XGame
Camera cam = Camera.main;
Vector3 forward = cam != null ? cam.transform.forward : Vector3.forward;
Vector3 right = cam != null ? cam.transform.right : Vector3.right;
forward.y = 0f;
right.y = 0f;
forward = forward.sqrMagnitude > 0.001f ? forward.normalized : Vector3.forward;
right = right.sqrMagnitude > 0.001f ? right.normalized : Vector3.right;
return MapMoveInputForTest(
joystickActive ? joystick : Vector2.zero,
forward,
right,
Input.GetKey(KeyCode.W),
Input.GetKey(KeyCode.S),
Input.GetKey(KeyCode.A),
Input.GetKey(KeyCode.D));
}
public static Vector3 MapMoveInputForTest(
Vector2 joystickInput,
Vector3 cameraForward,
Vector3 cameraRight,
bool keyW,
bool keyS,
bool keyA,
bool keyD)
{
cameraForward.y = 0f;
cameraRight.y = 0f;
Vector3 forward = cameraForward.sqrMagnitude > 0.001f ? cameraForward.normalized : Vector3.forward;
Vector3 right = cameraRight.sqrMagnitude > 0.001f ? cameraRight.normalized : Vector3.right;
Vector3 dir = Vector3.zero;
if (Input.GetKey(KeyCode.W)) dir += forward;
if (Input.GetKey(KeyCode.S)) dir -= forward;
if (Input.GetKey(KeyCode.A)) dir -= right;
if (Input.GetKey(KeyCode.D)) dir += right;
if (joystickActive) dir += right * joystick.x + forward * joystick.y;
if (keyW) dir += forward;
if (keyS) dir -= forward;
if (keyA) dir -= right;
if (keyD) dir += right;
if (joystickInput.sqrMagnitude > 0.001f) dir += right * joystickInput.x + forward * joystickInput.y;
return dir;
}
@@ -917,7 +942,7 @@ namespace XGame
rootObj = Instantiate(prefabObj);
UIManager.AttachTo(UILayer.HUD, rootObj);
rootObj.name = "UI_LobbyJoystick";
rootObj.name = "UI_VirtualJoystick";
joystickRoot = rootObj.GetComponent<RectTransform>();
if (joystickRoot == null)
{
@@ -926,6 +951,16 @@ namespace XGame
joystickRoot.localScale = Vector3.one; // 兜底:设备包 prefab 根缩放异常时仍可见
// 视觉容器:新预设里是 "Joystick" 子节点(预设自带定位);找不到则用根并由 ApplyJoystickLayout 代码定位
virtualJoystick = rootObj.GetComponent<VirtualJoystick>();
if (virtualJoystick == null)
{
virtualJoystick = rootObj.AddComponent<VirtualJoystick>();
}
virtualJoystick.Changed -= OnVirtualJoystickChanged;
virtualJoystick.Changed += OnVirtualJoystickChanged;
virtualJoystick.Ended -= OnVirtualJoystickEnded;
virtualJoystick.Ended += OnVirtualJoystickEnded;
Transform widget = joystickRoot.FindTransform("Joystick");
joystickWidget = (widget as RectTransform) ?? joystickRoot;
if (joystickWidget == joystickRoot)
@@ -939,9 +974,7 @@ namespace XGame
if (joystickKnob == null)
{
Debug.LogError("[LobbyWorldController] joystick prefab missing Knob node: " + JoystickPrefabPath);
Destroy(rootObj);
joystickRoot = null;
joystickWidget = null;
ReleaseJoystickUI(true);
yield break;
}
@@ -949,6 +982,59 @@ namespace XGame
UpdateJoystickVisual();
}
private void ReleaseJoystickUI(bool destroyObject)
{
if (virtualJoystick != null)
{
virtualJoystick.Changed -= OnVirtualJoystickChanged;
virtualJoystick.Ended -= OnVirtualJoystickEnded;
}
GameObject rootObj = joystickRoot != null ? joystickRoot.gameObject : null;
virtualJoystick = null;
joystickRoot = null;
joystickWidget = null;
joystickKnob = null;
joystickMouseCaptured = false;
joystickTouchFingerId = -1;
joystickLoading = false;
OnVirtualJoystickEnded();
if (destroyObject && rootObj != null)
{
DestroyUnityObject(rootObj);
}
}
private static void DestroyUnityObject(UObject obj)
{
if (obj == null)
{
return;
}
if (Application.isPlaying)
{
Destroy(obj);
}
else
{
DestroyImmediate(obj);
}
}
private void OnVirtualJoystickChanged(Vector2 value, bool active)
{
joystick = active ? value : Vector2.zero;
joystickActive = active;
}
private void OnVirtualJoystickEnded()
{
joystick = Vector2.zero;
joystickActive = false;
}
private Transform GetHudParent()
{
RectTransform hud = UIManager.GetLayer(UILayer.HUD);
@@ -1229,9 +1315,7 @@ namespace XGame
private bool IsInJoystickArea(Vector2 screenPosition)
{
Vector2 center = GetJoystickScreenCenter();
float radius = GetJoystickScreenRadius();
return Vector2.Distance(screenPosition, center) <= radius * JoystickAcquireRadiusMultiplier;
return virtualJoystick != null && virtualJoystick.IsScreenPositionInAcquireArea(screenPosition);
}
private bool EnsureCharacterConfig()