107 lines
4.5 KiB
C#
107 lines
4.5 KiB
C#
using Opsive.BehaviorDesigner.Runtime.Tasks;
|
||
using Opsive.BehaviorDesigner.Runtime.Tasks.Conditionals;
|
||
using Opsive.GraphDesigner.Runtime;
|
||
using Opsive.GraphDesigner.Runtime.Variables;
|
||
using Opsive.Shared.Utility;
|
||
using SLSUtilities.General;
|
||
using UnityEngine;
|
||
|
||
namespace Cielonos.MainGame.Characters.AI
|
||
{
|
||
[Description("使用伪随机分布(PRD/稳定概率)进行掷骰判定。连续判定失败会提升下次触发几率,判定成功后重置。用于防脸黑与连爆,制造更为平滑均匀的“伪随机”发生感。")]
|
||
[NodeIcon("69bf50f8923f54c4c8bb8e258883a411", "6c5770241610a4c4aae4ac3af0ac8bf8")]
|
||
[Category("Cielonos")]
|
||
public class StableRandomProbability : Conditional
|
||
{
|
||
[Tooltip("当前节点期望判定的成功概率(0~1)。")]
|
||
public SharedVariable<float> successProbability = 0.25f;
|
||
|
||
[Tooltip("是否启用跨节点共享概率机制。启用后,多个绑定了同一 SharedStableProbability 变量的 StableRandomProbability 节点将共享同一个 N 计数(但可独立设定各自的目标概率 P)。如果不启用,则每个节点独立追踪自己的 N 计数。")]
|
||
public bool useSharedProbability = false;
|
||
|
||
[Tooltip("【可选】跨节点共享状态槽。如果需要多个不同的判定节点(如:连续3次不同的普攻)共享同一个失败累计次数 N,请绑定同一个 SharedStableProbability 变量")]
|
||
public SharedVariable<StableProbability> sharedStableProbability;
|
||
|
||
[Tooltip("冷却时间(秒)。在此期间内如果节点被每帧重复求值(由于条件打断 Lower Priority/Both),将直接返回上一次的判定结果,不会进行新的掷骰。配置为 0 则不使用冷却。")]
|
||
public SharedVariable<float> rollCooldown = 0.5f;
|
||
|
||
private float _lastRollTime = -999f;
|
||
private bool _lastResult = false;
|
||
|
||
private StableProbability _internalProbability;
|
||
|
||
public override void OnAwake()
|
||
{
|
||
// 如果用户没有绑定共享变量,或者绑定了但由于某种原因无法引用
|
||
if (!useSharedProbability)
|
||
{
|
||
// 创建此节点私有的稳定概率对象
|
||
_internalProbability = new StableProbability(successProbability.Value);
|
||
}
|
||
else
|
||
{
|
||
// 如果绑定了共享变量,但在整棵树第一次唤醒时尚未初始化其值实例,则由此节点代为创建
|
||
if (sharedStableProbability.Value == null)
|
||
{
|
||
sharedStableProbability.Value = new StableProbability(successProbability.Value);
|
||
}
|
||
}
|
||
}
|
||
|
||
public override TaskStatus OnUpdate()
|
||
{
|
||
if (rollCooldown != null && rollCooldown.Value > 0f)
|
||
{
|
||
if (Time.time - _lastRollTime < rollCooldown.Value)
|
||
{
|
||
return _lastResult ? TaskStatus.Success : TaskStatus.Failure;
|
||
}
|
||
}
|
||
|
||
StableProbability prdInstance = null;
|
||
|
||
if (useSharedProbability && sharedStableProbability.Value != null)
|
||
{
|
||
prdInstance = sharedStableProbability.Value;
|
||
// 实时更新外接共享实例的期望概率 (防止外部在游戏运行中用其他 Action 更改了目标百分比)
|
||
prdInstance.Probability = successProbability.Value;
|
||
}
|
||
else
|
||
{
|
||
prdInstance = _internalProbability;
|
||
if (prdInstance != null)
|
||
{
|
||
prdInstance.Probability = successProbability.Value;
|
||
}
|
||
}
|
||
|
||
if (prdInstance == null)
|
||
{
|
||
return TaskStatus.Failure;
|
||
}
|
||
|
||
// 执行 Roll 判定并自动增加 N 计数 / 重置 N 计数
|
||
Debug.Log($"当前实际概率: {prdInstance.C * prdInstance.N:F2}");
|
||
bool isSuccess = prdInstance.Roll();
|
||
_lastResult = isSuccess;
|
||
_lastRollTime = Time.time;
|
||
return isSuccess ? TaskStatus.Success : TaskStatus.Failure;
|
||
}
|
||
|
||
public override void Reset()
|
||
{
|
||
base.Reset();
|
||
successProbability = 0.25f;
|
||
rollCooldown = 0.5f;
|
||
_lastRollTime = -999f;
|
||
_lastResult = false;
|
||
useSharedProbability = false;
|
||
sharedStableProbability = null;
|
||
if (_internalProbability != null)
|
||
{
|
||
_internalProbability.Reset();
|
||
}
|
||
}
|
||
}
|
||
}
|