유니티 RPG - 52. 플레이어 스킬구현 - 3
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() : 버프 이펙트가 플레이어를 따라가도록 하였습니다.
댓글남기기