Unity 사전캠프 11일차 - 게임 요소 간단하게 구현해보기

한예준·2025년 3월 31일

저번에 이어서 오버워치2 게임 요소를 간단하게나마 구현해보도록 하겠다. 저번 포스트에서 구상해봤던 Ally 회복 시스템, 데미지 이펙트 및 애니메이션, 투사체 조준 시스템 등을 구현해볼 생각이다.


Step10. 투사체 조준 시스템

  • 이른바 크로스 헤어 시스템이다. 마우스가 지정한 위치를 조준하고, 조준한 위치에 투사체를 발사한다.
  • 먼저 UI > Image를 생성, Crosshair라고 지정한다.
  • Crosshair에 에셋 스토어에서 받은 이미지를 넣었다.
  • 마우스 위치를 따라가게 하는 Crosshair.cs 스크립트를 작성한다.
public class Crosshair : MonoBehaviour
{
    void Start()
    {
    
    }

    void Update()
    {
        Vector2 mousePos = Input.mousePosition;
        transform.position = mousePos;
    }
}
  • 기본 마우스 커서가 보이지 않도록 하는 코드를 추가한다.
    void Start()
    {
        Cursor.visible = false;
    }
  • 기존의 Shooter.cs에서 Space bar 입력을 좌클릭(Input.GetMouseButtonDown(0))으로 변경한다.
  void Update()
    {
        if (Input.GetMouseButtonDown(0)) << 마우스 좌클릭
        {
            Fire();
        }
    }
  • 마우스 좌표를 월드 좌표로 변환한다.
Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        mouseWorldPos.z = 0f;
  • 플레이어 위치에서 마우스 위치까지의 방향을 계산한다.
Vector3 direction = (mouseWorldPos - transform.position).normalized;
  • 투사체에 방향을 적용한다.
        rb.AddForce(direction * shootForce, ForceMode2D.Impulse);


Step 11. Ally 회복 시스템

다음으로 투사체를 아군이 맞았을 때, 아군을 회복시켜주는 스크립트를 추가해보겠다.

  • 이미 어제 Ally.csheal()함수를 추가했으므로, bullet.cs에서 이를 가져오기만 하면 된다.
  • 또한, bullet.cs에서 Ally의 체력이 가득 차있을 경우 관통하는 기능을 이미 넣어놨기 때문에, bullet.cs에서 healAmount값을 설정하고 이를 구현하기만 하면 된다.
  • 회복량 변수 추가
    public int healAmount = 1;
  • 다음으로, allyhp가 가득 차있지 않을 경우 ally를 회복한다.
                if (ally.hp < ally.maxHp)
                {
                    ally.Heal(healAmount);
                    Destroy(gameObject);
                }
  • 만약 체력이 가득 차있을 경우 관통하는 기능은 유지한다.

Step 12. Enemy 공격 시스템

  • Enemy가 공격을 하고, 공격을 Ally가 맞았을 때 Ally의 체력이 닳는 시스템을 구현하고자 한다.
  • 먼저, Ally.csTakedamage()함수를 추가한다.
    public void TakeDamage(int amount)
    {
        hp -= amount;
        if (hp < 0)
        {
            hp = 0;
        }
        if (hp <= 0)
        {
            Destroy(gameObject);
        }
    }
  • bullet.cs에 적에게서 발사된 투사체인지 플레이어에게서 발사된 투사체인지를 판별하는 기능을 추가한다.
    public bool isFromEnemy = false;
  • 먼저 isFromEnemy라는 bool을 선언한다.

bool값에 따라 조건을 나눈다.

  • 적에게서 발사된 총알이 아군에 맞을 경우이다. Allydamage를 입는다.
        if (isFromEnemy && collision.CompareTag("Ally"))
        {
            Ally ally = collision.GetComponent<Ally>();
            if (ally != null)
            {
                ally.TakeDamage(damage);
                Destroy(gameObject);
            }
        }
  • 나에게서 발사된 총알이 적에게 맞을 경우이다. Enemydamage를 입는다.
        else if (!isFromEnemy && collision.CompareTag("Enemy"))
        {
            Enemy enemy = collision.GetComponent<Enemy>();
            if (enemy != null)
            {
                enemy.OnHit(this);
                Destroy(gameObject);
            }
        }
  • 나에게서 발사된 총알이 아군에 맞을 경우이다. Allyheal이 된다.
        else if (!isFromEnemy && collision.CompareTag("Ally"))
        {
            Ally ally = collision.GetComponent<Ally>();
            if (ally != null && ally.hp < ally.maxHp)
            {
                ally.Heal(healAmount);
                Destroy(gameObject);
            }
        }

Enemy.cs에 공격 기능을 추가한다.

  • 먼저, Enemy.cs에 발사할 총알 프리팹을 가져오고, 공격 주기와 발사 속도를 지정한다.
    public GameObject bulletPrefab;
    public float attackInterval = 2f;
    public float shootForce = 10f;
  • fire()함수를 추가한다. Enemy오브젝트의 위치를 불러와서, 발사 위치를 지정한다.
      void Fire()
    {
        Vector3 spawnPos = transform.position;
  • 총알을 생성한다.
        GameObject bullet = Instantiate(bulletPrefab, spawnPos, Quaternion.identity);
  • 일단 왼쪽으로 발사한다고 가정한다. 힘을 가한다.
        Vector2 direction = Vector2.left;

        Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
        rb.AddForce(direction * shootForce, ForceMode2D.Impulse);
  • Bullet.cs에서 이 총알이 Enemy로부터 왔음을 판별하도록 한다.
        Bullet bulletScript = bullet.GetComponent<Bullet>();
        if (bulletScript != null)
        {
            bulletScript.isFromEnemy = true;
        }
    }

오류가 발생하였다.

  • Enemy 오브젝트에서 투사체는 발사됐지만, Ally오브젝트와 충돌했을 때 Ally의 체력이 닳지 않았다. 디버그를 위해 코드를 추가해서 확인해보았다.
        Debug.Log("적 총알이 아군에 명중!");

  • 로그는 정상적으로 출력되었다. Ally.cs를 확인해보았다.
    public void Heal(int amount)
    {
        hp = Mathf.Min(hp + amount, maxHp);
        if (hpSlider != null)
        {
            hpSlider.value = hp;
        }
    }
  • heal()함수에는 hpSlider값을 갱신하는 코드가 있지만, TakeDamage()함수에는 존재하지 않았다.
    public void TakeDamage(int amount)
    {
        hp -= amount;
        if (hp < 0)
        {
            hp = 0;
        }
        if (hp <= 0)
        {
            Destroy(gameObject);
        }
    }
  • 이 코드를 추가하여 hpSlider값을 hp에 맞게 갱신해주었다.
        if (hpSlider != null)
        {
            hpSlider.value = hp;
        }
  • 결과를 확인해보았다.

  • BulletAlly에게 맞을 경우와 Enemy에게 맞을 경우를 구분하고, Player에게서 발사될 경우, Enemy에게서 발사될 경우에 따라 작용되는 효과가 다르도록 구현이 되었다.


Step 13. 데미지 이펙트 구현

  • BulletEnemyAlly에 충돌하면 피격 이펙트를 재생하고자 한다.

  • 먼저, 에셋 스토어에서 적절한 Effects를 다운로드했다.

  • 다음으로 Bullet.cs에 이펙트 생성 코드를 추가하였다.

public GameObject hitEffectPrefab;

    void OnTriggerEnter2D(Collider2D collision)
    {
        {
            if (collision.CompareTag("Enemy") || collision.CompareTag("Ally"))
            {
                if (hitEffectPrefab != null)
                {
                    GameObject effect = Instantiate(hitEffectPrefab, transform.position, Quaternion.identity);
                    Destroy(effect, 0.3f);
                }
            }
        }
  • 결과를 확인해보았다.
  • 현재는 이펙트가 고정 방향과 크기로 생성되므로, 이후 이펙트 회전 및 스케일 조절이 필요하다.

C#이나 Unity 사전 강의에서 배웠던 내용들이 굉장히 많이 사용된다. 모르는 것들은 구글링해가며 구현해나가고 있는데, 오류가 생길 때마다 많이 당황하게 되는 것 같다. 또, 스크립트들을 단순화할 수 있는 방법이 분명 있을 것 같은데, 이를 알아내는 눈이 아직 많이 부족한 것 같다. 반복 연습을 통해 그런 능력을 길러야겠다는 생각이 든다.

0개의 댓글