https://www.youtube.com/watch?v=07q9RUTRq4M&t=4s

 

 

일단 총알부터 만들자

엠티 오브젝트 생성 

 

이름은 Bullet HandGun

x y z 0 0 0 으로 해두고

 

 

Trail Renderer 추

 


트레일 렌더러 세부설정


width 빨간 그래프 

0.5 부터 시작해서 점점 감소되게 설정

time 0.3 정도
min vertex distance 0 

 

마테리알은 default line 적용.

 

 

리지드 바디, sphere 콜라이더도 만들기

 

스피어 콜라이더에

x y z 0 0 0 으로 설정하고

radius 조금 줄여줌

 

 

ctrl D 눌러서 복붙후

아래 이름은 Bullet SubMachineGun 으로 명명

 

+

 

 

네모난 탄피 오브젝트 만들기

(대충 cube 써서 만들어도 될 듯)

 

탄피, bullet case 는 

바닥에 떨어지는 이펙트 용도임

 

탄피에 리지드 바디, 박스 콜라이더  컴포넌트추

 

 

콜라이더 크기 오브젝트에 맞추기

 

 

+

 

 

Bullet 이라는 스크립트 하나 생성

 

 

 

만든 불렛 오브젝트들 세 개 다중선택후

컴포넌트 스크립트 Bullet 적용

 

 

일단 지금까지 스크립트 내용은 : 

using UnityEngine;


public class Bullet : MonoBehaviour
{

    public int damage;


void OnCollisionEnter(Collision collision)
{

        if (collision.gameObject.tag == "Floor")
        {
            Destroy(gameObject, 3); // 부딪친후 3초 후 삭제
        }
        else if (collision.gameObject.tag == "Wall")
        {
            Destroy(gameObject, 0);
        }


    }


}

 

오브젝트 명 Damage
Bullet HandGun 20
Bullet SubMachineGun 7
Bullet Case 0

 

인스펙터 창에서 이렇게 데미지 설정

 

 

+

 

 

프리팹 만든 후

x y z 0 0 0 으로 설정 

 

 

세 bullet 들 프리팹으로 만들었으면 하이어라키 창에서 지움

 

 

+

 

플레이어의 발사 구현, 애니메이션 설정

 

Animator 창에서

 

 

플레이어 오브젝트 열어서 Shot 애니메이션을 드래그.

 

예전 Swing 만들듯이 만듦

 

Any State 에서 우클 화살 트랜지션 연결후 , Exit 로 우클 연결

 

파라메터 + 로 추가후 trigger 섵낵

이름은 doShot

 

Any state 에서 Shot 애니로 들어오는 트랜지션의

컨디션은 doShot 으로 지정

 

 

+

 

플레이어의 발사 구현, 스크립트 만지기

 

Player 스크립트 로 간 후

 

            anim.SetTrigger(equipWeapon.type 
            == Weapon.Type.Melee ? "doSwing": "doShot");

 

웨펀 타입이 밀리인가?

맞으면 doSwing 을 하기

아니면 doShot 을 하기

라는 뜻임

 

 

 하이어라키 창에서

플레이어 오른손 아래 gun 과 sub machine gun 의

타입을 range 로 설정

 

 

 

 

총알이 데미지 있는거지 총은 데미지가 없으니

데미지는 0 

melee area 설정 안함

trail effect 설정 안

 

핸드건 rate, 공속은 0.4 로 설정

 

서브 머신건은 rate 0.15 로 설정

 

 

총에겐

총알이 발사되는 위치, 탄피가 나오는 위치 

가 필요하다

 

weapon 스크립트에 추가함 :

 

    public Transform bulletPos; //총알생성위치
    public GameObject bullet;
    public Transform bulletCasePos; //탄피생성위치
    public GameObject bulletCase;

 

 

 플레이어 아래 엠티 오브젝트 추가

 

Bullet Pos 로 이름 변경

 

총알 나가는 위치 즈음으로

Bullet Pos 의 x y z 를 이동시킴

일단 대략적인 위치를 지정함

 

 

 

탄피가 나가는 위치는 

오른손 아래 , 웨펀 포인트 아래, 핸드건 아래 크리에이트 엠티

 

이름은 Case Pos 로 명명

 

 

 

Case Pos  오브젝트 위치는 총알 옆 탄피 배출구 쪽으로 이동

 

위치 이동이 힘들면 Global / Local 로 전환

 

 

서브머신건 아래 자식오브젝트도 동일하게 Case Pos  만들기

 

 

이제 핸드건, 서브머신건의 스크립트에서 변수를 지정

 

 

 

    Vector3 caseVec = bulletCasePos.forward * -10

파란 Z 축 반대 방향으로 탄피를 나가겠다는 뜻

음수를 곱함

 

 

        Vector3 caseVec = bulletCasePos.forward * Random.Range(-3, -2)
    + Vector3.up * Random.Range(2, 3)

탄피가 z 축 반대 방향으로 나가는데

-2 ~ -3 의 랜덤값으로 나간다

근데 위쪽으로 2~3의 랜덤값으로 튄다

는 뜻

 

caseRigid.AddForce(caseVec, ForceMode.Impulse);

나가는 힘을 주겠다는 뜻

 

 

 

~~~

근데 비주얼 스튜디오에서 오류남

instantBullet 인가 intantBullet 인가 빨간줄 생김

 

그리고 velocity 에 취소선이 생김

 

 

~~~

 

 

벽에 Wall 태그 넣기

태그 Wall 만든 후 적용

 

 

 

테스트 모드 해보기

무기 앞에서 e 눌러 무기 줍기

2 번 3번 키로 무기 스왑

마우스 왼손으로 발사

 

+

 

재장전 구현하기

 

 

 if (type == Type.Range && curAmmo > 0 )

무기 타입이  원거리 이고

지금 탄환이 0보다 클 때 (1이상일때)

라는 뜻

 

            curAmmo--;

curAmmo 라는 변수를 한개씩 줄어들게 한다는 뜻

 

 

인풋 매니저

재장전 Reload 

포지티브 버튼 키보드 R 로 설정

 

 

    if(equipWeapon == null) return;
    if(equipWeapon.type == Weapon.Type.Melee) return;
    if (ammo == 0) return;

만약 무기가 없으면 하지 않겠다

만약 무기 타입이 근거리면 하지 않겠다

만약 총알이 0 개면 하지 않겠다

라는 뜻.

재장전 조건에 쓰일 거임

 

 

 

animator 에서 재장전 

shot 애니 만들듯이 만들기

파라메터 트리거, doReload 로 함

 

 

핸드건 cur ammo 변수, max ammo 변수 설정

처음엔 탄이 다 충전되어있음을 연출시키려고 7 , 7로 설정

서브 머신건은 30 , 30 으로 설정

 

+

 

마우스 위치 로 총알 발사시키기

 

    public Camera followCamera;

플레이어 스크립트에 추

 

 

플레이어 오브젝트에서 팔로우 카메라 변수를 메인 카메라를 연결

 

 

        // 마우스에 의한 회전 구현
        Ray ray = followCamera.ScreenPointToRay(Input.mousePosition);

화면상 마우스 위치를 반영한다는 뜻

 

 

out 함수는 리턴저럼 값을 저장하는 개념

 

                nextVec.y = 0;

플레이어가 y 회전,  눕혀지는 걸 방지하는 것

 

    void Turn()
    {
        // 키보드에 의한 회전 구현
        transform.LookAt(transform.position + moveVec);
        // 마우스에 의한 회전 구현
        if (fDown)
    {
            Ray ray = followCamera.ScreenPointToRay(Input.mousePosition);
            RaycastHit rayHit;
            if (Physics.Raycast(ray, out rayHit, 100))
            {
                Vector3 nextVec = rayHit.point - transform.position;
                nextVec.y = 0;
                transform.LookAt(transform.position + nextVec);
            }


        }
    }

마우스 왼쪽 버튼, 파이어 다운 을 누르면

플레이어가 마우스 위치 쪽을 바라보겠다 라는 뜻

 

 

~~~~~~

 

지금까지

 

풀 웨펀 스크립트

 

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 int maxAmmo;
    public int curAmmo;

    public BoxCollider meleeArea; //���ݹ���
    public TrailRenderer trailEffect; //�ܻ�����Ʈ

    public Transform bulletPos; //�Ѿ˻�����ġ
    public GameObject bullet;
    public Transform bulletCasePos; //ź�ǻ�����ġ
    public GameObject bulletCase;

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

        }
        else if (type == Type.Range && curAmmo > 0 )
        {
            curAmmo--;
            StopCoroutine("Shot");
            StartCoroutine("Shot");

        }

    }

    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;

    ResetTrailRenderer(trailEffect);   // Trail Renderer ����
        yield break; 
    }

    void ResetTrailRenderer // Trail Renderer ����
    (TrailRenderer trail)
    { trail.Clear(); }


    IEnumerator Shot()
    {
        // 총알 발사

        GameObject instantBullet =     Instantiate(bullet, bulletPos.position,bulletPos.rotation);
       Rigidbody bulletRigid = instantBullet.GetComponent<Rigidbody>();
        bulletRigid.linearVelocity = bulletPos.forward * 50;

        yield return null;

        // 탄피 발사
        GameObject instantCase =        Instantiate(bulletCase, bulletCasePos.position, bulletCasePos.rotation);
        Rigidbody caseRigid = instantCase.GetComponent<Rigidbody>();
        Vector3 caseVec = bulletCasePos.forward * Random.Range(-3, -2)
        + Vector3.up * Random.Range(2, 3);
        caseRigid.AddForce(caseVec, ForceMode.Impulse);
        caseRigid.AddTorque(Vector3.up * 10, ForceMode.Impulse);

    }
    




}

 

 

풀 플레이어 스크립트 : 

 

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 Camera followCamera;

    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 rDown; // 원거리 무기 재장전

    bool iDown;

    bool sDown1;
    bool sDown2;
    bool sDown3;

    bool isJump;
    bool isDodge;
    bool isSwap;
    bool isReload;
    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();
        Reload();
        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");
        rDown = Input.GetButtonDown("Reload");
        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 || isReload  || !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);
        // 마우스에 의한 회전 구현
        if (fDown)
    {
            Ray ray = followCamera.ScreenPointToRay(Input.mousePosition);
            RaycastHit rayHit;
            if (Physics.Raycast(ray, out rayHit, 100))
            {
                Vector3 nextVec = rayHit.point - transform.position;
                nextVec.y = 0;
                transform.LookAt(transform.position + nextVec);
            }


        }
    }

    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(equipWeapon.type 
            == Weapon.Type.Melee ? "doSwing": "doShot");
            fireDelay = 0;
        }

    }

    void Reload()
    {
    if(equipWeapon == null) return;
    if(equipWeapon.type == Weapon.Type.Melee) return;
    if (ammo == 0) return;

    if(rDown && isFireReady &&
    !isJump && !isDodge && !isSwap )
    {
            anim.SetTrigger("doReload");
    isReload = true;

            Invoke("ReloadOut", 0.5f);
        }

    }

    void ReloadOut()
    {
     int reAmmo = ammo < equipWeapon.maxAmmo ? ammo : equipWeapon.maxAmmo;
     equipWeapon.curAmmo = reAmmo;
     ammo -= reAmmo;
     isReload = false;
    }

    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;
    }
}

 

+

 

 

참고로, 

무기 스왑 1 키 2 키 3 키 눌렀는데 제대로 변경안되는 버그가 있을 땐

 


 int equipWeaponIndex = -1;

이렇게 맨위에 적혀있어야 함


원래 양수 이게 맞나 확인해야함

그냥 양수 1로 하면 안됨

 

 

 

반응형
Posted by 이름이 익명
: