게임개발 캠프 34일차

IIRU·2026년 7월 2일

자동공격 AutoAttack.cs

using System.Collections;
using System.Linq;
using UnityEngine;

public class AutoAttack : MonoBehaviour
{
    float range;
    float delay = 2f;
    int damage;
    
   [SerializeField] Transform target;
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        range = 4f;
        StartCoroutine(StartAutoAttack());
    }

    // Update is called once per frame
    void Update()
    {

    }
    IEnumerator StartAutoAttack()
    {
        WaitForSeconds wait = new WaitForSeconds(delay);
        while (true)
        {
            //적이 범위안에 들어올때까지 대기
            yield return new WaitUntil(()=>DetectEnemy());

            Attack();
            yield return wait;
        }
    }
    bool DetectEnemy() 
    {
        int layer = LayerMask.NameToLayer("Monster");
        int targetLayer = 1 << layer;
        Collider2D[] coliider = Physics2D.OverlapCircleAll(transform.position, range, targetLayer);

        if (coliider.Length == 0)
        {
            return false;
        }
        else
        {
            float min = 0;

            //foreach (var col in coliider)
            //{
            //    if (Vector2.Distance(col.transform.position, transform.position) < min)
            //    {
            //        min = Vector2.Distance(col.transform.position, transform.position);
            //        target = col.transform;
            //    }

            //}

            target = coliider.OrderBy(col => Vector2.Distance(col.transform.position, transform.position)).FirstOrDefault().transform;
            

            return true;
        }

    }

    void Attack()
    {
        //화살생성
        GameObject arr = ObjectPoolManager.instance.GetObject("arrow");
        arr.transform.position = transform.position;

        Vector2 direction = target.position - transform.position;
        float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
        Quaternion rotate = Quaternion.Euler(0, 0, angle);

        arr.transform.rotation = rotate;
        arr.GetComponent<Arrow>().SetDamage(5);
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.yellow;
        Gizmos.DrawSphere(transform.position, range);
    }

    // overlap
    // 1. OverlapCircle
    // 1-1. OverlapCircleAll
    // 
    // 2. OverlapBox
    // 2-1. OverlapBoxAll
    // 
    // 차이점
    // 1. 반환형 -> collider
    // 2. 충돌X - 검사를 함.
}

적이 범위 안에 들어오는 것을 기다림 공격 후 딜레이 wait에 2초

float delay = 2f;
IEnumerator StartAutoAttack()
    {
        WaitForSeconds wait = new WaitForSeconds(delay);
        while (true)
        {
            //적이 범위안에 들어올때까지 대기
            yield return new WaitUntil(()=>DetectEnemy());

            Attack();
            yield return wait;
        }
    }

범위 안에 몬스터라는 layer가 들어오면 coliider에 넣어서 제일 가까운 적을 감지.

 bool DetectEnemy() 
{
    int layer = LayerMask.NameToLayer("Monster");
    int targetLayer = 1 << layer;
    Collider2D[] coliider = Physics2D.OverlapCircleAll(transform.position, range, targetLayer);

    if (coliider.Length == 0)
    {
        return false;
    }
    else
    {
        float min = 0;
        target = coliider.OrderBy(col => Vector2.Distance(col.transform.position, transform.position)).FirstOrDefault().transform;
        return true;
    }

}
//범위를 그려주는 기즈모
private void OnDrawGizmos()
{
    Gizmos.color = Color.yellow;
    Gizmos.DrawSphere(transform.position, range);
}

화살을 오브젝트풀에서 꺼내서 위 코드에서 감지된 제일 가까운 적 target을 향해 화살 발사.

void Attack()
{
    //화살생성
    GameObject arr = ObjectPoolManager.instance.GetObject("arrow");
    arr.transform.position = transform.position;
    Vector2 direction = target.position - transform.position;
    float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
    Quaternion rotate = Quaternion.Euler(0, 0, angle);
    arr.transform.rotation = rotate;
    arr.GetComponent<Arrow>().SetDamage(5);
}

HPbar

using UnityEngine;
using UnityEngine.UI;

public class HPbar : MonoBehaviour
{
    Image img;
    [SerializeField] Transform target; //체력바가 붙을 대상
    Vector3 offset = Vector3.up;
    float gauge;

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        img = GetComponent<Image>();
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    private void LateUpdate()
    {
        if(target == null)
        {
            return;
        }
        transform.position = target.position + offset;
    }

    public void SetGauge(float gauge)
    {
        img.fillAmount = gauge;
    }

    public void SetTarget(Transform t)
    {
        target = t;
    }
}

1. Screen Space - Overlay (화면 고정 모드)

📺 "게임 화면 맨 앞에 스티커처럼 딱 붙여두기"
개념: 게임 속 카메라의 위치, 각도, 맵의 크기와 상관없이 무조건 모니터 화면 맨 앞에 UI를 띄웁니다.
특징: * 카메라가 멀어지거나 회전해도 UI는 항상 화면 정면에 고정되어 있습니다.

2. Screen Space - Camera (카메라 종속 모드)

🎥 "카메라 렌즈 앞 일정한 거리에 띄워두기"
개념 : UI를 특정 카메라(Render Camera) 앞의 지정된 거리(Plane Distance)에 배치합니다.
특징 : Overlay처럼 화면에 고정되어 있는 것처럼 보이지만, 카메라와 UI 사이에 실제 게임 오브젝트가 끼어들 수 있습니다.

3. World Space (게임 월드 배치 모드)

🌲 "UI를 나무나 상자 같은 하나의 게임 오브젝트로 만들기"
개념 : UI를 화면에 고정하는 것이 아니라, 게임 맵(World)의 특정 좌표에 직접 배치합니다.
특징 : * UI가 일반 캐릭터나 장애물처럼 취급되므로, 크기를 마음대로 줄여서 맵에 배치할 수 있습니다.

캐릭터 위에 체력바가 위치하도록 해주었음.

Vector3 offset = Vector3.up;
 private void LateUpdate()
{
    if(target == null)
    {
        return;
    }
    transform.position = target.position + offset;
}

Audio - SoundManager.cs

using System.Collections;
using UnityEngine;


public enum SFXType
{
    Arrow,
    Die,
    Attack
}


public class SoundManager : MonoBehaviour
{
    public static SoundManager instance;



    [SerializeField] AudioSource bgmAudioSource;
    [SerializeField] AudioSource sfxAudioSource;
    public AudioClip audioClip; // 배경음 

    public AudioClip[] soundClip; // 효과음


    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
        else
        {
            Destroy(gameObject);
        }
        DontDestroyOnLoad(gameObject);
    }

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        //PlayBGM();
    }

    // Update is called once per frame
    void Update()
    {
        
    }


    void PlayBGM()
    {
        bgmAudioSource.Play();
    }

    public void PlaySFX(SFXType type)
    {
        if((int)type > soundClip.Length)
        {
            return;
        }

        sfxAudioSource.PlayOneShot(soundClip[(int)type]);
    }

    IEnumerator BGMChange()
    {
        float vol = bgmAudioSource.volume;
        float timer = 0f;
        float duration = 5f;

        while(vol > 0)
        {
            timer += Time.deltaTime;
            bgmAudioSource.volume = Mathf.Lerp(vol, 0, timer / duration);
            yield return null;
        }

        bgmAudioSource.Stop();
    }

    //새로운 bgm으로 바꾸고 
    //점점 소리를 키우면됌.

}

효과음은 soundClip에 넣어주었고 그냥 bgm은 audioclip에 넣어주었다.
그리고 각자 불륨이나 세부적인 조정사항은 위에 audio Source를 만들어서 각자 관리를 할 수 있게 해주었음.

들어있는 효과음을 사용할 수 있도록 하는 함수.

 public void PlaySFX(SFXType type)
{
    if((int)type > soundClip.Length)
    {
        return;
    }

    sfxAudioSource.PlayOneShot(soundClip[(int)type]);
}

//이걸 다른곳에서 불러와주면 됌.
 SoundManager.instance.PlaySFX(SFXType.Arrow);

불륨이 점점 낮아지면서 bgm이 꺼지도록 하는 함수

IEnumerator BGMChange()
{
    float vol = bgmAudioSource.volume;
    float timer = 0f;
    float duration = 5f;

    while(vol > 0)
    {
        timer += Time.deltaTime;
        bgmAudioSource.volume = Mathf.Lerp(vol, 0, timer / duration);
        yield return null;
    }

    bgmAudioSource.Stop();
}

Animation


Add Property를 한 후에 sprite로 만들어진 이미지를 애니메이션 처럼 만들 수 있다.

profile
초보 개발자 블로그입니다!

0개의 댓글