코어, 벽, 적의 생성 및 충돌, 데미지 판정
This commit is contained in:
26
Assets/Scripts/Enemy/EnemyAttack.cs
Normal file
26
Assets/Scripts/Enemy/EnemyAttack.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class EnemyAttack : MonoBehaviour
|
||||
{
|
||||
[Header("Attack Settings")]
|
||||
[SerializeField] private float damage = 10f;
|
||||
[SerializeField] private float attackCooldown = 1.0f; // 공격 간격 (1초)
|
||||
|
||||
private float _nextAttackTime = 0f;
|
||||
|
||||
// EnemyAttack.cs
|
||||
private void OnCollisionStay(Collision collision)
|
||||
{
|
||||
if (Time.time >= _nextAttackTime)
|
||||
{
|
||||
// 상대방에게서 IDamageable 인터페이스가 있는지 확인
|
||||
IDamageable target = collision.gameObject.GetComponent<IDamageable>();
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
target.TakeDamage(damage);
|
||||
_nextAttackTime = Time.time + attackCooldown;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Assets/Scripts/Enemy/EnemyMoveDefault.cs
Normal file
45
Assets/Scripts/Enemy/EnemyMoveDefault.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI; // NavMesh 기능을 위해 필수
|
||||
|
||||
public class EnemyAI : MonoBehaviour
|
||||
{
|
||||
private NavMeshAgent _agent;
|
||||
private Transform _target;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_agent = GetComponent<NavMeshAgent>();
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
// --- 2번 방법: 위치 보정 로직 시작 ---
|
||||
// 현재 위치에서 반경 2.0f 이내에 가장 가까운 NavMesh가 있는지 검사합니다.
|
||||
if (NavMesh.SamplePosition(transform.position, out NavMeshHit hit, 2.0f, NavMesh.AllAreas))
|
||||
{
|
||||
// 에이전트를 해당 위치로 강제 순간이동(Warp) 시킵니다.
|
||||
// 이 작업은 에러를 방지하고 에이전트를 활성화합니다.
|
||||
_agent.Warp(hit.position);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"{gameObject.name} 근처에 NavMesh를 찾을 수 없습니다! 스폰 위치를 확인하세요.");
|
||||
}
|
||||
// --- 2번 방법 끝 ---
|
||||
|
||||
// 기존 타겟(Core) 설정 로직
|
||||
GameObject coreObj = GameObject.FindWithTag("Core");
|
||||
if (coreObj != null)
|
||||
{
|
||||
_target = coreObj.transform;
|
||||
}
|
||||
}
|
||||
|
||||
void FixedUpdate()
|
||||
{
|
||||
if (_target != null && _agent.isOnNavMesh)
|
||||
{
|
||||
_agent.SetDestination(_target.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Assets/Scripts/GameBase/Core.cs
Normal file
23
Assets/Scripts/GameBase/Core.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
public class Core : MonoBehaviour, IDamageable
|
||||
{
|
||||
[SerializeField] private float maxHealth = 100f;
|
||||
private float _currentHealth;
|
||||
|
||||
// 체력이 변경될 때 UI 등에 알리기 위한 이벤트 (Observer 패턴)
|
||||
public static event Action<float> OnHealthChanged;
|
||||
public static event Action OnCoreDestroyed;
|
||||
|
||||
void Awake() => _currentHealth = maxHealth;
|
||||
|
||||
public void TakeDamage(float amount)
|
||||
{
|
||||
_currentHealth -= amount;
|
||||
OnHealthChanged?.Invoke(_currentHealth / maxHealth);
|
||||
|
||||
if (_currentHealth <= 0)
|
||||
OnCoreDestroyed?.Invoke();
|
||||
}
|
||||
}
|
||||
36
Assets/Scripts/GameBase/GameManager.cs
Normal file
36
Assets/Scripts/GameBase/GameManager.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement; // 씬 재시작용
|
||||
|
||||
public class GameManager : MonoBehaviour
|
||||
{
|
||||
private bool _isGameOver = false;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// Core의 파괴 이벤트를 구독
|
||||
Core.OnCoreDestroyed += GameOver;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Core.OnCoreDestroyed -= GameOver;
|
||||
}
|
||||
|
||||
private void GameOver()
|
||||
{
|
||||
if (_isGameOver) return;
|
||||
|
||||
_isGameOver = true;
|
||||
Debug.Log("Game Over! Core has been destroyed.");
|
||||
|
||||
// 여기에 패배 UI 표시 로직 등을 넣습니다.
|
||||
// 예: 3초 후 게임 재시작
|
||||
Invoke(nameof(RestartGame), 3f);
|
||||
}
|
||||
|
||||
private void RestartGame()
|
||||
{
|
||||
// 현재 활성화된 씬을 다시 로드
|
||||
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
|
||||
}
|
||||
}
|
||||
18
Assets/Scripts/GameBase/Gate.cs
Normal file
18
Assets/Scripts/GameBase/Gate.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
// Gate.cs
|
||||
public class Gate : MonoBehaviour, IDamageable
|
||||
{
|
||||
[SerializeField] private float maxHealth = 50f;
|
||||
private float _currentHealth;
|
||||
|
||||
void Awake() => _currentHealth = maxHealth;
|
||||
|
||||
public void TakeDamage(float amount)
|
||||
{
|
||||
_currentHealth -= amount;
|
||||
if (_currentHealth <= 0)
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
5
Assets/Scripts/GameBase/IDamageable.cs
Normal file
5
Assets/Scripts/GameBase/IDamageable.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
// IDamageable.cs
|
||||
public interface IDamageable
|
||||
{
|
||||
void TakeDamage(float amount);
|
||||
}
|
||||
76
Assets/Scripts/GameBase/WaveManager.cs
Normal file
76
Assets/Scripts/GameBase/WaveManager.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[System.Serializable] // 이 속성이 있어야 인스펙터에 노출됩니다.
|
||||
public class Wave
|
||||
{
|
||||
public string waveName; // 웨이브 식별용 이름
|
||||
public GameObject enemyPrefab; // 소환할 적 프리팹
|
||||
public int count; // 소환할 마릿수
|
||||
public float spawnRate; // 적 한 마리당 소환 간격 (초)
|
||||
}
|
||||
|
||||
public class WaveManager : MonoBehaviour
|
||||
{
|
||||
[Header("Wave Settings")]
|
||||
[SerializeField] private List<Wave> waves; // 웨이브 데이터 리스트
|
||||
[SerializeField] private float timeBetweenWaves = 5f; // 웨이브 간 대기 시간
|
||||
[SerializeField] private Transform[] spawnPoints; // 적이 나타날 위치들
|
||||
|
||||
private Transform _target;
|
||||
|
||||
private int _currentWaveIndex = 0;
|
||||
|
||||
void Start()
|
||||
{
|
||||
_target = GameObject.FindWithTag("Core").transform;
|
||||
|
||||
// 게임 시작 시 웨이브 루틴 시작
|
||||
StartCoroutine(StartWaveRoutine());
|
||||
}
|
||||
|
||||
IEnumerator StartWaveRoutine()
|
||||
{
|
||||
// 모든 웨이브를 순회
|
||||
while (_currentWaveIndex < waves.Count)
|
||||
{
|
||||
Wave currentWave = waves[_currentWaveIndex];
|
||||
|
||||
Debug.Log($"Wave {_currentWaveIndex + 1}: {currentWave.waveName} 시작!");
|
||||
|
||||
// 1. 적 소환 로직
|
||||
for (int i = 0; i < currentWave.count; i++)
|
||||
{
|
||||
SpawnEnemy(currentWave.enemyPrefab);
|
||||
|
||||
// 지정된 간격만큼 대기 (이게 코루틴의 핵심입니다)
|
||||
yield return new WaitForSeconds(currentWave.spawnRate);
|
||||
}
|
||||
|
||||
// 2. 다음 웨이브 전까지 대기
|
||||
Debug.Log("웨이브 종료. 다음 웨이브 대기 중...");
|
||||
yield return new WaitForSeconds(timeBetweenWaves);
|
||||
|
||||
_currentWaveIndex++;
|
||||
}
|
||||
|
||||
Debug.Log("모든 웨이브가 종료되었습니다!");
|
||||
}
|
||||
|
||||
void SpawnEnemy(GameObject enemyPrefab)
|
||||
{
|
||||
// 1. 스폰 위치 선택
|
||||
Transform spawnPoint = spawnPoints[Random.Range(0, spawnPoints.Length)];
|
||||
|
||||
// 2. 방향 계산 (Core - SpawnPoint)
|
||||
Vector3 directionToCore = (_target.position - spawnPoint.position).normalized;
|
||||
|
||||
// 3. 방향을 Quaternion 회전값으로 변환 (Y축 기준으로만 회전하도록 설정)
|
||||
// 윗방향(Vector3.up)을 축으로 하여 해당 방향을 바라보게 함
|
||||
Quaternion lookRotation = Quaternion.LookRotation(new Vector3(directionToCore.x, 0, directionToCore.z));
|
||||
|
||||
// 4. 계산된 위치와 회전값으로 생성
|
||||
Instantiate(enemyPrefab, spawnPoint.position, lookRotation);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user