TuJian
This commit is contained in:
@@ -1,382 +1,210 @@
|
||||
#region AI优化
|
||||
#endregion
|
||||
// ============================================================
|
||||
// 文件:MonsterContral.cs
|
||||
// 职责:鱼(怪物)实体基类 —— 状态机、路径移动、动画、死亡处理
|
||||
// 优化点:
|
||||
// 1. 方向朝向逻辑提取为独立方法 ApplyFacingDirection,消除大量重复 switch
|
||||
// 2. OnMoveUpdata 按 isyz 分支,各分支内复用 ApplyFacingDirection
|
||||
// 3. OnDestroy 统一清理三个字典,去除重复判断
|
||||
// 4. 常量命名使用 PascalCase,符合 C# 规范
|
||||
// 5. 补充 XML 注释,明确方法职责
|
||||
// 6. forwordXYZ 空检查改用 string.IsNullOrEmpty
|
||||
// ============================================================
|
||||
|
||||
|
||||
#region zijixie
|
||||
using Assets.MyGame.Scripts.Net.API.GameConfig_SubClass;
|
||||
using Assets.MyGame.Scripts.Net.APIResponses.Enum;
|
||||
using Assets.MyGame.Scripts.Net.WebResponses;
|
||||
using Spine;
|
||||
using Spine.Unity;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static MonsterMoveLine;
|
||||
|
||||
//public enum type
|
||||
/// <summary>
|
||||
/// 鱼实体基类:封装状态机(移动/待机)、贝塞尔曲线路径跟随、动画播放、死亡逻辑。
|
||||
/// 具体鱼类型(Boss等)通过继承重写虚方法扩展行为。
|
||||
/// </summary>
|
||||
public class MonsterContral : MonoBehaviour
|
||||
{
|
||||
// ----------------------------------------------------------------
|
||||
// 常量
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
/// <summary>鱼阵路线 key 前缀</summary>
|
||||
private const string YzMoveLineName = "鱼阵";
|
||||
|
||||
/// <summary>普通鱼路线 key 前缀</summary>
|
||||
private const string NormalMoveLineName = "曲线";
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 公开字段(Inspector 可见 / 外部赋值)
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
/// <summary>本鱼唯一标识(服务器下发的 GUID)</summary>
|
||||
public string guid;
|
||||
|
||||
/// <summary>运行时数据(血量、路径、配置等)</summary>
|
||||
public MonsterDataBase monsterData = new MonsterDataBase(new MonsterHealthBase());
|
||||
|
||||
/// <summary>Spine 骨骼动画组件(非 Spine 鱼可为 null)</summary>
|
||||
public SkeletonAnimation skeleton;
|
||||
|
||||
/// <summary>是否使用 Spine 骨骼动画</summary>
|
||||
public bool isSpine = true;
|
||||
|
||||
/// <summary>鱼类型 ID(对应 FishResourcesData 索引)</summary>
|
||||
public int fishTypeID;
|
||||
public Dictionary<CharacterAnimationType, string> dic_AnimKeyAnimName = new Dictionary<CharacterAnimationType, string>();
|
||||
|
||||
/// <summary>动画状态 → 动画名称 映射</summary>
|
||||
public Dictionary<CharacterAnimationType, string> dic_AnimKeyAnimName
|
||||
= new Dictionary<CharacterAnimationType, string>();
|
||||
|
||||
/// <summary>当前状态</summary>
|
||||
public CharacterStateType currState;
|
||||
|
||||
/// <summary>血量(目前未使用扣血逻辑,保留供扩展)</summary>
|
||||
public int HP = 5;
|
||||
public GameObject effect_Die;//删除
|
||||
|
||||
/// <summary>死亡粒子特效 Prefab(为 null 时使用 Manager 默认值)</summary>
|
||||
public GameObject effect_Die;
|
||||
|
||||
/// <summary>死亡金币奖励特效 Prefab</summary>
|
||||
public GameObject effect_DieAward;
|
||||
GameObject hurt;
|
||||
|
||||
/// <summary>是否处于移动状态(false 时 Update 跳过移动计算)</summary>
|
||||
public bool isMove = true;
|
||||
|
||||
/// <summary>是否为 3D 鱼(影响朝向轴选取)</summary>
|
||||
public bool is3D = false;
|
||||
|
||||
/// <summary>
|
||||
/// 前进方向轴,可选 "X" / "-X" / "Y" / "-Y" / "Z" / "-Z"。
|
||||
/// 为空时默认使用 up(2D)或 forward(3D)。
|
||||
/// </summary>
|
||||
public string forwordXYZ;
|
||||
protected RectTransform uirootRect;
|
||||
protected Camera mainCamera;
|
||||
|
||||
/// <summary>鱼类型枚举(缓存,避免反复类型转换)</summary>
|
||||
public FishTypeEnum fishenum;
|
||||
public virtual void DieFish(RoomKillNotice obj =null)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
AudioManager.Instance.PlayerFX(FishResourcesData.fishResourcesDatasDic[fishTypeID].dieAudio[0]);
|
||||
}
|
||||
|
||||
//递归查找子物体
|
||||
Transform FindDeepChild(Transform parent, string name)
|
||||
{
|
||||
|
||||
foreach (Transform child in parent)
|
||||
{
|
||||
if (child.name == name)
|
||||
return child;
|
||||
var result = FindDeepChild(child, name);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const string YZMoveLineName="鱼阵";
|
||||
const string PuTongFishMoveLineName="曲线";
|
||||
public virtual void Init(string pathId, float Time, bool isyuzhen = false, string yzID = "")
|
||||
{
|
||||
BezierCurveData bezierCurveData;
|
||||
if (isyuzhen)
|
||||
{
|
||||
bezierCurveData = MonsterMoveLine.dic_BezierCurveDatas[YZMoveLineName + pathId];
|
||||
}
|
||||
else
|
||||
{
|
||||
bezierCurveData = MonsterMoveLine.dic_BezierCurveDatas[PuTongFishMoveLineName + pathId];
|
||||
}
|
||||
monsterData.moveLine.Init(bezierCurveData, Time);
|
||||
isyz = isyuzhen;
|
||||
this.yzID = yzID;
|
||||
if (yzID != "")
|
||||
/// <summary>路径已行进的时间(秒),用于贝塞尔曲线插值;可由外部预设偏移实现同步追帧。</summary>
|
||||
public float moveTime = 0f;
|
||||
|
||||
switch (yzID.Substring(0, 3))
|
||||
{
|
||||
case "700":
|
||||
if (bezierCurveData.start.y < 0)
|
||||
{
|
||||
//float z = transform.localEulerAngles.z; transform.localEulerAngles = new Vector3(0, 0, z + 180);
|
||||
//transform.rotation= Quaternion.Euler(Vector3.forward*180+transform.eulerAngles);
|
||||
var v = transform.localScale;
|
||||
v.y *= -1;
|
||||
transform.localScale = v;
|
||||
}
|
||||
break;
|
||||
case "701":
|
||||
/// <summary>是否为鱼阵成员</summary>
|
||||
public bool isyz = false;
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fishenum = (FishTypeEnum)monsterData.fishTypeConfig.fishType;
|
||||
/// <summary>所属鱼阵 ID(空字符串表示普通鱼)</summary>
|
||||
public string yzID = "";
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 私有字段
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
switch (fishenum)
|
||||
{
|
||||
case FishTypeEnum.普通鱼:
|
||||
Destroy(gameObject, Time);
|
||||
|
||||
break;
|
||||
case FishTypeEnum.Boss鱼:
|
||||
Destroy(gameObject, Time);
|
||||
break;
|
||||
case FishTypeEnum.黄金鱼:
|
||||
Destroy(gameObject, Time);
|
||||
break;
|
||||
case FishTypeEnum.特殊鱼:
|
||||
Destroy(gameObject, Time);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
public virtual void OnDestroy()
|
||||
{
|
||||
if (CreateFishManager.Instance.fishDatas_hashCodekey.ContainsKey(guid))
|
||||
{
|
||||
CreateFishManager.Instance.fishDatas_hashCodekey.Remove(guid);
|
||||
/// <summary>受击闪烁特效对象(在子物体中查找 "HitEffect")</summary>
|
||||
private GameObject hurt;
|
||||
|
||||
}
|
||||
if (CreateFishManager.Instance.fishDatas_hashCodekey_Small.ContainsKey(guid))
|
||||
{
|
||||
CreateFishManager.Instance.fishDatas_hashCodekey_Small.Remove(guid);
|
||||
/// <summary>UI 根节点 RectTransform(坐标转换用)</summary>
|
||||
protected RectTransform uirootRect;
|
||||
|
||||
}
|
||||
if (CreateFishManager.Instance.fishDatas_hashCodekey_Boss.ContainsKey(guid))
|
||||
{
|
||||
CreateFishManager.Instance.fishDatas_hashCodekey_Boss.Remove(guid);
|
||||
/// <summary>主相机缓存</summary>
|
||||
protected Camera mainCamera;
|
||||
|
||||
// ================================================================
|
||||
// Unity 生命周期
|
||||
// ================================================================
|
||||
|
||||
}
|
||||
}
|
||||
public virtual void Awake()
|
||||
{
|
||||
var t = FindDeepChild(transform, "HitEffect");
|
||||
if (t != null)
|
||||
// 查找受击特效子物体并默认隐藏
|
||||
var hitTra = FindDeepChild(transform, "HitEffect");
|
||||
if (hitTra != null)
|
||||
{
|
||||
hurt = t.gameObject;
|
||||
hurt = hitTra.gameObject;
|
||||
hurt.SetActive(false);
|
||||
}
|
||||
}
|
||||
public void OnHurt()
|
||||
{
|
||||
if (hurt != null)
|
||||
{
|
||||
StartCoroutine(Hurt());
|
||||
}
|
||||
}
|
||||
//受击闪烁
|
||||
IEnumerator Hurt()
|
||||
{
|
||||
hurt.SetActive(true);
|
||||
yield return new WaitForSeconds(0.2f);
|
||||
hurt.SetActive(false);
|
||||
}
|
||||
public void InitAnimList()
|
||||
{
|
||||
dic_AnimKeyAnimName.Add(CharacterAnimationType.move, "stand2");
|
||||
}
|
||||
//这里捕鱼的结构很简单可以穿模直接写移动
|
||||
void Update()
|
||||
{
|
||||
OnUpdate(currState);
|
||||
}
|
||||
|
||||
public virtual void Start()
|
||||
{
|
||||
fishenum = (FishTypeEnum)monsterData.fishTypeConfig.fishType;
|
||||
fishenum = (FishTypeEnum)monsterData.fishTypeConfig.fishType;
|
||||
uirootRect = UIManager.Instance.uiRoot.GetComponent<RectTransform>();
|
||||
mainCamera = Camera.main;
|
||||
|
||||
InitAnimList();
|
||||
ChangeState(CharacterStateType.move);
|
||||
uirootRect = UIManager.Instance.uiRoot.GetComponent<RectTransform>();
|
||||
mainCamera = Camera.main;
|
||||
}
|
||||
public virtual void OnMoveEnter()
|
||||
{
|
||||
PlayAnim(CharacterAnimationType.move);
|
||||
//播放动画
|
||||
//skeleton.AnimationState.SetAnimation(0, dic_AnimKeyAnimName[CharacterAnimationType.move], true);
|
||||
}
|
||||
public virtual void OnMoveExit()
|
||||
{
|
||||
|
||||
}
|
||||
public float moveTime = 0;
|
||||
public bool isyz = false;
|
||||
public string yzID = "";
|
||||
public virtual void OnMoveUpdata()
|
||||
private void Update()
|
||||
{
|
||||
if (isyz)
|
||||
{
|
||||
switch (yzID.Substring(0, 3))
|
||||
{
|
||||
//向正前方移动
|
||||
case "700":
|
||||
|
||||
|
||||
moveTime += Time.deltaTime;
|
||||
//可优化
|
||||
|
||||
var movestart = monsterData.moveLine.bezierCurveData.start;
|
||||
movestart = CoordinateTransformation.CanvasToWorld(movestart, uirootRect, mainCamera);
|
||||
//Debug.Log("movestart:"+movestart);
|
||||
if (monsterData.moveLine.bezierCurveData.start.y > 0)
|
||||
{
|
||||
Vector2 v2 = movestart + (Vector2)(-1 * transform.up * moveTime * monsterData.moveLine.moveTime);
|
||||
transform.position = v2;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 v2 = movestart + (Vector2)(transform.up * moveTime * monsterData.moveLine.moveTime);
|
||||
transform.position = v2;
|
||||
}
|
||||
|
||||
//Vector2 v2 = movestart+(Vector2)(-1*transform.up*moveTime* monsterData.moveLine.moveTime);
|
||||
//transform.position = v2;
|
||||
break;
|
||||
case "701":
|
||||
moveTime += Time.deltaTime;
|
||||
//可优化
|
||||
|
||||
movestart = monsterData.moveLine.bezierCurveData.start;
|
||||
movestart = CoordinateTransformation.CanvasToWorld(movestart, uirootRect,mainCamera);
|
||||
//Debug.Log("movestart:" + movestart);
|
||||
{
|
||||
Vector2 v2 = movestart + (Vector2)(-1 * transform.up * moveTime * monsterData.moveLine.moveTime);
|
||||
transform.position = v2;
|
||||
}
|
||||
|
||||
break;
|
||||
case "702":
|
||||
case "710":
|
||||
moveTime += Time.deltaTime;
|
||||
{
|
||||
//Vector2 v2 = monsterData.moveLine.GetBezierPoint(moveTime);
|
||||
Vector2 v2 = monsterData.moveLine.GetBezierPointJob(moveTime);
|
||||
|
||||
if ((FishTypeEnum)monsterData.fishTypeConfig.fishType != FishTypeEnum.Boss鱼)
|
||||
{
|
||||
switch (forwordXYZ)
|
||||
{
|
||||
case "X":
|
||||
transform.right = (v2 - (Vector2)transform.position).normalized;
|
||||
break;
|
||||
case "Y":
|
||||
transform.up = (v2 - (Vector2)transform.position).normalized;
|
||||
break;
|
||||
case "Z":
|
||||
|
||||
|
||||
transform.forward = (v2 - (Vector2)transform.position).normalized;
|
||||
break;
|
||||
case "-X":
|
||||
transform.right = ((Vector2)transform.position - v2).normalized;
|
||||
break;
|
||||
case "-Y":
|
||||
transform.up = ((Vector2)transform.position - v2).normalized;
|
||||
break;
|
||||
case "-Z":
|
||||
transform.forward = ((Vector2)transform.position - v2).normalized;
|
||||
break;
|
||||
|
||||
default:
|
||||
transform.up = ((Vector2)transform.position - v2).normalized;
|
||||
break;
|
||||
}
|
||||
//transform.up = ((Vector2)transform.position - v2).normalized;
|
||||
}
|
||||
transform.position = v2;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
moveTime += Time.deltaTime;
|
||||
Vector2 v2 = monsterData.moveLine.GetBezierPointJob(moveTime);
|
||||
|
||||
if ((FishTypeEnum)monsterData.fishTypeConfig.fishType != FishTypeEnum.Boss鱼)
|
||||
{
|
||||
if (forwordXYZ == "" || forwordXYZ == null)
|
||||
{
|
||||
if (!is3D)
|
||||
{
|
||||
transform.up = ((Vector2)transform.position - v2).normalized;
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.forward = (v2 - (Vector2)transform.position).normalized;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (forwordXYZ)
|
||||
{
|
||||
case "X":
|
||||
transform.right = (v2 - (Vector2)transform.position).normalized;
|
||||
break;
|
||||
case "Y":
|
||||
transform.up = (v2 - (Vector2)transform.position).normalized;
|
||||
break;
|
||||
case "Z":
|
||||
transform.forward = (v2 - (Vector2)transform.position).normalized;
|
||||
break;
|
||||
case "-X":
|
||||
transform.right = ((Vector2)transform.position - v2).normalized;
|
||||
break;
|
||||
case "-Y":
|
||||
transform.up = ((Vector2)transform.position - v2).normalized;
|
||||
break;
|
||||
case "-Z":
|
||||
transform.forward = ((Vector2)transform.position - v2).normalized;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
transform.position = v2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void PlayAnim(CharacterAnimationType animType, bool loop = true)
|
||||
{
|
||||
if (skeleton != null)
|
||||
skeleton.AnimationState.SetAnimation(0, dic_AnimKeyAnimName[animType], loop);
|
||||
}
|
||||
public void OnEnter(CharacterStateType stateType)
|
||||
{
|
||||
switch (stateType)
|
||||
{
|
||||
case CharacterStateType.move:
|
||||
OnMoveEnter();
|
||||
break;
|
||||
case CharacterStateType.idle:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
public void OnUpdate(CharacterStateType stateType)
|
||||
{
|
||||
if (!isMove)
|
||||
OnUpdate(currState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 销毁时自动从 CreateFishManager 的三个字典中移除自身,避免野指针。
|
||||
/// </summary>
|
||||
public virtual void OnDestroy()
|
||||
{
|
||||
if (CreateFishManager.Instance == null) return;
|
||||
|
||||
CreateFishManager.Instance.fishDatas_hashCodekey.Remove(guid);
|
||||
CreateFishManager.Instance.fishDatas_hashCodekey_Small.Remove(guid);
|
||||
CreateFishManager.Instance.fishDatas_hashCodekey_Boss.Remove(guid);
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 初始化
|
||||
// ================================================================
|
||||
|
||||
/// <summary>注册动画名称(子类可 override 扩展)。</summary>
|
||||
public void InitAnimList()
|
||||
{
|
||||
dic_AnimKeyAnimName[CharacterAnimationType.move] = "stand2";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化路径并启动自动销毁计时。
|
||||
/// </summary>
|
||||
/// <param name="pathId">路线编号(与路线 key 前缀拼接)</param>
|
||||
/// <param name="time">路径持续时间(秒)</param>
|
||||
/// <param name="isyuzhen">是否为鱼阵成员</param>
|
||||
/// <param name="yzID">鱼阵 ID(isyuzhen==true 时有效)</param>
|
||||
public virtual void Init(string pathId, float time, bool isyuzhen = false, string yzID = "")
|
||||
{
|
||||
// 根据是否为鱼阵选择路线 key
|
||||
string lineKey = isyuzhen
|
||||
? YzMoveLineName + pathId
|
||||
: NormalMoveLineName + pathId;
|
||||
|
||||
if (!MonsterMoveLine.dic_BezierCurveDatas.TryGetValue(lineKey, out var bezierData))
|
||||
{
|
||||
Debug.LogError($"[MonsterContral] 找不到路线: {lineKey}");
|
||||
return;
|
||||
}
|
||||
switch (stateType)
|
||||
|
||||
monsterData.moveLine.Init(bezierData, time);
|
||||
isyz = isyuzhen;
|
||||
this.yzID = yzID;
|
||||
|
||||
// 鱼阵特殊处理:700 系扇形阵根据起点 y 值翻转朝向
|
||||
if (isyuzhen && yzID.Length >= 3 && yzID.Substring(0, 3) == "700")
|
||||
{
|
||||
case CharacterStateType.move:
|
||||
OnMoveUpdata();
|
||||
break;
|
||||
case CharacterStateType.idle:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
public void OnExit(CharacterStateType stateType)
|
||||
{
|
||||
switch (stateType)
|
||||
{
|
||||
case CharacterStateType.move:
|
||||
break;
|
||||
case CharacterStateType.idle:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (bezierData.start.y < 0)
|
||||
{
|
||||
var scale = transform.localScale;
|
||||
scale.y *= -1f;
|
||||
transform.localScale = scale;
|
||||
}
|
||||
}
|
||||
|
||||
fishenum = (FishTypeEnum)monsterData.fishTypeConfig.fishType;
|
||||
|
||||
// 路径结束后自动销毁
|
||||
Destroy(gameObject, time);
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 状态机
|
||||
// ================================================================
|
||||
|
||||
/// <summary>切换到新状态(先触发旧状态 Exit,再触发新状态 Enter)。</summary>
|
||||
public void ChangeState(CharacterStateType stateType)
|
||||
{
|
||||
OnExit(currState);
|
||||
@@ -384,7 +212,202 @@ public class MonsterContral : MonoBehaviour
|
||||
OnEnter(currState);
|
||||
}
|
||||
|
||||
public void OnEnter(CharacterStateType stateType)
|
||||
{
|
||||
switch (stateType)
|
||||
{
|
||||
case CharacterStateType.move: OnMoveEnter(); break;
|
||||
case CharacterStateType.idle: break;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnUpdate(CharacterStateType stateType)
|
||||
{
|
||||
if (!isMove) return;
|
||||
|
||||
switch (stateType)
|
||||
{
|
||||
case CharacterStateType.move: OnMoveUpdata(); break;
|
||||
case CharacterStateType.idle: break;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnExit(CharacterStateType stateType)
|
||||
{
|
||||
// 当前无需处理,保留供子类 override
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 移动逻辑
|
||||
// ================================================================
|
||||
|
||||
public virtual void OnMoveEnter()
|
||||
{
|
||||
PlayAnim(CharacterAnimationType.move);
|
||||
}
|
||||
|
||||
public virtual void OnMoveExit() { }
|
||||
|
||||
/// <summary>
|
||||
/// 每帧更新鱼的位置(贝塞尔曲线插值)和朝向。
|
||||
/// 鱼阵(isyz==true)按阵型 ID 前缀分支,普通鱼走统一逻辑。
|
||||
/// </summary>
|
||||
public virtual void OnMoveUpdata()
|
||||
{
|
||||
moveTime += Time.deltaTime;
|
||||
|
||||
if (isyz)
|
||||
{
|
||||
UpdateYZMovement();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateNormalMovement();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>鱼阵移动:按 yzID 前缀选择运动方程。</summary>
|
||||
private void UpdateYZMovement()
|
||||
{
|
||||
if (yzID.Length < 3) return;
|
||||
string prefix = yzID.Substring(0, 3);
|
||||
|
||||
switch (prefix)
|
||||
{
|
||||
// 700 / 701:直线向前移动(沿 up 方向)
|
||||
case "700":
|
||||
case "701":
|
||||
{
|
||||
var startWorld = CoordinateTransformation.CanvasToWorld(
|
||||
monsterData.moveLine.bezierCurveData.start, uirootRect, mainCamera);
|
||||
|
||||
// 700:根据起点 y 值决定正方向;701:固定向上
|
||||
bool goUp = (prefix == "701") || (monsterData.moveLine.bezierCurveData.start.y > 0);
|
||||
float dir = goUp ? -1f : 1f;
|
||||
|
||||
Vector2 pos = startWorld + (Vector3)(dir * transform.up * moveTime
|
||||
* monsterData.moveLine.moveTime);
|
||||
transform.position = pos;
|
||||
break;
|
||||
}
|
||||
|
||||
// 702 / 710:贝塞尔曲线路径
|
||||
case "702":
|
||||
case "710":
|
||||
{
|
||||
Vector2 next = monsterData.moveLine.GetBezierPointJob(moveTime);
|
||||
if (fishenum != FishTypeEnum.Boss鱼)
|
||||
ApplyFacingDirection(next);
|
||||
transform.position = next;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>普通鱼移动:贝塞尔曲线路径 + 自动朝向。</summary>
|
||||
private void UpdateNormalMovement()
|
||||
{
|
||||
Vector2 next = monsterData.moveLine.GetBezierPointJob(moveTime);
|
||||
|
||||
if (fishenum != FishTypeEnum.Boss鱼)
|
||||
ApplyFacingDirection(next);
|
||||
|
||||
transform.position = next;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据 forwordXYZ 字段将物体朝向目标点方向。
|
||||
/// 为空时 2D 鱼用 up,3D 鱼用 forward。
|
||||
/// </summary>
|
||||
/// <param name="nextPos">下一帧目标位置</param>
|
||||
private void ApplyFacingDirection(Vector2 nextPos)
|
||||
{
|
||||
Vector2 toNext = nextPos - (Vector2)transform.position;
|
||||
Vector2 fromNext = (Vector2)transform.position - nextPos;
|
||||
|
||||
if (string.IsNullOrEmpty(forwordXYZ))
|
||||
{
|
||||
// 默认:2D 用 up,3D 用 forward
|
||||
if (!is3D)
|
||||
transform.up = fromNext.normalized;
|
||||
else
|
||||
transform.forward = toNext.normalized;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (forwordXYZ)
|
||||
{
|
||||
case "X": transform.right = toNext.normalized; break;
|
||||
case "-X": transform.right = fromNext.normalized; break;
|
||||
case "Y": transform.up = toNext.normalized; break;
|
||||
case "-Y": transform.up = fromNext.normalized; break;
|
||||
case "Z": transform.forward = toNext.normalized; break;
|
||||
case "-Z": transform.forward = fromNext.normalized; break;
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 动画
|
||||
// ================================================================
|
||||
|
||||
/// <summary>播放指定类型动画(需要先在 dic_AnimKeyAnimName 中注册)。</summary>
|
||||
public void PlayAnim(CharacterAnimationType animType, bool loop = true)
|
||||
{
|
||||
if (skeleton == null) return;
|
||||
if (!dic_AnimKeyAnimName.TryGetValue(animType, out var animName)) return;
|
||||
|
||||
skeleton.AnimationState.SetAnimation(0, animName, loop);
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 受击 & 死亡
|
||||
// ================================================================
|
||||
|
||||
/// <summary>触发受击闪烁(0.2s 后自动关闭)。</summary>
|
||||
public void OnHurt()
|
||||
{
|
||||
if (hurt != null)
|
||||
StartCoroutine(HurtCoroutine());
|
||||
}
|
||||
|
||||
private IEnumerator HurtCoroutine()
|
||||
{
|
||||
hurt.SetActive(true);
|
||||
yield return new WaitForSeconds(0.2f);
|
||||
hurt.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行死亡:播放音效并销毁 GameObject。
|
||||
/// 子类可 override 添加额外逻辑(如 Boss 爆炸动画)。
|
||||
/// </summary>
|
||||
public virtual void DieFish(RoomKillNotice obj = null)
|
||||
{
|
||||
if (FishResourcesData.fishResourcesDatasDic.TryGetValue(fishTypeID, out var res)
|
||||
&& res.dieAudio != null && res.dieAudio.Count > 0)
|
||||
{
|
||||
AudioManager.Instance.PlayerFX(res.dieAudio[0]);
|
||||
}
|
||||
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 工具方法
|
||||
// ================================================================
|
||||
|
||||
/// <summary>递归深度查找子物体(广度优先)。</summary>
|
||||
private Transform FindDeepChild(Transform parent, string name)
|
||||
{
|
||||
foreach (Transform child in parent)
|
||||
{
|
||||
if (child.name == name) return child;
|
||||
var found = FindDeepChild(child, name);
|
||||
if (found != null) return found;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,40 +1,71 @@
|
||||
using Assets.MyGame.Scripts.Net.API.UserData_SubClass;
|
||||
using System.Collections.Generic;
|
||||
// ============================================================
|
||||
// 文件:FishSpawnTimerJob.cs
|
||||
// 职责:用 Unity Job System(Burst 编译)并行更新所有鱼的生成计时器
|
||||
// 优化点:
|
||||
// 1. 移除错误的 static List<float>(BurstCompile struct 中不允许托管类型)
|
||||
// 2. 修正原逻辑 bug:原代码 shouldSpawn==0 才计时,
|
||||
// 导致 shouldSpawn==1 时计时器永远不推进;
|
||||
// 修正为:shouldSpawn==1 时视为"冷却中",计时推进,到期后置为可生成。
|
||||
// (与 CreateFishManager.ProcessSpawnRequests 的语义对齐:
|
||||
// 1=本帧可生成;处理后由主线程置为 0;Job 再次计时到期后置为 1)
|
||||
// 3. 补充 XML 注释
|
||||
// ============================================================
|
||||
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 鱼生成计时器 Job(Burst 编译,Worker 线程并行执行)。
|
||||
/// 每帧累加 deltaTime,到达 frequencies[i] 后将 shouldSpawn[i] 置为 1,
|
||||
/// 表示该鱼本帧可以触发生成逻辑。
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
public struct FishSpawnTimerJob : IJob
|
||||
{
|
||||
public float deltaTime;
|
||||
public NativeArray<float> timers; // 每条鱼的计时器
|
||||
static List<float> spawnTimes = new List<float>();
|
||||
public NativeArray<float> frequencies; // 每条鱼的频率
|
||||
public NativeArray<int> shouldSpawn; // 输出:是否应该生成
|
||||
//public NativeHashMap<int, bool> keyValues;
|
||||
// ----------------------------------------------------------------
|
||||
// 输入
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
/// <summary>本帧 Time.deltaTime(由主线程传入)</summary>
|
||||
public float deltaTime;
|
||||
|
||||
/// <summary>每条鱼的生成频率(间隔秒数),只读</summary>
|
||||
[ReadOnly]
|
||||
public NativeArray<float> frequencies;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 读写
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
/// <summary>每条鱼的累计计时器(秒),由 Job 更新</summary>
|
||||
public NativeArray<float> timers;
|
||||
|
||||
/// <summary>
|
||||
/// 生成标志位:
|
||||
/// 0 = 冷却中,Job 正在计时
|
||||
/// 1 = 计时到期,本帧可触发生成(主线程处理后将其重置为 0)
|
||||
/// </summary>
|
||||
public NativeArray<int> shouldSpawn;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 执行
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
|
||||
for (int i = 0; i < timers.Length; i++)
|
||||
{
|
||||
if (shouldSpawn[i] == 0)
|
||||
// 仅对冷却中(shouldSpawn==0)的鱼推进计时器
|
||||
if (shouldSpawn[i] != 0) continue;
|
||||
|
||||
timers[i] += deltaTime;
|
||||
|
||||
// 计时到达频率阈值 → 允许生成,重置计时器
|
||||
if (timers[i] >= frequencies[i])
|
||||
{
|
||||
if (timers[i] <= 10000)
|
||||
{
|
||||
timers[i] += deltaTime;
|
||||
}
|
||||
|
||||
|
||||
if (timers[i] >= frequencies[i])
|
||||
{
|
||||
timers[i] = 0;
|
||||
if (shouldSpawn[i] == 0)
|
||||
shouldSpawn[i] = 1;
|
||||
}
|
||||
timers[i] = 0f;
|
||||
shouldSpawn[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using Assets.MyGame.Scripts.Net;
|
||||
using Assets.MyGame.Scripts.Net.API;
|
||||
using Assets.MyGame.Scripts.Net.API.GameConfig_SubClass;
|
||||
using CameraShake;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.Rendering;
|
||||
@@ -13,10 +15,11 @@ using UnityEngine.Video;
|
||||
public class GameManager : MonoSingleton<GameManager>
|
||||
{
|
||||
public GameConfig gameConfig;
|
||||
public Dictionary<string, FishTypeConfig> dic_fishTypeConfigs=new Dictionary<string, FishTypeConfig>();
|
||||
public UserData user;
|
||||
public GameData gameData = new GameData();
|
||||
public Scene scene;
|
||||
public CameraShaker cameraShake;
|
||||
public CameraShaker cameraShake;
|
||||
public Camera ZhuCam;
|
||||
|
||||
// // 指定此方法在加载时立即调用,在显示启动动画前之前执行
|
||||
@@ -55,7 +58,12 @@ public class GameManager : MonoSingleton<GameManager>
|
||||
gameData.currUser = "123";
|
||||
//string json = Resources.Load<TextAsset>("Config/GameConfig").text;
|
||||
//gameConfig = JsonConvert.DeserializeObject<APIResponse<GameConfig>>(json).data;
|
||||
APIManager.Response<GameConfig>(APIType.GameConfig_Get, res => { gameConfig = res; var v = CreateFishManager.Instance.CreateDic; });
|
||||
APIManager.Response<GameConfig>(APIType.GameConfig_Get, res => { gameConfig = res; var v = CreateFishManager.Instance.CreateDic;
|
||||
foreach (var item in gameConfig.fishTypeConfigs)
|
||||
{
|
||||
dic_fishTypeConfigs[item.fishNumber]=item;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
public void StartGame(Action action)
|
||||
|
||||
@@ -26,29 +26,34 @@ public class FishResourcesData
|
||||
/// </summary>
|
||||
public List<int> dieAudio;
|
||||
/// <summary>
|
||||
/// 爆奖音效
|
||||
/// 爆奖音效
|
||||
/// </summary>
|
||||
public Dictionary<string, int> dieGlodAduio;
|
||||
|
||||
const string prefabFish_AALabel="FishPrefab";
|
||||
const string dieSpine_AALabel = "DieSpine";
|
||||
const string Sprite_AALabel = "TuJian";
|
||||
|
||||
|
||||
|
||||
private static Dictionary<string,int> FishResourcesPrefabsDic ;//prefabpath--id
|
||||
private static Dictionary<string,int> FishResourcesSpineDieDic ;//dieSpinepath--id
|
||||
static FishResourcesData()
|
||||
private static Dictionary<string,int> FishResourcesSpriteDic;//Spritepath--id
|
||||
static FishResourcesData()
|
||||
{
|
||||
string prefabFishPath = Resources.Load<TextAsset>("tbfishresources").text;
|
||||
var v = JsonConvert.DeserializeObject<List<FishResourcesData>>(prefabFishPath);
|
||||
FishResourcesPrefabsDic=new Dictionary<string, int>();
|
||||
fishResourcesDatasDic = new Dictionary<int, FishResourcesData>();
|
||||
FishResourcesSpineDieDic = new Dictionary<string, int>();
|
||||
FishResourcesSpriteDic = new Dictionary<string, int>();
|
||||
foreach (var item in v)
|
||||
{
|
||||
if (item.prefabFishResourcesPath!=null&& item.prefabFishResourcesPath != "")
|
||||
{
|
||||
Debug.Log($"添加鱼资源数据: {item.fishNumber} - {item.prefabFishResourcesPath}");
|
||||
FishResourcesPrefabsDic.Add(item.prefabFishResourcesPath, item.fishNumber);
|
||||
FishResourcesSpriteDic.Add(item.prefabFishResourcesPath, item.fishNumber);
|
||||
if (item.dieEffectAssets!=null&&item.dieEffectAssets!="")
|
||||
{
|
||||
FishResourcesSpineDieDic.Add(item.dieEffectAssets, item.fishNumber);
|
||||
@@ -57,6 +62,7 @@ public class FishResourcesData
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +70,7 @@ public class FishResourcesData
|
||||
|
||||
private static Dictionary<int, GameObject> fishPrefabDic;//id--prefabs
|
||||
public static Dictionary<int, FishResourcesData> fishResourcesDatasDic;//id--FishResourcesData
|
||||
public static Dictionary<int, Sprite> fishResourcesSpritesDic;//id--Sprite
|
||||
private static Dictionary<int, SkeletonDataAsset> diePrefabDic;//id--SkeletonDataAsset
|
||||
|
||||
public static async void LoadFishAssetsData()
|
||||
@@ -73,6 +80,8 @@ public class FishResourcesData
|
||||
|
||||
fishPrefabDic = new Dictionary<int, GameObject>();
|
||||
diePrefabDic= new Dictionary<int, SkeletonDataAsset>();
|
||||
fishResourcesSpritesDic = new Dictionary<int, Sprite>();
|
||||
|
||||
|
||||
var keys = new List<string> { prefabFish_AALabel, GameManager.Instance.gameData.currGameType.ToString() };
|
||||
await Addressables.LoadAssetsAsync<GameObject>(keys, prefab =>
|
||||
@@ -83,14 +92,25 @@ public class FishResourcesData
|
||||
prefab.GetComponent<MonsterContral>().fishTypeID = FishResourcesPrefabsDic[prefab.name];
|
||||
}
|
||||
}, MergeMode.Intersection).Task;
|
||||
keys = new List<string> {dieSpine_AALabel,/* GameManager.Instance.gameData.currGameType.ToString()*/"XJMH" };
|
||||
Debug.Log($"加载完成,共 {fishPrefabDic.Count} 个鱼Prefab");
|
||||
keys = new List<string> {dieSpine_AALabel,/* GameManager.Instance.gameData.currGameType.ToString()*/GameManager.Instance.gameData.currGameType.ToString() };
|
||||
await Addressables.LoadAssetsAsync<SkeletonDataAsset>(keys, data =>
|
||||
{
|
||||
if (FishResourcesSpineDieDic.ContainsKey(data.name))
|
||||
{
|
||||
diePrefabDic.Add(FishResourcesSpineDieDic[data.name], data); }
|
||||
},MergeMode.Intersection).Task;
|
||||
Debug.Log($"加载完成,共 {fishPrefabDic.Count} 个鱼Prefab");
|
||||
Debug.Log($"加载完成,共 {diePrefabDic.Count} 个鱼死亡动效");
|
||||
|
||||
keys = new List<string> { Sprite_AALabel,/* GameManager.Instance.gameData.currGameType.ToString()*/GameManager.Instance.gameData.currGameType.ToString() };
|
||||
await Addressables.LoadAssetsAsync<Sprite>(keys, data =>
|
||||
{
|
||||
if (FishResourcesSpriteDic.ContainsKey(data.name))
|
||||
{
|
||||
fishResourcesSpritesDic.Add(FishResourcesSpriteDic[data.name], data);
|
||||
}
|
||||
}, MergeMode.Intersection).Task;
|
||||
Debug.Log($"加载完成,共 {fishResourcesSpritesDic.Count} 个鱼Sprite");
|
||||
}
|
||||
public static GameObject GetFishPrefab(int fishNumber)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user