Files
ichni_Official/Assets/Scripts/Game/Components/TransformSubmodule.cs
SoulliesOfficial e9393c64f7 调整谱面开放
2026-04-01 01:35:17 -04:00

196 lines
7.2 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Ichni.RhythmGame.Beatmap;
using UniRx;
using UniRx.Triggers;
using Unity.VisualScripting;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Ichni.RhythmGame
{
public class TransformSubmodule : SubmoduleBase
{
#region [] Transform Cached States
public Vector3 originalPosition;
public Vector3 originalEulerAngles;
public Vector3 originalScale;
public Vector3 positionOffset;
public Vector3 eulerAnglesOffset;
public Vector3 scaleOffset;
public Vector3 currentPosition;
public Vector3 currentEulerAngles;
public Vector3 currentScale;
public bool positionDirtyMark;
public bool eulerAnglesDirtyMark;
public bool scaleDirtyMark;
public bool eulerAnglesOffsetLock;
public IDisposable observer;
#endregion
#region [] Constructors & Initialization
public TransformSubmodule(GameElement attachedGameElement) : base(attachedGameElement)
{
this.originalPosition = Vector3.zero;
this.originalEulerAngles = Vector3.zero;
this.originalScale = Vector3.one;
positionOffset = Vector3.zero;
eulerAnglesOffset = Vector3.zero;
scaleOffset = Vector3.zero;
currentPosition = Vector3.zero;
currentEulerAngles = Vector3.zero;
currentScale = Vector3.one;
positionDirtyMark = true;
eulerAnglesDirtyMark = true;
scaleDirtyMark = true;
eulerAnglesOffsetLock = false;
if (!HaveSameSubmodule && attachedGameElement is IHaveTransformSubmodule host)
{
host.transformSubmodule = this;
host.SetTransformObserver();
}
}
public TransformSubmodule(GameElement attachedGameElement,
Vector3 originalPosition, Vector3 originalEulerAngles, Vector3 originalScale) : base(attachedGameElement)
{
this.originalPosition = originalPosition;
this.originalEulerAngles = originalEulerAngles;
this.originalScale = originalScale;
positionOffset = Vector3.zero;
eulerAnglesOffset = Vector3.zero;
scaleOffset = Vector3.zero;
currentPosition = originalPosition;
currentEulerAngles = originalEulerAngles;
currentScale = originalScale;
positionDirtyMark = true;
eulerAnglesDirtyMark = true;
scaleDirtyMark = true;
eulerAnglesOffsetLock = false;
if (!HaveSameSubmodule && attachedGameElement is IHaveTransformSubmodule host)
{
host.transformSubmodule = this;
host.SetTransformObserver();
}
}
#endregion
#region [] Lifecycle & State Refresh
public override void Refresh()
{
positionDirtyMark = true;
eulerAnglesDirtyMark = true;
scaleDirtyMark = true;
}
private bool HaveAnimation() => attachedGameElement.childElementList
.Any(element => element is Displacement or Swirl or Scale or LookAt);
public override void CheckAndRemoveObservers()
{
if (!HaveAnimation())
{
observer?.Dispose();
}
}
#endregion
}
#region [] Component Interface
public interface IHaveTransformSubmodule
{
TransformSubmodule transformSubmodule { get; set; }
/// <summary>
/// 设置物体Transform的监听顺序为Scale -> EulerAngles -> Position
/// 如果有一些特殊的物体例如CameraElementFolder需要自定义监听可以重写这个方法
/// </summary>
public void SetTransformObserver()
{
// 旧版的 UniRx 各自监听已淘汰,现由 GameManager 中枢在 LateUpdate 统一下发 UpdateTransform()
// 如果有一些特殊物体需要极其特殊时序,可覆盖此方法或手动管理
}
public void UpdateTransform(bool refreshAll = true)
{
if(transformSubmodule == null) return;
GameElement attachedGameElement = transformSubmodule.attachedGameElement;
bool willRefresh = false;
if (transformSubmodule.scaleDirtyMark)
{
transformSubmodule.currentScale = transformSubmodule.originalScale + transformSubmodule.scaleOffset;
attachedGameElement.transform.localScale = transformSubmodule.currentScale;
transformSubmodule.scaleDirtyMark = false;
willRefresh = true;
transformSubmodule.scaleOffset = Vector3.zero;
}
if (!transformSubmodule.eulerAnglesOffsetLock && transformSubmodule.eulerAnglesDirtyMark)
{
transformSubmodule.currentEulerAngles = transformSubmodule.originalEulerAngles + transformSubmodule.eulerAnglesOffset;
attachedGameElement.transform.localEulerAngles = transformSubmodule.currentEulerAngles;
transformSubmodule.eulerAnglesDirtyMark = false;
willRefresh = true;
transformSubmodule.eulerAnglesOffset = Vector3.zero;
}
if (transformSubmodule.positionDirtyMark)
{
transformSubmodule.currentPosition = transformSubmodule.originalPosition + transformSubmodule.positionOffset;
attachedGameElement.transform.localPosition = transformSubmodule.currentPosition;
transformSubmodule.positionDirtyMark = false;
willRefresh = true;
transformSubmodule.positionOffset = Vector3.zero;
}
if(refreshAll && willRefresh)
{
attachedGameElement.Refresh();
}
}
public void UpdateLookAt(LookAt lookAt) // 处理LookAt
{
Transform target = lookAt.targetGameElement.transform;
Transform self = transformSubmodule.attachedGameElement.transform;
if (transformSubmodule.eulerAnglesOffsetLock && transformSubmodule.eulerAnglesDirtyMark)
{
Vector3 lookingDirection = (target.position - self.position).normalized;
Vector3 eulerAnglesOffset = Quaternion.LookRotation(lookingDirection).eulerAngles;
transformSubmodule.eulerAnglesOffset += eulerAnglesOffset;
transformSubmodule.currentEulerAngles = transformSubmodule.originalEulerAngles + transformSubmodule.eulerAnglesOffset;
self.localEulerAngles = transformSubmodule.currentEulerAngles;
transformSubmodule.eulerAnglesDirtyMark = false;
transformSubmodule.eulerAnglesOffsetLock = false;
transformSubmodule.eulerAnglesOffset = Vector3.zero;
}
}
}
#endregion
}