备份CatanBuilding瘦身独立工程

This commit is contained in:
JSD\13999
2026-05-26 16:15:54 +08:00
commit 2d0e6a61b7
12001 changed files with 2431925 additions and 0 deletions

View 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;
}
}

View File

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

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 63598da602454054aa518a6863a912b5
timeCreated: 1756386503

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 31bfe17ee3534f48b09cb6a6b10ae6df
timeCreated: 1756212375

View File

@@ -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>");
}
}
}

View File

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

View File

@@ -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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7cfa1fbc294f4b3891a453dee65082b2
timeCreated: 1756463240

View File

@@ -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>");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5d47ee27a5e74fe8be48fe171e06ab9a
timeCreated: 1756386459

View File

@@ -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>");
}
}
}

View File

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

View File

@@ -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>");
}
}
}

View File

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

View File

@@ -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);
}
}
}
}

View File

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

View File

@@ -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>");
}
}
}

View File

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