refactor(Facility): 使用继承让所有机关都拥有了独自的 可交互 可编辑 可连接设定。大面积重构

This commit is contained in:
2025-10-22 17:34:58 +08:00
parent acd8282ff0
commit 5198a80318
61 changed files with 1516 additions and 889 deletions

View File

@@ -0,0 +1,124 @@
using System.Collections.Generic;
using Script.Gameplay.Connect;
using Script.Gameplay.Interface;
using UnityEngine;
namespace Script.Gameplay.Facility
{
public class BaseFacilityController : MonoBehaviour, IInteractable, IEditableComponent, IConnectable, ISignalSender,
ISignalReceiver, IGaze
{
[SerializeField] protected bool isEnableInteract = true;
[SerializeField] protected bool isEnableEdit = true;
[SerializeField] protected bool isEnableConnect = true;
[SerializeField] protected string componentName;
[SerializeField] protected int needSignalCount = 1;
[SerializeField] protected bool isOpenInEditor = true;
protected int CurrentSignalCount = 0;
public virtual bool IsEnableInteract
{
get => isEnableInteract;
set => isEnableInteract = value;
}
public virtual bool IsEnableEdit
{
get => isEnableEdit;
set => isEnableEdit = value;
}
public virtual bool IsOpenInEditor
{
get => isOpenInEditor;
set
{
isOpenInEditor = value;
IsEnableInteract = value;
IsEnableConnect = value;
}
}
public virtual bool IsEnableConnect
{
get => isEnableConnect;
set => isEnableConnect = value;
}
public string ComponentName
{
get
{
// 如果没有自定义名称,则使用组件名称
return string.IsNullOrEmpty(componentName) ? GetType().Name : componentName;
}
set => componentName = value;
}
public List<ConnectionLine> ConnectionLines { get; set; } = new List<ConnectionLine>();
public string GetConnectableName()
{
return gameObject.name;
}
public GameObject GetGameObject()
{
return gameObject;
}
public virtual string GetInteractPrompt()
{
return $"Interact with {componentName}";
}
public Vector3 GetPosition()
{
return transform.position;
}
public virtual void Interact(GameObject interactor)
{
// Implement interaction logic here
}
public virtual void OnGazeEnter(GameObject go)
{
// Handle gaze enter
}
public virtual void OnGazeExit(GameObject go)
{
// Handle gaze exit
}
public virtual void OnSignalReceived(bool active, GameObject sender)
{
if (active)
{
CurrentSignalCount++;
}
else
{
CurrentSignalCount = Mathf.Max(0, CurrentSignalCount - 1);
}
if (CurrentSignalCount < needSignalCount)
{
return;
}
// Implement signal received logic here
}
public virtual void SendSignal(bool active, GameObject sender)
{
if (ConnectionLines != null)
{
foreach (var line in ConnectionLines)
{
line.OnSignalReceived(active, this.gameObject);
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8e9f3dfa12a48a74ab978e8c7c8cb0af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Script.Gameplay.Interface;
using Script.Gameplay.Connect;
namespace Script.Gameplay.Facility
{
public class ButtonInteractController : BaseFacilityController
{
[SerializeField] private float signalDuration = 1.0f; // 信号持续时间(秒)
private bool IsPressing = true;
public override string GetInteractPrompt()
{
return "按F按下按钮";
}
public override void Interact(GameObject interactor)
{
if (!IsPressing) return;
StartCoroutine(SendSignalCoroutine());
Debug.Log("Button pressed");
}
private IEnumerator SendSignalCoroutine()
{
SendSignal(true, this.gameObject);
IsPressing = false;
// 按钮压下的动画或效果可以在这里添加
yield return new WaitForSeconds(signalDuration);
SendSignal(false, this.gameObject);
IsPressing = true;
// 按钮弹起的动画或效果可以在这里添加
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7f5359facd804a0cb94d416c9c54a167
timeCreated: 1760923022

View File

@@ -0,0 +1,50 @@
using System.Collections.Generic;
using Script.Gameplay.Connect;
using Script.Gameplay.Interface;
using UnityEngine;
namespace Script.Gameplay.Facility
{
public class ConsoleController : BaseFacilityController
{
[SerializeField] private List<MonoBehaviour> controlTarget;
[SerializeField] private bool IsBeActive = false;
private List<ISignalReceiver> controlTargetSignalReceiver = new List<ISignalReceiver>();
private bool lastSendSignal = false;
void Awake()
{
foreach (var target in controlTarget)
{
if (target is ISignalReceiver receiver)
{
controlTargetSignalReceiver.Add(receiver);
}
else
{
if(target != null)
Debug.Log(target.name + " is not a supported control target");
}
}
}
public override void OnSignalReceived(bool active, GameObject sender)
{
base.OnSignalReceived(active, sender);
IsBeActive = active;
}
public override void Interact(GameObject interactor)
{
if (IsBeActive && controlTargetSignalReceiver != null)
{
var signal = !lastSendSignal;
foreach (var receiver in controlTargetSignalReceiver)
{
receiver.OnSignalReceived(signal, this.gameObject);
}
lastSendSignal = signal;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cc0b1376aefb4cccb530717966b48bf5
timeCreated: 1761039706

View File

@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using Script.Gameplay.Player;
using UnityEngine;
using Script.Gameplay.Interface;
using Script.Gameplay.Connect;
namespace Script.Gameplay.Facility
{
public class DoorInteractController : BaseFacilityController
{
private bool isOpened = false;
public override string GetInteractPrompt()
{
return "按F进行交互";
}
public override void Interact(GameObject interactor)
{
if (isOpened)
{
CloseDoor();
isOpened = false;
}
else
{
OpenDoor();
isOpened = true;
}
}
public override void OnGazeEnter(GameObject go)
{
// 可扩展:门被注视时的逻辑
}
public override void OnGazeExit(GameObject go)
{
// 可扩展:门离开注视时的逻辑
}
private void OpenDoor()
{
transform.rotation = Quaternion.Euler(transform.rotation.eulerAngles + new Vector3(0, -90, 0));
}
private void CloseDoor()
{
transform.rotation = Quaternion.Euler(transform.rotation.eulerAngles + new Vector3(0, 90, 0));
}
public override void OnSignalReceived(bool active, GameObject sender)
{
Interact(sender);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3bdc5380046d4cacb0dd579877f320eb
timeCreated: 1760693181

View File

@@ -0,0 +1,64 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Script.Gameplay.Connect;
using Script.Gameplay.Interface;
namespace Script.Gameplay.Facility
{
public class EmitterController : BaseFacilityController
{
[Header("发射器设置")]
[SerializeField] private GameObject prefabToEmit;
[SerializeField] private Transform emitPoint;
[SerializeField] private Vector3 emitDirection = Vector3.forward;
[SerializeField] private float emitForce = 10f;
[SerializeField] private float emitInterval = 1f;
[Header("生成对象销毁时间")]
[SerializeField] private float destroyDelay = 5f; // 生成对象多少秒后销毁
private Coroutine emitCoroutine;
// 接收信号
public override void OnSignalReceived(bool active, GameObject sender)
{
base.OnSignalReceived(active, sender);
if(!isOpenInEditor) return;
if (active)
{
if (emitCoroutine == null)
emitCoroutine = StartCoroutine(EmitRoutine());
}
else
{
if (emitCoroutine != null)
{
StopCoroutine(emitCoroutine);
emitCoroutine = null;
}
}
}
private IEnumerator EmitRoutine()
{
while (true)
{
Emit();
yield return new WaitForSeconds(emitInterval);
}
}
private void Emit()
{
if (prefabToEmit == null || emitPoint == null) return;
var obj = Instantiate(prefabToEmit, emitPoint.position, emitPoint.rotation);
var rb = obj.GetComponent<Rigidbody>();
if (rb != null)
{
rb.AddForce(emitPoint.TransformDirection(emitDirection.normalized) * emitForce, ForceMode.Impulse);
}
// 添加销毁逻辑
Destroy(obj, destroyDelay);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1a5a563132c84e50b983b557eb3da442
timeCreated: 1761028056

View File

@@ -0,0 +1,36 @@
using System.Collections.Generic;
using UnityEngine;
using Script.Gameplay.Interface;
using Script.Gameplay.Connect;
namespace Script.Gameplay.Facility
{
public class LeverInteractController : BaseFacilityController
{
private bool isPulled = false;
public override string GetInteractPrompt()
{
return "按F拉动拉杆";
}
public override void Interact(GameObject interactor)
{
if (!IsEnableInteract) return;
isPulled = !isPulled;
SendSignal(isPulled, this.gameObject);
// 可选:拉杆动画
//Debug.Log(isPulled ? "Lever pulled down" : "Lever reset");
}
public override void OnGazeEnter(GameObject go)
{
// 可选:高亮拉杆
}
public override void OnGazeExit(GameObject go)
{
// 可选:取消高亮
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6b9b9a895b0d40edb9a6d7213afbb256
timeCreated: 1760925436

View File

@@ -0,0 +1,62 @@
using UnityEngine;
using Script.Gameplay.Connect;
using Script.Gameplay.Interface;
namespace Script.Gameplay.Facility
{
public class MovingPlatformController : BaseFacilityController
{
[SerializeField] private GameObject startPositionObject;
[SerializeField] private GameObject targetPositionObject;
[SerializeField] public float moveSpeed = 2f;
private Vector3 startPosition;
private Vector3 targetPosition;
private bool isMoving = false;
private bool forward = true;
private void Awake()
{
startPosition = startPositionObject.transform.position;
targetPosition = targetPositionObject.transform.position;
transform.position = startPosition;
}
private void Update()
{
if (!isMoving) return;
Vector3 destination = forward ? targetPosition : startPosition;
transform.position = Vector3.MoveTowards(transform.position, destination, moveSpeed * Time.deltaTime);
if (Vector3.Distance(transform.position, destination) < 0.01f)
{
forward = !forward;
}
}
public override void OnSignalReceived(bool active, GameObject sender)
{
base.OnSignalReceived(active, sender);
if (active)
{
isMoving = true;
}
else
{
isMoving = false;
}
}
// 可选:如果需要编辑激活状态,可以重写属性
public override bool IsEnableEdit
{
get => base.IsEnableEdit;
set
{
base.IsEnableEdit = value;
isMoving = value;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 115d4e847c444833bc8dce4a03a78e55
timeCreated: 1761038093

View File

@@ -0,0 +1,53 @@
using System.Collections.Generic;
using UnityEngine;
using Script.Gameplay.Connect;
namespace Script.Gameplay.Facility
{
// 号码枚举类型
public enum NumberType
{
Zero,
One,
Two,
Three,
Four,
Five,
// Six,
// Seven,
// Eight,
// Nine
}
// 号码槽
public class NumberSlotController : BaseFacilityController
{
[Header("号码槽设置")]
[SerializeField] private NumberType currentNumber = NumberType.Zero;
[SerializeField] private NumberType correctNumber = NumberType.One;
// 接收信号
public override void OnSignalReceived(bool active, GameObject sender)
{
base.OnSignalReceived(active, sender);
if (active)
{
SwitchToNextNumber();
}
}
// 切换到下一个号码
private void SwitchToNextNumber()
{
currentNumber = (NumberType)(((int)currentNumber + 1) % System.Enum.GetValues(typeof(NumberType)).Length);
CheckNumberAndSendSignal();
}
// 检查号码并发信号
private void CheckNumberAndSendSignal()
{
bool isCorrect = currentNumber == correctNumber;
SendSignal(isCorrect, this.gameObject);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 301178d89b6b43e0844e3d39aa579442
timeCreated: 1760961220

View File

@@ -0,0 +1,35 @@
using System.Collections.Generic;
using UnityEngine;
using Script.Gameplay.Connect;
namespace Script.Gameplay.Facility
{
public class PressurePlateController : BaseFacilityController
{
[SerializeField] private LayerMask detectLayer = ~0; // 检测所有层可在Inspector中指定
[SerializeField] private Vector3 plateSize = new Vector3(1, 0.2f, 1);
[SerializeField] private Vector3 plateOffset = Vector3.up * 0.1f;
private bool lastState = false;
private void FixedUpdate()
{
if (!isEnableInteract) return;
bool hasObject = Physics.CheckBox(transform.position + plateOffset, plateSize * 0.5f, Quaternion.identity, detectLayer);
if (hasObject != lastState)
{
SendSignal(hasObject, this.gameObject);
lastState = hasObject;
}
}
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireCube(transform.position + plateOffset, plateSize);
}
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2557c7d42f7a4d8896712ab2ae980b6f
timeCreated: 1760924285