备份CatanBuilding瘦身独立工程
This commit is contained in:
340
Assets/Scripts/UI/PartnerGather/EventPartnerGatherAct.cs
Normal file
340
Assets/Scripts/UI/PartnerGather/EventPartnerGatherAct.cs
Normal file
@@ -0,0 +1,340 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using DataCenter;
|
||||
using game;
|
||||
using GameCore;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UI.PartnerGather.Mining;
|
||||
|
||||
|
||||
public class EventPartnerGatherAct : AGameAct
|
||||
{
|
||||
//
|
||||
private GameObject _miningScene;
|
||||
// 好友界面
|
||||
private GameObject _friendsUI;
|
||||
// Spin 相关界面
|
||||
private GameObject _drawUI;
|
||||
|
||||
private EventPartnerMiningSceneController _miningController;
|
||||
private EventPartnerDrawMiningPanel _drawMiningPanel;
|
||||
private EventPartnerGatherManager _gatherManager;
|
||||
|
||||
// UI组件缓存
|
||||
private EventPartnerDrawMiningPanel _drawPanelComponent;
|
||||
private Transform _friendsPanelComponent;
|
||||
public override async Task<bool> StartAsync()
|
||||
{
|
||||
Log("StartAsync - Loading mining scene");
|
||||
// 获取管理器
|
||||
_gatherManager = GContext.container.Resolve<EventPartnerGatherManager>();
|
||||
_gatherManager.SetGameAct(this);
|
||||
|
||||
await ShowUI();
|
||||
// 加载挖矿场景
|
||||
_miningScene = await Addressables.InstantiateAsync("EventPartnerMiningScene").Task;
|
||||
// 初始化挖矿控制器
|
||||
InitializeMiningController();
|
||||
// 绑定UI与控制器关系
|
||||
BindUIAndControllers();
|
||||
// 初始化数据同步
|
||||
InitializeDataSync();
|
||||
// 显示UI
|
||||
return await base.StartAsync();
|
||||
}
|
||||
|
||||
private void InitializeMiningController()
|
||||
{
|
||||
Log("初始化挖矿场景控制器");
|
||||
|
||||
// 查找或添加挖矿场景控制器
|
||||
_miningController = _miningScene.GetComponentInChildren<EventPartnerMiningSceneController>();
|
||||
if (_miningController == null)
|
||||
{
|
||||
var controllerObject = _miningScene.transform.Find("MiningController");
|
||||
if (controllerObject == null)
|
||||
{
|
||||
controllerObject = new GameObject("MiningController").transform;
|
||||
controllerObject.SetParent(_miningScene.transform);
|
||||
}
|
||||
|
||||
_miningController = controllerObject.gameObject.AddComponent<EventPartnerMiningSceneController>();
|
||||
}
|
||||
|
||||
Log($"挖矿控制器初始化完成: {_miningController.name}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绑定UI与控制器的关系
|
||||
/// </summary>
|
||||
private void BindUIAndControllers()
|
||||
{
|
||||
Log("开始绑定UI与控制器关系");
|
||||
|
||||
// 验证UI对象是否存在
|
||||
if (_drawUI == null)
|
||||
{
|
||||
Log("错误: 抽奖UI对象为空,无法进行组件绑定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取UI组件
|
||||
_drawPanelComponent = _drawUI.GetComponent<EventPartnerDrawMiningPanel>();
|
||||
if (_drawPanelComponent == null)
|
||||
{
|
||||
Log("错误: 未找到EventPartnerDrawMiningPanel组件,采集功能将不可用");
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 绑定采集场景与抽奖UI的关系
|
||||
if (_miningController != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_drawPanelComponent.SetMiningScene(_miningController);
|
||||
_miningController.SetUIPanel(_drawPanelComponent);
|
||||
Log("✓ 抽奖UI与采集场景关系绑定成功");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log($"错误: 绑定采集场景时发生异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("警告: 挖矿控制器为空,无法绑定场景关系");
|
||||
}
|
||||
|
||||
// 2. 绑定管理器与各组件的关系
|
||||
if (_gatherManager != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 给管理器设置引用
|
||||
_gatherManager.SetMiningController(_miningController);
|
||||
_gatherManager.SetDrawPanel(_drawPanelComponent);
|
||||
Log("✓ 管理器与组件关系绑定成功");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log($"错误: 绑定管理器关系时发生异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("错误: EventPartnerGatherManager 为空,无法完成组件绑定");
|
||||
}
|
||||
|
||||
// 3. 设置好友UI的事件绑定(如果需要)
|
||||
SetupFriendsUIEvents();
|
||||
|
||||
// 4. 验证所有关键组件是否正确绑定
|
||||
ValidateComponentBindings();
|
||||
|
||||
Log("所有UI与控制器关系绑定完成");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证组件绑定是否正确
|
||||
/// </summary>
|
||||
private void ValidateComponentBindings()
|
||||
{
|
||||
bool allValid = true;
|
||||
|
||||
if (_drawPanelComponent == null)
|
||||
{
|
||||
Log("验证失败: 抽奖面板组件未正确绑定");
|
||||
allValid = false;
|
||||
}
|
||||
|
||||
if (_miningController == null)
|
||||
{
|
||||
Log("验证失败: 挖矿控制器未正确绑定");
|
||||
allValid = false;
|
||||
}
|
||||
|
||||
if (_gatherManager == null)
|
||||
{
|
||||
Log("验证失败: 采集管理器未正确绑定");
|
||||
allValid = false;
|
||||
}
|
||||
|
||||
if (allValid)
|
||||
{
|
||||
Log("✓ 所有关键组件绑定验证通过");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("⚠ 部分组件绑定验证失败,可能影响游戏功能");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化数据同步
|
||||
/// </summary>
|
||||
private void InitializeDataSync()
|
||||
{
|
||||
Log("初始化数据同步");
|
||||
|
||||
if (_gatherManager?.GatherData != null)
|
||||
{
|
||||
// 同步初始数据到UI
|
||||
if (_drawPanelComponent != null)
|
||||
{
|
||||
var data = _gatherManager.GatherData;
|
||||
_drawPanelComponent.SetPoints(data.CurrentPoints);
|
||||
_drawPanelComponent.SetMiningProgress(data.CurrentMiningProgress);
|
||||
_drawPanelComponent.SetTrainProgress(data.TrainProgress, _gatherManager.MaxTrainNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置好友UI的事件绑定
|
||||
/// </summary>
|
||||
private void SetupFriendsUIEvents()
|
||||
{
|
||||
// 如果好友UI有特殊事件需要绑定,在这里处理
|
||||
// 例如:点击进入挖矿按钮等
|
||||
if (_friendsUI != null)
|
||||
{
|
||||
// TODO: 如果好友UI有特定组件需要绑定事件,在这里处理
|
||||
Log("好友UI事件绑定完成");
|
||||
}
|
||||
}
|
||||
private async Task ShowUI()
|
||||
{
|
||||
// await Task.Delay(500);
|
||||
_friendsUI = await UIManager.Instance.ShowUI(new UIType("EventPartnerMiningPanel"));
|
||||
_drawUI = await UIManager.Instance.ShowUI(new UIType("EventPartnerDrawMiningPanel"));
|
||||
_drawUI.SetActive(false); // 先隐藏掉
|
||||
}
|
||||
|
||||
public override async Task StopAsync()
|
||||
{
|
||||
Log("StopAsync - 清理挖矿场景和组件关系");
|
||||
|
||||
// 清理组件关系
|
||||
CleanupBindings();
|
||||
|
||||
// 清理场景
|
||||
if (_miningScene != null)
|
||||
{
|
||||
Addressables.ReleaseInstance(_miningScene);
|
||||
Destroy(_miningScene);
|
||||
}
|
||||
|
||||
// 清理UI
|
||||
UIManager.Instance.DestroyUI(new UIType("EventPartnerMiningPanel"));
|
||||
UIManager.Instance.DestroyUI(new UIType("EventPartnerDrawMiningPanel"));
|
||||
await Awaiters.NextFrame;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理组件之间的绑定关系
|
||||
/// </summary>
|
||||
private void CleanupBindings()
|
||||
{
|
||||
Log("清理组件绑定关系");
|
||||
|
||||
// 清理抽奖UI关系
|
||||
if (_drawPanelComponent != null)
|
||||
{
|
||||
_drawPanelComponent.SetMiningScene(null);
|
||||
}
|
||||
|
||||
// 清理挖矿控制器关系
|
||||
if (_miningController != null)
|
||||
{
|
||||
_miningController.SetUIPanel(null);
|
||||
}
|
||||
|
||||
// 清理管理器关系
|
||||
if (_gatherManager != null)
|
||||
{
|
||||
_gatherManager.SetMiningController(null);
|
||||
_gatherManager.SetDrawPanel(null);
|
||||
}
|
||||
}
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
Log("销毁Act,清理所有引用");
|
||||
|
||||
// 清理组件引用
|
||||
_miningScene = null;
|
||||
_miningController = null;
|
||||
_drawPanelComponent = null;
|
||||
_friendsPanelComponent = null;
|
||||
|
||||
// 清理管理器引用
|
||||
if (_gatherManager != null)
|
||||
{
|
||||
_gatherManager.SetGameAct(null);
|
||||
_gatherManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 进入采集界面(从好友界面切换到抽奖界面)
|
||||
/// </summary>
|
||||
public void OnEnterGather()
|
||||
{
|
||||
Log("进入采集界面");
|
||||
|
||||
if (_friendsUI != null) _friendsUI.SetActive(false);
|
||||
if (_drawUI != null) _drawUI.SetActive(true);
|
||||
|
||||
// 同步最新数据到UI
|
||||
RefreshUIData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新UI数据显示
|
||||
/// </summary>
|
||||
public void RefreshUIData()
|
||||
{
|
||||
if (_drawPanelComponent != null && _gatherManager?.GatherData != null)
|
||||
{
|
||||
var data = _gatherManager.GatherData;
|
||||
_drawPanelComponent.SetPoints(data.CurrentPoints);
|
||||
_drawPanelComponent.SetMiningProgress(data.CurrentMiningProgress);
|
||||
_drawPanelComponent.SetTrainProgress(data.TrainProgress, _gatherManager.MaxTrainNodes);
|
||||
Log("已刷新UI数据显示");
|
||||
}
|
||||
}
|
||||
|
||||
private static void Log(string message)
|
||||
{
|
||||
Debug.Log($"<color=cyan>EventPartnerGatherMiningAct => {message}</color>");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭事件,退出到上一个Act
|
||||
/// </summary>
|
||||
public void OnCloseEvent()
|
||||
{
|
||||
Log("关闭事件,准备退出Act");
|
||||
|
||||
// 发送退出事件
|
||||
GContext.Publish(new UnloadActToNextAct());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取挖矿控制器引用(供外部调用)
|
||||
/// </summary>
|
||||
public EventPartnerMiningSceneController GetMiningController()
|
||||
{
|
||||
return _miningController;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取抽奖UI面板引用(供外部调用)
|
||||
/// </summary>
|
||||
public EventPartnerDrawMiningPanel GetDrawPanel()
|
||||
{
|
||||
return _drawPanelComponent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22c79d71f057b0a45b555f0be825a3d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/Scripts/UI/PartnerGather/Mining.meta
Normal file
3
Assets/Scripts/UI/PartnerGather/Mining.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63598da602454054aa518a6863a912b5
|
||||
timeCreated: 1756386503
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31bfe17ee3534f48b09cb6a6b10ae6df
|
||||
timeCreated: 1756212375
|
||||
@@ -0,0 +1,497 @@
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using asap.core;
|
||||
|
||||
namespace UI.PartnerGather.Mining
|
||||
{
|
||||
/// <summary>
|
||||
/// 事件合作伙伴挖矿角色控制器
|
||||
/// 控制角色的移动、动画播放、挖矿行为等
|
||||
///
|
||||
/// 建议挂载在:角色模型的根对象上
|
||||
/// 需要组件:CharacterController(必须)
|
||||
///
|
||||
/// 场景结构建议:
|
||||
/// Character (挂载此脚本 + CharacterController)
|
||||
/// ├── Model (角色模型)
|
||||
/// │ └── Animator (动画控制器)
|
||||
/// └── Body (可选的身体Transform,用于旋转控制)
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(CharacterController))]
|
||||
public class EventPartnerMiningCharacterController : MonoBehaviour
|
||||
{
|
||||
private static readonly int BasicMove = Animator.StringToHash("_basicMove");
|
||||
private static readonly int BackToIdle = Animator.StringToHash("_backToIdle");
|
||||
private static readonly int ToBasic = Animator.StringToHash("_toBasic");
|
||||
private static readonly int BasicGathering = Animator.StringToHash("_basicGathering");
|
||||
|
||||
|
||||
[Header("组件引用")] public Animator animator;
|
||||
public Transform body;
|
||||
public CharacterController characterController;
|
||||
|
||||
[Header("移动配置")] public float moveSpeed = 2f;
|
||||
public float idleThreshold = 0.05f;
|
||||
public float rotationSpeed = 5f;
|
||||
|
||||
[Header("模型配置")] [Tooltip("是否平滑旋转")]
|
||||
public bool smoothRotation = true;
|
||||
|
||||
[Tooltip("旋转动画持续时间")] [Range(0.1f, 1f)]
|
||||
public float rotationDuration = 0.2f;
|
||||
|
||||
[Header("调试配置")] public bool showDebugInfo = true;
|
||||
public bool showMovementPath = true;
|
||||
public Color targetPointColor = Color.red;
|
||||
public Color pathColor = Color.yellow;
|
||||
public Color directionColor = Color.green;
|
||||
|
||||
[Header("动画参数")] public string runAnimParam = "run";
|
||||
public string mineAnimParam = "mine";
|
||||
public string idleAnimParam = "idle";
|
||||
|
||||
// 调试信息
|
||||
private Vector3 _debugTargetPosition;
|
||||
private Vector3 _debugStartPosition;
|
||||
private bool _isMoving = false;
|
||||
private Vector3 _debugMoveDirection;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (characterController == null)
|
||||
characterController = GetComponent<CharacterController>();
|
||||
if (body == null) body = transform;
|
||||
}
|
||||
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
if (body != null && showDebugInfo)
|
||||
{
|
||||
// 绘制角色位置和朝向
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawWireSphere(body.position, 0.5f);
|
||||
|
||||
// 绘制前方向量(蓝色)
|
||||
Gizmos.color = Color.blue;
|
||||
Gizmos.DrawRay(body.position, body.forward * 2f);
|
||||
|
||||
// 绘制右方向量(红色)
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawRay(body.position, body.right * 1.5f);
|
||||
|
||||
// 绘制上方向量(黄色,较短)
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawRay(body.position, body.up * 1f);
|
||||
|
||||
// === 新增:移动调试可视化 ===
|
||||
if (_isMoving)
|
||||
{
|
||||
// 绘制目标点
|
||||
Gizmos.color = targetPointColor;
|
||||
Gizmos.DrawWireSphere(_debugTargetPosition, 0.8f);
|
||||
Gizmos.DrawCube(_debugTargetPosition + Vector3.up * 0.5f, Vector3.one * 0.3f);
|
||||
|
||||
// 绘制起始点
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawWireCube(_debugStartPosition, Vector3.one * 0.4f);
|
||||
|
||||
if (showMovementPath)
|
||||
{
|
||||
// 绘制移动路径线
|
||||
Gizmos.color = pathColor;
|
||||
Gizmos.DrawLine(_debugStartPosition, _debugTargetPosition);
|
||||
|
||||
// 绘制当前位置到目标的连线
|
||||
Gizmos.color = Color.white;
|
||||
Gizmos.DrawLine(body.position, _debugTargetPosition);
|
||||
|
||||
// 绘制移动方向箭头
|
||||
Vector3 directionArrow = _debugMoveDirection * 3f;
|
||||
Gizmos.color = directionColor;
|
||||
Gizmos.DrawRay(body.position + Vector3.up * 0.2f, directionArrow);
|
||||
|
||||
// 绘制箭头头部
|
||||
Vector3 arrowEnd = body.position + Vector3.up * 0.2f + directionArrow;
|
||||
Vector3 arrowSide1 = Quaternion.Euler(0, 30, 0) * (-directionArrow.normalized * 0.5f);
|
||||
Vector3 arrowSide2 = Quaternion.Euler(0, -30, 0) * (-directionArrow.normalized * 0.5f);
|
||||
Gizmos.DrawLine(arrowEnd, arrowEnd + arrowSide1);
|
||||
Gizmos.DrawLine(arrowEnd, arrowEnd + arrowSide2);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示四个主要方向(运行时)
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
// 显示四个主要方向
|
||||
Gizmos.DrawRay(body.position + Vector3.up * 0.1f, Vector3.forward * 0.8f); // 前
|
||||
Gizmos.DrawRay(body.position + Vector3.up * 0.1f, Vector3.back * 0.8f); // 后
|
||||
Gizmos.DrawRay(body.position + Vector3.up * 0.1f, Vector3.left * 0.8f); // 左
|
||||
Gizmos.DrawRay(body.position + Vector3.up * 0.1f, Vector3.right * 0.8f); // 右
|
||||
}
|
||||
|
||||
// 显示默认朝向(编辑器中)
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
Gizmos.color = Color.magenta;
|
||||
// Unity默认前方向量
|
||||
Gizmos.DrawRay(body.position, Vector3.forward * 1.5f);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// 在编辑器中显示文字信息
|
||||
if (body != null && showDebugInfo)
|
||||
{
|
||||
UnityEditor.Handles.color = Color.white;
|
||||
string info = $"Current Rotation: {body.rotation.eulerAngles}\n";
|
||||
info += $"Forward: {body.forward.ToString("F2")}\n";
|
||||
info += $"Right: {body.right.ToString("F2")}";
|
||||
|
||||
if (_isMoving)
|
||||
{
|
||||
info += $"\n--- 移动调试 ---";
|
||||
info += $"\n起点: {_debugStartPosition.ToString("F1")}";
|
||||
info += $"\n目标: {_debugTargetPosition.ToString("F1")}";
|
||||
info += $"\n当前: {body.position.ToString("F1")}";
|
||||
info += $"\n方向: {_debugMoveDirection.ToString("F2")}";
|
||||
float distance = Vector3.Distance(body.position, _debugTargetPosition);
|
||||
info += $"\n剩余距离: {distance:F2}";
|
||||
}
|
||||
|
||||
UnityEditor.Handles.Label(body.position + Vector3.up * 2f, info);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移动到指定的3D位置
|
||||
/// </summary>
|
||||
/// <param name="targetPosition">目标位置</param>
|
||||
/// <param name="speed">移动速度,0表示使用默认速度</param>
|
||||
public async Task MoveToPosition(Vector3 targetPosition, float speed = 0f)
|
||||
{
|
||||
if (speed <= 0f) speed = moveSpeed;
|
||||
|
||||
// 设置调试信息
|
||||
_debugStartPosition = body.position;
|
||||
_debugTargetPosition = targetPosition;
|
||||
_isMoving = true;
|
||||
|
||||
|
||||
Vector3 startPos = body.position;
|
||||
Vector3 direction = (targetPosition - startPos);
|
||||
|
||||
// 忽略Y轴方向,只考虑水平面移动
|
||||
direction.y = 0f;
|
||||
_debugMoveDirection = direction.normalized;
|
||||
|
||||
// float bodyForward = Mathf.Atan2(body.forward.x, body.forward.z) * Mathf.Rad2Deg;
|
||||
// 检查水平移动距离是否足够大
|
||||
if (direction.magnitude > 0.01f)
|
||||
{
|
||||
Log($"开始移动: 从 {startPos} 到 {targetPosition}, 方向: {_debugMoveDirection}");
|
||||
// 设置角色朝向目标方向
|
||||
await SetCharacterDirectionVector(direction);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"目标位置太近,跳过移动: 距离 {direction.magnitude:F3}");
|
||||
_isMoving = false;
|
||||
return;
|
||||
}
|
||||
// return;
|
||||
// 开始移动动画
|
||||
SetAnimationState("moving");
|
||||
|
||||
//TODO:LF 开发暂时停止,优先制作六边形场景,目前人物移动的配置还有问题,3D 模型上面的UI的结构要调整
|
||||
// 移动到目标位置
|
||||
var bodyStandPosition = body.position;
|
||||
bodyStandPosition.y = 0;
|
||||
|
||||
while (Vector3.Distance(bodyStandPosition, targetPosition) > idleThreshold)
|
||||
{
|
||||
var moveDirection = (targetPosition - bodyStandPosition).normalized;
|
||||
moveDirection.y = 0f;
|
||||
_debugMoveDirection = moveDirection; // 更新当前移动方向
|
||||
Vector3 moveVector = moveDirection * speed * Time.deltaTime;
|
||||
if (characterController && characterController.enabled)
|
||||
{
|
||||
// moveVector.y = -9.81f * Time.deltaTime;
|
||||
characterController.Move(moveVector);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3 newPos = body.position + moveVector;
|
||||
newPos.y = body.position.y;
|
||||
body.position = newPos;
|
||||
}
|
||||
bodyStandPosition = body.position;
|
||||
bodyStandPosition.y = 0;
|
||||
await Awaiters.NextFrame;
|
||||
}
|
||||
|
||||
|
||||
// SetAnimationState();
|
||||
// 精确定位
|
||||
// animator.SetTrigger(BackToIdle);
|
||||
|
||||
// animator.SetTrigger(gam);
|
||||
// SetAnimationState("gathering");
|
||||
Vector3 finalPos = targetPosition;
|
||||
finalPos.y = body.position.y;
|
||||
body.position = finalPos;
|
||||
_ = SetCharacterDirectionVector(Vector3.right);
|
||||
Log($"移动完成: 到达 {body.position}");
|
||||
// 停止移动动画
|
||||
// SetAnimationState("gathering");
|
||||
// 清除调试信息
|
||||
_isMoving = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置角色朝向方向(基于方向向量)
|
||||
/// </summary>
|
||||
/// <param name="direction">目标方向向量</param>
|
||||
private async Task SetCharacterDirectionVector(Vector3 direction)
|
||||
{
|
||||
direction.y = 0f;
|
||||
// 检查方向向量是否有效(在标准化之前)
|
||||
if (direction.magnitude < 0.01f) return;
|
||||
|
||||
direction.Normalize();
|
||||
|
||||
// 标准Unity旋转计算
|
||||
float targetYRotation = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
|
||||
Quaternion targetRotation = Quaternion.Euler(0f, targetYRotation - 90, 0f);
|
||||
if (smoothRotation)
|
||||
{
|
||||
Quaternion startRotation = body.rotation;
|
||||
float rotationTime = 0f;
|
||||
|
||||
while (rotationTime < rotationDuration)
|
||||
{
|
||||
rotationTime += Time.deltaTime;
|
||||
float t = rotationTime / rotationDuration;
|
||||
body.rotation = Quaternion.Slerp(startRotation, targetRotation, t);
|
||||
await Awaiters.NextFrame;
|
||||
}
|
||||
}
|
||||
body.rotation = targetRotation;
|
||||
Log($"朝向设置: 目标方向{direction} -> Y旋转{targetYRotation:F1}° -> 最终旋转{targetRotation.eulerAngles}");
|
||||
}
|
||||
|
||||
public async Task PlayMining( float duration)
|
||||
{
|
||||
SetAnimationState("gathering");
|
||||
await Awaiters.Seconds(duration);
|
||||
animator.SetTrigger(BackToIdle);
|
||||
}
|
||||
|
||||
public void SetIdle()
|
||||
{
|
||||
SetAnimationState("idle");
|
||||
}
|
||||
|
||||
private void SetAnimationState(string state)
|
||||
{
|
||||
if (animator == null) return;
|
||||
if (state == "moving")
|
||||
{
|
||||
animator.SetTrigger(ToBasic);
|
||||
}
|
||||
|
||||
if (state == "gathering")
|
||||
{
|
||||
animator.SetTrigger(BasicGathering);
|
||||
}
|
||||
|
||||
if (state == "idle")
|
||||
{
|
||||
animator.SetTrigger(BackToIdle);
|
||||
}
|
||||
|
||||
|
||||
// 重置所有动画状态
|
||||
// animator.SetBool(runAnimParam, false);
|
||||
// animator.SetBool(idleAnimParam, false);
|
||||
//
|
||||
// switch (state.ToLower())
|
||||
// {
|
||||
// case "moving":
|
||||
// animator.SetBool(runAnimParam, true);
|
||||
// break;
|
||||
//
|
||||
// case "mining":
|
||||
// animator.SetTrigger(mineAnimParam);
|
||||
// break;
|
||||
//
|
||||
// case "idle":
|
||||
// default:
|
||||
// animator.SetBool(idleAnimParam, true);
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
|
||||
// 获取当前是否在移动
|
||||
public bool IsMoving()
|
||||
{
|
||||
return animator != null && animator.GetBool(runAnimParam);
|
||||
}
|
||||
|
||||
// 强制停止所有动作
|
||||
public void StopAllActions()
|
||||
{
|
||||
SetAnimationState("idle");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 立即设置角色朝向(基于方向向量,不带动画)
|
||||
/// </summary>
|
||||
/// <param name="direction">目标方向向量</param>
|
||||
public void SetDirectionVectorImmediate(Vector3 direction)
|
||||
{
|
||||
direction.y = 0f;
|
||||
// 检查方向向量是否有效(在标准化之前)
|
||||
if (direction.magnitude < 0.01f) return;
|
||||
direction.Normalize();
|
||||
// 标准Unity旋转计算
|
||||
float targetYRotation = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
|
||||
body.rotation = Quaternion.Euler(0f, targetYRotation, 0f);
|
||||
Log($"立即朝向: 目标方向{direction} -> Y旋转{targetYRotation:F1}°");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 立即朝向目标位置(不带动画)
|
||||
/// </summary>
|
||||
/// <param name="targetPosition">目标位置</param>
|
||||
public void LookAtPositionImmediate(Vector3 targetPosition)
|
||||
{
|
||||
Vector3 direction = (targetPosition - body.position).normalized;
|
||||
SetDirectionVectorImmediate(direction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置到默认朝向
|
||||
/// </summary>
|
||||
public void ResetToDefaultRotation()
|
||||
{
|
||||
body.rotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前面向的方向
|
||||
/// </summary>
|
||||
/// <returns>1为右,-1为左,0为其他方向</returns>
|
||||
public float GetCurrentDirection()
|
||||
{
|
||||
Vector3 forward = body.forward;
|
||||
float dot = Vector3.Dot(forward, Vector3.right);
|
||||
|
||||
if (dot > 0.5f) return 1f;
|
||||
if (dot < -0.5f) return -1f;
|
||||
return 0f;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// 编辑器辅助:重置到默认朝向
|
||||
/// </summary>
|
||||
[UnityEngine.ContextMenu("重置到默认朝向")]
|
||||
private void ResetToDefault()
|
||||
{
|
||||
ResetToDefaultRotation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑器辅助:测试朝向前
|
||||
/// </summary>
|
||||
[UnityEngine.ContextMenu("测试朝向前")]
|
||||
private void TestFaceForward()
|
||||
{
|
||||
SetDirectionVectorImmediate(Vector3.forward);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑器辅助:测试移动到指定位置
|
||||
/// </summary>
|
||||
[UnityEngine.ContextMenu("测试移动(右前方)")]
|
||||
private async void TestMoveToPosition()
|
||||
{
|
||||
Vector3 testTarget = body.position + new Vector3(5f, 0f, 5f);
|
||||
Debug.Log($"测试移动到位置: {testTarget}");
|
||||
await MoveToPosition(testTarget, 3f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑器辅助:测试移动(向右)
|
||||
/// </summary>
|
||||
[UnityEngine.ContextMenu("测试移动(向右)")]
|
||||
private async void TestMoveRight()
|
||||
{
|
||||
Vector3 testTarget = body.position + new Vector3(3f, 0f, 0f);
|
||||
Debug.Log($"测试向右移动到: {testTarget}");
|
||||
await MoveToPosition(testTarget, 2f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑器辅助:测试移动(向前)
|
||||
/// </summary>
|
||||
[UnityEngine.ContextMenu("测试移动(向前)")]
|
||||
private async void TestMoveForward()
|
||||
{
|
||||
Vector3 testTarget = body.position + new Vector3(0f, 0f, 3f);
|
||||
Debug.Log($"测试向前移动到: {testTarget}");
|
||||
await MoveToPosition(testTarget, 2f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑器辅助:显示当前朝向信息
|
||||
/// </summary>
|
||||
[UnityEngine.ContextMenu("显示当前朝向")]
|
||||
private void ShowCurrentDirection()
|
||||
{
|
||||
Debug.Log($"=== 角色朝向信息 ===");
|
||||
Debug.Log($"当前旋转: {body.rotation.eulerAngles}");
|
||||
Debug.Log($"Forward: {body.forward.ToString("F3")}");
|
||||
Debug.Log($"Right: {body.right.ToString("F3")}");
|
||||
Debug.Log($"Up: {body.up.ToString("F3")}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑器辅助:验证旋转逻辑
|
||||
/// </summary>
|
||||
[UnityEngine.ContextMenu("验证旋转逻辑")]
|
||||
private void ValidateRotationLogic()
|
||||
{
|
||||
Debug.Log($"=== 标准Unity旋转逻辑验证 ===");
|
||||
|
||||
// 测试四个主要方向
|
||||
Vector3[] testDirections =
|
||||
{
|
||||
Vector3.right, // 向右 (1, 0, 0)
|
||||
Vector3.left, // 向左 (-1, 0, 0)
|
||||
Vector3.forward, // 向前 (0, 0, 1)
|
||||
Vector3.back // 向后 (0, 0, -1)
|
||||
};
|
||||
|
||||
string[] directionNames = { "右", "左", "前", "后" };
|
||||
|
||||
for (int i = 0; i < testDirections.Length; i++)
|
||||
{
|
||||
Vector3 direction = testDirections[i];
|
||||
float expectedYRotation = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
|
||||
|
||||
Debug.Log($"方向{directionNames[i]}: " +
|
||||
$"输入向量{direction} -> " +
|
||||
$"Y旋转{expectedYRotation:F1}°");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private static void Log(object t)
|
||||
{
|
||||
Debug.Log($"<color=cyan>EventPartnerMiningCharacterController-> {t} </color>");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e00cece946416e443b17a44d0933fa1e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,170 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI.PartnerGather.Mining
|
||||
{
|
||||
/// <summary>
|
||||
/// 等级状态枚举
|
||||
/// </summary>
|
||||
public enum LevelStatus
|
||||
{
|
||||
Normal, // 普通状态(未到达)
|
||||
Current, // 当前状态(进行中)
|
||||
Done // 完成状态(已完成)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 事件合作伙伴挖矿等级控制器
|
||||
/// 负责管理单个等级节点的视觉状态切换
|
||||
///
|
||||
/// 建议挂载在:每个等级节点的UI对象上
|
||||
///
|
||||
/// UI结构建议:
|
||||
/// LevelNode (挂载此脚本)
|
||||
/// ├── bg_normal (普通状态背景)
|
||||
/// ├── bg_current (当前状态背景,通常高亮显示)
|
||||
/// └── bg_done (完成状态背景,通常显示勾选标记)
|
||||
/// </summary>
|
||||
public class EventPartnerMiningLevelController : MonoBehaviour
|
||||
{
|
||||
#region UI引用配置
|
||||
[Header("=== 状态背景配置 ===")]
|
||||
[Tooltip("普通状态背景GameObject")]
|
||||
public GameObject bg_normal;
|
||||
|
||||
[Tooltip("当前进行状态背景GameObject")]
|
||||
public GameObject bg_current;
|
||||
|
||||
[Tooltip("已完成状态背景GameObject")]
|
||||
public GameObject bg_done;
|
||||
|
||||
[Header("=== 调试信息 ===")]
|
||||
[SerializeField] private LevelStatus _currentStatus = LevelStatus.Normal;
|
||||
#endregion
|
||||
|
||||
#region Unity生命周期
|
||||
private void Awake()
|
||||
{
|
||||
ValidateComponents();
|
||||
// 初始化为普通状态
|
||||
UpdateStatus(LevelStatus.Normal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证必要的UI组件是否设置
|
||||
/// </summary>
|
||||
private void ValidateComponents()
|
||||
{
|
||||
bool hasError = false;
|
||||
|
||||
if (bg_normal == null)
|
||||
{
|
||||
Log("错误:缺少普通状态背景组件 (bg_normal)");
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (bg_current == null)
|
||||
{
|
||||
Log("错误:缺少当前状态背景组件 (bg_current)");
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (bg_done == null)
|
||||
{
|
||||
Log("错误:缺少完成状态背景组件 (bg_done)");
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (!hasError)
|
||||
{
|
||||
Log("✓ 等级控制器组件验证完成");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 公共接口
|
||||
/// <summary>
|
||||
/// 更新等级状态
|
||||
/// </summary>
|
||||
/// <param name="status">新的状态</param>
|
||||
public void UpdateStatus(LevelStatus status)
|
||||
{
|
||||
if (_currentStatus == status) return; // 避免重复设置
|
||||
|
||||
_currentStatus = status;
|
||||
|
||||
// 先隐藏所有背景
|
||||
SetAllBackgroundsActive(false);
|
||||
|
||||
// 根据状态激活对应背景
|
||||
switch (status)
|
||||
{
|
||||
case LevelStatus.Normal:
|
||||
if (bg_normal != null) bg_normal.SetActive(true);
|
||||
break;
|
||||
|
||||
case LevelStatus.Current:
|
||||
if (bg_current != null) bg_current.SetActive(true);
|
||||
break;
|
||||
|
||||
case LevelStatus.Done:
|
||||
if (bg_done != null) bg_done.SetActive(true);
|
||||
break;
|
||||
}
|
||||
|
||||
Log($"等级状态已更新为: {status}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前状态
|
||||
/// </summary>
|
||||
public LevelStatus GetCurrentStatus()
|
||||
{
|
||||
return _currentStatus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置到普通状态
|
||||
/// </summary>
|
||||
public void ResetToNormal()
|
||||
{
|
||||
UpdateStatus(LevelStatus.Normal);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
/// <summary>
|
||||
/// 设置所有背景的激活状态
|
||||
/// </summary>
|
||||
private void SetAllBackgroundsActive(bool active)
|
||||
{
|
||||
if (bg_normal != null) bg_normal.SetActive(active);
|
||||
if (bg_current != null) bg_current.SetActive(active);
|
||||
if (bg_done != null) bg_done.SetActive(active);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志输出
|
||||
/// </summary>
|
||||
private static void Log(object message)
|
||||
{
|
||||
Debug.Log($"<color=cyan>EventPartnerMiningLevelController-> {message} </color>");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 编辑器辅助
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// 编辑器下的状态预览功能
|
||||
/// </summary>
|
||||
[UnityEngine.ContextMenu("预览普通状态")]
|
||||
private void PreviewNormal() => UpdateStatus(LevelStatus.Normal);
|
||||
|
||||
[UnityEngine.ContextMenu("预览当前状态")]
|
||||
private void PreviewCurrent() => UpdateStatus(LevelStatus.Current);
|
||||
|
||||
[UnityEngine.ContextMenu("预览完成状态")]
|
||||
private void PreviewDone() => UpdateStatus(LevelStatus.Done);
|
||||
#endif
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7cfa1fbc294f4b3891a453dee65082b2
|
||||
timeCreated: 1756463240
|
||||
@@ -0,0 +1,44 @@
|
||||
using asap.core;
|
||||
using DataCenter;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI.PartnerGather.Mining
|
||||
{
|
||||
public class EventPartnerMiningPanel : MonoBehaviour
|
||||
{
|
||||
private Button _btnConfirm;
|
||||
|
||||
private EventPartnerGatherManager _partnerGatherManager;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_partnerGatherManager = GContext.container.Resolve<EventPartnerGatherManager>();
|
||||
_btnConfirm = transform.Find("root/finished/Btn_confirm/btn_green").GetComponent<Button>();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_btnConfirm.onClick.AddListener(OnBtnConfirm);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Log("OnDestroy");
|
||||
}
|
||||
|
||||
//UIEvent
|
||||
private void OnBtnConfirm()
|
||||
{
|
||||
Log("OnBtnConfirm");
|
||||
// var View = await UIManager.Instance.ShowUI(UITypes.GetOrNew("EventPartnerDrawMiningPanel"));
|
||||
// UIManager.Instance.DestroyUI(gameObject.name);
|
||||
_partnerGatherManager.StartMining();
|
||||
}
|
||||
|
||||
private void Log(object t)
|
||||
{
|
||||
Debug.Log($"<Color=cyan> EventPartnerMiningPanel {t} </color>");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d47ee27a5e74fe8be48fe171e06ab9a
|
||||
timeCreated: 1756386459
|
||||
@@ -0,0 +1,103 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI.PartnerGather.Mining
|
||||
{
|
||||
/// <summary>
|
||||
/// 事件合作伙伴挖矿点管理器
|
||||
/// 管理所有挖矿点的位置,提供按顺序获取挖矿点的功能
|
||||
///
|
||||
/// 建议挂载在:MiningPoints容器对象上
|
||||
///
|
||||
/// 场景结构建议:
|
||||
/// MiningPoints (挂载此脚本)
|
||||
/// ├── MiningPoint1 (挖矿点1)
|
||||
/// ├── MiningPoint2 (挖矿点2)
|
||||
/// ├── MiningPoint3 (挖矿点3)
|
||||
/// └── ... (更多挖矿点)
|
||||
///
|
||||
/// 使用说明:
|
||||
/// - 将所有挖矿点的Transform拖拽到points数组中
|
||||
/// - 或者脚本会自动获取所有子Transform作为挖矿点
|
||||
/// </summary>
|
||||
public class EventPartnerMiningPointManager : MonoBehaviour
|
||||
{
|
||||
[Header("挖矿点配置")]
|
||||
[Tooltip("挖矿点Transform数组 - 手动拖拽或自动获取子Transform")]
|
||||
public List<Transform> points = new List<Transform>();
|
||||
|
||||
[Header("调试信息")]
|
||||
[Tooltip("当前挖矿点索引")]
|
||||
[SerializeField] private int _index = 0;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// 如果没有手动设置挖矿点,自动获取子Transform
|
||||
if (points == null || points.Count == 0)
|
||||
{
|
||||
AutoCollectMiningPoints();
|
||||
}
|
||||
|
||||
ValidateMiningPoints();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自动收集所有子Transform作为挖矿点
|
||||
/// </summary>
|
||||
private void AutoCollectMiningPoints()
|
||||
{
|
||||
points = new List<Transform>();
|
||||
|
||||
for (int i = 0; i < transform.childCount; i++)
|
||||
{
|
||||
Transform child = transform.GetChild(i);
|
||||
if (child.gameObject.activeInHierarchy)
|
||||
{
|
||||
points.Add(child);
|
||||
}
|
||||
}
|
||||
|
||||
Log($"自动收集到 {points.Count} 个挖矿点");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证挖矿点配置
|
||||
/// </summary>
|
||||
private void ValidateMiningPoints()
|
||||
{
|
||||
if (points == null || points.Count == 0)
|
||||
{
|
||||
Log("警告:未找到任何挖矿点,请检查配置");
|
||||
return;
|
||||
}
|
||||
|
||||
// 移除空引用
|
||||
for (int i = points.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (!points[i])
|
||||
{
|
||||
points.RemoveAt(i);
|
||||
Log($"移除了空的挖矿点引用,索引:{i}");
|
||||
}
|
||||
}
|
||||
|
||||
Log($"✓ 挖矿点验证完成,有效点数:{points.Count}");
|
||||
}
|
||||
public Transform GetNextPoint()
|
||||
{
|
||||
if (points == null || points.Count == 0) return null;
|
||||
var t = points[_index % points.Count];
|
||||
_index++;
|
||||
return t;
|
||||
}
|
||||
public void ResetPoints()
|
||||
{
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
private static void Log(object t)
|
||||
{
|
||||
Debug.Log($"<color=cyan>EventPartnerMiningPointManager-> {t} </color>");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b68317ca9f30be45bece30649c8d0c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,415 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using asap.core;
|
||||
using Cinemachine;
|
||||
using DataCenter;
|
||||
using UniRx;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace UI.PartnerGather.Mining
|
||||
{
|
||||
/// <summary>
|
||||
/// 事件合作伙伴挖矿场景控制器
|
||||
/// 负责管理整个挖矿场景的逻辑,包括角色移动、挖矿动画、相机跟随等
|
||||
/// 建议挂载在:挖矿场景的根对象上
|
||||
/// </summary>
|
||||
public class EventPartnerMiningSceneController : MonoBehaviour
|
||||
{
|
||||
#region 组件引用配置
|
||||
[Header("=== 核心组件引用 ===")]
|
||||
[Tooltip("角色控制器 - 建议挂载在角色GameObject上")]
|
||||
public EventPartnerMiningCharacterController character;
|
||||
|
||||
[Header("第二主角")]
|
||||
public EventPartnerMiningCharacterController Character2; // 好友的
|
||||
|
||||
[Tooltip("挖矿点管理器 - 建议挂载在MiningPoints容器对象上")]
|
||||
public EventPartnerMiningPointManager miningPoints;
|
||||
|
||||
[Tooltip("火车控制器 - 建议挂载在Train容器对象上")]
|
||||
public EventPartnerTrainController trainController;
|
||||
|
||||
[Header("=== 资源配置 ===")]
|
||||
[Tooltip("矿石资源预制体数组 - 从Project窗口拖拽矿石Prefab到这里")]
|
||||
public GameObject[] miningResourcePrefabs; // P_miningresource_01~05
|
||||
|
||||
|
||||
// [Header("=== 相机跟随设置 ===")]
|
||||
// [Tooltip("跟随相机Transform - 通常是Main Camera")]
|
||||
// public Transform followCamera;
|
||||
// [Range(-10f, 10f)]
|
||||
// public float cameraOffsetX = 2f;
|
||||
// [Range(0f, 20f)]
|
||||
// public float cameraY = 5f;
|
||||
// [Range(-50f, 0f)]
|
||||
// public float cameraZ = -10f;
|
||||
// [Range(1f, 20f)]
|
||||
// public float cameraSmooth = 8f;
|
||||
|
||||
[Header("=== 动画与移动配置 ===")]
|
||||
[Range(0.5f, 10f)]
|
||||
[Tooltip("角色移动速度")]
|
||||
public float characterMoveSpeed = 2f;
|
||||
[Range(0.5f, 5f)]
|
||||
[Tooltip("挖矿动画持续时间")]
|
||||
public float miningDuration = 2f;
|
||||
[Range(0.01f, 1f)]
|
||||
[Tooltip("矿石生成间隔")]
|
||||
public float oreGenerateDelay = 0.5f;
|
||||
#endregion
|
||||
|
||||
|
||||
#region 运行时数据
|
||||
private CinemachineVirtualCameraBase _targetVcam;
|
||||
// 动态生成的矿石对象缓存
|
||||
private readonly List<GameObject> _activeMiningResources = new();
|
||||
// 管理矿石堆,一个一个的场景可能更好
|
||||
private readonly List<Transform> _listOre = new();
|
||||
|
||||
// 数据管理与事件订阅
|
||||
private EventPartnerGatherManager _manager;
|
||||
|
||||
//
|
||||
private CompositeDisposable _disposables = new();
|
||||
// UI面板引用
|
||||
private EventPartnerDrawMiningPanel _drawMiningPanel;
|
||||
|
||||
// 数据同步缓存(用于检测变化)
|
||||
private int _lastMiningProgress = -1;
|
||||
private int _lastTrainProgress = -1;
|
||||
private int _lastPoints = -1;
|
||||
#endregion
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// 获取数据管理器
|
||||
_manager = GContext.container.Resolve<EventPartnerGatherManager>();
|
||||
// 自动查找子组件
|
||||
InitializeComponents();
|
||||
Log("EventPartnerMiningSceneController 初始化完成");
|
||||
// 第二主角
|
||||
Log(Character2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自动查找和初始化子组件
|
||||
/// 如果Inspector中未手动分配组件,会尝试在子对象中查找
|
||||
/// </summary>
|
||||
private void InitializeComponents()
|
||||
{
|
||||
Log("开始初始化挖矿场景组件...");
|
||||
|
||||
// 查找挖矿点管理器
|
||||
if (!miningPoints)
|
||||
{
|
||||
miningPoints = GetComponentInChildren<EventPartnerMiningPointManager>();
|
||||
Log(miningPoints != null
|
||||
? $"✓ 自动找到挖矿点管理器: {miningPoints.name}"
|
||||
: "⚠ 警告:未找到EventPartnerMiningPointManager组件");
|
||||
}
|
||||
|
||||
// 查找角色控制器
|
||||
if (!character)
|
||||
{
|
||||
character = GetComponentInChildren<EventPartnerMiningCharacterController>();
|
||||
Log(character ? $"✓ 自动找到角色控制器: {character.name}" : "⚠ 警告:未找到EventPartnerMiningCharacterController组件");
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 查找火车控制器
|
||||
if (!trainController)
|
||||
{
|
||||
trainController = GetComponentInChildren<EventPartnerTrainController>();
|
||||
Log(trainController ? $"✓ 自动找到火车控制器: {trainController.name}" : "⚠ 警告:未找到EventPartnerTrainController组件");
|
||||
}
|
||||
|
||||
// // 查找跟随相机(如果未设置)
|
||||
// if (followCamera == null)
|
||||
// {
|
||||
// var mainCamera = Camera.main;
|
||||
// if (mainCamera != null)
|
||||
// {
|
||||
// followCamera = mainCamera.transform;
|
||||
// Log($"✓ 自动设置跟随相机: {followCamera.name}");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Log("⚠ 警告:未找到主相机,相机跟随功能将不可用");
|
||||
// }
|
||||
// }
|
||||
|
||||
// _vCam = transform.Find("CameraRoot/Camera").GetComponent<CinemachineVirtualCamera>();
|
||||
_targetVcam = transform.Find("CameraRoot/CameraSwitch").GetComponent<CinemachineVirtualCamera>();
|
||||
|
||||
// 验证矿石资源预制体
|
||||
if (miningResourcePrefabs == null || miningResourcePrefabs.Length == 0)
|
||||
{
|
||||
Log("⚠ 警告:未设置矿石资源预制体,矿石生成功能将不可用");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"✓ 矿石资源预制体数量: {miningResourcePrefabs.Length}");
|
||||
}
|
||||
|
||||
Log("挖矿场景组件初始化完成");
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
GContext.OnEvent<EventPartnerGatherPointsUpdate>().Subscribe(_ => OnPointsUpdate()).AddTo(_disposables);
|
||||
GContext.OnEvent<EventPartnerGatherMiningProgressUpdate>().Subscribe(evt => OnMiningProgressUpdate(evt)).AddTo(_disposables);
|
||||
GContext.OnEvent<EventPartnerGatherTrainProgressUpdate>().Subscribe(evt => OnTrainProgressUpdate(evt)).AddTo(_disposables);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_listOre.Add(transform.Find("ore01"));
|
||||
_listOre.Add(transform.Find("ore02"));
|
||||
_listOre.Add(transform.Find("ore03"));
|
||||
_listOre.Add(transform.Find("ore04"));
|
||||
_listOre.Add(transform.Find("ore05"));
|
||||
|
||||
SyncAllData();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// UpdateCameraFollow();
|
||||
PollDataChanges();
|
||||
}
|
||||
|
||||
#region 相机跟随 (来自EventPartnerMiningController)
|
||||
// private void UpdateCameraFollow()
|
||||
// {
|
||||
// if (followCamera == null || character == null) return;
|
||||
// var pos = followCamera.position;
|
||||
// pos.x = Mathf.Lerp(pos.x, character.transform.position.x + cameraOffsetX, Time.deltaTime * cameraSmooth);
|
||||
// pos.y = cameraY;
|
||||
// pos.z = cameraZ;
|
||||
// followCamera.position = pos;
|
||||
// }
|
||||
#endregion
|
||||
|
||||
#region 数据同步监听 (来自EventPartnerMiningController)
|
||||
private void PollDataChanges()
|
||||
{
|
||||
if (_manager?.GatherData == null) return;
|
||||
|
||||
if (_lastPoints != _manager.GatherData.CurrentPoints)
|
||||
{
|
||||
_lastPoints = _manager.GatherData.CurrentPoints;
|
||||
}
|
||||
|
||||
if (_lastMiningProgress != _manager.GatherData.CurrentMiningProgress)
|
||||
{
|
||||
int delta = _manager.GatherData.CurrentMiningProgress - _lastMiningProgress;
|
||||
_lastMiningProgress = _manager.GatherData.CurrentMiningProgress;
|
||||
for (int i = 0; i < Mathf.Max(0, delta); i++)
|
||||
{
|
||||
DoAutoMiningStep();
|
||||
}
|
||||
}
|
||||
|
||||
if (_lastTrainProgress != _manager.GatherData.TrainProgress)
|
||||
{
|
||||
_lastTrainProgress = _manager.GatherData.TrainProgress;
|
||||
UpdateUIPanel();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPointsUpdate()
|
||||
{
|
||||
UpdateUIPanel();
|
||||
}
|
||||
|
||||
private void OnMiningProgressUpdate(EventPartnerGatherMiningProgressUpdate evt)
|
||||
{
|
||||
Log($"接收到挖矿进度更新事件: {evt.Progress}");
|
||||
// 触发自动挖矿动画(如果进度增加)
|
||||
int delta = evt.Progress - _lastMiningProgress;
|
||||
if (delta > 0)
|
||||
{
|
||||
_lastMiningProgress = evt.Progress;
|
||||
for (int i = 0; i < delta; i++)
|
||||
{
|
||||
DoAutoMiningStep();
|
||||
}
|
||||
}
|
||||
UpdateUIPanel();
|
||||
}
|
||||
|
||||
private void OnTrainProgressUpdate(EventPartnerGatherTrainProgressUpdate evt)
|
||||
{
|
||||
Log($"接收到火车进度更新事件: {evt.Progress}/{evt.MaxNodes}");
|
||||
_lastTrainProgress = evt.Progress;
|
||||
UpdateUIPanel();
|
||||
}
|
||||
|
||||
private void SyncAllData()
|
||||
{
|
||||
if (_manager?.GatherData == null) return;
|
||||
_lastPoints = _manager.GatherData.CurrentPoints;
|
||||
_lastMiningProgress = _manager.GatherData.CurrentMiningProgress;
|
||||
_lastTrainProgress = _manager.GatherData.TrainProgress;
|
||||
UpdateUIPanel();
|
||||
}
|
||||
|
||||
// ReSharper disable Unity.PerformanceAnalysis
|
||||
private async void DoAutoMiningStep()
|
||||
{
|
||||
if (miningPoints == null || character == null) return;
|
||||
var target = miningPoints.GetNextPoint();
|
||||
if (target == null) return;
|
||||
await character.MoveToPosition(target.position, characterMoveSpeed);
|
||||
await character.PlayMining(miningDuration);
|
||||
UpdateUIPanel();
|
||||
}
|
||||
|
||||
private void UpdateUIPanel()
|
||||
{
|
||||
if (_drawMiningPanel == null || _manager?.GatherData == null) return;
|
||||
_drawMiningPanel.SetPoints(_manager.GatherData.CurrentPoints);
|
||||
_drawMiningPanel.SetMiningProgress(_manager.GatherData.CurrentMiningProgress);
|
||||
_drawMiningPanel.SetTrainProgress(_manager.GatherData.TrainProgress, _manager.MaxTrainNodes);
|
||||
}
|
||||
|
||||
public void SetUIPanel(EventPartnerDrawMiningPanel panel)
|
||||
{
|
||||
_drawMiningPanel = panel;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private Transform _transPoint;
|
||||
#region 手动采集流程 (原MiningSceneController功能)
|
||||
public async Task StartMiningSequence(int point)
|
||||
{
|
||||
// 获取下一个挖矿点
|
||||
var targetPoint = miningPoints.GetNextPoint();
|
||||
if (targetPoint == null) return;
|
||||
UpdateActive();
|
||||
|
||||
var userPoint = targetPoint.Find("user_point");
|
||||
var trainPoint = targetPoint.Find("train_point");
|
||||
|
||||
await character.MoveToPosition(userPoint.position, characterMoveSpeed);
|
||||
trainController.StartMoveTo(trainPoint.position);
|
||||
|
||||
await OnEnterMining(point,targetPoint);
|
||||
}
|
||||
|
||||
// 进入阶段,需要积分,计算挖矿到什么阶段
|
||||
private async Task OnEnterMining(int points,Transform targetPoints)
|
||||
{
|
||||
await character.PlayMining(miningDuration);
|
||||
await GenerateAndCollectOres(points);
|
||||
}
|
||||
|
||||
// 更新当前激活对象
|
||||
private void UpdateActive()
|
||||
{
|
||||
// _activeOri =
|
||||
}
|
||||
|
||||
private async Task GenerateAndCollectOres(int points)
|
||||
{
|
||||
Log($"GenerateAndCollectOres-> {points}");
|
||||
// 根据点数生成对应数量的矿石
|
||||
var oreCount = CalculateOreCount(points);
|
||||
var oreList = new List<GameObject>();
|
||||
var listOrePos = CalculateOrePos(points);
|
||||
|
||||
// for (var i = 0; i < oreCount; ++i)
|
||||
// {
|
||||
// var pos = listOrePos[i % listOrePos.Count];
|
||||
// oreList.Add(CreateOre(pos));
|
||||
// }
|
||||
// foreach (var ore in oreList)
|
||||
// {
|
||||
// await Awaiters.Seconds(oreGenerateDelay);
|
||||
// _ = trainController.CollectOre(ore, points);
|
||||
// }
|
||||
for (int i = 0; i < oreCount; i++)
|
||||
{
|
||||
// await Task.Delay((int)(oreGenerateDelay * 1000));
|
||||
await Awaiters.Seconds(oreGenerateDelay);
|
||||
var pos = listOrePos[i % listOrePos.Count];
|
||||
// 生成矿石
|
||||
var ore = CreateOre(pos);
|
||||
if (ore != null)
|
||||
{
|
||||
// 矿石飞入对应车厢
|
||||
_ = trainController.CollectOre(ore, points);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int CalculateOreCount(int points)
|
||||
{
|
||||
return points switch
|
||||
{
|
||||
// return points;
|
||||
// 根据点数计算矿石数量
|
||||
// if (points >= 50) return 5;
|
||||
// if (points >= 30) return 3;
|
||||
// if (points >= 20) return 2;
|
||||
// return 1;
|
||||
< 100 => 8,
|
||||
< 200 => 16,
|
||||
_ => 1
|
||||
};
|
||||
}
|
||||
|
||||
private List<Vector3> CalculateOrePos(int points)
|
||||
{
|
||||
var ore = _listOre.First();
|
||||
return (from Transform child in ore select child.position).ToList();
|
||||
}
|
||||
private GameObject CreateOre(Vector3 position)
|
||||
{
|
||||
if (miningResourcePrefabs == null || miningResourcePrefabs.Length == 0) return null;
|
||||
// 随机选择一个矿石预制体
|
||||
|
||||
var orePos = new Vector3(position.x, position.y + 2, position.z);
|
||||
|
||||
var prefab = miningResourcePrefabs[Random.Range(0, miningResourcePrefabs.Length)];
|
||||
var ore = Instantiate(prefab, orePos, Quaternion.identity);
|
||||
_activeMiningResources.Add(ore);
|
||||
return ore;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void ResetScene()
|
||||
{
|
||||
// 清理场景中的矿石
|
||||
foreach (var ore in _activeMiningResources)
|
||||
{
|
||||
if (ore != null) DestroyImmediate(ore);
|
||||
}
|
||||
_activeMiningResources.Clear();
|
||||
|
||||
// 重置挖矿点
|
||||
miningPoints?.ResetPoints();
|
||||
|
||||
// 重置火车状态
|
||||
trainController?.ResetTrain();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
_disposables.Dispose();
|
||||
_disposables = new CompositeDisposable();
|
||||
if (_targetVcam == null) return;
|
||||
// 方法1:将该相机移动到当前优先级子队列顶部,通常会成为Live或在混合后成为Live
|
||||
_targetVcam.MoveToTopOfPrioritySubqueue();
|
||||
}
|
||||
|
||||
private void Log(object t)
|
||||
{
|
||||
Debug.Log($"<Color=cyan> EventPartnerMiningSceneController {t} </color>");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f787d728ea5bd754cbe353a37729a308
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using cfg;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI.PartnerGather.Mining
|
||||
{
|
||||
public class EventPartnerSpinItemView : MonoBehaviour
|
||||
{
|
||||
private Transform highlight;
|
||||
private Transform prize_nml;
|
||||
private Transform prize_medium;
|
||||
private Transform prize_super;
|
||||
private Transform prize_free;
|
||||
private Transform prize_more;
|
||||
|
||||
|
||||
// 调试
|
||||
private TMP_Text _textId;
|
||||
private TMP_Text _textIndex;
|
||||
|
||||
//
|
||||
private int _dataId;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
prize_nml = transform.Find("prize/prize_nml");
|
||||
prize_medium = transform.Find("prize/prize_medium");
|
||||
prize_super = transform.Find("prize/prize_super");
|
||||
prize_free = transform.Find("prize/prize_free");
|
||||
prize_more = transform.Find("prize/prize_more");
|
||||
highlight = transform.Find("prize/height");
|
||||
|
||||
_textId = transform.Find("prize/text_id").GetComponent<TMP_Text>();
|
||||
_textIndex = transform.Find("prize/text_index").GetComponent<TMP_Text>();
|
||||
}
|
||||
|
||||
public void Bind(EventPartnerSpinPoint data,int index)
|
||||
{
|
||||
// Log($"OldId :{_dataId} : new {data.ID}");
|
||||
var param = data.SpinPointParam;
|
||||
_dataId = data.ID;
|
||||
_textId.text = _dataId.ToString();
|
||||
_textIndex.text = index.ToString();
|
||||
|
||||
switch (param)
|
||||
{
|
||||
case FreeSpinPoint point:
|
||||
prize_nml.gameObject.SetActive(false);
|
||||
prize_medium.gameObject.SetActive(false);
|
||||
prize_super.gameObject.SetActive(false);
|
||||
prize_free.gameObject.SetActive(true);
|
||||
prize_more.gameObject.SetActive(false);
|
||||
UpdateText(prize_free,point.SpinPoints.ToString());
|
||||
break;
|
||||
case MoreSpinPoint point:
|
||||
prize_nml.gameObject.SetActive(false);
|
||||
prize_medium.gameObject.SetActive(false);
|
||||
prize_super.gameObject.SetActive(false);
|
||||
prize_free.gameObject.SetActive(false);
|
||||
prize_more.gameObject.SetActive(true);
|
||||
UpdateText(prize_more,Mathf.Round(point.BonusPercent * 100) + "%");
|
||||
break;
|
||||
case NormalSpinPoint point:
|
||||
prize_nml.gameObject.SetActive(true);
|
||||
prize_medium.gameObject.SetActive(false);
|
||||
prize_super.gameObject.SetActive(false);
|
||||
prize_free.gameObject.SetActive(false);
|
||||
prize_more.gameObject.SetActive(false);
|
||||
UpdateText(prize_nml,point.SpinPoints.ToString());
|
||||
break;
|
||||
}
|
||||
|
||||
void UpdateText(Transform rootUI,string content)
|
||||
{
|
||||
var label = rootUI.GetComponentInChildren<TMP_Text>(true);
|
||||
if (label)
|
||||
{
|
||||
label.text = content;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void SetSelected(bool on)
|
||||
{
|
||||
Log($"SetSelected-> {on}");
|
||||
if (highlight)
|
||||
{
|
||||
highlight.gameObject.SetActive(on);
|
||||
}
|
||||
}
|
||||
private static void Log(object t)
|
||||
{
|
||||
Debug.Log($"<color=cyan>EventPartnerSpinItemView -> {t} </color>");
|
||||
}
|
||||
|
||||
public void UpdateSelected(int targetId)
|
||||
{
|
||||
if (highlight)
|
||||
{
|
||||
highlight.gameObject.SetActive(_dataId == targetId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35ca66dc9398d9a438d444a12b759ba1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,294 @@
|
||||
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>");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03272daa085aaa94294a2ed7c94b6a39
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user