ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • RPG 듀토리얼 11. Player Skill (Skill 00 & Calulate)
    유니티 프로젝트/RPG 듀토리얼

    안녕하세요!!!

    오늘은 각 공격 스킬에 대해 알아보려고 합니다.

     

    각 공격 스킬에는

    1. 파이어 볼 : 가장 기본적인 스킬 입니다.

    2. 애로우 샷 : 관통을 하는 스킬입니다.

    3. 블랙홀 : 범위 내에 적을 기절시킵니다.

    4. 힐 : 체력을 최대 20% 올려줍니다.

     

    4가지의 이름을 지어 봤습니다 ㅎㅎ

     

    1, 2, 3번 같은 경우에는 

    총 세 가지 이펙트가 존재합니다.

    1) 캐스팅을 시전 할 때

    2) 스킬 발사를 할 때

    3) 스킬을 맞았을 때

     

    4번은

    자체적인 힐 스킬로

    1) 스킬 발사를 할 때 만 있습니다.

    쿨타임은 길지만

    캐스팅 시간은 짧게 구현하여 즉시 발동되게 해 봤습니다.

     

    각 스킬 이펙트는

    제가 Particle System을 이용해서 만든 파이어볼과 

    파이어볼 히트 이펙트를 제외하곤

    만들어져 있는 에셋을 이용해서 필요한 부분만 가져와 

    하나의 오브젝트의 자식들로 만들어 사용해 봤습니다.

    이펙트 이게 생각보다 원하는 구현을 하기가 힘들더라고요...


    자.. 그럼 파이어볼을 발사하는 Skillfire00 스크립트를 생성하여

    구현하는 동작을 보겠습니다!

     

    우선 가장 먼저 선언 변수들입니다.

    targetNot : 타깃이 없을때 날아갈 타겟 방향입니다.

     

    (이 부분은 notthingAtkPosition이 담당하는데요. 실제로 끝까지 날아가진 않고 

    DisableObj의 함수로 이용해 날아가는 도중 꺼지게끔 여러 번 테스트를 하였습니다)

    00 : 10초

    100마디 말보다는 짧은 영상 하나가 훨씬 효과적일 것 같아 영상을 첨부했습니다.

     

    여기서 문제점이 스킬이 날아가던 도 중 플레이어가 움직이게 되면 

    targetNot(notthingAtkPosition)의 위치가 변경이 되어서

    스킬이 일정 범위 안에서 혼자 춤을 추는 상황이 생겼었습니다.

    그러기에 SkillBtn 스크립트에 있는 WaitForScond

    (PlayerController에서 onCasting 이 flase로 바뀌는 함수를 Invoke로 실행해 주는 지연 시간)

    즉, 움직일 수 있는 시간의 조절이 필요했고.

    테스트를 해보며 적정 시간을 잘 맞출 수 있었습니다.


    음음.. SkillFire00 스크립트로 다시 돌아가 선언 변수들을 계속 보자면

     

    target : 스킬이 날아갈 타깃입니다.

    이 타깃은 PlayerController에서 OnPointerDown이벤트에서

    맞은 transform이 "Enemy" 였으면 Targeting() 함수를 실행했었죠?

    여기서 target은 hit.transform으로 맞은 transform으로 지정했었고요

    이 타깃은 기본 타깃이며

    Casting팅 함수에서 atkTarget = target을 지정해 줬었습니다.

     

    네! 그 PlayerController에 있는 atkTarget을 가져와서 담을 target변수입니다.

    (캐스팅을 사용했을 시!)

    (공격을 하지 않았다면 클릭/터치를 하지 않는 이상 target을 가져올 필요가 없습니다!)

     

    speed : 스킬 오브젝트의 스피드입니다.

    hitEffect : 스킬에 맞았을 시 생길 오브젝트입니다.

     

    pc : PlayerController를 받아와 제어할 변수입니다.

     

    skillPower : 스킬 대미지입니다.

     

    dmg : 총 대미지입니다

    (스킬 파워 + 플레이어 공격력 + dmgRate(대미지 증감률)) 

    로 만들 것입니다.

     


    매 활성화 시 처음 실행되는 OnEnable 함수입니다.

    Start함수와 Awake함수도 활성화 시 실행이 되지만 딱 1회 실행이 됩니다.

     

    보자면 우선 target을 pc(playerControlloer)의 atkTarget으로 지정해 주는 모습입니다.

     

    Manager.instance.managerSE.seAudio.PlayOneShot(Manager.instance.managerSE.fly_Attack00, 0.7f)

    여느 때와 똑같이 하나의 싱글톤 패턴인 Manager에 접근하여

    managerSEAudioSourceclip을 넣어 실행하는 동작입니다.

     

    다만 소리가 조금 큰 것 같아 뒤에 0.7f의 인자를 넣어 주었습니다.

    이 인자는 volume의 값이며 0.0f~1f의 min - max 값을 같고 있습니다.

     

    AudioSource의 volume
    Manager 스크립트의 Clip들 중 스킬 부분

    즉, Manager.instance.managerSE.seAudio.PlayOneShot(Manager.instance.managerSE.fly_Attack00, 0.7f) 코드는 

    0번 스킬의 발사(fly_Attack00) 사운드입니다.


    이후 StartCoroutine로 코 루틴 함수 "Skill00"를 실행시켜줍니다.

     

    코 루틴 함수 Skill00 함수에서는 while문으로 로직을 구현했습니다.

     

    스킬이 발사가 되면 OnEable 함수로 코 루틴 함수 Skill00이 실행되며

     

    조건문을 통해 pc.targetnull(비어있는지) null이 아닌지 조건을 걸어 줍니다.

    이는 타깃이 없을 때 공격 스킬을 사용했을 경우 스킬을 앞으로 발사하기 위함입니다.

     

    if (pc.target!= null) : pc.target이 null이 아닐 경우입니다.

     

    Vector3 dir = target.position + new Vector3(0, 1, 0);

    Vector3로 방향을 담을 변수 dir을 선언해 주었습니다.

    보통 Direction(방향)을 줄인 'dir'을 붙여 변수명으로 사용한다고 합니다!

     

    dir은 target의 position +

    (여기선 Enemy입니다.)

     

    new Vector3로 y축을 1 만큼 높여 주었습니다.

    스킬이 바라보는 방향을 높여주기 위함입니다.

     

    transform.LookAt(dir)

    LookAt 함수를 이용해 transform(Skill00)의 회전과 바라보는 앞 방향을 dir로 지정해 주었습니다.

     

    transform.Translate(Vector3.forward * speed * Time.deltaTime)이후 Translate의 함수를 이용해 forward(앞 방향) * speed(스피드) * Time.deltaTime을 곱해 주어앞 방향으로 이동 되게 구현을 하였습니다.

     

     

    반대로 if (pc.target == null) : pc.target이 null(비어있을) 경우입니다.

     

    Vector3 dir = targetNot.position + new Vector3(0, 1, 0)구현 코드는 똑같지만. dir을 구하는 타깃이 targetNot 보게 했습니다.

    targetNot은 Player의 일직선 상 앞에 빈 오브젝트로 구현을 해놓았습니다.


     

    Skill00이 Enemy에게 맞으면 구현이 될 함수는 OnTriggerEnter에서 작성하였습니다.

    OnTriggerEnter / OnTriggerExit / OnTriggerStay /

    충돌 시 / 충돌 해제 시 / 충돌 중일 시입니다.

     

    OnTriggerEnter의 코드 일부분입니다.

     

    if(other.tag == "Enemy" || other.tag == "Dead" || other.tag == "Enemy_Boss")

    우선 첫 번째 조건문을 보자면 현재 적의 태그는 Enemy와 Boss 두 가지로 구현할 예정입니다.

    태그를 Dead까지 넣은 이유는, Enemy가 죽으면 Dead로 태그가 변경되는데

    스킬이 날아가는 도중 Enemy가 죽을 수 있기 때문입니다.

     

    충돌 체크가 되면 StopCoroutine으로 코 루틴 함수 Skill00를 멈춰 줍니다.

    이후 SetActive를 false로 비활성화를 해주게 되며 

    hitEffect SetActive = true로 활성화를 시켜줍니다.

    생성될 hitEffect

    이후 hitEffect의 위치와 대미지 증감률을 계산해주는 함수를 따로 만들어 실행해 주는 모습입니다.

    함수의 코드가 길어지며 가독성이 떨어지기에 따로 구현하였습니다.

    우선 hitEffect의 위치를 잡아줄 OnHitEffect 함수 코드부터 보겠습니다.

     

    hitEffect.transform.position = hitPoint + new Vector3(0, 1, 0)

    hitEffect의 위치를 new Vector3로 y값을 1만큼 올려 충돌체의 윗부분에 생성이 되게 해 주었습니다.

     

    CalulateDmg()

    PlayerStats playerStats = Manager.instance.playerController.player.GetComponent <PlayerStats>()

    대미지 계산을 하기 위해 플레이어의 스텟을 가져왔습니다.

     

    대미지의 증감률을 담을 변수 dmgRate는 Random.Range 함수로 0.8f~1.2f의 랜덤 값을 부여해 주었으며

     

    크리티컬을 구현은 

    cir에 Random.Range로 0~100까지의 값을 부여받아

    player의 cir * 100보다 작다면 크리티컬이 적용되게 구현하였습니다.

    player의 cri에 100을 곱해준 이유는 player의 cri는 0f~1.0f의 값을 갖고 있기 때문이며

    Random.Range 값으로 부여받은 값이 player의 cri보다 작다면

    그 확률이 적용 되게 구현을 하였습니다.

     

    마지 막으로 현재 구현된

    플레이어의 공격력 + 스킬 파워 + 대미지 증감률을 곱해 주어 dmg변수에 적용해 줍니다.

    이 dmg변수는 Skill00의 대미지를 담을 변수입니다.

     

    dmgText.GetComponent <TextMeshProUGUI>(). text = dmg.ToString()

    dmg를 text에 넣기 위해 ToString으로 문자열로 변환을 해 준 뒤

    폰트 사이즈와 위치를 잡고 

    SetActive(true)로 활성화를 해 주는 모습입니다.

     

     

    00 : 15초

     

    댓글

김효겸 / Tel. 010-7735-0580 / E-mail. dollzzang2@hanmail.net