어제 나는 BulletManager.cs를 통해 모든 투사체가 상속받을 속성을 설정했고, 이를 상속받을 기본 투사체의 스크립트를 Bullet.cs에 작성하였다. 이후 에디터에서 Bullet의 TargetTag를 Enemy로 설정하니, 정상적으로 실행되는 것을 확인하였다. 오늘은 BulletManager.cs를 생성함에 맞춰서 다른 스크립트들을 수정하고, 다른 투사체들도 구현해보고, 효과음과 사망 시 이펙트 등을 구현해볼 생각이다.
BulletManager.cs에 맞춰 Enemy.cs도 수정해주었다.
현재 Enemy가 자동공격을 해도 TargetTag가 없어서 투사체의 대상이 없지만, TargetTag를 Ally로 바꿔서 투사체가 아군에게 맞도록 수정하였다.
다음으로 Shooter.cs를 수정하겠다. Shooter.cs는 투사체를 발사하는 방향, 발사체의 속도 등을 구현하는 스크립트였는데, 나는 이것을
1,2,3 등의 버튼을 눌러서 투사체를 변경.이렇게 작성해 볼 생각이다. 먼저 기존 스크립트는 다음과 같다.
using UnityEngine;
public class Shooter : MonoBehaviour
{
public GameObject bulletPrefab;
public float shootForce = 10f;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Fire();
}
}
void Fire()
{
Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mouseWorldPos.z = 0f;
Vector3 direction = (mouseWorldPos - transform.position).normalized;
GameObject bullet = Instantiate(bulletPrefab, transform.position, Quaternion.identity);
Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
rb.AddForce(direction * shootForce, ForceMode2D.Impulse);
}
}
using UnityEngine;
public class Shooter : MonoBehaviour
{
public GameObject BulletPrefab;
public GameObject healingBulletPrefab;
public GameObject explosionBulletPrefab;
private GameObject currentBulletPrefab;
private string currentTargetTag;
void Start()
{
EquipBullet(BulletPrefab, "Enemy");
}
void Update()
{
HandleInput();
}
void HandleInput()
{
if (Input.GetMouseButtonDown(0))
{
Fire();
}
if (Input.GetKeyDown(KeyCode.Alpha1))
{
EquipBullet(BulletPrefab, "Enemy");
Debug.Log("일반");
}
else if (Input.GetKeyDown(KeyCode.Alpha2))
{
EquipBullet(healingBulletPrefab, "Ally");
Debug.Log("회복");
}
else if (Input.GetKeyDown(KeyCode.Alpha3))
{
EquipBullet(explosionBulletPrefab, "Enemy");
Debug.Log("폭발");
}
}
void EquipBullet(GameObject prefab, string targetTag)
{
currentBulletPrefab = prefab;
currentTargetTag = targetTag;
}
void Fire()
{
if (currentBulletPrefab == null) return;
Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mouseWorldPos.z = 0f;
Vector3 direction = (mouseWorldPos - transform.position).normalized;
GameObject bullet = Instantiate(currentBulletPrefab, transform.position, Quaternion.identity);
Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
BulletManager bulletScript = bullet.GetComponent<BulletManager>();
if (rb != null)
{
float force = (bulletScript != null) ? bulletScript.shootForce : 10f;
rb.AddForce(direction * force, ForceMode2D.Impulse);
}
if (bulletScript != null)
{
bulletScript.targetTag = currentTargetTag;
}
}
}
Prefab을 가져온다.currentBulletPrefab의 초기값을 설정하고, EquipBullet 함수를 정의한다. private GameObject currentBulletPrefab;
void Start()
{
EquipBullet(BulletPrefab, "Enemy");
}
void EquipBullet(GameObject prefab, string targetTag)
{
currentBulletPrefab = prefab;
currentTargetTag = targetTag;
}
EquipBullet을 1,2,3 키를 통하여 변경할 수 있도록 한다.void HandleInput()
{
if (Input.GetKeyDown(KeyCode.Alpha1))
{
EquipBullet(BulletPrefab, "Enemy");
Debug.Log("일반");
}
else if (Input.GetKeyDown(KeyCode.Alpha2))
{
EquipBullet(healingBulletPrefab, "Ally");
Debug.Log("회복");
}
else if (Input.GetKeyDown(KeyCode.Alpha3))
{
EquipBullet(explosionBulletPrefab, "Enemy");
Debug.Log("폭발");
}
}
기존 Fire()는 약간의 수정 이후 그대로 사용한다.
이렇게 구조를 고치고 실행해보니 오류가 발생하여 발사가 실행되지 않았다.
Debug.Log를 통해 발사가 실제로 실행됐는지 확인해보았다.Bullet Prefab들을 Player에게 붙이지 않아서 발생한 오류였다.
Player와 충돌하고 있음을 알았다.PlayerSprite에 있는 Ally Tag를 Player로 바꿔주었다.Heal()을 구현할 수 있도록 스크립트를 수정해주었다.
EnemyTag를 가진 오브젝트 확인하기.explosionRadius변수를 선언하고 2를 저장한다. public float explosionRadius = 2f;explosionRadius 안에 있는 적들을 hits 배열 안에 넣는다. protected override void OnHit(Collider2D col)
{
Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, explosionRadius);
}collider 중 targetTag에 있는 오브젝트들을 확인한다. foreach (Collider2D hit in hits)
{
if (hit.CompareTag(targetTag)) {
UnitHealth unit = hit.GetComponent<UnitHealth>();
if (unit != null)
{
unit.TakeDamage(damage);
}
}
}
Destroy(gameObject);
}Enemy를 더 소환하고 결과를 확인해보았다.
if (hit.CompareTag(targetTag)) if (hit.CompareTag(targetTag)||hit.CompareTag("Ground"))BulletManager.cs를 다시 확인해보았다. protected override void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag(targetTag) || collision.CompareTag("Ground"))
{
if (hitEffectPrefab != null)
{
GameObject fx = Instantiate(hitEffectPrefab, transform.position, Quaternion.identity);
Destroy(fx, 0.5f);
}
OnHit(collision);
}
}
targetTag와 Ground 태그와 '충돌'했을 때 hitEffectPrefab을 불러 오고 On hit 기능도 수행할 수 있도록 코드를 추가하였다.
다음으로는 적군 혹은 아군이 사망했을 때, 오브젝트가 즉시 사라지는 것이 아니라
잠시 떠오르며 사라지는 애니메이션을 구현하고자 했다.
먼저 원하는 효과를 키프레임으로 표현하기 위해 애니메이션 클립을 새로 생성하였다.
DeathAnim으로 설정하였다.Position, Rotation, Scale, Color 속성들을 조절해서Animator Controller도 새로 생성하고 (DeathAnimatorCon) 오브젝트에 연결하였다.
DeathAnim을 추가하고 Any State에서 해당 State로 트랜지션을 연결하였다.Death를 생성하고 연결하였다.이후 UnitHealth.cs의 OnDeath() 함수에서 트리거를 실행하도록 아래와 같이 작성하였다:
protected virtual void OnDeath()
{
animator.SetTrigger("Death");
Destroy(gameObject, 1.0f);
}
그러나 애니메이션을 테스트해본 결과, 게임 시작과 동시에 애니메이션이 자동으로 재생되는 문제가 발생했다.
Animator의 기본 상태로 설정된 애니메이션이 DeathAnim이었기 때문에,
오브젝트가 생성되자마자 해당 애니메이션이 실행되어 버린 것이다.
이를 해결하기 위해 Entry에서 DeathAnim으로 이어지는 트랜지션을 제거하거나,
Idle 상태를 추가한 뒤, 조건을 통해 Death 상태로 넘어가게 해야 한다는 점을 확인했다.
현재까지는 Ally 오브젝트에만 애니메이션을 적용한 상태이며,
적군(Enemy) 오브젝트에는 아직 적용하지 않았다.
내일은 다음과 같은 작업을 이어갈 계획이다: