UMiniGame/Assets/Games/Scripts/EnemyManager.cs
2025-12-27 15:29:20 +08:00

224 lines
7.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.InputSystem;
public class EnemyManager : MonoBehaviour
{
public float attackRadius = 5f;
public Transform[] startLoc = new Transform[] { };
public Transform[] pathesLoc = new Transform[] { };
[Tooltip("每只怪出现的间隔")]
public float enemyAppearDuration = 0.1f;
[Tooltip("爆炸音效")]
public AudioClip boomClip;
[Tooltip("爆炸音效的音量")]
public float boomVolumeScale = 1f;
//public Transform endLoc;
public Transform[] enemyPrefabList = new Transform[] { };
//private List<Transform> _enemies = new List<Transform>();
// Start is called before the first frame update
private bool _isAttacking = false;
/// <summary>
/// 怪物攻击的当前波次
/// </summary>
private int _currAttackIndex = 0;
///// <summary>
///// 当前版怪物攻击的总波次
///// </summary>
//public int enemyAttackIndex = 20;
/// <summary>
/// 每波怪物的攻击间隔,单位秒
/// </summary>
public float attackInterval = 10f;
/// <summary>
/// 怪物移动速度
/// </summary>
public float enemyMoveSpeed = 1f;
[Tooltip("每波怪的个数")]
public float initEachEnemyCount = 50;
private Camera _cam;
private void Awake()
{
_cam = Camera.main;
}
void Start()
{
_isAttacking = true;
StartCoroutine(EnemyStartAttack());
}
// Update is called once per frame
//void Update()
//{
//}
private void OnDestroy()
{
StopAllCoroutines();
}
private IEnumerator EnemyStartAttack()
{
yield return new WaitForSeconds(2f); // Adjust the interval as needed
while (_isAttacking)
{
if (_currAttackIndex >= enemyPrefabList.Length)
{
_currAttackIndex = 0;
}
for (int i = 0; i < initEachEnemyCount; i++)
{
//var pos = new Vector3(Random.Range(0f, Screen.width), Random.Range(Screen.height + 125f, Screen.height + 353f), _cam.nearClipPlane - _cam.transform.position.z);
var enemyComp = SpawnEnemy(Mathf.FloorToInt(Random.value * enemyPrefabList.Length));
//float duration = Mathf.Max(0.2f, Random.value / _currAttackIndex * 10);
//if (i % 5 == 0)
//{
// yield return new WaitForSeconds(1f); // Adjust the interval as needed
//}
//else
//{
enemyComp.transform.name = $"Enemy_{i}";
if (enemyComp != null)
{
yield return null; // Adjust the interval as needed
enemyComp.ResetMaterial();
}
yield return new WaitForSeconds(enemyAppearDuration); // Adjust the interval as needed
//}
}
_currAttackIndex++;
yield return new WaitForSeconds(attackInterval); // Adjust the interval as needed
}
}
public Enemy SpawnEnemy(int enemyIndex)
{
if (enemyIndex < 0 || enemyIndex >= enemyPrefabList.Length)
{
Debug.LogError("Invalid enemy index: " + enemyIndex);
return null;
}
//var fromPos = _cam.ScreenToWorldPoint(spawnPosition);
//spawnPosition.y = 350;//移动到指定位置
//var toPos = _cam.ScreenToWorldPoint(spawnPosition);
var pos = Vector3.zero;
var enemy = Instantiate(enemyPrefabList[enemyIndex], Vector3.zero, Quaternion.identity);
//if (startLoc.Length == 0 && pathesLoc.Length > 0)
//{
var startPos = startLoc.Last().position;
var bounds = enemy.GetComponentInChildren<Collider>().bounds;
//float angle = Mathf.PI * 2 * Random.value; // 修正角度计算逻辑
float x = Random.Range(0, Screen.width);
float y = Screen.height + 100;
pos = new Vector3(x, y, _cam.transform.position.z);
bool isLeft = pos.x < Screen.width * 0.5f;
pos = _cam.ScreenToWorldPoint(pos);
pos.y = pathesLoc.Last().position.y;
pos.z = startPos.z;
//往中聚拢怪物尺寸的长度
if (isLeft)
{
pos.x += bounds.size.magnitude * 0.5f;
}
else
{
pos.x -= bounds.size.magnitude * 0.5f;
}
//pathesLoc.Last().position + new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * attackRadius;
//}
//else
//{
// pos = startLoc[Random.Range(0, startLoc.Length)].position;
//}
enemy.position = pos;
var enemyComp = enemy.GetComponent<Enemy>();
enemy.eulerAngles = new Vector3(enemy.eulerAngles.x + enemyComp.addEulerX, enemy.eulerAngles.y + 180, enemy.eulerAngles.z);
if (boomClip != null)
{
enemyComp.boomClip = boomClip;
enemyComp.boomVolumeScale = boomVolumeScale;
}
//Debug.Log(enemy.eulerAngles);
Global.Enemies.Add(enemyComp);
// 初始化时将物体移动到最近的NavMesh点
NavMeshHit initHit;
if (NavMesh.SamplePosition(enemy.position, out initHit, 8.0f, NavMesh.AllAreas))
{
enemy.position = initHit.position;
}
else
{
Debug.LogWarning("init failed");
}
var destPos = pathesLoc[enemyComp.ToPosIndex].position;
destPos = new Vector3(enemy.position.x, destPos.y, destPos.z);
ToDestination(enemyComp, destPos);
enemyComp.SetAlphaMaterial();
return enemyComp;
//enemy.DOMove(toPos, enemyMoveSpeed)
// .SetEase(Ease.Linear) // 强制匀速,无加速减速
// .SetSpeedBased(); // 标记为可回收,完成后回收到池; // 关键:将第二个参数视为“速度”而非“时间”
}
private void ToDestination(Enemy enemy, Vector3 position)
{
if (enemy.ToPosIndex >= pathesLoc.Length)
{
return;
}
var screenPt = _cam.WorldToScreenPoint(position);
Ray ray = _cam.ScreenPointToRay(screenPt);
if (Physics.Raycast(ray, out RaycastHit hit, 100))
{
// 2. 确保射线击中的点在NavMesh上采样最近的有效NavMesh点
NavMeshHit navHit;
if (NavMesh.SamplePosition(hit.point, out navHit, 3.0f, NavMesh.AllAreas))
{
var agent = enemy.GetComponentInChildren<UnityEngine.AI.NavMeshAgent>();
agent.speed = enemyMoveSpeed;
// 3. 设置NavMeshAgent的目标位置
agent.SetDestination(navHit.position);
enemy.OnReachDestination = (toPosIndex) =>
{
if (toPosIndex >= pathesLoc.Length)
{
enemy.OnReachDestination = null;
Global.DespawnEnemy(enemy);
return;
}
ToDestination(enemy, pathesLoc[toPosIndex].position);
};
}
else
{
Debug.LogWarning("射线击中的位置不在NavMesh烘焙区域内");
}
}
}
}