fix: load lobby characters from config
This commit is contained in:
@@ -41,6 +41,12 @@ public sealed class CharacterConfigDataTests
|
|||||||
Assert.That(item.prefabPath, Is.EqualTo("Assets/Game/Art/Actor/Prefab/cityboy_sk.prefab"));
|
Assert.That(item.prefabPath, Is.EqualTo("Assets/Game/Art/Actor/Prefab/cityboy_sk.prefab"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetPrefabPathReturnsConfiguredPathForCityboy()
|
||||||
|
{
|
||||||
|
Assert.That(CreateConfig().GetPrefabPath(5), Is.EqualTo("Assets/Game/Art/Actor/Prefab/cityboy_sk.prefab"));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void GetByFigureUsesDefaultForAnInvalidIndex()
|
public void GetByFigureUsesDefaultForAnInvalidIndex()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -70,6 +70,12 @@ namespace XGame
|
|||||||
|
|
||||||
return GetDefault();
|
return GetDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetPrefabPath(int figure)
|
||||||
|
{
|
||||||
|
CharacterConfigItem item = GetByFigure(figure);
|
||||||
|
return item == null ? null : item.prefabPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
|
|||||||
@@ -29,18 +29,8 @@ namespace XGame
|
|||||||
public bool IsPlaceholder; // Obj 当前是胶囊占位(真实角色预设还在异步加载),加载完成后会原地升级
|
public bool IsPlaceholder; // Obj 当前是胶囊占位(真实角色预设还在异步加载),加载完成后会原地升级
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly string[] ActorPaths =
|
|
||||||
{
|
|
||||||
"Game/Art/Actor/Prefab/Assasin.prefab",
|
|
||||||
"Game/Art/Actor/Prefab/bls.prefab",
|
|
||||||
"Game/Art/Actor/Prefab/boy.prefab",
|
|
||||||
"Game/Art/Actor/Prefab/captain.prefab",
|
|
||||||
"Game/Art/Actor/Prefab/habit.prefab",
|
|
||||||
"Game/Art/Actor/Prefab/rhino.prefab",
|
|
||||||
"Game/Art/Actor/Prefab/tiger.prefab",
|
|
||||||
};
|
|
||||||
|
|
||||||
private const string JoystickPrefabPath = "Assets/Game/Art/UI/Prefab/UI_LobbyJoystick.prefab";
|
private const string JoystickPrefabPath = "Assets/Game/Art/UI/Prefab/UI_LobbyJoystick.prefab";
|
||||||
|
private const string CharacterConfigResourcePath = "Config/CharacterConfig";
|
||||||
private const string SelectedCharacterKey = "CharacterSwitch.SelectedCharacterId";
|
private const string SelectedCharacterKey = "CharacterSwitch.SelectedCharacterId";
|
||||||
private const float MoveSpeed = 1f;
|
private const float MoveSpeed = 1f;
|
||||||
private const float StopDistance = 0.05f;
|
private const float StopDistance = 0.05f;
|
||||||
@@ -68,6 +58,7 @@ namespace XGame
|
|||||||
private readonly Dictionary<int, GameObject> prefabCache = new Dictionary<int, GameObject>();
|
private readonly Dictionary<int, GameObject> prefabCache = new Dictionary<int, GameObject>();
|
||||||
private readonly HashSet<int> prefabLoading = new HashSet<int>();
|
private readonly HashSet<int> prefabLoading = new HashSet<int>();
|
||||||
private Action<FrameworkOpcode, byte[]> send;
|
private Action<FrameworkOpcode, byte[]> send;
|
||||||
|
private CharacterConfigData characterConfig;
|
||||||
private Transform worldRoot;
|
private Transform worldRoot;
|
||||||
private int selfPlayerId;
|
private int selfPlayerId;
|
||||||
private int selfFigure;
|
private int selfFigure;
|
||||||
@@ -103,12 +94,25 @@ namespace XGame
|
|||||||
send = sendFramework;
|
send = sendFramework;
|
||||||
selfPlayerId = playerId;
|
selfPlayerId = playerId;
|
||||||
selfName = string.IsNullOrEmpty(playerName) ? "P" + playerId : playerName;
|
selfName = string.IsNullOrEmpty(playerName) ? "P" + playerId : playerName;
|
||||||
selfFigure = Mathf.Clamp(figure <= 0 ? GetSelectedFigure() : figure, 1, ActorPaths.Length);
|
if (!EnsureCharacterConfig())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selfFigure = figure <= 0 ? GetSelectedFigure() : ResolveFigure(figure);
|
||||||
|
if (selfFigure <= 0)
|
||||||
|
{
|
||||||
|
Debug.LogError("[LobbyWorldController] cannot initialize without a valid character figure.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
EnsureWorldRoot();
|
EnsureWorldRoot();
|
||||||
worldRoot.gameObject.SetActive(true);
|
worldRoot.gameObject.SetActive(true);
|
||||||
EnsureCamera();
|
EnsureCamera();
|
||||||
StartCoroutine(EnsureJoystickUIAsync());
|
StartCoroutine(EnsureJoystickUIAsync());
|
||||||
GetOrCreateAvatar(selfPlayerId, selfFigure, selfName);
|
if (GetOrCreateAvatar(selfPlayerId, selfFigure, selfName) == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
receivedSelfState = false;
|
receivedSelfState = false;
|
||||||
lastMoveDirection = Vector3.zero;
|
lastMoveDirection = Vector3.zero;
|
||||||
nextMoveCommandTime = 0f;
|
nextMoveCommandTime = 0f;
|
||||||
@@ -151,7 +155,12 @@ namespace XGame
|
|||||||
|
|
||||||
public void RefreshSelectedFigure()
|
public void RefreshSelectedFigure()
|
||||||
{
|
{
|
||||||
int nextFigure = Mathf.Clamp(GetSelectedFigure(), 1, ActorPaths.Length);
|
int nextFigure = GetSelectedFigure();
|
||||||
|
if (nextFigure <= 0)
|
||||||
|
{
|
||||||
|
Debug.LogError("[LobbyWorldController] cannot refresh the selected figure because the character catalog is unavailable.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (selfFigure == nextFigure)
|
if (selfFigure == nextFigure)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -174,6 +183,10 @@ namespace XGame
|
|||||||
}
|
}
|
||||||
|
|
||||||
Avatar nextAvatar = GetOrCreateAvatar(selfPlayerId, selfFigure, selfName);
|
Avatar nextAvatar = GetOrCreateAvatar(selfPlayerId, selfFigure, selfName);
|
||||||
|
if (nextAvatar == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (hadAvatar && nextAvatar.Obj != null)
|
if (hadAvatar && nextAvatar.Obj != null)
|
||||||
{
|
{
|
||||||
nextAvatar.Obj.transform.position = position;
|
nextAvatar.Obj.transform.position = position;
|
||||||
@@ -214,6 +227,10 @@ namespace XGame
|
|||||||
{
|
{
|
||||||
seen.Add(player.PlayerId);
|
seen.Add(player.PlayerId);
|
||||||
Avatar avatar = GetOrCreateAvatar(player.PlayerId, player.Figure, player.Name);
|
Avatar avatar = GetOrCreateAvatar(player.PlayerId, player.Figure, player.Name);
|
||||||
|
if (avatar == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
Vector3 pos = new Vector3(player.X, player.Y, player.Z);
|
Vector3 pos = new Vector3(player.X, player.Y, player.Z);
|
||||||
if (player.PlayerId == selfPlayerId)
|
if (player.PlayerId == selfPlayerId)
|
||||||
{
|
{
|
||||||
@@ -330,6 +347,10 @@ namespace XGame
|
|||||||
{
|
{
|
||||||
avatar = GetOrCreateAvatar(move.PlayerId, 1, "P" + move.PlayerId);
|
avatar = GetOrCreateAvatar(move.PlayerId, 1, "P" + move.PlayerId);
|
||||||
}
|
}
|
||||||
|
if (avatar == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
Vector3 target = new Vector3(move.X, move.Y, move.Z);
|
Vector3 target = new Vector3(move.X, move.Y, move.Z);
|
||||||
if (!avatar.HasServerPosition && avatar.Obj != null)
|
if (!avatar.HasServerPosition && avatar.Obj != null)
|
||||||
{
|
{
|
||||||
@@ -511,7 +532,12 @@ namespace XGame
|
|||||||
|
|
||||||
private Avatar GetOrCreateAvatar(int playerId, int figure, string name)
|
private Avatar GetOrCreateAvatar(int playerId, int figure, string name)
|
||||||
{
|
{
|
||||||
figure = Mathf.Clamp(figure <= 0 ? 1 : figure, 1, ActorPaths.Length);
|
figure = ResolveFigure(figure);
|
||||||
|
if (figure <= 0)
|
||||||
|
{
|
||||||
|
Debug.LogError("[LobbyWorldController] cannot resolve figure because the character catalog is unavailable.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (avatars.TryGetValue(playerId, out Avatar avatar))
|
if (avatars.TryGetValue(playerId, out Avatar avatar))
|
||||||
{
|
{
|
||||||
if (avatar.Figure == figure)
|
if (avatar.Figure == figure)
|
||||||
@@ -594,8 +620,16 @@ namespace XGame
|
|||||||
|
|
||||||
private IEnumerator CoLoadActorPrefab(int figure)
|
private IEnumerator CoLoadActorPrefab(int figure)
|
||||||
{
|
{
|
||||||
|
string prefabPath = GetPrefabPath(figure);
|
||||||
|
if (string.IsNullOrEmpty(prefabPath))
|
||||||
|
{
|
||||||
|
prefabLoading.Remove(figure);
|
||||||
|
Debug.LogError("[LobbyWorldController] missing prefab path for figure=" + figure);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
UObject loaded = null;
|
UObject loaded = null;
|
||||||
yield return XResLoader.coLoadRes(ActorPaths[figure - 1], typeof(GameObject), o => loaded = o);
|
yield return XResLoader.coLoadRes(prefabPath, typeof(GameObject), o => loaded = o);
|
||||||
prefabLoading.Remove(figure);
|
prefabLoading.Remove(figure);
|
||||||
GameObject prefab = loaded as GameObject;
|
GameObject prefab = loaded as GameObject;
|
||||||
if (prefab == null)
|
if (prefab == null)
|
||||||
@@ -1201,21 +1235,63 @@ namespace XGame
|
|||||||
return Vector2.Distance(screenPosition, center) <= radius * JoystickAcquireRadiusMultiplier;
|
return Vector2.Distance(screenPosition, center) <= radius * JoystickAcquireRadiusMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetSelectedFigure()
|
private bool EnsureCharacterConfig()
|
||||||
{
|
{
|
||||||
string selected = PlayerPrefs.GetString(SelectedCharacterKey, string.Empty).ToLowerInvariant();
|
if (characterConfig != null)
|
||||||
for (int i = 0; i < ActorPaths.Length; i++)
|
|
||||||
{
|
{
|
||||||
string path = ActorPaths[i].ToLowerInvariant();
|
return characterConfig.GetDefault() != null;
|
||||||
int slash = path.LastIndexOf('/');
|
}
|
||||||
int dot = path.LastIndexOf('.');
|
|
||||||
string id = dot > slash ? path.Substring(slash + 1, dot - slash - 1) : path;
|
TextAsset asset = Resources.Load<TextAsset>(CharacterConfigResourcePath);
|
||||||
if (id == selected)
|
if (asset == null)
|
||||||
{
|
{
|
||||||
return i + 1;
|
Debug.LogError("[LobbyWorldController] missing character config: Resources/" + CharacterConfigResourcePath + ".json");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
characterConfig = JsonUtility.FromJson<CharacterConfigData>(asset.text);
|
||||||
}
|
}
|
||||||
return 1;
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError("[LobbyWorldController] parse character config failed: " + ex.Message);
|
||||||
|
characterConfig = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (characterConfig == null || characterConfig.GetDefault() == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("[LobbyWorldController] character config is empty or invalid.");
|
||||||
|
characterConfig = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ResolveFigure(int figure)
|
||||||
|
{
|
||||||
|
if (!EnsureCharacterConfig())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return characterConfig.GetFigure(characterConfig.GetByFigure(figure));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetSelectedFigure()
|
||||||
|
{
|
||||||
|
if (!EnsureCharacterConfig())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return characterConfig.GetFigure(PlayerPrefs.GetString(SelectedCharacterKey, string.Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetPrefabPath(int figure)
|
||||||
|
{
|
||||||
|
return EnsureCharacterConfig() ? characterConfig.GetPrefabPath(figure) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user