4 분 소요

Intro

이번 포스팅은 스킬에 사운드를 추가하고, 공격판정을 구현하는것을 마무리하였습니다.

스킬의 사운드와 공격판정은 각 스킬 클래스에서 구현하였습니다.

Slash.cs

검 스킬인 Slash는 SphereCast를 사용하여 공격판정을 구현하였습니다.

공격판정의 타이밍을 맞추기위하여 코루틴을 사용해 딜레이를 주었습니다.

Skill 클래스는 추상클래스로, MonoBehaviour를 상속받지 않았으므로, 코루틴을 실행하기위해서

SkillManager의 인스턴스를 통해 코루틴을 실행시켰습니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/*
                        Slash
          
            - 검스킬
            - 공격판정 : SphereCast
*/

public class Slash : Skill
{
    public Slash(SkillData data) : base(data) { }
    private float attackRadius = 2f;
    private float attackRange = 1f;
    
    // 스킬 사용(공격판정, 이펙트, 사운드)
    public override bool Activate(GameObject user)
    {
        // 올바른 무기를 장착했는지 여부
        bool hasWeapon = WeaponManager.Instance.currentWeapon.type == WeaponType.Sword;

        if (anim == null)
        {
            Debug.Log($"{user} 의 Animator가 존재하지 않음.");
            return false;
        }
        else if(!hasWeapon)
        {
            Debug.Log($"장착한 무기로는 스킬을 사용할 수 없습니다.");
            return false;
        }
        else
        { 
            anim.SetTrigger("Skill");
            anim.SetInteger("SkillId", data.AnimId);

            if(cachedEffect == null)
            {
                // 생성된 이펙트가 없으면 생성
                cachedEffect = UnityEngine.Object.Instantiate(effectPrefab,
                user.transform.position + new Vector3(0,1,0),
                user.transform.rotation, SkillManager.Instance.gameObject.transform);
            }
            else
            {
                // 생성된 이펙트가 있으면 새로운 위치 지정
                cachedEffect.transform.position = user.transform.position + new Vector3(0, 1, 0);
                cachedEffect.transform.rotation = user.transform.rotation;
            }

            cachedEffect.SetActive(true);
            SkillManager.Instance.StartCoroutine(Attack());
            AudioManager.Instance.PlaySFX(data.Name, 0.3f);     // 공격음 재생
            return true;
        }
    }

    // 공격판정(SphereCast 사용)
    private void EnableHitBox()
    {
        Vector3 origin = GameManager.Instance.player.transform.position + GameManager.Instance.player.transform.forward * 2f;
        Vector3 dir = GameManager.Instance.player.transform.forward;

        RaycastHit[] hits = Physics.SphereCastAll(
            origin,
            attackRadius,
            dir,
            attackRange,
            LayerMask.GetMask("Monster", "BossMonster")
        );

        foreach(RaycastHit hit in hits)
        {
            if(hit.collider.CompareTag("Monster"))
            {
                Monster monster = hit.collider.GetComponent<Monster>();
                if (monster != null)
                {
                    // 데미지
                    monster.GetDamaged((DataManager.Instance.GetPlayerData().Damage + data.Damage) * Random.Range(0.8f, 1f));
                }
            }
            else if(hit.collider.CompareTag("BossMonster"))
            {
                BossMonster boss = hit.collider.GetComponent<BossMonster>();
                if (boss != null)
                {
                    // 데미지
                    boss.GetDamaged((DataManager.Instance.GetPlayerData().Damage + data.Damage) * Random.Range(0.8f, 1f));
                }
            }
        }
    }
    
    // 애니메이션에 맞춰 공격판정 ON
    private IEnumerator Attack()
    {
        yield return new WaitForSeconds(0.5f);

        EnableHitBox();
    }
}
  • 또한 스킬에 적절한 효과음을 구하여 Addressable 로 등록한뒤, AudioManager를 통해 효과음을 출력하도록 하였습니다.

IceShot.cs

스태프 스킬인 IceShot은 Collider를 통해 구현하였습니다.

장판형 공격스킬로, 장판안에있으면 지속적으로 데미지를 주도록 구현하였습니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/*
                        IceShot
          
            - 마법스킬
             
*/

public class IceShot : Skill
{
    public IceShot(SkillData data) : base(data) { }

    // 스킬 사용
    public override bool Activate(GameObject user)
    {
        // 올바른 무기를 장착했는지 여부
        bool hasWeapon = WeaponManager.Instance.currentWeapon.type == WeaponType.Staff;

        if (anim == null)
        {
            Debug.Log($"{user} 의 Animator가 존재하지 않음.");
            return false;
        }
        else if (!hasWeapon)
        {
            Debug.Log($"장착한 무기로는 스킬을 사용할 수 없습니다.");
            return false;
        }
        else
        {
            // 애니메이션 설정
            anim.SetTrigger("Skill");
            anim.SetInteger("SkillId", data.AnimId);

            // 이펙트 처리
            if (cachedEffect == null)
            {
                // 생성된 이펙트가 없으면 생성
                cachedEffect = UnityEngine.Object.Instantiate(effectPrefab,
                user.transform.position + user.transform.forward * 3f,
                user.transform.rotation, SkillManager.Instance.gameObject.transform);
            }
            else
            {
                // 생성된 이펙트가 있으면 새로운 위치 지정
                cachedEffect.transform.position = user.transform.position + user.transform.forward * 2f;
                cachedEffect.transform.rotation = user.transform.rotation;
            }

            cachedEffect.SetActive(true);
            SkillManager.Instance.StartCoroutine(EnableHitbox(cachedEffect));
            AudioManager.Instance.PlaySFX(data.Name);
            return true;
        }
    }

    // 공격판정
    private IEnumerator EnableHitbox(GameObject effect)
    {
        effect.TryGetComponent<StayHitbox>(out StayHitbox hitbox);
        if(hitbox == null)
        {
            hitbox = effect.AddComponent<StayHitbox>();
            hitbox.damage = data.Damage + Random.Range(DataManager.Instance.GetPlayerData().Damage * 0.1f, DataManager.Instance.GetPlayerData().Damage * 0.2f);
        }

        // 지속시간 3초
        yield return new WaitForSeconds(3f);

        effect.SetActive(false);
    }
}
  • 공격의 판정을 Collider로 구현하였기때문에, IceShot 클래스가 아닌, StayHitbox 클래스를 만들어 구현하였습니다.


StayHitbox.cs

OnTriggerStay() 함수를 활용하여, 몬스터가 장판위에 있을경우에 지속적으로 데미지를 입도록 구현하였습니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/*
                지속공격형 스킬

            - 공격 간격마다 데미지 발생
 */
public class StayHitbox : MonoBehaviour
{
    public float damage;
    public float attackInterval = 0.5f;             // 공격 간격(0.5초)

    private float timer = 0f;

    private void OnTriggerStay(Collider other)
    {
        if (other.CompareTag("Monster"))
        {
            Monster monster = other.GetComponent<Monster>();

            timer += Time.deltaTime;
            if (timer >= 0.5f)
            {
                monster.GetDamaged(damage);
                timer = 0;
            }
        }
        else if(other.CompareTag("BossMonster"))
        {
            BossMonster boss = other.GetComponent<BossMonster>();

            timer += Time.deltaTime;
            if (timer >= 0.5f)
            {
                boss.GetDamaged(damage);
                timer = 0;
            }
        }
    }
}

  • IceShot 클래스에서 데미지를 넘겨받습니다.
  • 타이머를통해 0.5초마다 데미지를 주도록 하였습니다.

Buff.cs

버프 스킬은 플레이어의 공격력과 방어력 수치를 올라가도록 하였습니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/*
                        Buff
          
            - 버프스킬
            - Buff 스킬 인터페이스 상속
                - FollowingEffect : 버프 이펙트가 플레이어를 따라다님
*/

public class Buff : Skill, IBuffSkill
{
    public Buff(SkillData data) : base(data) { }

    // 스킬 사용
    public override bool Activate(GameObject user)
    {
        // 무기를 장착했는지 여부
        bool hasWeapon = WeaponManager.Instance.currentWeapon != null;

        if (anim == null)
        {
            Debug.Log($"{user} 의 Animator가 존재하지 않음.");
            return false;
        }
        else if (!hasWeapon)
        {
            Debug.Log($"장착한 무기가 없습니다.");
            return false;
        }
        else
        {
            // 애니메이션 설정
            anim.SetTrigger("Skill");
            anim.SetInteger("SkillId", data.AnimId);

            if(cachedEffect == null)
            {
                cachedEffect = UnityEngine.Object.Instantiate(effectPrefab, SkillManager.Instance.gameObject.transform);

                FollowingEffect(user);
            }
            else
            {
                // 생성된 이펙트가 있으면 새로운 위치 지정
                cachedEffect.transform.position = user.transform.position;
                cachedEffect.transform.rotation = user.transform.rotation;
            }

            cachedEffect.SetActive(true);

            // 버프 효과 적용
            SkillManager.Instance.StartCoroutine(EnhanceStatus());
            // 사운드 출력
            AudioManager.Instance.PlaySFX(data.Name, 0.3f);             

            return true;
        }
    }

    // 버프 이펙트가 플레이어를 따라다니도록
    public void FollowingEffect(GameObject user)
    {
        FollowTarget follow = cachedEffect.AddComponent<FollowTarget>();
        follow.target = user.transform;
        follow.duration = data.Cooldown / 2;
    }

    // 버프 : 능력치 상승
    private IEnumerator EnhanceStatus()
    {
        // 공격력, 방어력 증가
        DataManager.Instance.GetPlayerData().Defense += data.Damage;
        DataManager.Instance.GetPlayerData().Damage += data.Damage;

        // 지속시간
        yield return new WaitForSeconds(data.Cooldown / 2);

        // 공격력, 방어력 복구
        DataManager.Instance.GetPlayerData().Defense -= data.Damage;
        DataManager.Instance.GetPlayerData().Damage -= data.Damage;
    }
}

  • 코루틴을 사용하여 지속시간(쿨타임의 절반)동안 플레이어의 공격력과 방어력을 상승시킵니다.
  • FollowingEffect() : 버프 이펙트가 플레이어를 따라가도록 하였습니다.

테스트영상

댓글남기기