https://www.youtube.com/watch?v=Zfoyagdz1y0

 

 

Weapon 이름의 새 스크립트 생성후

 

 

핸드 뼈  아래에 자식으로 위치시킨 무기 세개, 저번에 만든 거

인스펙터창 에 weapon 스크립트 드래그 해서 적용

 

 

근접공격이기에 망치만 편집해보도록 하겟습니다

 

 

 

using UnityEngine;


public class Weapon : MonoBehaviour
{
        
public enum Type { Melee, Range }; // 물리냐 원거리냐 
public Type type;
    public int damage; //공격 데미지
    public float rate;  //공격속도
    public BoxCollider meleeArea; //공격범위
    public TrailRenderer trailEffect; //잔상이펙트


}

 

weapon 스크립트 내용 여기까지 씀

 

 

영상에선 망치, 내 파일에선 WP_Cylinder 

 

비활성화 된 걸 활성화 시키기

 

Damage 는 20으로 설정

Rate 0.4로. 0.4 초마다 휘두르는 거임

 

 

공격범위 설정을 위해

아래 바로 애드 콤포넌트 . Box Collider 생성

 

 

박스 콜라이더 생성

is trigger on

 

그후 바로 위 

melee area 탭으로 드래그

 

 

 

Melee 태그 하나 만든 후 

해머 웨펀에 지정

 

 

,

 

 

이펙트 만들기

 

해머 아래에 자식에 create empty

 

 

엠피 이름은 Effect

 

Trail Renderer 컴포 하나넣기

 

 

망치 위치 움직여보면 뭔가 잔상이 보임

 

 

트레일 렌더러의 

material 을 default line 으로 설정

 

 

상단 빨간 Width 에서 우클, add key 누른후 점 생성후

그래프를 원하는대로 만들면

꼬리가 가 형태로 변경됨

 

 

길이가 너무 길면 Time 을 5가 아니라 0.7 정도로 낮추기

min vertex distance  값이 줄어들수록 부드럽고, 올릴수록 각진 모양이 됨

 

 

망치 웨펀 오브젝트로 돌아가서,

trail effect 칸에 아까 만든 트레일 렌더러 이펙트를 지정

 

 

설정 끝났으면 해머의

공격범위 박스 콜라이더, 트레일 렌더러 둘다 비활성화

 

 

 

+

 

 

코루틴은 유용하니 꼭 알아둘 것

 

 

 

Use() 메인루틴 -> Swing() 서브루틴 -> Use()메인루틴
코루틴 아니면 순차적 실행

Use() 메인루틴 + Swing() 코루틴

코루틴에선 동시에 실행

 

 

 

 

코루틴에선     yield 라는게 꼭 들어감

 

 

코루틴은 IEnumerator 으로 시작 함

 

 

 

 

 

        yield return new WaitForSeconds(0.1f); 는 0.1 초 대기하겠다는 뜻


 

        yield return 는 값을 적용 시킨다는 뜻

        yield break 는 이제 끝내고 싶다는 마침표 개념

 

 yield break는 맨 아래에 설정해야 함

맨 위에 놓으면 그 다음 이어지는 함수가 다 활성화 안되니 주의

 

 

 

 

StopCoroutine 은

실행 하는 도중에도 중단하고 아예 멈추는 함수

 

        StopCoroutine("Swing");
        StartCoroutine("Swing");

로직이 꼬이지 않게 일단 stop 부터 시작후 start 를 그 후에 배치

 

 

    IEnumerator Swing()
    {

        yield return new WaitForSeconds(0.1f); // 0.1초 대기
        meleeArea.enabled = true;
        trailEffect.enabled = true;

        yield return new WaitForSeconds(0.1f);
        meleeArea.enabled = false;

        yield return new WaitForSeconds(0.1f);
        trailEffect.enabled = false ;

        yield break;
    }

 

0.1초 지나고

공격범위 활성화

잔상효과 활성화

0.1초 지나고

공격범위 끔

0.1초 지나고

잔상효과 끔

이라는 

 

 

 

코루틴 이 아니라 인보크 를 이용하면 더 복잡해졌을 것임 

 

+

 

if (equipWeapon == null)
    return;

만약 equipWeapon이 null이라면, 아무 무기가 없다면

공격을 하지 않고 메서드를 종료합니다.

 

 

+

 

 

애니메이터 창, 파라메터 창, + 버튼 으로 트리거 하나 생성

이름은 doSwing

 

 

 

스왑 애니 만들듯이

저렇게 화살표 우클로 연결

 

위 화살표에선, 컨디션을 doSwap 으로 지정

 

아래 화살표에선 딱히 지정 할게 없음.

트랜지션 듀레이션은 0.1 정도로 취향것 설정

 

 

 

지금 껏 만든 거

e 키로 아이템 앞에 있는 루팅을 먹고

1 키로 아이템 망치 장착하고

마우스 왼클 (mouse 0) 로 망치 휘두름

 

 

 

 

플레이어 스크립트 풀 :

 

using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Rendering;

public class PlayerSC : MonoBehaviour
{
    public float speed;
    public GameObject[] weapons;
    public bool[] hasWeapons;

    public GameObject[] grenades;
    public int hasGrenades;

    public int ammo;
    public int coin;
    public int health;


    public int maxAmmo;
    public int maxCoin;
    public int maxHealth;
    public int maxHasGrenades;

    float hAxis;
    float vAxis;


    bool wDown;   // Walk를 위한 shift 꾹
    bool jDown;
    bool fDown; // 공격키
    bool iDown;

    bool sDown1;
    bool sDown2;
    bool sDown3;

    bool isJump;
    bool isDodge;
    bool isSwap;
    bool isFireReady = true;

    Vector3 moveVec;
    Vector3 dodgeVec;

    Rigidbody rigid;
    Animator anim;

    GameObject nearObject;
    Weapon equipWeapon;
    int equipWeaponIndex = 1;
    float fireDelay;

    void Awake()
    {
        rigid = GetComponent<Rigidbody>();
        anim = GetComponentInChildren<Animator>();
    }

    void Update()
    {
        GetInput();
        Move();
        Turn();
        Jump();
        Attack();
        Dodge();
        Swap();
        Interaction(); // 키보드 e 키 눌르면 무기 입수
    }

    void FixedUpdate()
    {
        Move();
    }

    void GetInput()
    {
        hAxis = Input.GetAxisRaw("Horizontal");
        vAxis = Input.GetAxisRaw("Vertical");
        wDown = Input.GetButton("Walk"); // GetButton은 꾹 누를 때만 적용
        jDown = Input.GetButtonDown("Jump");
        fDown = Input.GetButtonDown("Fire1");
        iDown = Input.GetButtonDown("Interaction");
        sDown1 = Input.GetButtonDown("Swap1");
        sDown2 = Input.GetButtonDown("Swap2");
        sDown3 = Input.GetButtonDown("Swap3");
    }

    void Move()
    {
        moveVec = new Vector3(hAxis, 0, vAxis).normalized;
        // Walk 걸을 때 속도 감소

        if (isDodge)
            moveVec = dodgeVec;

        if (isSwap  || !isFireReady)
            moveVec = Vector3.zero;

        if (wDown)
            transform.position += moveVec * speed * 0.2f * Time.deltaTime;
        else
            transform.position += moveVec * speed * 1.0f * Time.deltaTime;

        anim.SetBool("isRun", moveVec != Vector3.zero);
        anim.SetBool("isWalk", wDown);
    }

    void Turn()
    {
        // 회전 구현
        transform.LookAt(transform.position + moveVec);
    }

    void Jump()
    {
        // 제자리에서 점프는 점프
        if (jDown 
    && moveVec == Vector3.zero 
    && !isJump && !isDodge && !isSwap)
        {
            rigid.AddForce(Vector3.up * 7, ForceMode.Impulse);
            anim.SetBool("isJump", true);
            anim.SetTrigger("doJump");
            isJump = true;
        }
    }

    // 방향키 눌러서 점프는 닷지

    void Attack()
    {
        if (equipWeapon == null)
            return;
        fireDelay += Time.deltaTime;
        isFireReady = equipWeapon.rate < fireDelay;

        if ( fDown && isFireReady && !isDodge && !isSwap )
        {
            equipWeapon.Use();
            anim.SetTrigger("doSwing");
            fireDelay = 0;
        }

    }

    void Dodge()
    {
        if (jDown 
    && moveVec != Vector3.zero 
    && !isJump && !isDodge && !isSwap)
        {
            dodgeVec = moveVec;
            speed *= 1.5f;
            anim.SetTrigger("doDodge");
            isDodge = true;

            Invoke("DodgeOut", 0.5f);
        }
    }

    void DodgeOut()
    {
        speed /= 1.5f; // 원래 속도로 되돌리기
        isDodge = false;
    }

    void Swap()
{

        if (sDown1 && (!hasWeapons[0] || equipWeaponIndex == 0))
            return;
        if (sDown2 && (!hasWeapons[1] || equipWeaponIndex == 1))
            return;
        if (sDown3 && (!hasWeapons[2] || equipWeaponIndex == 2))
            return;

        int weaponIndex = -1;
        if (sDown1) weaponIndex = 0;
        if (sDown2) weaponIndex = 1;
        if (sDown3) weaponIndex = 2;

        if ((sDown1 || sDown2 || sDown3) && !isJump && !isDodge)
        {
            if (equipWeapon != null)
            equipWeapon.gameObject.SetActive(false);

            equipWeaponIndex = weaponIndex;
             equipWeapon = weapons[weaponIndex].GetComponent<Weapon>() ;
            equipWeapon.gameObject.SetActive(true);

            anim.SetTrigger("doSwap");

            isSwap = true;

            Invoke("SwapOut", 0.4f);

        }
    }

    void SwapOut()
    {
        isSwap = false;

    }



    void Interaction()
    {
        if (iDown && nearObject != null 
    && !isJump && !isDodge)
        {
            if (nearObject.tag == "Weapon")
            {
                Item item = nearObject.GetComponent<Item>();
                int weaponIndex = item.value;
                hasWeapons[weaponIndex] = true;

                Destroy(nearObject);
            }
        }
    }

    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "Floor")
        {
            anim.SetBool("isJump", false);
            isJump = false;
        }
    }

    void OnTriggerEnter(Collider other)
    {
        if(other.tag == "Item")
     {
    Item item = other.GetComponent<Item>();
    switch(item.type) 
             { 
              case Item.Type.Ammo:
                  ammo += item.value;
                  if (ammo > maxAmmo) ammo = maxAmmo;
                  break;
              case Item.Type.Coin:
                  coin += item.value;
                  if (coin > maxCoin) coin = maxCoin;
                  break;
              case Item.Type.Heart:
                  health += item.value;
                  if (health > maxHealth) health = maxHealth;
                  break;
                case Item.Type.Grenade:
                if (hasGrenades < maxHasGrenades) 
                   grenades[hasGrenades].SetActive(true); 
                   hasGrenades += item.value;
                 if (hasGrenades == maxHasGrenades)
                    return;
                    break;
            }
           Destroy(other.gameObject);
        }

    }

    void OnTriggerStay(Collider other)
    {
        if (other.tag == "Weapon")
            nearObject = other.gameObject;
    }

    void OnTriggerExit(Collider other)
    {
        if (other.tag == "Weapon")
            nearObject = null;
    }
}

 

 

 

웨펀 스크립트 풀 :

 

using Febucci.UI.Effects;
using System.Collections;
using UnityEngine;


public class Weapon : MonoBehaviour
{
        
public enum Type { Melee, Range }; // 물리냐 원거리냐 
public Type type;
    public int damage; //공격 데미지
    public float rate;  //공격속도
    public BoxCollider meleeArea; //공격범위
    public TrailRenderer trailEffect; //잔상이펙트

   public void Use()
   {
       if (type == Type.Melee)
        { 
        StopCoroutine("Swing");
        StartCoroutine("Swing"); 

        }

   }

    IEnumerator Swing()
    {
// 애니메이션과 잘 안 맞으면 이 아래 부분을 조정

        yield return new WaitForSeconds(0.12f); // 0.1초 대기
        meleeArea.enabled = true;
        trailEffect.enabled = true;

        yield return new WaitForSeconds(0.1f);
        meleeArea.enabled = false;

        yield return new WaitForSeconds(0.1f);
        trailEffect.enabled = false;

        // Trail Renderer 리셋
    ResetTrailRenderer(trailEffect); 
    yield break; 
    } 

void ResetTrailRenderer
(TrailRenderer trail)
{ trail.Clear(); }
    



}

 

 

 

골드메탈님 동영상에선 안 나왔지만,

아래 처럼

스크립트 마지막 부분에서

트레일 렌더러를 리셋해야 깔끔하게 재생이 됨

아래 안 하면 끝이 다음 시작때 남는 지저분한 이펙트가 되버림

 

    // Trail Renderer 리셋
    ResetTrailRenderer(trailEffect); 
    yield break; 
    }

    // Trail Renderer 리셋
    void ResetTrailRenderer
(TrailRenderer trail)
{ trail.Clear(); }
반응형
Posted by 이름이 익명
: