채널톡 아이콘

인프런 커뮤니티 질문&답변

MOOOON님의 프로필 이미지

작성한 질문수 6

[유니티 레벨 업!] 모듈식으로 개발하는 스킬 시스템

공격할 때마다 일정 확률로 추가 투사체 발사

작성

·

129

·

수정됨

0

특정 아이템 효과로 기본 공격할 때마다 일정 확률로 추가 투사체를 발사하도록 하고 싶습니다.

패시브 스킬을 만들어 아이템 장착/해제시 스킬을 등록/해제하도록 만들려고 하는데, 기능을 어떻게 만드는 것이 좋을지 고민이 됩니다.

 

제가 생각해 본 방법으로는, UseCondition에 IsRunningSkillCondition으로 특정 스킬을 사용하고 있는지 검사하고, IsPassRandomValueCondition으로 일정 확률을 통과했는지 검사한 후에 SpawnProjectileAction으로 투사체를 타겟에게 발사하도록 하면 될 것 같습니다.

근데 이 방법으로 아마 동작은 될 것 같은데, 스킬의 UseCondition을 계속 체크해야해서 괜찮을지 의문이 생겼습니다. 마음같아선 OnDealDamage 이벤트에 일정 확률로 추가 투사체를 발사하는 메소드를 등록시키고 싶은데, 이를 스킬 시스템으로 어떻게 구현해야할지 잘 모르겠습니다.

 

어떤 방식으로 하는 것이 좋을 것 같은지 강사님의 생각이 궁금합니다.

 

추가로, 특정 아이템 장착시 기본 공격에 넉백이나 스턴 등 특정 이펙트를 추가하려면 어떻게 하는 것이 좋을지도 궁금합니다.

답변 2

0

Developer G님의 프로필 이미지
Developer G
지식공유자

수강해주셔서 감사합니다.

공격할 때마다 일정 확률로 추가 투사체 발사라는게, 3발 쏘던 원거리 공격이 확률적으로 4발이 나갈 수 있다는 얘기신지, 아니면 그냥 근접 공격처럼 아무 공격이나 하다보면 발사체가 나갈 수가 있다는 얘기신지 애매하네요.

어쨌거나, '일정 확률로 추가 투사체 발사'는 Buff 효과로 적용할 수 있을 것 같습니다. 전에 Effect를 event와 연결해서 Apply 시키는 법을 설명드렸었는데, 그 내용을 똑같이 적용하면 됩니다.
Skill의 발동 단계마다 event를 만들고(onStart, onPrecedingActionStart, onApply 등등) , 원하는 event에 Effect를 연결해서 랜덤으로 투사체를 발사해주면 되겠죠.
예를 들어, 투사체를 3발 발사하는 Skill이라면 Skill의 onApply event에 Effect의 Callback 함수를 연결해서 Skill의 발동을 감지하여 랜덤으로 추가 발사체를 발사하면 될겁니다.

그런데 평소에는 3발로 나가다가, 4발로 나갈 때 원뿔형으로 예쁘게 쏘게 만들고 싶을 수 있습니다. 단순히 이후에 발사체 하나를 추가로 더 쏘는 경우에는 4개가 예쁘게 정렬된 형태로 쏠 수가 없죠? 이 경우에는 SpawnProejectileAction에 발사체 수를 계산하는 action을 만들어서 Effect가 SpawnProejectileAction에 직접 접근하여 이 action에 callback 함수를 연결해주면 됩니다.
이게 글로 설명드리자니 잘 이해가 안되실거 같은데,

foreach (var calc in CalcDamage.GetInvocationList().Cast<CalcDamageHandler>())
 damage = calc.Invoke(this, damage);


Invocation을 통해서 연결된 Action들을 순회하면서 최종 값을 구할 수 있습니다.(복잡한 RPG 게임에서 매우 많이 사용되는 방법입니다.)


예를 들어, 초기 Damage가 10이고
CalcDamage Action에
(damage) => damage - 2; // 10 - 2

(damage) => damage - 2; // 8 -2

(damage) => damage - 1; // 6 - 1
이렇게 세 개의 Callback 함수가 연결되어 있으면
foreach문을 돌았을 때 최종 Damage가 5가 되는거죠.

이걸 이용해서 다음처럼 발사체 수를 그때그때 바꿔주면 되는겁니다.
(projectileCount) => projectileCount + Random.Range(0, 2);

말씀하신 Condition을 계속 확인하는 것도 나쁘지 않습니다. 성능이 걱정되시는 것 같은데, 성능 문제는 실제로 그 문제가 터지고나서 생각해봐야 합니다. 매 Frame 조건 검사 좀 한다고 큰 문제가 생길거라 생각이 들 것 같지도 않구요. 섣부른 최적화는 하지 말라는 말이 있으며, 당장 다른 해결책이 안떠오른다면 최선을 방법을 선택하는게 맞다고 생각이 듭니다.

감사합니다.

MOOOON님의 프로필 이미지
MOOOON
질문자

루난의 허리케인 같은 아이템 효과를 확률형으로 만든다는 의미였습니다!

 

EffectAction에 SpawnProjectileAction을 따로 만들어서 저번처럼 적용시키면 된다는 의미인가요??

Developer G님의 프로필 이미지
Developer G
지식공유자

첫번째는 Skill 동작마다 event를 만들고 저번처럼 Effect에서 연결하라는 소리입니다.

두번째는 Skill의 SpawnProjectileAction에 접근하라는 얘기였습니다. EffectAction에서 owner.Action as SpawnProjectileAction식으로 접근할 수 있습니다.

Developer G님의 프로필 이미지
Developer G
지식공유자

다만, 추가 투사체라는게 Stat과 연동해서 구현할 수도 있는거고, Action 자체의 기능으로도 구현할 수 있는거고 구현할 방법은 굉장히 많기 때문에 뭐가 정답이라고 딱 잘라 말씀드릴 수 있는 부분이 아니란걸 꼭 기억해주시면 좋겠습니다.

MOOOON님의 프로필 이미지
MOOOON
질문자

다시 봤는데 제가 좀 애매하게 말씀드린거 같아 죄송합니다ㅠㅠ

제 생각을 다시 한 번 정리해서 말씀드리면,

기본 공격 스킬에서 투사체 발사 -> 스킬 액션

투사체를 발사할 때 특정 이벤트 등록 -> 이펙트 액션

특정 이벤트를 통해 추가 투사체(기본 공격과 다른 투사체) 발사 -> 스킬 액션

근데, 여기서 이펙트 액션에서 이벤트에 등록한 것이 스킬을 발동시키는 것이라는 점이 저한테는 뭔가 이상하게 느껴집니다..ㅠ

이펙트는 스킬을 통해 적용되고 동작하는데, 반대로 이펙트가 스킬을 동작시키게 하는 구조가 뭔가 어색하게 느껴집니다.

동작하게 할 수 있는 방법은 여러가지로 정답은 따로 없겠지만 이펙트가 스킬을 발동시키는 구조가 맞는걸까 자꾸 의심이 듭니다.

제가 너무 복잡하게 생각하고 있는 걸까요??

Developer G님의 프로필 이미지
Developer G
지식공유자

제 설명에 대해 오해가 있는 것 같습니다.

기본 공격시 추가 투사체를 발사하는 Buff Effect 만들었다고 가정해보겠습니다.
Entity에게 적용된 이 Buff는 지속 시간동안 Entity가 기본 공격을 할 시 랜덤으로 추가 투사체를 발사합니다.(EffectAction=SpawnAdditionalProjectileAction)

첫번째, 이전에 Effect에서 event를 등록하는걸 가르쳐드린 글과 똑같이 Start에서


var entity = effect.Owner.Owner
var skillSystem = entity.SkillSystem;
skillSystem.DefaultAttack.onApply += OnApply;


OnApply() => if (Random.Range(0, 2) == 0) 투사체 발사

이런 식으로 Event를 등록하면 Skill이 발동될 때마다(onApply를 실행할 때마다) 랜덤으로 투사체가 발사되겠죠.
Effect는 Skill에게서 event를 통해 신호를 받아서 자기 일을 할 뿐입니다.
event를 중개자로 Effect가 Skill에 Docking된 상태라고 생각하시면 됩니다.

두번째,

(skillSystem.DefaultAttack.Action as SpawnProjecitleAction).CalcProjectile += OnCalcProjectile;

int OnCalcProjectile(int currentCount) => currentCount + Random.Range(0, 2)

첫번째에서 Effect가 기본 공격 Skill에 Docking된 상태라면, 두번째는 Effect가 기본 공격 Skill의 Action에 Docking된 상태인겁니다.

Effect는 기본 공격 Skill을 자신의 효과를 발동시키기 위한 Trigger로 이용하고 있을 뿐입니다.

MOOOON님의 프로필 이미지
MOOOON
질문자

자꾸 귀찮게 해드려 죄송합니다 ㅠ

강사님이 말씀하신 방법으로 구현하는 것도 생각했었고 구현도 시도해봤었습니다. (EffectAction = SpawnAdditionalProjectileAction)

구현해보고 만난 문제들로인해 투사체 스폰 액션은 스킬 액션으로 만드는게 맞다는 생각이 자꾸 들어서 질문을 했었습니다.

강사님이 말씀하신 방법으로 구현하면, 추가 투사체 버프를 통해 생성된 투사체가 적에게 닿았을 때 투사체를 초기화할 때 세팅해뒀던 스킬의 이펙트를 적용하게 될텐데

// Projectile.cs

// OnTriggerEnter 메소드

// 스킬 이펙트 적용

entity.SkillSystem.Apply(Skill);

해당 과정에서 여러 문제가 생겨서 (버프를 등록하는 패시브 스킬이 없어지는 문제, 다른 이펙트를 추가하지 못하는 문제 등) 투사체를 스폰하는 것은 이펙트 액션이 아닌 스킬 액션으로 넣어야 한다고 생각했습니다.

제가 구현하려는 것은 리그오브레전드의 루난의 허리케인 같이 기본 공격을 하면 해당 타겟에게 추가 투사체를 발사하는 아이템입니다.

이펙트 액션으로 해도 괜찮다면 어떻게 수정을 해야할지, 아니면 다른 방법은 무엇이 있을지 알고 싶습니다. 감사합니다.

 

Developer G님의 프로필 이미지
Developer G
지식공유자

아, 이해했습니다. 제가 잘못 이해해서 불필요한 정보들을 제공드려 죄송합니다.

이 경우에는 간단하게는 추가 투사체 발사를 Passive Skill로 만드는게 맞을거 같구요, UseConditon을 계속 Check 하는게 성능에 문제가 될까봐 걱정되시는 부분은 앞서 말씀드린대로 섣부른 최적화이고, 검사 로직이 수천 번의 복잡한 계산으로 이뤄지는게 아니라면 성능에 문제가 생길 가능성이 극히 낮습니다. 이 정도로 문제가 생긴다면 A* 같은 길찾기 알고리즘은 사용하는걸 꿈도 못 꾸겠죠. 섣부른 최적화는 지양해야 하고, 자신이 생각하는 최선을 방향으로 진행을 한 뒤 문제가 생기면 그때 수정하는게 개발 시간을 아끼는 길입니다.(코드를 막 짜라는 얘기는 아닙니다. 지킬건 지키되 너무 '성능'이라는 글자에 매몰되면 안된다는 얘기입니다.)

다른 방법으로는, 설명드렸던 Effect를 활용하는 방법이 여전히 유효합니다. Effect의 Action이 투사체의 효과를 담은 Skill을 변수로 들고 있고, 투사체를 생성하면서 이 Skill의 Clone을 투사체에게 넘겨주면 됩니다.

// EffectAction - SpawnAdditionalProjectileAction.cs
public GameObject projectilePrefab;
public Skill skill;
public int skillLevel;

// skill의 onApply에 등록 or Entity의 onDealDamage에 등록
OnApply()

{

// Skill을 Effect들을 담은 박스로 활용
var cloneSkill = skill.Clone() as Skill;
clone.Setup(Owner.Owner, skillLevel);
var proejctile = Instantiate(projectilePrefab, pos, rot);
projectile.Setup(Owner.Owner, cloneSkill, ...);
}

EffectAction을 활용하면 Skill을 관리하기 좀 귀찮아지는 감이 있긴 합니다. 상황에 따라 적절히 두 방법 중 하나를 활용하면 될 것 같습니다.

위 방법으로 하셨는데 말씀하신 문제가 생긴거라면, 제가 Code를 본게 아니기 때문에 그것까지 어떻게 답변을 드리긴 힘들 것 같습니다.

또 다른 방법으로는, UseCondtion도 EffectAction처럼 좀 개조를 해서 event를 등록할 수 있게 만들어줄 수 있습니다. 평소에는 false만 return하다가 OnDealAction에서 event가 호출되면 변수를 true로 전환하여 return 해주는 방식이죠. SkillSystem의 Logic을 수정하는 귀찮은 작업을 해야하지만 이후 사용에 있어 성능적으로 훨씬 나은 방법이 될거 같습니다.

0

인프런 AI 인턴님의 프로필 이미지

안녕하세요, 인프런 AI 인턴입니다. MOOOON님.

기본 공격 시 일정 확률로 추가 투사체를 발사하는 시스템 구현에 대해 고민이시군요. 직접적인 패시브 스킬을 통해 아이템의 효과를 발휘하려는 접근 방식은 매우 훌륭합니다. 관련하여 자세하게 설명드리겠습니다.

핵심 아이디어:
1. 패시브 스킬로 효과 추가: 패시브 스킬을 만들어, 이를 통해 아이템 장착 시 자동으로 등록하고 해제 시 제거하는 방법은 유연한 접근입니다.
2. 이벤트 리스너 활용: OnDealDamage 이벤트와 같은 시스템을 활용하여 일정 확률로 추가 투사체를 발사할 수 있습니다. 추가적으로, 이를 스킬 시스템과 통합하여 사용하는 방법에 대해 설명드리겠습니다.

구현 방법:
1. 이벤트 기반 발사 시스템:
- 추가 투사체 발사는 OnDealDamage 이벤트 리스너에 연결하여 처리할 수 있습니다.
- 이때 SkillSystem에 추가적인 이벤트 핸들러를 만들어 일정 확률로 발사하도록 설정할 수 있습니다.

  1. 스킬 등록 및 조건:
    • 사용하신 방법처럼 UseCondition에 IsPassRandomValueCondition을 추가하여 확률 체크를 수행하고, 해당 확률을 통과했을 때 추가 투사체를 설정된 타겟에게 발사합니다.
    • 스킬의 조건 체크를 지속적으로 수행하는 대신, 특정 이벤트 발생 시에만 실행되도록 시스템을 설계하면 성능 면에서도 효과적일 수 있습니다.

코드 예시:

public class PassiveProjectileSkill : MonoBehaviour
{
    public float chanceToFire = 0.2f; // 20% 확률
    private SkillSystem skillSystem;

    void Start()
    {
        skillSystem = GetComponent<SkillSystem>();
        skillSystem.OnDealDamage += TryFireAdditionalProjectile;
    }

    void TryFireAdditionalProjectile(Entity target, float damage)
    {
        if (Random.value < chanceToFire)
        {
            // 추가 투사체 발사 로직
            Debug.Log("추가 투사체 발사!");
            skillSystem.UseSkill(ConfigureProjectileSkill(target));
        }
    }

    Skill ConfigureProjectileSkill(Entity target)
    {
        Skill additionalProjectileSkill = new Skill();
        // 설정 로직 추가
        return additionalProjectileSkill;
    }
}

이 방식은 UseCondition보다 이벤트 기반으로 명확히 설계되므로, 성능과 코드 유지 보수 측면에서 더 유리할 수 있습니다.

해당 내용과 유사한 구현 방법에 대한 추가적인 정보는 질문 해결에서 찾아보실 수 있습니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.