294 lines
9.8 KiB
C#
294 lines
9.8 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Threading.Tasks;
|
||
using UnityEngine;
|
||
using asap.core;
|
||
using PlayFab.Internal;
|
||
using Object = UnityEngine.Object;
|
||
|
||
namespace UI.PartnerGather.Mining
|
||
{
|
||
[System.Serializable]
|
||
public class TrainCar
|
||
{
|
||
public Transform carTransform;
|
||
public Transform oreContainer; // 矿石收集容器
|
||
public Transform collectPoint; // 收集点
|
||
|
||
//
|
||
public int pointsRange; // 对应的点数范围 (如 0-10, 10-20, 20-30, 30-40, 40+)
|
||
public List<GameObject> collectedOres = new List<GameObject>();
|
||
public int maxCapacity = 10;
|
||
|
||
public bool CanCollect(int points)
|
||
{
|
||
return collectedOres.Count < maxCapacity;
|
||
}
|
||
|
||
public void AddOre(GameObject ore)
|
||
{
|
||
if (oreContainer != null && ore != null)
|
||
{
|
||
ore.transform.SetParent(oreContainer);
|
||
// 简单的堆叠布局
|
||
var count = collectedOres.Count;
|
||
ore.transform.localPosition = new Vector3(
|
||
(count % 3 - 1) * 0.5f,
|
||
(count / 3) * 0.3f,
|
||
0f
|
||
);
|
||
ore.transform.localScale = Vector3.one * 0.8f;
|
||
collectedOres.Add(ore);
|
||
}
|
||
}
|
||
|
||
public void ClearOres()
|
||
{
|
||
foreach (var ore in collectedOres)
|
||
{
|
||
if (ore != null)
|
||
{
|
||
Object.DestroyImmediate(ore);
|
||
}
|
||
}
|
||
collectedOres.Clear();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 事件合作伙伴火车控制器
|
||
/// 管理火车车厢系统,包括矿石收集、车厢管理、矿石飞行动画等
|
||
/// 建议挂载在:Train容器对象上,确保包含所有车厢的Transform
|
||
///
|
||
/// 场景结构建议:
|
||
/// Train (挂载此脚本)
|
||
/// ├── TrainCar1 (第一节车厢)
|
||
/// │ └── OreContainer (矿石容器)
|
||
/// ├── TrainCar2 (第二节车厢)
|
||
/// │ └── OreContainer (矿石容器)
|
||
/// └── ... (更多车厢)
|
||
/// </summary>
|
||
public class EventPartnerTrainController : MonoBehaviour
|
||
{
|
||
[Header("火车车厢配置")]
|
||
public TrainCar[] trainCars = new TrainCar[5]; // 5节车厢
|
||
|
||
[Header("动画配置")]
|
||
public float oreFlightDuration = 1f;
|
||
public AnimationCurve oreFlightCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
|
||
|
||
[Header("移动目标(两种方式二选一)")]
|
||
public Transform target; // 目标点(可选)
|
||
public Vector3 targetWorldPos; // 备用的世界坐标目标点(当 target 为空时使用)
|
||
public bool useTargetTransform = true;
|
||
[Header("移动参数")]
|
||
[Min(0.01f)]
|
||
public float duration = 1f; // 总时长(秒)
|
||
[Tooltip("位移归一化曲线:X=时间(0~1), Y=位移进度(0~1)。建议是S形:起点0,终点1,中间变快,末尾变慢。")]
|
||
public AnimationCurve positionCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
|
||
[Header("调试/外观")]
|
||
public bool autoStartOnPlay = false; // 勾选后在 Start 自动移动到目标
|
||
public Color gizmoColor = new Color(0.2f, 0.8f, 1f, 0.8f);
|
||
// 运行时
|
||
private Vector3 _startPos;
|
||
private Vector3 _endPos;
|
||
private float _t; // 0~1
|
||
private bool _isMoving;
|
||
|
||
private void Start()
|
||
{
|
||
InitializeTrainCars();
|
||
|
||
// 设一个合理的默认曲线(Slow -> Fast -> Stop),确保末尾Y=1停住
|
||
if (positionCurve == null || positionCurve.length < 2)
|
||
{
|
||
positionCurve = new AnimationCurve(
|
||
new Keyframe(0f, 0f, 0f, 2f), // 起步慢
|
||
new Keyframe(0.5f, 0.8f, 1.5f, 1.5f), // 中段加速
|
||
new Keyframe(1f, 1f, 0f, 0f) // 末尾速度收敛到0
|
||
);
|
||
}
|
||
// if (autoStartOnPlay)
|
||
// {
|
||
// Vector3 end = useTargetTransform && target != null ? target.position : targetWorldPos;
|
||
// StartMoveTo(end);
|
||
// }
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
if (!_isMoving) return;
|
||
_t += Time.deltaTime / Mathf.Max(0.0001f, duration);
|
||
float t01 = Mathf.Clamp01(_t);
|
||
float p = positionCurve.Evaluate(t01);
|
||
transform.position = Vector3.LerpUnclamped(_startPos, _endPos, p);
|
||
if (_t >= 1f)
|
||
{
|
||
// 确保精确落点
|
||
transform.position = _endPos;
|
||
_isMoving = false;
|
||
}
|
||
}
|
||
|
||
// 开始从当前位置移动到目标世界坐标
|
||
public void StartMoveTo(Vector3 worldTarget)
|
||
{
|
||
_startPos = transform.position;
|
||
_endPos = worldTarget;
|
||
_t = 0f;
|
||
_isMoving = true;
|
||
}
|
||
//
|
||
public void StartMoveBy(Vector3 delta)
|
||
{
|
||
StartMoveTo(transform.position + delta);
|
||
}
|
||
|
||
// 重新设置目标(例如在运行时切换目标物体后调用)
|
||
public void SetTarget(Transform newTarget)
|
||
{
|
||
target = newTarget;
|
||
useTargetTransform = true;
|
||
}
|
||
|
||
// public async void MoveToDest(Vector3 targetPosition)
|
||
// {
|
||
// StartMoveTo(targetPosition);
|
||
// }
|
||
|
||
// 示例:在检视面板点击按钮的替代做法(可用ContextMenu手动触发)
|
||
[ContextMenu("Move To Target Once")]
|
||
private void Context_MoveToTarget()
|
||
{
|
||
Vector3 end = useTargetTransform && target != null ? target.position : targetWorldPos;
|
||
StartMoveTo(end);
|
||
}
|
||
void OnDrawGizmosSelected()
|
||
{
|
||
Vector3 end = useTargetTransform && target != null ? target.position : targetWorldPos;
|
||
Gizmos.color = gizmoColor;
|
||
// 预览当前配置下的终点
|
||
Gizmos.DrawWireSphere(end, 0.15f);
|
||
// 路径线
|
||
Gizmos.DrawLine(Application.isPlaying ? _startPos : transform.position, end);
|
||
}
|
||
|
||
private void InitializeTrainCars()
|
||
{
|
||
// 设置每节车厢对应的点数范围
|
||
for (var i = 0; i < trainCars.Length; i++)
|
||
{
|
||
if (trainCars[i] != null)
|
||
{
|
||
trainCars[i].pointsRange = (i + 1) * 100;
|
||
}
|
||
}
|
||
}
|
||
|
||
public async Task CollectOre(GameObject ore, int points)
|
||
{
|
||
Log("CollectOre");
|
||
if (ore == null) return;
|
||
|
||
// 选择对应的车厢
|
||
var targetCar = SelectTargetCar(points);
|
||
if (targetCar == null) return;
|
||
|
||
// 矿石飞行动画
|
||
await AnimateOreFlight(ore, targetCar);
|
||
|
||
// 添加到车厢
|
||
targetCar.AddOre(ore);
|
||
}
|
||
|
||
private TrainCar SelectTargetCar(int points)
|
||
{
|
||
// 根据点数选择对应的车厢
|
||
for (int i = 0; i < trainCars.Length; i++)
|
||
{
|
||
var car = trainCars[i];
|
||
if (car != null && car.CanCollect(points))
|
||
{
|
||
// 简单的分配逻辑:按点数范围分配
|
||
if (points <= car.pointsRange)
|
||
{
|
||
return car;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果没有合适的车厢,返回第一个有空间的车厢
|
||
foreach (var car in trainCars)
|
||
{
|
||
if (car != null && car.CanCollect(points))
|
||
{
|
||
return car;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private async Task AnimateOreFlight(GameObject ore, TrainCar targetCar)
|
||
{
|
||
if (ore == null || targetCar?.oreContainer == null) return;
|
||
|
||
var startPos = ore.transform.position;
|
||
var endPos = targetCar.collectPoint.position;
|
||
// 添加抛物线轨迹
|
||
var midPos = (startPos + endPos) * 0.5f;
|
||
midPos.y += 2f; // 抛物线高度
|
||
float t = 0f;
|
||
while (t < oreFlightDuration)
|
||
{
|
||
t += Time.deltaTime / oreFlightDuration;
|
||
var curveT = oreFlightCurve.Evaluate(t);
|
||
// 贝塞尔曲线插值
|
||
var pos = CalculateBezierPoint(curveT, startPos, midPos, endPos);
|
||
ore.transform.position = pos;
|
||
// 旋转效果
|
||
ore.transform.Rotate(0, 0, 180 * Time.deltaTime / oreFlightDuration);
|
||
await Awaiters.NextFrame;
|
||
}
|
||
|
||
ore.transform.position = endPos;
|
||
}
|
||
|
||
private static Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
|
||
{
|
||
var u = 1 - t;
|
||
var tt = t * t;
|
||
float uu = u * u;
|
||
return uu * p0 + 2 * u * t * p1 + tt * p2;
|
||
}
|
||
|
||
public void ResetTrain()
|
||
{
|
||
foreach (var car in trainCars)
|
||
{
|
||
car?.ClearOres();
|
||
}
|
||
}
|
||
|
||
public int GetTotalCollectedOres()
|
||
{
|
||
int total = 0;
|
||
foreach (var car in trainCars)
|
||
{
|
||
if (car != null)
|
||
{
|
||
total += car.collectedOres.Count;
|
||
}
|
||
}
|
||
return total;
|
||
}
|
||
|
||
//
|
||
private void Log(object t)
|
||
{
|
||
Debug.Log($"<Color=cyan> EventPartnerMiningSceneController {t} </color>");
|
||
}
|
||
|
||
|
||
}
|
||
} |