fix: polish lobby chat and joystick
This commit is contained in:
@@ -36,7 +36,7 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 208, y: 158}
|
||||
m_AnchoredPosition: {x: 260, y: 220}
|
||||
m_SizeDelta: {x: 148, y: 148}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!225 &8720693099983328153
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using TMPro;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using XGame;
|
||||
using XWorld.Framework.Protocol;
|
||||
|
||||
public sealed class WorldChatModuleTests
|
||||
{
|
||||
private GameObject view;
|
||||
private GameObject host;
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
if (view != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(view);
|
||||
}
|
||||
if (host != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(host);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EndEditingInput_SubmitsTrimmedWorldChat()
|
||||
{
|
||||
WorldChatModule module = CreateBoundModule();
|
||||
TMP_InputField input = view.transform.FindTransform("InputHitArea").GetComponent<TMP_InputField>();
|
||||
string sent = null;
|
||||
module.OnSendWorldChat = text => sent = text;
|
||||
|
||||
input.text = " hello world ";
|
||||
input.onEndEdit.Invoke(input.text);
|
||||
|
||||
Assert.That(sent, Is.EqualTo("hello world"));
|
||||
Assert.That(input.text, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReceivedMessage_UpdatesCollapsedPreview()
|
||||
{
|
||||
WorldChatModule module = CreateBoundModule();
|
||||
TMP_Text collapsedText = view.transform.FindTransform("Txt_CollapsedPlaceholder").GetComponent<TMP_Text>();
|
||||
|
||||
module.HandleWorldChatMessage(new WorldChatMessageMsg
|
||||
{
|
||||
PlayerId = 2,
|
||||
PlayerName = "Bob",
|
||||
Text = "hello world"
|
||||
});
|
||||
|
||||
Assert.That(collapsedText.text, Is.EqualTo("Bob: hello world"));
|
||||
}
|
||||
|
||||
private WorldChatModule CreateBoundModule()
|
||||
{
|
||||
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Game/Art/UI/Prefab/UI_CommonChat.prefab");
|
||||
Assert.That(prefab, Is.Not.Null);
|
||||
|
||||
view = UnityEngine.Object.Instantiate(prefab);
|
||||
host = new GameObject("WorldChatModuleTestHost");
|
||||
WorldChatModule module = host.AddComponent<WorldChatModule>();
|
||||
module.Initialize(1, _ => { });
|
||||
|
||||
SetPrivateField(module, "view", view);
|
||||
InvokePrivate(module, "CacheControls");
|
||||
InvokePrivate(module, "BindEvents");
|
||||
return module;
|
||||
}
|
||||
|
||||
private static void SetPrivateField(object target, string name, object value)
|
||||
{
|
||||
FieldInfo field = target.GetType().GetField(name, BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
Assert.That(field, Is.Not.Null);
|
||||
field.SetValue(target, value);
|
||||
}
|
||||
|
||||
private static void InvokePrivate(object target, string name)
|
||||
{
|
||||
MethodInfo method = target.GetType().GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
Assert.That(method, Is.Not.Null);
|
||||
method.Invoke(target, Array.Empty<object>());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f953f6437b2d4314b8c8c77d531223e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -3,6 +3,7 @@
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"XWorld.Link",
|
||||
"XWorld.Framework.Shared",
|
||||
"Unity.TextMeshPro",
|
||||
"GUID:119d45324677fde49923efc4ea54b313"
|
||||
],
|
||||
|
||||
@@ -584,6 +584,7 @@ namespace XGame
|
||||
lobbyWorld?.HandleFrame(frame);
|
||||
break;
|
||||
case FrameworkOpcode.WorldChatMessage:
|
||||
Debug.Log("[CSharpClientApp] recv WorldChatMessage bytes=" + (frame.Payload?.Length ?? 0));
|
||||
worldChat?.HandleWorldChatMessage(WorldChatMessageMsg.Decode(frame.Payload));
|
||||
break;
|
||||
}
|
||||
@@ -701,13 +702,25 @@ namespace XGame
|
||||
private void SendWorldChat(string text)
|
||||
{
|
||||
text = (text ?? string.Empty).Trim();
|
||||
if (text.Length == 0 || lobbyNet == null)
|
||||
if (text.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (lobbyNet == null)
|
||||
{
|
||||
Debug.LogWarning("[CSharpClientApp] drop WorldChatSend: lobbyNet is null");
|
||||
return;
|
||||
}
|
||||
if (!lobbyNet.IsConnected)
|
||||
{
|
||||
Debug.LogWarning("[CSharpClientApp] drop WorldChatSend: lobbyNet is disconnected");
|
||||
return;
|
||||
}
|
||||
byte[] payload = new WorldChatSendMsg { Text = text }.Encode();
|
||||
Debug.Log("[CSharpClientApp] send WorldChatSend bytes=" + payload.Length);
|
||||
lobbyNet.SendFramework(
|
||||
FrameworkOpcode.WorldChatSend,
|
||||
new WorldChatSendMsg { Text = text }.Encode());
|
||||
payload);
|
||||
}
|
||||
|
||||
private void CloseWorldChat()
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace XGame
|
||||
private const float DirectionChangeDot = 0.98f;
|
||||
private const float JoystickRadius = 74f;
|
||||
private const float JoystickKnobRadius = 28f;
|
||||
private const float JoystickPadding = 34f;
|
||||
private const float JoystickPadding = 100.0f;
|
||||
private const float JoystickDeadZone = 0.18f;
|
||||
private const float JoystickAcquireRadiusMultiplier = 1.6f;
|
||||
private const float CameraMinDistance = 3.5f;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6508ec0f8203b6d4e868b5a74194a926
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+42
@@ -24,10 +24,13 @@ namespace XGame
|
||||
private TMP_InputField inputField;
|
||||
private TMP_Text inputPlaceholder;
|
||||
private TMP_Text inputValue;
|
||||
private TMP_Text collapsedPreviewText;
|
||||
private ScrollRect scrollMessages;
|
||||
private bool opening;
|
||||
private int openVersion;
|
||||
private int localPlayerId;
|
||||
private string collapsedDefaultText;
|
||||
private string lastCollapsedPreview;
|
||||
private readonly Queue<GameObject> messageRows = new Queue<GameObject>();
|
||||
|
||||
public Action<string> OnSendWorldChat;
|
||||
@@ -89,7 +92,10 @@ namespace XGame
|
||||
inputField = null;
|
||||
inputPlaceholder = null;
|
||||
inputValue = null;
|
||||
collapsedPreviewText = null;
|
||||
scrollMessages = null;
|
||||
collapsedDefaultText = null;
|
||||
lastCollapsedPreview = null;
|
||||
opening = false;
|
||||
}
|
||||
|
||||
@@ -128,6 +134,12 @@ namespace XGame
|
||||
scrollMessages = FindComponent<ScrollRect>("Scroll_Messages");
|
||||
inputPlaceholder = FindComponent<TMP_Text>("Txt_InputPlaceholder");
|
||||
inputValue = FindComponent<TMP_Text>("Txt_InputValue");
|
||||
collapsedPreviewText = FindComponent<TMP_Text>("Txt_CollapsedPlaceholder");
|
||||
if (collapsedPreviewText != null && string.IsNullOrEmpty(collapsedDefaultText))
|
||||
{
|
||||
collapsedDefaultText = collapsedPreviewText.text;
|
||||
}
|
||||
RefreshCollapsedPreview();
|
||||
EnsureInputField();
|
||||
}
|
||||
|
||||
@@ -142,6 +154,8 @@ namespace XGame
|
||||
{
|
||||
inputField.onSubmit.RemoveListener(OnInputSubmitted);
|
||||
inputField.onSubmit.AddListener(OnInputSubmitted);
|
||||
inputField.onEndEdit.RemoveListener(OnInputEndEdit);
|
||||
inputField.onEndEdit.AddListener(OnInputEndEdit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,6 +273,11 @@ namespace XGame
|
||||
SendCurrentInput();
|
||||
}
|
||||
|
||||
private void OnInputEndEdit(string text)
|
||||
{
|
||||
SendCurrentInput();
|
||||
}
|
||||
|
||||
private void SendCurrentInput()
|
||||
{
|
||||
string text = inputField == null ? string.Empty : inputField.text;
|
||||
@@ -271,6 +290,7 @@ namespace XGame
|
||||
{
|
||||
text = text.Substring(0, MaxInputLength);
|
||||
}
|
||||
Debug.Log("[WorldChatModule] send world chat length=" + text.Length);
|
||||
OnSendWorldChat?.Invoke(text);
|
||||
ClearInput();
|
||||
FocusInput();
|
||||
@@ -326,6 +346,28 @@ namespace XGame
|
||||
{
|
||||
StartCoroutine(ScrollToBottomNextFrame());
|
||||
}
|
||||
SetCollapsedPreview(message, isSelf);
|
||||
}
|
||||
|
||||
private void SetCollapsedPreview(WorldChatMessageMsg message, bool isSelf)
|
||||
{
|
||||
string name = isSelf ? "\u6211" : message.PlayerName;
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
name = "\u73A9\u5BB6";
|
||||
}
|
||||
lastCollapsedPreview = name + ": " + (message.Text ?? string.Empty);
|
||||
RefreshCollapsedPreview();
|
||||
}
|
||||
|
||||
private void RefreshCollapsedPreview()
|
||||
{
|
||||
if (collapsedPreviewText != null)
|
||||
{
|
||||
collapsedPreviewText.text = string.IsNullOrEmpty(lastCollapsedPreview)
|
||||
? (collapsedDefaultText ?? string.Empty)
|
||||
: lastCollapsedPreview;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator ScrollToBottomNextFrame()
|
||||
@@ -25,7 +25,7 @@
|
||||
"anchorMin": [0, 0],
|
||||
"anchorMax": [0, 0],
|
||||
"pivot": [0.5, 0.5],
|
||||
"anchoredPosition": [208, 158],
|
||||
"anchoredPosition": [260, 220],
|
||||
"sizeDelta": [148, 148]
|
||||
},
|
||||
"components": [
|
||||
|
||||
@@ -221,6 +221,7 @@ namespace XWorld.Server.Gateway
|
||||
{
|
||||
if (!_sessions.IsConnected(pid))
|
||||
{
|
||||
_logger.Warn($"world chat ignored pid={pid} reason=not_connected");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -228,6 +229,7 @@ namespace XWorld.Server.Gateway
|
||||
string text = (req.Text ?? string.Empty).Trim();
|
||||
if (text.Length == 0)
|
||||
{
|
||||
_logger.Warn($"world chat ignored pid={pid} reason=empty");
|
||||
return;
|
||||
}
|
||||
if (text.Length > MaxWorldChatTextLength)
|
||||
@@ -246,7 +248,9 @@ namespace XWorld.Server.Gateway
|
||||
ServerTimeMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
|
||||
};
|
||||
byte[] encoded = msg.Encode();
|
||||
foreach (int targetPid in _sessions.ConnectedPlayerIds())
|
||||
IReadOnlyList<int> targets = _sessions.ConnectedPlayerIds();
|
||||
_logger.Info($"world chat pid={pid} name={name} length={text.Length} targets={targets.Count}");
|
||||
foreach (int targetPid in targets)
|
||||
{
|
||||
SendFramework(targetPid, FrameworkOpcode.WorldChatMessage, encoded);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user