新人敌人随机生成
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace IndianOceanAssets.Engine2_5D
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏出生点管理器 —— 每次进入场景时,将玩家和敌人随机分配到出生点。
|
||||
///
|
||||
/// 使用方式:
|
||||
/// 1. 场景中创建空物体,挂上此脚本
|
||||
/// 2. 将玩家预制体拖到 playerPrefab
|
||||
/// 3. 将 enemy_01~05 预制体拖到 enemyPrefabs 数组
|
||||
/// 4. 设置每种敌人的数量
|
||||
/// 5. 运行时自动从 EnemySpawnPoint 中随机选取位置
|
||||
/// </summary>
|
||||
public class GameSpawnManager : MonoBehaviour
|
||||
{
|
||||
[Header("玩家")]
|
||||
[Tooltip("玩家预制体")]
|
||||
[SerializeField] GameObject playerPrefab;
|
||||
|
||||
[Header("敌人配置")]
|
||||
[Tooltip("敌人预制体数组(enemy_01 ~ enemy_05)")]
|
||||
[SerializeField] GameObject[] enemyPrefabs;
|
||||
|
||||
[Tooltip("每种敌人的数量(数组长度需与 enemyPrefabs 一致)")]
|
||||
[SerializeField] int[] enemyCounts;
|
||||
|
||||
[Header("设置")]
|
||||
[Tooltip("敌人总数上限(即使 enemyCounts 总和超过此值也不会多生成)")]
|
||||
[SerializeField] int maxEnemies = 5;
|
||||
|
||||
[Tooltip("玩家出生点离敌人的最小安全距离")]
|
||||
[SerializeField] float playerSafeDistance = 5f;
|
||||
|
||||
// 运行时
|
||||
private List<Transform> _allSpawnPoints;
|
||||
private readonly List<EnemyAI> _spawnedEnemies = new List<EnemyAI>();
|
||||
private GameObject _spawnedPlayer;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// 收集所有出生点
|
||||
var spawnPointComponents = FindObjectsByType<EnemySpawnPoint>(FindObjectsSortMode.None);
|
||||
_allSpawnPoints = new List<Transform>();
|
||||
foreach (var sp in spawnPointComponents)
|
||||
{
|
||||
_allSpawnPoints.Add(sp.transform);
|
||||
}
|
||||
|
||||
if (_allSpawnPoints.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("[GameSpawnManager] 场景中没有找到任何 EnemySpawnPoint!");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log($"[GameSpawnManager] 找到 {_allSpawnPoints.Count} 个出生点");
|
||||
|
||||
// 打乱出生点顺序
|
||||
Shuffle(_allSpawnPoints);
|
||||
|
||||
int usedIndex = 0;
|
||||
|
||||
// 1. 生成玩家
|
||||
if (playerPrefab != null)
|
||||
{
|
||||
// 找一个离已使用点较远的位置
|
||||
Transform playerSpawn = FindSafeSpawnPoint(usedIndex, playerSafeDistance);
|
||||
if (playerSpawn == null)
|
||||
playerSpawn = _allSpawnPoints[usedIndex];
|
||||
|
||||
// 将玩家使用的点交换到 usedIndex 位置,标记为已使用
|
||||
int playerIdx = _allSpawnPoints.IndexOf(playerSpawn);
|
||||
if (playerIdx != usedIndex)
|
||||
{
|
||||
var temp = _allSpawnPoints[usedIndex];
|
||||
_allSpawnPoints[usedIndex] = _allSpawnPoints[playerIdx];
|
||||
_allSpawnPoints[playerIdx] = temp;
|
||||
}
|
||||
usedIndex++;
|
||||
|
||||
_spawnedPlayer = Instantiate(playerPrefab, playerSpawn.position, playerSpawn.rotation);
|
||||
Debug.Log($"[GameSpawnManager] 玩家出生在 #{GetSpawnPointIndex(playerSpawn)} 位置=({playerSpawn.position.x:F1}, {playerSpawn.position.y:F1}, {playerSpawn.position.z:F1})");
|
||||
|
||||
// 将摄像机挂到玩家身上
|
||||
var camFollow = Camera.main != null ? Camera.main.GetComponent<CameraFollow>() : null;
|
||||
if (camFollow == null && Camera.main != null)
|
||||
camFollow = Camera.main.gameObject.AddComponent<CameraFollow>();
|
||||
if (camFollow != null)
|
||||
{
|
||||
camFollow.SetTarget(_spawnedPlayer.transform);
|
||||
Debug.Log("[GameSpawnManager] 摄像机已跟随玩家");
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 生成敌人
|
||||
if (enemyPrefabs != null && enemyCounts != null)
|
||||
{
|
||||
int prefabCount = Mathf.Min(enemyPrefabs.Length, enemyCounts.Length);
|
||||
for (int p = 0; p < prefabCount; p++)
|
||||
{
|
||||
if (enemyPrefabs[p] == null) continue;
|
||||
|
||||
for (int i = 0; i < enemyCounts[p] && usedIndex < _allSpawnPoints.Count; i++)
|
||||
{
|
||||
// 检查是否达到敌人总数上限
|
||||
if (_spawnedEnemies.Count >= maxEnemies)
|
||||
break;
|
||||
|
||||
Transform spawnPos = _allSpawnPoints[usedIndex++];
|
||||
var enemy = Instantiate(enemyPrefabs[p], spawnPos.position, spawnPos.rotation);
|
||||
var ai = enemy.GetComponent<EnemyAI>();
|
||||
if (ai != null)
|
||||
_spawnedEnemies.Add(ai);
|
||||
Debug.Log($" 敌人{p + 1}#{_spawnedEnemies.Count}: {enemyPrefabs[p].name} 位置=({spawnPos.position.x:F1}, {spawnPos.position.y:F1}, {spawnPos.position.z:F1}) 出生点=#{GetSpawnPointIndex(spawnPos)}");
|
||||
}
|
||||
|
||||
// 外层也检查
|
||||
if (_spawnedEnemies.Count >= maxEnemies)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"[GameSpawnManager] 生成完毕:1 个玩家 + {_spawnedEnemies.Count} 个敌人");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 找一个离已使用点都较远的出生点。
|
||||
/// </summary>
|
||||
Transform FindSafeSpawnPoint(int startIndex, float minDistance)
|
||||
{
|
||||
for (int i = startIndex; i < _allSpawnPoints.Count; i++)
|
||||
{
|
||||
bool safe = true;
|
||||
for (int j = 0; j < startIndex; j++)
|
||||
{
|
||||
if (Vector3.Distance(_allSpawnPoints[i].position, _allSpawnPoints[j].position) < minDistance)
|
||||
{
|
||||
safe = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (safe) return _allSpawnPoints[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取出生点编号(用于调试日志)。
|
||||
/// </summary>
|
||||
int GetSpawnPointIndex(Transform spawnPoint)
|
||||
{
|
||||
var comp = spawnPoint.GetComponent<EnemySpawnPoint>();
|
||||
return comp != null ? comp.Index : -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fisher-Yates 洗牌算法。
|
||||
/// </summary>
|
||||
void Shuffle<T>(List<T> list)
|
||||
{
|
||||
for (int i = list.Count - 1; i > 0; i--)
|
||||
{
|
||||
int j = Random.Range(0, i + 1);
|
||||
T temp = list[i];
|
||||
list[i] = list[j];
|
||||
list[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有已生成的敌人。
|
||||
/// </summary>
|
||||
public List<EnemyAI> GetSpawnedEnemies()
|
||||
{
|
||||
_spawnedEnemies.RemoveAll(e => e == null);
|
||||
return _spawnedEnemies;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取已生成的玩家。
|
||||
/// </summary>
|
||||
public GameObject GetSpawnedPlayer() => _spawnedPlayer;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user