397 lines
12 KiB
C#
397 lines
12 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using AibisDream.Framework;
|
|
using Cinemachine;
|
|
using JetBrains.Annotations;
|
|
using UnityEngine;
|
|
|
|
namespace AibisDream.FixSystem
|
|
{
|
|
public class BodyModuleSystem : MonoBehaviour
|
|
{
|
|
#region 内部索引
|
|
|
|
public List<BodyModule> BodyModules { get; private set; }
|
|
|
|
private GameObject _bodyModuleGroup;
|
|
private GameObject _headModuleGroup;
|
|
|
|
public CableSystem CableSystem { get; private set; }
|
|
[HideInInspector] public HeatMapController heatMapController;
|
|
|
|
#endregion
|
|
|
|
#region 系统状态
|
|
|
|
private readonly ModuleSystemData _data = new();
|
|
public bool inHotErr;
|
|
private int _heatValue;
|
|
public bool isAvailable;
|
|
|
|
private int HeatValue
|
|
{
|
|
get => _heatValue;
|
|
set
|
|
{
|
|
_heatValue = value;
|
|
inHotErr = _heatValue > 0;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
[SerializeField] public float plugSnappingRange = 1f;
|
|
|
|
private Vector3 _headModuleOriginalPos;
|
|
private Coroutine _breathingCoroutine;
|
|
private Coroutine _painShakeCoroutine;
|
|
private Coroutine _continuousPainCoroutine;
|
|
|
|
private void Awake()
|
|
{
|
|
InitComponentRefs();
|
|
}
|
|
|
|
private void InitComponentRefs()
|
|
{
|
|
// 注册数据
|
|
StorageSystem.Instance.RegisterData(_data);
|
|
|
|
// 处理内部索引
|
|
_bodyModuleGroup = transform.Find("Body Module Group")?.gameObject;
|
|
_headModuleGroup = transform.Find("Head Module Group")?.gameObject;
|
|
CableSystem = transform.Find("Cable System").GetComponent<CableSystem>();
|
|
if (transform.Find("HeatMapController"))
|
|
{
|
|
heatMapController = transform.Find("HeatMapController").GetComponent<HeatMapController>();
|
|
}
|
|
|
|
FixSystemCenter.SystemDic.Register(this);
|
|
|
|
// 注册Camera
|
|
var virtualCam = transform.GetComponentInChildren<ICinemachineCamera>();
|
|
CameraKit.Instance.RegisterCamera(CameraEnum.BodyModule, virtualCam);
|
|
var virtualCam2 = transform.Find("Body Module Camera Center").GetComponent<ICinemachineCamera>();
|
|
CameraKit.Instance.RegisterCamera(CameraEnum.BodyModuleCenter, virtualCam2);
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
StorageSystem.Instance.UnregisterData<ModuleSystemData>();
|
|
CameraKit.Instance.UnRegisterCamera(CameraEnum.BodyModule);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 寻找最接近的Module
|
|
/// </summary>
|
|
/// <param name="sourcePos">源位置</param>
|
|
/// <param name="targetModule">最近的Module</param>
|
|
/// <returns>是否能找到目标范围内的Module</returns>
|
|
public bool TryFindClosestModule(Vector3 sourcePos, out BodyModule targetModule)
|
|
{
|
|
var closestDistance = Mathf.Infinity;
|
|
BodyModule closestModule = null;
|
|
|
|
if (BodyModules is not { Count: > 0 })
|
|
{
|
|
targetModule = null;
|
|
return false;
|
|
}
|
|
|
|
// 寻找最接近的BodyModule
|
|
foreach (var bodyModule in BodyModules)
|
|
{
|
|
if (!bodyModule.IsAvailable()) // 检查模块是否可用
|
|
{
|
|
continue; // 跳过不可用的模块
|
|
}
|
|
|
|
float tempDistance = Vector3.Distance(sourcePos, bodyModule.GetSocketPos());
|
|
|
|
if (tempDistance < closestDistance)
|
|
{
|
|
closestDistance = tempDistance;
|
|
closestModule = bodyModule;
|
|
}
|
|
}
|
|
|
|
bool res;
|
|
// 判断距离是否在目标范围内,在的话就返回,不在的话返回个空的
|
|
if (closestDistance < plugSnappingRange && closestModule)
|
|
{
|
|
targetModule = closestModule;
|
|
res = true;
|
|
}
|
|
else
|
|
{
|
|
targetModule = null;
|
|
res = false;
|
|
}
|
|
|
|
HandleModuleSlotState(targetModule);
|
|
return res;
|
|
}
|
|
|
|
private void HandleModuleSlotState([CanBeNull]BodyModule closestModule)
|
|
{
|
|
foreach (var bodyModule in BodyModules)
|
|
{
|
|
if (closestModule && bodyModule == closestModule)
|
|
{
|
|
bodyModule.OpenPlugSlot();
|
|
}
|
|
else
|
|
{
|
|
bodyModule.ClosePlugSlot();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 寻找指定名称的BodyModule
|
|
/// </summary>
|
|
/// <param name="modulename">模块名称</param>
|
|
/// <returns>对应名称的BodyModule</returns>
|
|
public BodyModule FindBodyModuleByName(string modulename)
|
|
{
|
|
return BodyModules?.FirstOrDefault(module => module.data.moduleName == modulename);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置发热
|
|
/// </summary>
|
|
public void SetHeat(int targetHeatValue)
|
|
{
|
|
HeatValue = targetHeatValue;
|
|
|
|
heatMapController.SetTemptureDirect(HeatValue);
|
|
}
|
|
|
|
public void ShowHeatMap()
|
|
{
|
|
heatMapController.ShowHeatMap();
|
|
DisableSystem();
|
|
ResetPlug();
|
|
}
|
|
|
|
public void HideHeatMap()
|
|
{
|
|
heatMapController.HideHeatMap();
|
|
EnableSystem();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 冷却后处理
|
|
/// </summary>
|
|
public void OnCooling()
|
|
{
|
|
if (!inHotErr) return;
|
|
// 系统冷却
|
|
_heatValue = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 插头重新插入初始插孔
|
|
/// </summary>
|
|
public void ResetPlug()
|
|
{
|
|
CableSystem.ResetPlug();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 激活交互系统
|
|
/// </summary>
|
|
public void EnableSystem()
|
|
{
|
|
isAvailable = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 停止交互系统
|
|
/// </summary>
|
|
public void DisableSystem()
|
|
{
|
|
isAvailable = false;
|
|
}
|
|
|
|
public void SwitchModuleGroup(ModuleState moduleState)
|
|
{
|
|
// 先切换Group
|
|
switch (moduleState)
|
|
{
|
|
case ModuleState.Body:
|
|
_bodyModuleGroup?.SetActive(true);
|
|
_headModuleGroup?.SetActive(false);
|
|
break;
|
|
case ModuleState.Head:
|
|
_bodyModuleGroup?.SetActive(false);
|
|
_headModuleGroup?.SetActive(true);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(moduleState), moduleState, null);
|
|
}
|
|
|
|
// 重新获取ModuleGroup
|
|
var root = moduleState == ModuleState.Body ? _bodyModuleGroup : _headModuleGroup;
|
|
BodyModules = root?.GetComponentsInChildren<BodyModule>().ToList();
|
|
// 更改data
|
|
_data.moduleState = moduleState;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 从模块中清除
|
|
/// </summary>
|
|
/// <param name="module">将要被移除的模块</param>
|
|
public void RemoveModule(BodyModule module)
|
|
{
|
|
BodyModules.Remove(module);
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
if (_headModuleGroup != null)
|
|
{
|
|
_headModuleOriginalPos = _headModuleGroup.transform.localPosition;
|
|
StartBreathing();
|
|
}
|
|
}
|
|
|
|
public void StartBreathing()
|
|
{
|
|
if (_breathingCoroutine != null)
|
|
{
|
|
StopCoroutine(_breathingCoroutine);
|
|
}
|
|
_breathingCoroutine = StartCoroutine(BreathingAnimation());
|
|
}
|
|
|
|
public void StopBreathing()
|
|
{
|
|
if (_breathingCoroutine != null)
|
|
{
|
|
StopCoroutine(_breathingCoroutine);
|
|
_breathingCoroutine = null;
|
|
}
|
|
if (_headModuleGroup != null)
|
|
{
|
|
_headModuleGroup.transform.localPosition = _headModuleOriginalPos;
|
|
}
|
|
}
|
|
|
|
private IEnumerator BreathingAnimation()
|
|
{
|
|
float time = 0;
|
|
while (true)
|
|
{
|
|
time += Time.deltaTime;
|
|
float yOffset = Mathf.Sin(time * 2f) * 0.02f; // 调整呼吸幅度和速度
|
|
_headModuleGroup.transform.localPosition = _headModuleOriginalPos + new Vector3(0, yOffset, 0);
|
|
yield return null;
|
|
}
|
|
// ReSharper disable once IteratorNeverReturns
|
|
}
|
|
|
|
public void TriggerPainShake()
|
|
{
|
|
if (_painShakeCoroutine != null)
|
|
{
|
|
StopCoroutine(_painShakeCoroutine);
|
|
}
|
|
_painShakeCoroutine = StartCoroutine(PainShakeAnimation());
|
|
}
|
|
|
|
private IEnumerator PainShakeAnimation()
|
|
{
|
|
StopBreathing();
|
|
float duration = 0.5f;
|
|
float elapsed = 0f;
|
|
Vector3 originalPos = _headModuleGroup.transform.localPosition;
|
|
|
|
while (elapsed < duration)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
float intensity = 1f - (elapsed / duration);
|
|
_headModuleGroup.transform.localPosition = originalPos + new Vector3(
|
|
UnityEngine.Random.Range(-0.1f, 0.1f) * intensity,
|
|
UnityEngine.Random.Range(-0.1f, 0.1f) * intensity,
|
|
UnityEngine.Random.Range(-0.1f, 0.1f) * intensity
|
|
);
|
|
yield return null;
|
|
}
|
|
|
|
_headModuleGroup.transform.localPosition = originalPos;
|
|
StartBreathing();
|
|
}
|
|
|
|
public void StartContinuousPain()
|
|
{
|
|
if (_continuousPainCoroutine != null)
|
|
{
|
|
StopCoroutine(_continuousPainCoroutine);
|
|
}
|
|
_continuousPainCoroutine = StartCoroutine(ContinuousPainAnimation());
|
|
}
|
|
|
|
public void StopContinuousPain()
|
|
{
|
|
if (_continuousPainCoroutine != null)
|
|
{
|
|
StopCoroutine(_continuousPainCoroutine);
|
|
_continuousPainCoroutine = null;
|
|
}
|
|
if (_headModuleGroup != null)
|
|
{
|
|
_headModuleGroup.transform.localPosition = _headModuleOriginalPos;
|
|
}
|
|
StartBreathing();
|
|
}
|
|
|
|
private IEnumerator ContinuousPainAnimation()
|
|
{
|
|
StopBreathing();
|
|
while (true)
|
|
{
|
|
// 抖动阶段
|
|
float shakeDuration = 1f;
|
|
float shakeElapsed = 0f;
|
|
Vector3 originalPos = _headModuleGroup.transform.localPosition;
|
|
|
|
while (shakeElapsed < shakeDuration)
|
|
{
|
|
shakeElapsed += Time.deltaTime;
|
|
_headModuleGroup.transform.localPosition = originalPos + new Vector3(
|
|
UnityEngine.Random.Range(-0.05f, 0.05f),
|
|
UnityEngine.Random.Range(-0.05f, 0.05f),
|
|
UnityEngine.Random.Range(-0.05f, 0.05f)
|
|
);
|
|
yield return null;
|
|
}
|
|
|
|
// 暂停阶段
|
|
_headModuleGroup.transform.localPosition = originalPos;
|
|
yield return new WaitForSeconds(0.5f);
|
|
}
|
|
// ReSharper disable once IteratorNeverReturns
|
|
}
|
|
}
|
|
|
|
[LoadIndex(2)]
|
|
public class ModuleSystemData : IData
|
|
{
|
|
public ModuleState moduleState;
|
|
|
|
public IEnumerator Load()
|
|
{
|
|
FixSystemCenter.SystemDic.Get<BodyModuleSystem>().SwitchModuleGroup(moduleState);
|
|
yield break;
|
|
}
|
|
}
|
|
|
|
public enum ModuleState
|
|
{
|
|
Body,
|
|
Head
|
|
}
|
|
} |