feat: trigger fighter behavior from lobby action button

This commit is contained in:
ud18010
2026-07-30 17:00:31 +08:00
parent 03cee1036d
commit e9efa8a567
9 changed files with 674 additions and 0 deletions
@@ -0,0 +1,89 @@
using System;
using System.Collections;
using System.IO;
using System.Text;
using UnityEngine;
using XWorld.Framework.ActorBehavior;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace XGame.ActorBehavior
{
public static class ActorBehaviorConfigLoader
{
public const string ConfigRoot = "Assets/Game/Config/ActorBehavior";
public static IEnumerator LoadCatalog(string type, Action<BehaviorConfigCatalog> onLoaded)
{
string skillJson = null;
string flyObjectJson = null;
string buffJson = null;
yield return LoadText(GetSkillPath(type), text => skillJson = text);
yield return LoadText(GetFlyObjectPath(type), text => flyObjectJson = text);
yield return LoadText(GetBuffPath(type), text => buffJson = text);
if (string.IsNullOrEmpty(skillJson) || string.IsNullOrEmpty(flyObjectJson) || string.IsNullOrEmpty(buffJson))
{
Debug.LogError("[ActorBehaviorConfigLoader] missing behavior config for type=" + type);
onLoaded?.Invoke(null);
yield break;
}
try
{
onLoaded?.Invoke(BehaviorConfigCatalog.LoadFromJson(type, skillJson, flyObjectJson, buffJson));
}
catch (Exception ex)
{
Debug.LogError("[ActorBehaviorConfigLoader] parse behavior config failed type=" + type + ", error=" + ex.Message);
onLoaded?.Invoke(null);
}
}
public static string GetSkillPath(string type)
{
return ConfigRoot + "/" + type + "_skill.json";
}
public static string GetFlyObjectPath(string type)
{
return ConfigRoot + "/" + type + "_flyobj.json";
}
public static string GetBuffPath(string type)
{
return ConfigRoot + "/" + type + "_buff.json";
}
private static IEnumerator LoadText(string assetPath, Action<string> onLoaded)
{
#if UNITY_EDITOR
TextAsset asset = AssetDatabase.LoadAssetAtPath<TextAsset>(assetPath);
if (asset != null)
{
onLoaded?.Invoke(asset.text);
yield break;
}
#endif
string filePath = Path.Combine(Application.dataPath, assetPath.StartsWith("Assets/", StringComparison.Ordinal)
? assetPath.Substring("Assets/".Length)
: assetPath);
if (File.Exists(filePath))
{
onLoaded?.Invoke(File.ReadAllText(filePath, Encoding.UTF8));
yield break;
}
byte[] bytes = null;
string runtimePath = assetPath.StartsWith("Assets/", StringComparison.Ordinal)
? assetPath.Substring("Assets/".Length)
: assetPath;
yield return XResLoader.coLoadFile(runtimePath, data => bytes = data);
onLoaded?.Invoke(bytes != null ? Encoding.UTF8.GetString(bytes) : null);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 749877ee582c42058dfc794500488f28
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,114 @@
using System.Collections.Generic;
using UnityEngine;
using XGame.VirtualInput;
using XWorld.Framework.ActorBehavior;
namespace XGame.ActorBehavior
{
public sealed class LobbyActorBehaviorRuntime
{
public const string DefaultCatalogType = "fighter";
public const string SecondaryABehaviorId = "quantao01";
public const ActionButtonId SecondaryAButtonId = ActionButtonId.Skill1;
private readonly BehaviorWorld world = new BehaviorWorld(new BehaviorWorldOptions
{
Mode = BehaviorRunMode.ClientPresentation
});
public void RegisterCatalog(BehaviorConfigCatalog catalog)
{
world.RegisterCatalog(catalog);
}
public bool SwitchActorCatalog(int actorId, string catalogType)
{
return world.SwitchActorCatalog(actorId, catalogType);
}
public bool RemoveActor(int actorId)
{
return world.RemoveActor(actorId);
}
public void RegisterOrUpdateActor(int actorId, Vector3 position, Vector3 forward)
{
world.AddActor(new BehaviorActorState
{
ActorId = actorId,
CatalogType = DefaultCatalogType,
TeamId = actorId,
Position = ToBehavior(position),
Forward = ToBehaviorForward(forward),
Radius = 0.5f,
Alive = true
});
}
public BehaviorStartResult HandleActionButton(ActionButtonEvent evt, int actorId, Vector3 fallbackForward)
{
if (!IsSecondaryATrigger(evt))
{
return BehaviorStartResult.Fail("button ignored");
}
BehaviorInput input = CreateDirectionInput(evt, fallbackForward);
return world.TryStartBehavior(actorId, SecondaryABehaviorId, input);
}
public void Tick(float deltaTime)
{
world.Tick(deltaTime);
}
public IReadOnlyList<BehaviorEvent> DrainEvents()
{
return world.DrainEvents();
}
public bool HasActiveBehavior(int actorId)
{
return world.HasActiveBehavior(actorId);
}
public static bool IsSecondaryATrigger(ActionButtonEvent evt)
{
return evt.ButtonId == SecondaryAButtonId
&& !evt.IsCanceled
&& evt.Phase == ActionButtonPhase.Tap;
}
private static BehaviorInput CreateDirectionInput(ActionButtonEvent evt, Vector3 fallbackForward)
{
Vector2 direction = evt.Direction;
if (direction.sqrMagnitude > 0.0001f)
{
return BehaviorInput.FromDirection(direction.x, direction.y);
}
fallbackForward.y = 0f;
if (fallbackForward.sqrMagnitude <= 0.0001f)
{
fallbackForward = Vector3.forward;
}
fallbackForward.Normalize();
return BehaviorInput.FromDirection(fallbackForward.x, fallbackForward.z);
}
private static BehaviorVector3 ToBehavior(Vector3 value)
{
return new BehaviorVector3(value.x, value.y, value.z);
}
private static BehaviorVector3 ToBehaviorForward(Vector3 value)
{
value.y = 0f;
if (value.sqrMagnitude <= 0.0001f)
{
value = Vector3.forward;
}
value.Normalize();
return new BehaviorVector3(value.x, 0f, value.z);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8f25b35931c34b66982c41c5fd329fe2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: