做不出来
This commit is contained in:
@@ -6,13 +6,27 @@ using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Characters.AI
|
||||
{
|
||||
public enum AttributeCompareMode
|
||||
{
|
||||
Value,
|
||||
Percentage
|
||||
}
|
||||
|
||||
[Category("Cielonos")]
|
||||
[Description("检查一个属性是否满足条件")]
|
||||
public class CheckAttribute : AutomataConditionalBase
|
||||
{
|
||||
private AttributeSubmodule attributeSm;
|
||||
|
||||
[Tooltip("检查的属性名,例如 Health")]
|
||||
public string attributeName;
|
||||
|
||||
[Tooltip("比较模式:数值或百分比")]
|
||||
public AttributeCompareMode compareMode = AttributeCompareMode.Value;
|
||||
|
||||
[Tooltip("百分比比较时作为分母的属性名。留空时默认使用 Maximum + attributeName (例如 MaximumHealth)")]
|
||||
public string percentBaseAttribute;
|
||||
|
||||
public bool isBiggerThan;
|
||||
public SharedVariable<float> biggerThanValue;
|
||||
public bool isSmallerThan;
|
||||
@@ -30,18 +44,32 @@ namespace Cielonos.MainGame.Characters.AI
|
||||
public override TaskStatus OnUpdate()
|
||||
{
|
||||
float attributeValue = attributeSm[attributeName];
|
||||
|
||||
float compareBiggerValue = biggerThanValue.Value;
|
||||
float compareSmallerValue = smallerThanValue.Value;
|
||||
float compareEqualValue = equalValue.Value;
|
||||
|
||||
if (compareMode == AttributeCompareMode.Percentage)
|
||||
{
|
||||
string baseAttr = string.IsNullOrEmpty(percentBaseAttribute) ? "Maximum" + attributeName : percentBaseAttribute;
|
||||
float baseValue = attributeSm[baseAttr];
|
||||
// 使用逆运算进行比较,防止分母为 0 导致浮点数异常
|
||||
compareBiggerValue = baseValue * compareBiggerValue;
|
||||
compareSmallerValue = baseValue * compareSmallerValue;
|
||||
compareEqualValue = baseValue * compareEqualValue;
|
||||
}
|
||||
|
||||
if (isBiggerThan && attributeValue <= biggerThanValue.Value)
|
||||
if (isBiggerThan && attributeValue <= compareBiggerValue)
|
||||
{
|
||||
return TaskStatus.Failure;
|
||||
}
|
||||
|
||||
if (isSmallerThan && attributeValue >= smallerThanValue.Value)
|
||||
if (isSmallerThan && attributeValue >= compareSmallerValue)
|
||||
{
|
||||
return TaskStatus.Failure;
|
||||
}
|
||||
|
||||
if (isEqual && Mathf.Abs(attributeValue - equalValue.Value) > tolerance)
|
||||
if (isEqual && Mathf.Abs(attributeValue - compareEqualValue) > tolerance)
|
||||
{
|
||||
return TaskStatus.Failure;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using Opsive.Shared.Editor.UIElements;
|
||||
using Opsive.Shared.Editor.UIElements.Controls;
|
||||
using Opsive.Shared.Editor.UIElements.Controls.Types;
|
||||
using Cielonos.MainGame.Characters.AI;
|
||||
|
||||
namespace Cielonos.MainGame.Characters.AI.Editor
|
||||
{
|
||||
[ControlType(typeof(CheckAttribute))]
|
||||
public class CheckAttributeControl : TypeControlBase
|
||||
{
|
||||
public override bool UseLabel => false;
|
||||
|
||||
protected override VisualElement GetControl(TypeControlInput input)
|
||||
{
|
||||
var task = input.Value as CheckAttribute;
|
||||
if (task == null) return new Label("[CheckAttribute] 节点引用无效");
|
||||
|
||||
var container = new VisualElement();
|
||||
|
||||
var baseAttrContainer = new VisualElement();
|
||||
|
||||
// 动态显示隐藏逻辑
|
||||
Action updateVisibilities = () => {
|
||||
if (task.compareMode == AttributeCompareMode.Percentage)
|
||||
{
|
||||
baseAttrContainer.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
else
|
||||
{
|
||||
baseAttrContainer.style.display = DisplayStyle.None;
|
||||
}
|
||||
};
|
||||
|
||||
// 1. 添加常规控制字段
|
||||
AddField(input, task, "attributeName", container);
|
||||
AddField(input, task, "compareMode", container, updateVisibilities);
|
||||
|
||||
// 2. 百分比分母属性配置字段
|
||||
AddField(input, task, "percentBaseAttribute", baseAttrContainer);
|
||||
container.Add(baseAttrContainer);
|
||||
|
||||
// 3. 比较判断及比较值字段
|
||||
AddField(input, task, "isBiggerThan", container);
|
||||
AddField(input, task, "biggerThanValue", container);
|
||||
AddField(input, task, "isSmallerThan", container);
|
||||
AddField(input, task, "smallerThanValue", container);
|
||||
AddField(input, task, "isEqual", container);
|
||||
AddField(input, task, "equalValue", container);
|
||||
|
||||
// 4. 初始化可见性
|
||||
updateVisibilities();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private void AddField(TypeControlInput input, CheckAttribute task, string fieldName, VisualElement container, Action postChangeAction = null)
|
||||
{
|
||||
FieldInspectorView.AddField(input.UnityObject, task, fieldName, container, (object newValue) =>
|
||||
{
|
||||
if (newValue != null)
|
||||
{
|
||||
if (newValue is CheckAttribute)
|
||||
{
|
||||
// Opsive 通过底层 Binding 更新了该 Task 对象,无需手动赋值
|
||||
}
|
||||
else
|
||||
{
|
||||
FieldInfo fieldInfo = typeof(CheckAttribute).GetField(fieldName);
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
if (fieldInfo.FieldType.IsEnum)
|
||||
{
|
||||
try
|
||||
{
|
||||
fieldInfo.SetValue(task, Enum.ToObject(fieldInfo.FieldType, newValue));
|
||||
}
|
||||
catch
|
||||
{
|
||||
fieldInfo.SetValue(task, newValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fieldInfo.SetValue(task, newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input.OnChangeEvent(task);
|
||||
postChangeAction?.Invoke();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f22bb5b924e17b245a8eb0201de79c84
|
||||
@@ -16,12 +16,14 @@ namespace Cielonos.MainGame.Characters.AI
|
||||
public string tagPrefix = "EnemyAttack";
|
||||
|
||||
private MusicBeatSystem _beatSystem;
|
||||
private ILocalRhythmTracker _localTracker;
|
||||
private BeatMarker _lastTriggeredBeat;
|
||||
|
||||
public override void OnAwake()
|
||||
{
|
||||
base.OnAwake();
|
||||
_beatSystem = CombatManager.GetCombatSystem<MusicBeatSystem>();
|
||||
_localTracker = gameObject.GetComponent<ILocalRhythmTracker>();
|
||||
}
|
||||
|
||||
public override void OnStart()
|
||||
@@ -31,12 +33,29 @@ namespace Cielonos.MainGame.Characters.AI
|
||||
|
||||
public override TaskStatus OnUpdate()
|
||||
{
|
||||
if (_beatSystem == null || !_beatSystem.IsActive || _beatSystem.CurrentBeatData == null)
|
||||
MusicBeatData activeData = null;
|
||||
float currentSongTime = 0f;
|
||||
bool isActive = false;
|
||||
|
||||
if (_localTracker != null && _localTracker.CurrentBeatData != null)
|
||||
{
|
||||
activeData = _localTracker.CurrentBeatData;
|
||||
currentSongTime = _localTracker.CurrentSongTime;
|
||||
isActive = _localTracker.IsPlaying;
|
||||
}
|
||||
else if (_beatSystem != null && _beatSystem.IsActive && _beatSystem.CurrentBeatData != null)
|
||||
{
|
||||
activeData = _beatSystem.CurrentBeatData;
|
||||
currentSongTime = _beatSystem.CurrentSongTime;
|
||||
isActive = _beatSystem.IsPlaying;
|
||||
}
|
||||
|
||||
if (!isActive || activeData == null)
|
||||
{
|
||||
return TaskStatus.Failure;
|
||||
}
|
||||
|
||||
BeatMarker targetBeat = FindNextMatchingBeat();
|
||||
BeatMarker targetBeat = FindNextMatchingBeat(activeData, currentSongTime);
|
||||
if (targetBeat == null)
|
||||
{
|
||||
return TaskStatus.Failure;
|
||||
@@ -48,7 +67,7 @@ namespace Cielonos.MainGame.Characters.AI
|
||||
return TaskStatus.Failure;
|
||||
}
|
||||
|
||||
float timeUntilBeat = targetBeat.time - _beatSystem.CurrentSongTime;
|
||||
float timeUntilBeat = targetBeat.time - currentSongTime;
|
||||
|
||||
// Check if we are within the lead time window
|
||||
if (timeUntilBeat <= leadTime)
|
||||
@@ -60,10 +79,9 @@ namespace Cielonos.MainGame.Characters.AI
|
||||
return TaskStatus.Failure;
|
||||
}
|
||||
|
||||
private BeatMarker FindNextMatchingBeat()
|
||||
private BeatMarker FindNextMatchingBeat(MusicBeatData activeData, float currentTime)
|
||||
{
|
||||
float currentTime = _beatSystem.CurrentSongTime;
|
||||
var markers = _beatSystem.CurrentBeatData.beatMarkers;
|
||||
var markers = activeData.beatMarkers;
|
||||
|
||||
if (markers == null) return null;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user