FututeWand初步
This commit is contained in:
@@ -52,6 +52,12 @@ namespace Cielonos.MainGame.Characters
|
||||
}
|
||||
}
|
||||
|
||||
protected override void InitializeSubcontrollers()
|
||||
{
|
||||
base.InitializeSubcontrollers();
|
||||
behaviorSc?.Initialize();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@@ -93,9 +99,7 @@ namespace Cielonos.MainGame.Characters
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected virtual void UpdateGetHit()
|
||||
{
|
||||
_getHitTimer -= selfTimeSm.DeltaTime;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Cielonos.MainGame.Buffs.Character;
|
||||
using Opsive.BehaviorDesigner.Runtime;
|
||||
using SLSUtilities.General;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
@@ -18,10 +19,74 @@ namespace Cielonos.MainGame.Characters
|
||||
navMeshAgent.isStopped = true;
|
||||
animatorMoveSpeedX = new LerpFloat(0f, 5f);
|
||||
animatorMoveSpeedZ = new LerpFloat(0f, 5f);
|
||||
//owner.selfTimeSm.timeScaleObservable.Subscribe(OnTimeScaleChanged).AddTo(owner);
|
||||
}
|
||||
|
||||
private Vector3 lastDirection;
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────
|
||||
// Time Scale Sync
|
||||
// ────────────────────────────────────────────────────────────────────
|
||||
|
||||
private float unscaledNavSpeed;
|
||||
private float unscaledNavAccel;
|
||||
private float unscaledNavAngularSpeed;
|
||||
private bool wasNavStoppedBeforePause;
|
||||
private float lastTimeScale = -1f;
|
||||
private bool isTimePaused = false;
|
||||
|
||||
private void OnTimeScaleChanged(float timeScale)
|
||||
{
|
||||
if (navMeshAgent == null || !navMeshAgent.enabled)
|
||||
{
|
||||
if (timeScale == 0f && !isTimePaused)
|
||||
{
|
||||
isTimePaused = true;
|
||||
}
|
||||
else if (timeScale > 0f && isTimePaused)
|
||||
{
|
||||
isTimePaused = false;
|
||||
}
|
||||
lastTimeScale = timeScale;
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeScale == 0f)
|
||||
{
|
||||
if (!isTimePaused)
|
||||
{
|
||||
isTimePaused = true;
|
||||
wasNavStoppedBeforePause = navMeshAgent.isStopped;
|
||||
navMeshAgent.isStopped = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
navMeshAgent.isStopped = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isTimePaused)
|
||||
{
|
||||
isTimePaused = false;
|
||||
navMeshAgent.isStopped = wasNavStoppedBeforePause;
|
||||
}
|
||||
|
||||
if (Mathf.Abs(navMeshAgent.speed - (unscaledNavSpeed * timeScale)) > 0.01f)
|
||||
{
|
||||
unscaledNavSpeed = navMeshAgent.speed;
|
||||
unscaledNavAccel = navMeshAgent.acceleration;
|
||||
unscaledNavAngularSpeed = navMeshAgent.angularSpeed;
|
||||
}
|
||||
|
||||
navMeshAgent.speed = unscaledNavSpeed * timeScale;
|
||||
navMeshAgent.acceleration = unscaledNavAccel * timeScale;
|
||||
navMeshAgent.angularSpeed = unscaledNavAngularSpeed * timeScale;
|
||||
}
|
||||
|
||||
lastTimeScale = timeScale;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
@@ -2,6 +2,8 @@ using System.Collections.Generic;
|
||||
using Opsive.BehaviorDesigner.Runtime;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
using UniRx;
|
||||
using Cielonos.MainGame.Buffs.Character;
|
||||
|
||||
namespace Cielonos.MainGame.Characters
|
||||
{
|
||||
@@ -13,6 +15,37 @@ namespace Cielonos.MainGame.Characters
|
||||
public NavMeshAgent navMeshAgent;
|
||||
public Dictionary<string, Subtree> subBehaviorTrees;
|
||||
|
||||
private System.IDisposable timeScaleDisposable;
|
||||
private bool isTimePaused = false;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
owner.selfTimeSm.timeScaleObservable.Subscribe(OnTimeScaleChanged).AddTo(owner);
|
||||
}
|
||||
|
||||
private void OnTimeScaleChanged(float timeScale)
|
||||
{
|
||||
if (mainBehaviorTree == null) return;
|
||||
|
||||
if (timeScale == 0f)
|
||||
{
|
||||
if (!isTimePaused)
|
||||
{
|
||||
isTimePaused = true;
|
||||
mainBehaviorTree.StopBehavior(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isTimePaused)
|
||||
{
|
||||
isTimePaused = false;
|
||||
mainBehaviorTree.StartBehavior();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AutomataEvent
|
||||
{
|
||||
public float Timestamp;
|
||||
|
||||
@@ -72,11 +72,120 @@ namespace Cielonos.MainGame.Characters
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 瞬移/闪现至目标位置(支持高速移动过程)
|
||||
/// 动态追踪折跃:在移动过程中实时追踪目标位置,确保降落时完美对齐且不产生重叠
|
||||
/// </summary>
|
||||
/// <param name="target">折跃追踪的目标敌人</param>
|
||||
/// <param name="duration">移动时间(秒)。若为 0 则为即时瞬移</param>
|
||||
/// <param name="stopDistance">目标表面前的停止缓冲距离(默认为 1.0f)</param>
|
||||
/// <param name="onComplete">完成回调</param>
|
||||
public void Teleport(CharacterBase target, float duration, float stopDistance = 1.0f, Action onComplete = null)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
Teleport(owner.transform.position, duration, onComplete);
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 如果是即时瞬移(duration <= 0)
|
||||
if (duration <= 0f)
|
||||
{
|
||||
Vector3 instantDest = GetSafePositionNearTarget(target, stopDistance);
|
||||
if (NavMesh.SamplePosition(instantDest, out NavMeshHit hit, 5.0f, NavMesh.AllAreas))
|
||||
{
|
||||
instantDest = hit.position;
|
||||
}
|
||||
PerformMove(instantDest);
|
||||
SmartTurnToTarget(target, 360f, true);
|
||||
onComplete?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 带有追踪的动态折跃位移
|
||||
Vector3 startPos = owner.transform.position;
|
||||
|
||||
// 赋予无敌状态
|
||||
owner.statusSm.AddStatus(StatusType.Invincible, duration);
|
||||
|
||||
// 锁定移动和旋转输入(使用高优先级 99 隔离普通移动控制)
|
||||
canMove.Modify(false, 99);
|
||||
canRotate.Modify(false, 99);
|
||||
|
||||
// 开启残影生成(位移开始时生成单个残影)
|
||||
var afterImageGen = owner.GetComponent<AfterImageGenerator>();
|
||||
if (afterImageGen != null)
|
||||
{
|
||||
afterImageGen.CreateAfterImageSnapshot();
|
||||
}
|
||||
|
||||
// 获取并临时关闭物理/寻路组件
|
||||
var agent = owner.GetComponent<NavMeshAgent>();
|
||||
bool wasAgentActive = agent != null && agent.isActiveAndEnabled;
|
||||
if (wasAgentActive) agent.enabled = false;
|
||||
|
||||
var cc = owner.collisionSc != null ? owner.collisionSc.characterController : null;
|
||||
bool wasCcActive = cc != null && cc.enabled;
|
||||
if (wasCcActive) cc.enabled = false;
|
||||
|
||||
var rb = owner.collisionSc != null ? owner.collisionSc.mainRigidbody : null;
|
||||
bool wasRbKinematic = rb != null && rb.isKinematic;
|
||||
if (rb != null) rb.isKinematic = true;
|
||||
|
||||
// 使用本地计时器,更新过程中的插值位移并实时朝向目标
|
||||
float elapsed = 0f;
|
||||
owner.selfTimeSm.AddLocalTimer(duration,
|
||||
onComplete: () =>
|
||||
{
|
||||
// 确保移动完毕并最终强制对齐
|
||||
Vector3 finalDest = GetSafePositionNearTarget(target, stopDistance);
|
||||
if (NavMesh.SamplePosition(finalDest, out NavMeshHit hit, 5.0f, NavMesh.AllAreas))
|
||||
{
|
||||
finalDest = hit.position;
|
||||
}
|
||||
owner.transform.position = finalDest;
|
||||
|
||||
// 恢复组件状态
|
||||
if (rb != null) rb.isKinematic = wasRbKinematic;
|
||||
if (cc != null && wasCcActive) cc.enabled = true;
|
||||
if (agent != null && wasAgentActive)
|
||||
{
|
||||
agent.enabled = true;
|
||||
agent.Warp(finalDest);
|
||||
}
|
||||
|
||||
// 解锁移动和旋转输入
|
||||
canMove.Modify(true, 99);
|
||||
canRotate.Modify(true, 99);
|
||||
|
||||
// 最终朝向目标
|
||||
SmartTurnToTarget(target, 360f, true);
|
||||
|
||||
// 触发完成回调
|
||||
onComplete?.Invoke();
|
||||
},
|
||||
onUpdate: () =>
|
||||
{
|
||||
elapsed += DeltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / duration);
|
||||
|
||||
// 实时获取当前目标最新位置对应的安全落点,实现平滑追踪
|
||||
Vector3 currentDest = GetSafePositionNearTarget(target, stopDistance);
|
||||
Vector3 currentPos = Vector3.Lerp(startPos, currentDest, t);
|
||||
|
||||
if (rb != null) rb.position = currentPos;
|
||||
owner.transform.position = currentPos;
|
||||
|
||||
// 移动中保持看向目标
|
||||
SmartTurnToTarget(target, 360f, true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 静态坐标折跃:瞬移/闪现至目标位置(支持高速移动过程,无追踪)
|
||||
/// </summary>
|
||||
/// <param name="destination">目标位置</param>
|
||||
/// <param name="duration">移动时间(秒)。若为 0 则为即时瞬移</param>
|
||||
/// <param name="onComplete">瞬移完成后(或即时瞬移时)的回调函数,可用于生成 VFX/音效</param>
|
||||
/// <param name="onComplete">瞬移完成后(或即时瞬移时)的回调函数</param>
|
||||
public void Teleport(Vector3 destination, float duration, Action onComplete = null)
|
||||
{
|
||||
// 1. 进行 NavMesh 采样验证(寻找最近的可行进点)
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Cielonos.MainGame.Characters
|
||||
public partial class SelfTimeSubmodule : SubmoduleBase<CharacterBase>
|
||||
{
|
||||
private TimeManager timeManager => TimeManager.Instance;
|
||||
private IObservable<float> timeScaleObservable;
|
||||
public IObservable<float> timeScaleObservable { get; private set; }
|
||||
|
||||
public FloatReactiveProperty localTimeScale;
|
||||
private float globalTimeScale => timeManager.globalTimeScale.Value;
|
||||
|
||||
@@ -40,12 +40,15 @@ namespace Cielonos.MainGame.Characters
|
||||
{
|
||||
backpackSm.ObtainItem<Polychrome>();
|
||||
backpackSm.ObtainItem<FutureWand>();
|
||||
backpackSm.ObtainItem<DualHarmony>();
|
||||
//backpackSm.ObtainItem<DualHarmony>();
|
||||
backpackSm.ObtainItem<Ascension>();
|
||||
backpackSm.ObtainItem<Passion>();
|
||||
backpackSm.ObtainItem<ThermalDetonator>();
|
||||
backpackSm.ObtainItem<PhotonWarper>();
|
||||
|
||||
backpackSm.ObtainItem<PhotonDissociator>();
|
||||
backpackSm.ObtainItem<PhotonAccelerator>();
|
||||
backpackSm.ObtainItem<PhotonPolarizer>();
|
||||
backpackSm.ObtainItem<DisorderedPhotonCollector>();
|
||||
foreach (MainWeaponBase mainWeapon in backpackSm.mainWeapons)
|
||||
{
|
||||
equipmentSm.PrepareMainWeapon(mainWeapon);
|
||||
|
||||
Reference in New Issue
Block a user