arp auto rig pro 에 적합한
viewport display color는?
 (실제 모델 컬러아니고 뷰포트 셰이딩보기용 컬러)

 

 

arp 컨트롤러 자체의 컬러 조정 가능

 

+

 

 

 

 

상단

뷰포트 디스플레이 옵션

 

사방에서 빛을 받는 저거 선택함

저게 스컵팅때 좋음

 

cavity 옵션을 더 줘서 명암을 더강하게 함

 

 


 

후보

 

 

#545454FF

 

 

 

57492CFF

 

 

 

 

03 어두운빨강 6B3439FF

03 어두운 노랑 434343FF

 

03 회색 494949FF

 

02어두운 연두색394F00FF

 

어두운노랑524513FF

 

ㄴㄴ

 

ㄴㄴ

02 핑보라 891472FF

 

 

02 밸류 0056 샵 434343FF

ㄴㄴ 파랑이 안보여


 

 

탈락

 

반응형
Posted by 이름이 익명
:

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

 

 

 

캔버스 세팅

 

하이어라키 우클 ui - canvas 누르면

유아이 의 기초가 되는 캔버스가 생성됨

 

근데 밑에 바로 event system 이 같이 딸려나옴

 

 

이벤트 시스템은 캔버스의 인풋 입력을 도와주는 시스템임

 

 

 

캔버스를 더블클릭하거나 f 키를 누르면

화면이 캔버스에 집중됨

 

ui 보는데 에 집중하고 싶으면 상단에 2D 버튼을 누르기

 

 

유니티 3D 에선

인게임 월드 

ui 스크린

두 가지로 나뉜다

 

 

 

월드에서의 좌표값은 Transform 이라 부르고

스크린에서의 좌표값은 RectTransform 이라 부름

 

 

캔버스 크기는 기본적으로

카메라 해상도 (카메라 크기) 를 따라간다

 

 

도트 스프라이트 이미지를 다룰 땐

canvas 의 pixel perfect 체크 온

 

canvan - 인스펙터 창 - canvas scaler - ui scale mode

constant : ui가 변해도 크기는 그대로로 할 거다

scale with screen size ui 가 변하면 늘리거나 줄일 거다

 

 이번 강의에선 FHD 풀에이치디 1080p, 1920 1080 으로 설정할거임

reference resolution 창에 1920 1080 으로 설정

 

+

 

 

Canvase 아래 자식오브젝트로 우클 - ui - Panel 생성

 

이름은 Menu Panel

색상에서 A, 알파 투명도 값을 조절 가능

 

 

판넬 자식 오브젝트로,  ui , image 하나 생성

 

 

source image 에 타이틀 적용

 

근데, png 그대로 적용 안되는 문제가 있음

 

상단 window - package manager - unity registry - 2D sprite 다운로드

 

에셋 창에서 불러온 png 파일을

texture Type 에서 sprite 2d and ui 로 설정

 

window - 2d - sprite editor - 

 

 

왼위 부터 오른아래까지 쭉 드래그 

 

 

인스펙터 창에도 apply , 

스프라이트 에디터 창에도  apply 

 

 

 

이제 png를 스프라이트로 이용 가능해짐

 

 

 

아래 어셋 창에서 화살표 모양이 생겨야 스프라이트 화가 된거임

 

 

,

 

다시 캔버스의 자식 오브젝트인 image 로 돌아와서

맨 밑 버튼 Set Native Size 클릭

 

 

 

 

스코어 점수 연출

width height 0 0 으로 크기조정

아래 overflow 를 ,  nowrap,  overflow 로 정하기

 

 

text transform 의 scale 을 모두 0.5 로 만들면

깨져 보이는 게 덜해짐

 

 

+

 

폰트 넣기

 

폰트는 파일이 무거워서 느려지니까,

너무 많은 폰트들을 불러오지는 말것

 

 

 

불러온 폰트를

상단 window- font - font asset creator 로 불러와서 한번더 세이브 해줘야 함

 

 

+

 

 

 

메뉴 판넬 아래 자식으로 ui 버튼 생성

 

 

 

simple 로 한후 set native size 클릭 후

slice 로 바꾸기

 

 

 

텍스트의
Left Top Pos Z Right Bottom 은 여백을 의미함

 

 

.

 

하이어라키 창에서 이름 다 변경해주고,

일단 메뉴 판넬을 비활성화 시킴

 

 

+

 

 

인게임 ui 만들 panel 새로 하나 제작

판넬의 인스펙터창 - image - color - A 알파값은 0으로 함

 

 

자식으로브젝트로 엠티 오브젝트 생성

이름은 Score Group

 

ui - image , 

ui - text 메쉬프로 하나씩 생성

 

image, text 쉬프트클릭으로 다중선택

인스펙터 상단 Anchor Presets 눌러보기 

 

 

shift , alt 키 누르면 모드가 전환됨

 

 

 

부모 오브젝트의 Score Goup 의 w h 500 500 안에서 못 벗어나

앵커 프리셋이 한정되게 연출됨

 

이번 강의에선 크기 제한이 불필요함으로 0 으로 두기

 

 

 

 

스코어 텍스트는 alignment 왼쪽 정렬이라 숫자가 많아질수록 오른쪽으로 많아짐

(그리고 no wrap, overflow 로 설정)

 

 

맨위 엠티오브젝트였던 Score Group 와 그 자식들도

앵커프리셋 알트 쉬프트 누르고 좌상단 눌러

왼위로 기준 맞추기

 

 

무브 툴로 이동

 

 


최상단 엠티 오브젝트


Score Image


Score Text

 

 

 

 

 

완성되었으면 Score Group 을 ctrl D 눌러 복붙

이름은 Status Group

 

 

 

 

ctrl d 눌러 세 쌍을 만듦

 

이건 플레이어의 소지

체력, 총알, 동전 을 의미함

 

 

아마도? ui 의 기본 그리드 크기는 100 px x 100 px 인듯?

 

 

 

 

이렇게 이름 명명

 

 

맨위 엠티 오브젝트 pos x , pos y 여백 20 씩 주기

 

 

 

 

스코어 엠티, 이미지, 텍스트 복붙해서 오른위로 배치

 

이름은 Stage Group

 

 

 

Stage Image 의 pos y 값은 Score image 의 pos y 값과 동일. 둘다 0 으로 정렬

 

 

 

 

 

Stage Image 와 Text 복붙후

Time Image 와 Text 를 만듦

그 후 아이콘 두개를 왼쪽으로 이동해서 보기 좋게 배치

 

 

하이어라키 다중 복붙 후 하이어라키 계층 레이어 맨 아래 로 이동 단축키는

ctrl 키와 - 키를 같이 누르면 됨 

 

 

맨위 엠티 오브젝트 하이어라키 20 씩 여백을 주는데,

이 여백주는 걸 가장 마지막에 해야함

일단 먼저 각 하얀 모서리에 맞춘 후

맨 나중에 20씩 여백을 줘야 딱딱 예쁘게 맞춤 가능

 

+

 

 

아마 layout grid 를 바닥에 깐 상태에서

ui 를 정렬하면 더욱 좋게 될 듯 함

 

 

딱 딱 sf 컨셉이면 강박이 생기고, ui 맞추기도 어려워짐

 

좀 스타일라이즈 하고

좀 유동적인 컨셉의 그래픽이면

약간 삐뚤삐뚤해도 허용되게 됨

 

내 게임은 좀 스타일라이즈를 넣어서 약간 삐뚤삐뚤한 ui 컨셉을 정해야 할 거 같음

 

 

+

 

 

강의 내에선 이렇게 정렬을 했음

 

왼쪽은 글자 왼쪽 정렬

오른쪽은 글자 오른쪽 정렬 함

 

+

 

장비 ui 만들기

panel 아래 자식 오브젝트로 엠티 하나만들어주고,  width height 0 0 

앵커는 shift alt 아래쪽으로 배치

 

equip 아래 ui image , 스프라이트 생성

패널 네모난걸 적용

셋 네이티브 이미지 클릭

이거도 앵커 아래쪽으로 쉬프트 알트로 배치

 

 

이미지 아래 또 자식이미지를 만듦

 

위 이미지는 칸 이미지를,

아래 이미지는 장비 이미지를 넣음

 

 

이미지 하나 더 만든후 

1 이라는 단축키를 상징하는 png 스프라이트 적용

 

 

좌측부터 차례대로

아래로 정렬. 

수동으로 일일히 정렬

 

 

pos z, pos y 값도 일일히 숫자 넣어가며 수동으로 정렬

 

 

맨위 부모 엠티 오브젝트의 scale 값을 줄여서

하위 자식들의 전체 크를 조절해도 됨

 

+

 

주의점!

 

그리드와

 

중앙 선은 서로 다르니 헷갈리지 말것

 

 

왼쪽 마젠타 색이 중앙선임..

오른쪽 파란선은 페이크 그리드 ;;

 

 

 

 

 

+

 

보스 체력 ui 만들기

 

Boss Group 엠티 오브젝트 w / h 0 0 으로설정후 기준점 맨위 중앙으로 설정

 

 

체력바 바탕될 이미지 생성

원본은 직사각형 네모임

 

boss group 아래 ui -  image 로 생성

이미지 타입

simple 했다 셋네이티브 클릭 후

sliced로 설

 

 

 

ui 바가 이상하게 늘려지면, 

9 sliced 기법을 써야한다

 

image type 은

sliced 로 하고

 

 

 

상단 window - 2d - sprite editor 열어서 컨트롤 눌러 저렇게 9등분하면 됨

 

크기는 w 600 h 200

 

 

이미지 아래 자식오브젝트로

ui - 이미지 하나 더 추가

이거도 600 200 , sliced 로 하기

이미지 컬러는 빨강으로 설정

 

 

기본 이미지가 흰색 이어야지

유니티에서 덧대는 Color를 자유롭게 지정 가능함

 

보스 그룹 아래 이미지 아래 자식오브젝트 ui 이미지 생성

 

BOSS 커스텀 이미지를 삽입

 

앵커는 쉬프트 알트 눌러 좌측으로 설정

 

스케일 크기 미세조정은 알트 키 누르며 조정가능

 

커스텀 글자 이미지도 추가

 

보스 hp 바 빨강색을 담당하는 ui image 의 width 를 줄여보며 잘 되나 테스트 해보기

 

ui 모서리 부분 겹치는게 가려지는게 싫다면,

material 설정 후

transparent, multiply 블랜딩 모드를 사용할 것

 

다 끝났으면 맨외 부모의 여백을 줌

pos y -30 정도 줌

 

 

+

 

유니티상 레이어 순서는 포토샵과 반대임

 

하이어라키 위에 있으면 아래로 깔림

 

+

 

메인메뉴 그룹 인게임 그륩

 

ui 는 이렇게 두개로 분할 하였음

 

두 개를 나중에 껐다 켰다

하면서 전환하려고 함

 

 

반응형
Posted by 이름이 익명
:

 

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

 

 

 

    anim = GetComponentInChildren<Animator>();
    meshs = GetComponentsInChildren<MeshRenderer>();

 

여러 컴포넌트를 스크립트에선 불러올 땐

GetComponentsInChildren

 

s 가 중간에 붙으니 주의할것

 

 

 

 

새 게임 오브젝트 생성, 에너미 불렛 기본 세팅하기

 

오브젝트 이름은 EnemyBullet

태그는 EnemyBullet (난 인덱스 7번)

레이어는 EnemyBullet (난 인덱스 13번)

박스 콜라이더,  is trigger 체크 

저번에 만든 Bullet 스크립트 컴포 적용, damage 는 10으로 설정

 

 

 

 

    SkinnedMeshRenderer[] skinnedMeshs;



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


    IEnumerator OnDamage()
    {
        isDamage = true;
        foreach (SkinnedMeshRenderer mesh in skinnedMeshs)
        {
            mesh.material.color = Color.yellow;
        }
        yield return new WaitForSeconds(1f);
        isDamage = false;
        foreach (SkinnedMeshRenderer mesh in skinnedMeshs)
        {
            mesh.material.color = Color.white;
        }

    }

 

주의점

 

mesh renderer 컴포가 아닌

SkinnedMeshRenderer 컴포를 사용시 위 스크립트처럼 변경해야 함

 

 

+

 

 

 

Enemy A 아래 자식으로 아까만든 EnemyBullet 을 넣음

 

근접공격 구현할거라

EnemyBullet 위치를 몬스터 입 앞쪽에 배치

 

 

    public BoxCollider meleeArea;

로 변수칸 인스펙터에 생성되게 함

EnemyBullet 자식으로 넣은걸 Melee Area 변수칸에 지정

 

 

 

Player 의 레이어가 Player 이어야 함

 

 

 

IEnumerator Attack()
   {
    isChase = false;
    isAttack = true;
    anim.SetBool("isAttack", true);


     yield return new WaitForSeconds(0.2f) ;
        meleeArea.enabled = true;
        yield return new WaitForSeconds(1f);
        meleeArea.enabled = false;

        isChase = true;
        isAttack = false;
        anim.SetBool("isAttack", false);

    }

Enemy 스크립트에서,

 

쫓아가는 거랑 어택하는 거랑 서로 반대라는 뜻임

다 이해할 필요는 없고

 

어택 일 때랑 

체이스 할 때랑

true false가 서로 반대 된다는 거만 알아두셈

 

 

적 자식 EnemyBullet 박스 콜라이더는 비활성화 시킴

 

+

 

-0-

영상 16 : 34 

여기까지 동영상대로 했는데,

적이 쫓아오기만 하고, 공격모션을 안취함

데미지도 안줌;;

 

 

+

 

 

돌격형 몬스터 Enemy_B 만들기

 

 

Enemy_A 에 있는 컴포들 다 복붙

EnemyBullet 도 컨트롤 D 로복붙후 Enemy_B 아래 자식으로 넣기

 

 

Animator 는 아래 자식 Mesh object 에 넣음

-> 근데 내 오브젝트는 root 위에 위치해야하기 때문에 최상단 부모 오브젝트에 넣기로 함

 

 

Enemy B 의 애니메이터는

Enemy_A 애니메이션 컨트롤러 복붙후 만든 거 이름만 Enemy_B 로 변경후 적용

 

 

 

Nav Mesh Agent 컴포넌트의 steering

speed 이속

angular speed 회전속도

acceleration 가속도

 

    public enum Type { A, B , C};
    public Type enemyType;

Enemy 스크립트에서 변수 칸 생성

 

스크립트 인스펙터에서 Enemy  A는 A로 설정 Enemy B는 B로 설정

 

+

 

-0-

여기까지 했는데 버그남

 

적이 공격 모션은 하는데,

플레이어에게 데미지 안줌

플레이어 피격 컬러 변경 (yellow) 도 안됨

-> enemy B 의 EnemyBullet 의 박스 콜라이더 컴포넌트를 활성화 하면 될듯 ?

 

 

게다가, 개인적인 문제인데,

저번에 player 모델 fbx 를 교체했더니

캐릭터 root - 자식 아래 -자식아래.... hand. r 오른 손에 연결된 무기들이 다 없어짐.

이거 저번 강의 보고 다시 재설정 해야함. 해머 (둔기 웨펀1) 이라도 다시 구현해야할듯..

 

 

+

 

enemy c 타입은 원거리 공격형임

 

 

 

.미사일 오브젝트의 하이어라키 구조임

맨 위 부모는 엠티 오브젝트로 설정

아래에 메쉬, 파티클 오브젝트를 자식으로 설정

 

 

enemy c 입에서 나오듯이 

missile 오브젝트 회전을 줌

 

missile 오브젝트에 missile 신규 스크립트 적용

 

빙글빙글 자전히며 돌아가는 로직 임

 

public class Missile : MonoBehaviour
{
   void Update()
   {
      transform.Rotate(Vector3.right * 30 * Time.deltaTime);
   }
}

스크립트 내용은 이러함

 

 

 

미사일에 파티클 될  자식 엠티 오브젝트 하나 생성

 

 

미사일에 박스 콜라이더도 추가

이게 미사일의 피격범위가 될 거임

is trigger 체크 온

 

리지드 바디도 추가

 

저번에 만든 Bullet 스크립트 넣고

데미지는 15 라고 입력

 

태그와 레이어를 EnemyBullet 이라고 설정

확인버튼 나오면 yes children ㅇ 함

 

 

여기까지 만든 missile 오브젝트를 어셋창으로 드래그해서 프리팹으로 만듦

 

 

더블 클릭해서 오픈 프리팹 창으로 들가기 

x y z 0 0 0 으로 위치 초기화

F 눌러서 화면 중앙맞춤

 

 

 

파란색 Z 축이 앞으로 되게

파티클과 오브젝트를 회전함

파티클에선 shape 창을 건드리면 됨

 

프리팹 내 맨위 엠티 부모 오브젝트의 x y z 는 0 0 0 이어야 함

그리고 로테이션도 0 0 0

(자식 오브젝트로 회전값 조정함)

스케일은 1 1 1 이어야 함

 

 

이제 맵 상에  배치된 missile 은 지워줌

 

+

 

 

 

애니메이션 컨트롤러 Enemy A 를 복붙해 Enemy C 라고 이름지은 후

Enemy C 아래 자식 오브젝트의 메쉬 오브젝트에 컴포넌트로 추가

 

 

에셋 - 오브젝트 화살표 펼쳐서 - 애니메이터 창에 애니메이션 드래그해서 교체

 

 

몬스터의 태그와 레이어를 Enemy 로 설정

확인창 나오면 yes children  클릭

단, Enemy Bullet 은  Enemy Bullet  으로 태그 및 레이어로 하기

 

 

edit - project settings - physics - 레이어 콜리젼 매트릭스

enemy 와 enemy bullet 사이 충돌 안되게 설정

 

 

이걸 설정안하면 

적이 적 총알에 맞는 불상사가 벌어짐

 

 

    public GameObject bullet;

Enemy 스크립트 내에서,

bullet 변수칸에

미사일 프리팹 적용

 

 

+

 

벽에서 붙어있을 때,

적의 enemybullet이 파괴되서 플레이어에게 영향 안주는 경우를 고려해야 하니

스크립트를 다듬어야 함

 

+

 

 

적이 빠르게 다가와 플레이어와 부딪치면

플레이어가 너무 높게 날아가 버리는 문제 해결하기

 

플레이어의 리지드 바디 값 mass 부터 damping 까지 수치를 올려 무겁게 표현하기

 

    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Enemy"))
        {
            Rigidbody rb = GetComponent<Rigidbody>();
            rb.velocity = new Vector3(rb.velocity.x, 0, rb.velocity.z); // Y축 속도 초기화
        }
    }

를 플레이어 스크립트에 넣기

 

 

+

 

 

여기까지 만든 플레이어 스크립트 풀 내용 :

더보기 클릭

 

더보기

 

using System.Collections;
using System.Collections.Generic;
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 GameObject grenadeObj;
    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 gDown; // 수류탄키
    bool rDown; // 원거리 무기 재장전

    bool iDown;

    bool sDown1;
    bool sDown2;
    bool sDown3;

    bool isJump;
    bool isDodge;
    bool isSwap;
    bool isReload;
    bool isFireReady = true;
    bool isBorder;
    bool isDamage;

    Vector3 moveVec;
    Vector3 dodgeVec;

    Rigidbody rigid;
    Animator anim;
    MeshRenderer[] meshs;
    SkinnedMeshRenderer[] skinnedMeshs;

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

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

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

    void FreezeRotation()
    {
        rigid.angularVelocity = Vector3.zero;
    }
    
    void StopToWall()
    {
     Debug.DrawRay(transform.position, transform.forward*5,
    Color.green);
        isBorder = Physics.Raycast(transform.position,
    transform.forward, 1, LayerMask.GetMask("Wall"));
    }

    void FixedUpdate()
    {
        FreezeRotation();
        Move();
        StopToWall();
    }

    void GetInput()
    {
        hAxis = Input.GetAxisRaw("Horizontal");
        vAxis = Input.GetAxisRaw("Vertical");
        wDown = Input.GetButton("Walk"); // GetButton은 꾹 누름
        jDown = Input.GetButtonDown("Jump");
        fDown = Input.GetButton("Fire1");
        gDown = Input.GetButtonDown("Fire2");
        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;

        if (isDodge)
            moveVec = dodgeVec;

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

        if (!isBorder)
          transform.position += moveVec 
         * speed * (wDown ? 0.2f : 1.0f) * Time.deltaTime;
        // Walk 걸을 때 속도 감소

        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 Grenade()
    {
       if (hasGrenades == 0)
       return; // 수류탄 개수가 0개면 발동 안됨
      if (gDown && 
       !isReload && !isSwap)
      {
         Ray ray = followCamera.ScreenPointToRay(Input.mousePosition);
         RaycastHit rayHit;
         if (Physics.Raycast(ray, out rayHit, 100))
         {
         Vector3 nextVec = rayHit.point - transform.position;
         nextVec.y = 7;

         GameObject instantGrenade
         = Instantiate(grenadeObj, transform.position,
         transform.rotation);

         Rigidbody rigidGrenade =instantGrenade.GetComponent<Rigidbody>();
          rigidGrenade.AddForce(nextVec, ForceMode.Impulse);
         rigidGrenade.AddTorque 
         (Vector3.back * 10, ForceMode.Impulse);

         hasGrenades --;
                //인덱스가 유효한지 확인
        if (hasGrenades >= 0 && hasGrenades < grenades.Length)
            { grenades[hasGrenades].SetActive(false); }

         }
        }
    }

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

        if (collision.gameObject.CompareTag("Enemy"))
        {
            Rigidbody rb = GetComponent<Rigidbody>();
            rb.velocity = new Vector3(rb.velocity.x, 0, rb.velocity.z); // Y축 속도 초기화
        }

    }



    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);
        }
        else if (other.tag == "EnemyBullet")
        {
            if (!isDamage)
            {
                Bullet enemyBullet = other.GetComponent<Bullet>();
                health -= enemyBullet.damage;

                if (other.GetComponent<Rigidbody>() != null)
                    Destroy(other.gameObject);

                StartCoroutine(OnDamage());
            }
        }
    }

    IEnumerator OnDamage()
    {
        isDamage = true;
        foreach (SkinnedMeshRenderer mesh in skinnedMeshs)
        {
            mesh.material.color = Color.yellow;
        }
        yield return new WaitForSeconds(1f);
        isDamage = false;
        foreach (SkinnedMeshRenderer mesh in skinnedMeshs)
        {
            mesh.material.color = Color.white;
        }

    }

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

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

 

 

 

여기까지 만든 Enemy 스크립트 풀 내용 :

더보기 클릭

더보기

 

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Animations;
using UnityEngine.UIElements;

public class Enemy : MonoBehaviour
{

    public enum Type { A, B , C};
    public Type enemyType;
    public int maxHealth;
    public int curHealth;
    public Transform target;
    public BoxCollider meleeArea;
    public GameObject bullet;
    public bool isChase;
    public bool isAttack;


    Rigidbody rigid;
    BoxCollider boxCollider;
    Material mat;
    NavMeshAgent nav;
    Animator anim;


    void Awake()
    {
        rigid = GetComponent<Rigidbody>();
        boxCollider = GetComponent<BoxCollider>();

        SkinnedMeshRenderer skinnedMeshRenderer = GetComponentInChildren<SkinnedMeshRenderer>();
        Material[] mats = skinnedMeshRenderer.materials;
        mat = mats[0]; // ù ��° Material �Ҵ�

        nav = GetComponent<NavMeshAgent>();
        anim = GetComponentInChildren<Animator>();

        Invoke("ChaseStart", 2); // 2�� �� ChaseStart ȣ��
    }


    void ChaseStart()
{
        isChase = true;
        anim.SetBool("isWalk", true);
    }

    void Update()
    {
        if (nav.enabled)
        {
            nav.SetDestination(target.position);
            nav.isStopped = !isChase;
            Targeting();
        }
        
    }


    void FreezeVelocity()
    {
        if (isChase)
        { 
        rigid.linearVelocity = Vector3.zero;
        rigid.angularVelocity = Vector3.zero;
        }  
}

   void Targeting()
   {
        float targetRadius = 0;
        float targetRange = 0;

        switch (enemyType)
        {
            case Type.A:
                targetRadius = 0.5f;
                targetRange = 0.5f;
                break;
            case Type.B:
                targetRadius = 0.5f;
                targetRange = 1f;
                break;
            case Type.C:
                targetRadius = 0.5f;
                targetRange = 25f;
                break;

        }

        RaycastHit[] rayHits = Physics.SphereCastAll
    (transform.position,
    targetRadius, transform.forward,
    targetRange,
    LayerMask.GetMask("Player") );

        if (rayHits.Length > 0 && !isAttack)
        {
            StartCoroutine(Attack());
        }
    }

IEnumerator Attack()
   {
    isChase = false;
    isAttack = true;
    anim.SetBool("isAttack", true);

    switch (enemyType) 
{
 case Type.A:
                yield return new WaitForSeconds(0.2f);
                meleeArea.enabled = true;
                yield return new WaitForSeconds(1f);
                meleeArea.enabled = false;
                yield return new WaitForSeconds(1f);
                break;
case Type.B:
                yield return new WaitForSeconds(0.8f);
                rigid.AddForce(transform.forward * 20, ForceMode.Impulse);
                meleeArea.enabled = true;
                yield return new WaitForSeconds(0.5f);
                rigid.angularVelocity = Vector3.zero;
                meleeArea.enabled = false ;
                yield return new WaitForSeconds(2f);
                break;

case Type.C:
                yield return new WaitForSeconds(0.5f);
                GameObject instantBullet = Instantiate(bullet, transform.position, transform.rotation);

                Rigidbody rigidBullet = instantBullet.GetComponent<Rigidbody>(); rigidBullet.linearVelocity = transform.forward * 20;

                yield return new WaitForSeconds(2f);


                break;

        }

        isChase = true;
        isAttack = false;
        anim.SetBool("isAttack", false);

    }


    void FixedUpdate()
    {

        FreezeVelocity();
    }



    void OnTriggerEnter(Collider other)
    {
        if(other.tag == "Melee")
        {
            Weapon weapon = other.GetComponent<Weapon>();
            curHealth -= weapon.damage;
           Vector3 reactVec = transform.position
           - other.transform.position;
            StartCoroutine(OnDamage(reactVec, false));
        }
        else if (other.tag == "Bullet")
            {
            Bullet bullet = other.GetComponent<Bullet>();
            curHealth -= bullet.damage;
           Vector3 reactVec = transform.position
           - other.transform.position;
             Destroy(other.gameObject);
            StartCoroutine(OnDamage(reactVec, false));
        }
    }

    public void HitByGrenade(Vector3 explosionPos)
    {
    curHealth -= 100;
    Vector3 reactVec = transform.position - explosionPos;
    StartCoroutine (OnDamage(reactVec, true));
    }

    IEnumerator OnDamage(Vector3 reactVec,
    bool isGrenade)
    {
        mat.color = Color.red;
        yield return new WaitForSeconds(0.1f);

        if (curHealth > 0)
        { mat.color = Color.white; }
        else
        { 
            mat.color = Color.gray;
            gameObject.layer = 12;
            isChase = false;
            nav.enabled = false;
            anim.SetTrigger("doDie");

            if (isGrenade)
            {
                reactVec = reactVec.normalized;
                reactVec += Vector3.up * 2.2f ;

                rigid.freezeRotation = false;
                rigid.AddForce(reactVec * 4, ForceMode.Impulse);
                rigid.AddTorque(reactVec * 10, ForceMode.Impulse);

            }
            else 
            {
                reactVec = reactVec.normalized;
                reactVec += Vector3.up;
                rigid.AddForce(reactVec * 5, ForceMode.Impulse);
            }


            Destroy(gameObject, 3);
        }

    }


}

 

 

 

여기까지 만든 Missile 스크립트 풀 내용 :

더보기 클릭

더보기
using UnityEngine;


public class Missile : MonoBehaviour
{

   void Update()
   {
      transform.Rotate(Vector3.right * 30 * Time.deltaTime);
   }


}

 

여기까지 만든 Bullet 스크립트 풀 내용 :

더보기 클릭

더보기
using UnityEngine;

public class Bullet : MonoBehaviour
{

    public int damage;
    public bool isMelee;

    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "Floor")
        {
            Destroy(gameObject, 1.0f); // 부딪친후 1초 후 삭제
        }
    }
    void OnTriggerEnter(Collider other)
    { 
        if (!isMelee && other.gameObject.tag == "Wall")
        {
            Destroy(gameObject);
        }
    }
}

 

 

+

 

 

플레이어 피격시 강의에서 나온 white 가 아니라 오리지널 컬러로 되돌리는 법

 

플레이어 스크립트 : 

public class Player : MonoBehaviour
{
    SkinnedMeshRenderer[] skinnedMeshs;
    Color[] originalColors;

    void Awake()
    {
        skinnedMeshs = GetComponentsInChildren<SkinnedMeshRenderer>();
        originalColors = new Color[skinnedMeshs.Length];
        for (int i = 0; i < skinnedMeshs.Length; i++)
        {
            originalColors[i] = skinnedMeshs[i].material.color;
        }
    }

    IEnumerator OnDamage()
    {
        isDamage = true;
        foreach (SkinnedMeshRenderer mesh in skinnedMeshs)
        {
            mesh.material.color = Color.yellow;
        }
        yield return new WaitForSeconds(1f);
        isDamage = false;
        for (int i = 0; i < skinnedMeshs.Length; i++)
        {
            skinnedMeshs[i].material.color = originalColors[i]; // 원래 컬러로 되돌리기
        }
    }
}

 

반응형
Posted by 이름이 익명
:

 

스프링본 웨이트 페인팅 레퍼런스

 

 

가슴 

많이 움직 : 가슴 중앙, 가슴 아래

적게 움직 : 어깨, 겨드랑이밑, 가슴 위, 가슴 사이 

 

 

 

 

엉덩

많이 움직 : 엉덩이 중앙, 왼위와 오위

적게 움직 : 엉덩이와 이어진 허벅지

 

 

많이 움직 : 배아래

적게 움직 : 옆구리, 명치

 


 

검색 키워드

spring bone glute, breast, hair

 

 

 

EZ soft bone 무료 에셋

 


공식 유튜브 강좌
중국어긴 한데, 화면만 보고 대충 따라할 수 있음
https://www.youtube.com/@ethanzack2967/videos

공식 에셋스토어 링크, 가격 FREE
https://assetstore.unity.com/packages/tools/physics/ezsoftbone-148136
업데이트  2021년 10월 14일 에 멈췄지만
2024월 12월 urp 에서 정상 작동함

 

 

 

가슴 뼈를 만들 땐

블렌더 바닐라 정석대로 만들면 안됨

 

오토리그 프로에서 add limb 로 만들어야 함

 

 

가슴뼈 디폴트는 저렇게 두 뼈가 위로 향하게 보임

 

 

ARP 탭에서 Add Limb 버튼으로 만들어야 함

저기 리스트에 Breast 프리셋 있으니 그걸 써야 함

 

ARP 로 뼈를 만들었음 유니티 에서 하이어라키 창에서

alt 왼클릭으로 펼쳐보기로

breast 뼈들이 정상적으로 등장하는 걸 확인 가능

 

 

 

 

뼈 새로 만들거나

뼈 위치 조정할 때마다 그냥 에디트 모드 쓰면 안됨

ARP 탭에서

Edit Reference Bones < - > Match to Rig 왔다 갔다 해야함

 

( 뼈 제대로 포징 안되면 

오브젝트랑 리그 같이 선택후 skin- bind 클릭 )

 

 

생성된 뼈는

Deform 에 Assign 해야 유니티에서 적용됨

Deform 뼈 레이어가 유니티에서 사용하는 뼈임

Main, Scondary, Reference, internal 뼈는 블렌더에서만 적용되는 뼈임

 

 

단일 뼈가 아니라

2개 이상의 부모-자식 연결된 뼈 이어야 정상작동함

 

 

가슴 페어런트 연결 작업

 

secondary 에 있는 메인 가슴 본

그리고 reference 레이어 에 있는 ref 본

전부 페어런트를 edit mode 에서 정해줘야 함

 

페어런트 연결작업은 굳이 ARP 탭의 Edit Reference Bones < - > Match to Rig 왔다 갔다 할 필요는 없음

 

 


블렌더 뼈 리그 - edit mode - breast 02 의 부모가 01 이어야 함
(반대 일수도,.,?)

 

 

 


웨이트 페인팅 할 땐
뼈 breast ref 가 아니라 
breast 뼈 이어야 함

유니티에서 나오는 뼈 이름도 ref 가 아니어야 함

 

 

 



해당 뼈에 웨이트 적용됬나 보려면

뼈는 pose mode 로 놔둔 채

바디- 웨이트 모드 - 뼈 알트 왼클릭

 

오른쪽 vertex groups 에서도 해당 뼈 웨이트 확인

 

 

오브젝트 바디 선택 - weight paint 모드 에서 - c_breast_02.r (뼈 이름 그대로)

라고 이름지으면
자동으로 해당 뼈로 웨이트 연결해줌

 

 



웨이트 페인팅 시 뼈가 안보여서 불편하면
viewport display show shapes 체크해제

 

 

 

힙 1의 웨이트 페인팅 예씨

힙 2 의 웨이트 페인팅 예시

 

 

블렌더에서 - spring bones , spring 체크 온 하면

블렌더 내에서 스프링 본 테스트 가능

 

근데 유니티랑 블렌더렝 별개임

저거 설정해도 유니티에서는 적용 X

스프링본 따로 해줘야 함

 

 

 

EZ Soft Bone 메인 스크립트 컴포넌트 는

위쪽 오브젝트 인스펙터 창에 적용함

 

Player 오브젝트에 넣어도 되고

root 오브젝트에 넣어도 됨

spine 02 x 오브젝트에 넣어도 됨

 

 



유니티에서 SBMAT : 마테리알이라 이름지었지만, 또잉또잉 강도 설정값임

 


SBMAT프리셋 만들고 싶음 에셋 복붙후 조절하면 됨
경로는 \Assets\EZhex1991\EZSoftBone\Tests\Presets

 

 

 

가슴에 빨간 선이 보여야

가슴 모핑이 제대로 적용되었다는 말임

 

 

 

테스트할 땐 마테리알  0 0 0 0 으로 함

이게 최대 또잉또잉값임

이렇게 해서 또잉 또잉 잘 되나 보기

 

 

gravity 옵션은

y 는 전부 -0.5 정도 주면 좋음

 

z 에 -0.2 주면 안 쳐지고 탱글 탱글해짐

 

스티프니스를 낮춰 강하게 한 결과값

 

 

 

스티프니스 높여서 안 흔들리게 한 결과값

 



지금은 어색하긴한데
블렌더에서 breast 01 , breast 02 페어런트 순서 변경하거나
뼈의 위치 값 조정하면 더 자연스러워질거임

 

 

 

 

 


시행 착오 및 오류 모음

 

 

블렌더에서 만든 커스텀 본이

유니티 상에서 하이어라키 메뉴에 없음

 

ARP (블렌더 오토리그 프로 애드온) 쓰면 메뉴얼 대로 뼈 만들면 안됨

 

ref 본과 기존 본 둘다 가지는 체계이기 때문에 arp 자체의 뼈 생성 구조가 따로 있기 때문임

 

 

매뉴얼대로 만든 butt bone L R 두개를 지우고 난 후

arp 메뉴에서 add limb 에서 다시 새로 생성해야 함

 

 

+

 


유니티 하이어라키 맨아래
c breast 자식 아래제일속 칸 에다가 ezsoftbone 인스펙터 적용
: ㄴㄴ 안움직임

브레스트 바로 윗단계인 spine 02 x 에 적용
: ㅇㅇ? 되긴한데 목 숄더까지 적용되버림

 


정석은
상단 부모 오브젝트에게 ez soft bone 스크립트 적용,
인스펙터 창 root 에 자식 오브젝트뼈인 머리카락뼈를 입력해야 하는 거임.

 

 


spine 02 를 루트로 함 : ㅇㅋ 몸 전체가 흔들림

neck x 를 루트로 함 : ㅇㅋ 목만 흔들림

c_breast 01 , 02 를 루트로 함 ㄴㄴ 안흔들림
(부모 연결이 없는 단일 뼈라 움직임이 없는 거임)

 

블렌더에서 : breast 01 을 02 에게 부모 연결
(기본 부모는 spine 02 x 로 연결되어 있었음 )
성공!

 

 

+

 

질답에서 얻은 헤어 본 팁

 

 add limb에서 tail 본을 가져다가 머리카락에 쓰셔도 됩니다.

 

혹시 머리카락을 자주 교체하실거면 오토리그 프로의 리그와 분리해서

따로 머리카락 아마추어를 만든 후,

오토리그 프로의 리그와 페런츠하는 방법이 있습니다.

 

+

 

ARP 에서 커스텀 본 매뉴얼 대로 만들기

(엉덩이 뼈 추가하는 하나의 방법이 될거임)

 

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

 

 

 

+

 

드라이버 팔근육 표현

유니티에서 되는지는 안해봤음  

 

https://www.godhasdone.com/31

 

블렌더 강좌 2.90 - 드라이버 - 팔 근육 표현

팔을 굽히면 알통 근육이 부풀어 오르는 작업을 하겠습니다. 먼저 믹사모 사이트에서 무료 캐릭터 중에 근육질 캐릭터 Ortiz 를 다운로드 합니다. 캐릭터 기준으로 왼쪽 팔만 작업 하겠습니다. 일

www.godhasdone.com

 

+

반응형
Posted by 이름이 익명
:

 

 

카페알바 디자인외주 상하차 등 여러 일 해봄

'일' 이라는 거엔 공통점이 있음

 

 

일을 하면서 알아야 하는 토막지식은 300개가 넘는다

그 중 선배들에게서 배울 수 있는건 100가지.

그 중 인터넷에 떠도는 정보는 50가지.

그 중 내가 직접 체험해야하는건 150가지.

 

 

욕 겨자먹기로 처 먹으면서 1년 참고 다니는 게 답임

내가 남들보다 더 많이 아는 게 되버림

 

 

 

+

 

 

여러 사람들을 보는 관리자들은 눈이 높아질 수 밖에 없다

 

경력있는 상위 10%에게만 내일도 나와주겠냐고 제안함

 

현실상 평범한 50/100점 짜리에게도 일거리를 제안해주는게 옳은데,

그렇게는 안함

평범한 사람은 뭔가 게을러보이기 때문에 아무도 안뽑으려 함

 

가뜩이나 저출산, 나태무능MZ 때문에 일손부족이라

평범인도 뽑아야 하는데

관리자들은 눈이 높아 경력직만 뽑으려 함

 

실업과 일손부족이 둘 다 공존하는 나쁜 상태임

 

 

+

 

같은 직종 주7일 금지법

이런 이상한 법때문에

투잡을 여기 따로 저기 따로 나눠서 일하려는 사람이 많음

 

상하차 알바에서도 투잡 뛰는 사람이 많음

 

몸 쓰는 일은 주말에 한두번 나오고

머리 쓰는 일은 평일 주5일 하는 사람들 많음

 

몸 쓰는 일은 회복이 일주일 걸리기 때문에 연달아 하긴 힘듦

 

 

+

 

 

상하차 알바 뛰는 투잡러 중엔 능력자도 간간히 보인다

 

영어 가능해서 해외여행 자주 가는 사람

사업 벌려놔서 월 500+~1000+ 이상 버는 사람

회사 정년퇴임하고 용돈 벌러 오는 사람

등등 은수저 계층이 종종 보인다

 

이들과 친해져 사업 마케팅(지연 학연 군연) 을 시도해도 좋다

 

 

디시에선 '상하차는 못 배워먹는 고졸 새키들만 많다'

는데 반은 맞음

근데 반은 틀림

걸러야 할 사람도 있지만

친구해야 할 ㅅㅌㅊ 인재도 있음

 

 

 

 

+

 

자취하는 서울 사는 흙수저 기준

주 며칠 일해야 하냐 기준 표

 

주 4일 =  통장 마이너스

주 5일 + 주휴 = 월 용돈 10 언저리

주 6일 + 주휴 = 월 50 저축 가능

주 7일 + 주휴 = 월 100 저축 가능

 

 

+

 

 

엘리트 직원 중엔

3개월~12개월 만에 다른 더 좋은 곳으로 가는 사람들 많음

 

 

직원 평가표

 

 

저급 : 말귀도 못 알아듣고 괘씸할 정도로 느린 사람

 

평타 : 딱 평균정도로 하는 적당히 느린 신입 

 

상위 30% : 열심히 하려하는 신입

 

상위 1% : 비슷한 업무 경력 있는 사람

 

상위 0.1% : 비슷한 업무 경력 있고 집도 가까운 사람

 

상위 0.01% : 비슷한 업무 경력 있고 집도 가깝고 이직 생각도 없는 사람

= 관리직으로 업그레이드 가능할 인재

 

 

+

 

 

상하차 알바는 주로 하긴 그렇고

언젠간 전직 해야함

 

 

인간사료 먹어가며 저축해서 다른 좋은 직장에 취직하기

OR

오더피커 지게차 같은 더 상위 직업으로 전직하기

 

 

나는 돈 모아서 노트북 좋은 거 산 후

게임 개발자나 해서 후원이나 받으려고 함

 

 

+

 

상하차에서도 

 

스캔 찍기, 오도착 미분류 파손상품 정리, 비닐pb상품 분류, 토트박스 운반, 빈 RT 운반, 카트 운반 등등

 

힘 안 들이고도 할 수 있는 일이 많음

이 일은 노년층, 여성이 주로 함

 

 

여성 우대가 너무 심한거 아니냐?

왜 남성은 어려운 일만 시키냐?

남성하대하냐?

하는데

 

반은 맞고 반은 틀림

 

어차피 사람은 부족하고 일거리는 많음

여자에게라도 쉬운 일 몰빵해야함

 

 

 

+

 

그리고

곧 2025년 이후 로봇노동 사회가 되니끼

여성 노동자냐 남성 노동자냐 의미 없어짐

 

 

+

 

토트박스

롤테이너(알티)

엘카트 (유카트)

빠레트

PB (단프라)

이사박스가 뭔지

 

파지가 뭔지

 

신입은 이거만 외워서 가도 좋음

 

 

 

+

 

 

신입중엔 수준 낮은 사람 엄청 많음

되돌아보니

그에 비해 난 신입 때 너무 열심히 하려했나 싶음

내가 너무 자기 비하를 했구나 싶었음

 

 

 

 

+

 

비슷한 봉사활동, 사업경험, 알바경험 있으면

취직시 경쟁력 100 대 1 정도는 우습게 통과 가능함

 

 

+

 

세상은

시끄러운 E F 충들이 지배하는 것처럼 보이지만,

 

 

i t 계열들이 

조용하게  사회에 깊숙히

그리고 조용히 뿌리 잡아있음

 

 

 

내가 mbti T 계열이면 과감히 취직 & 협업

용기있게 해도 됨

 

T 는 T 정말로 환영함

T 사장은 T 신입을 정말, 진.심. 으로 환영함

 

 

+

 

 

 

 

 

반응형
Posted by 이름이 익명
:

 

블렌더에선 잘보이는데 유니티에서 fbx로 불러오면 다 깨짐

 

그냥 그대로 fbx 추출하면 엉망됨

 

블렌더에서, flip normal 하고 난 후 모든 마테리알을 surface - backface culling 체크 세개 on 해줘야 함

 

커스텀 노드가 적용된 마테리알은 유니티 상에서 그대로 불러올 시 적용 안 됨

따로 추가작업 해줘야 함

 

 

 

이렇게 눈 만 따로 텍스쳐 있을 때,

 

유니티 안에서 구현하는 법 기초

 

단, 단순 2D 표현이라 명암 반영 안됨.

그건 다른 강의 따로 들어야 함

 

 

 

얼굴 될 텍스쳐의 png 파일은 유니티에서 불러온 후

Alpha is Transparency 체크 온

 

 

 

 

새로운 셰이더 그래프 만들기

Create - Shader Graph - URP - Unit Shader Graph

 

 

open shader editor 

 

왼위 + 눌러서 

texture 2d 를 두 개 만들어줌

 

하나는 기본 전체 그림인 Base, 

하나는 얼굴을 표현한 Layer 1

 

 

 

두 개를 바탕에 드래그

 

 

 

Create Node - Sample Texture 2D 만듦

 

 

Base, 

Layer 1 

각각 하나씩 Sample Texture 2D 를 이어줌

 

 

 

Create Node - Blend 도 만들어서 연결해줌

 

Layer1 표정의 Alpha 는 Opacity 에 연결

 

 

블렌드 타입은 오버라이트

 

 

Fragment base color 에 마지막 줄을 연결

 

 

 

이게 뼈대 완성임

 

 

 

끝 지점에 replace color, contrast, hue, channel mixer 등의 양념칠 가능

 

좀 귀찮긴 하지만

변경할 때마다 저장을 눌러줘야 결과값 볼 수 있음

 

 

마테리엘에 만든 셰이더 그래프 할당

 

 

오브젝트 우측 인스펙터 창 , shader graph 펼쳐보기 누르면 베이스 맵, 그 위에 덮어 쓸 layer1 (표정 될 png 텍스쳐) 지정 가능

 

 

아래 오브젝트 - materials - remap 설정칸에 해당 셰이더 그래프를 할당 

 

+

 

smooth shading 하는 법

 

 

어셋 폴더에서 오브젝트 클릭 - 오위 상단 model - 아래 normals calculate -

smooth angle 30도~ 60도~ 90도~ 180 도 원하는 만큼 지정

낮은 값일수록 딱딱해져보임

 

 

반응형
Posted by 이름이 익명
:

 

https://www.youtube.com/watch?v=FBY_cmtCNHw&ab_channel=%EA%B3%A8%EB%93%9C%EB%A9%94%ED%83%88

 

 

적 프리팹을 맵에 놓기

 

 

적(부모 최상단하이어라키)에 Rigidbody, Box Collider, Enemy스크립트 적용

 

프리즈 로테이션 x , z 할당

박스 콜라이더는 메쉬에 비슷하게 조정

에너미 스크립트의 헬스 값은 둘다 50정도로

 

 

Tag 와 Layer 를 Enemy 로 설정

레이어 설정시 확인 메시지 뜨면 맨왼쪽 Yes Children 설정

 

Enemy 태그 없으면 만들기

 

 

 

 

 

Enemy 스크립트를 비주얼 스튜디오에서 킨다

 

 

적 프리팹은

하이어라키 창 자식의 자식 속에 마테리알이 있기 때문에

 

    mat = GetComponent<MeshRenderer>().material;

이게 아니라

 

    mat = GetComponentInChildren<MeshRenderer>().material;

이렇게 되어야 함

 

 

 

 저번에 만든 테스트 에너미는 맵 상에서 삭제

 

 

+

 

a i  만들기

 

 

컴포넌트에 Nav Mesh Agent 추

 

 

Enemy 스크립트 맨 위

 

using UnityEngine; 

밑에

using UnityEngine.AI;

한줄 추가해야함

 

 

    void Update()
    {
        nav.SetDestination(target.position);
    }

실시간으로 타겟의 위치 를 목표로 삼는다는 뜻

 

 

 

Enemy  스크립트에 타겟 지정을 Player로 할당

 

(  public Transform target; 을 만들어서 생긴 변수 칸임 )

 

 

상단 window - AI - Navigation 창

 

 

!!! 막힘

 

여기까지 강의가 6:45

 

 

강의 에선 Bake 버튼이 있지만, 최신버전에선 Bake 버전이 없음

 

아마 floor 바닥 오브젝트에 NavMeshSurface  컴포넌트를 추가해줘야 하는 듯함

 

 

바닥 오브젝트 선택 후 - NavMeshSurface 컴포추 - 아래 Bake 클릭후 청록색 필드 펼쳐지면 성공

 

 

 

Mesh Renderer 가 아니라

텍스쳐를 입힌 Skinned Mesh Renderer 면

오른쪽 스크립트에서도 SkinnedMeshRenderer  라고 써줘야함

 

 

한 오브젝트의 마테리알이 여러개면

mat = 어쩌구

가 아니라

mats = 어쩌구 가 되어야 함

 

 

그리고 맵 상에 TestEnemy 없어야함 얘네들은 마테리알 없어서 마테리알null 오류 남

그래서 지워버림

 

 

자세한건 챗봇이 써줌

 void Awake()
 {
     rigid = GetComponent<Rigidbody>();
     boxCollider = GetComponent<BoxCollider>();

     SkinnedMeshRenderer skinnedMeshRenderer = GetComponentInChildren<SkinnedMeshRenderer>();
     Material[] mats = skinnedMeshRenderer.materials;
     mat = mats[0]; // 첫 번째 Material 할당

     nav = GetComponent<NavMeshAgent>();
     anim = GetComponentInChildren<Animator>();

     Invoke("ChaseStart", 2); // 2초 뒤 ChaseStart 호출
 }

 

위 스크립트는

마테리알 어쩌구 오류났던 거 수정한 부분임

보이드 어웨이크 부문 일부임

 

+

 

 

저번에 만든 벽을 복붙해서 기둥을 만들어보자

 

 

지형이 바뀌었으면 Bake 다시 누르기

 

+

 

        rigid.velocity = Vector3.zero;

  rigid.velocity 하면 취소선 오류남

        rigid.linearVelocity = Vector3.zero;

rigid.linearVelocity 로 변형해야 함

 

 

+

 

애니메이션 지정하기

 

 

create - animation -  animator controller 새로 만들기

 

이름은 AC_Enemy_A

 

 

 

root 뼈 위 오브젝트에게 AC_Enemy_A 컴포 할당 ?

 

 

애니메이션 컨트롤러 두번 클릭해서

Animator 창 들어감

 

 

 

아래 프리팹 을 열어보면 4 개의 애니메이션이 속해있는 걸 볼 수 있

Attak , Die , Idle , Walk

 

 

4개의 애니메이션을 애니메이터 창으로 다 드래그 해 오기

 

idle (사진에선 a pose) 를 우클 - default state 로 만들기

 

 

idle 과 walk 는 우클 make transition 으로 연결

 

 

어택은 워크와 쌍방향 연결

 

 

Any State 에서 Die 로 트랜지션 일방향 연결

 

 

좌상단 파라메터 세 개 생성

bool - isWalk

bool - isAttack

Trigger - doDie

 

 

Idle, Walk, Attack 의 Has Exit Time 은 다 체크 해제

 

 

die 의 컨디션은 doDie

 

 

 

 

워크 어택 트루 팔스는 위처럼 설정

 

 

+

 

 

    if (curHealth > 0)
    { mat.color = Color.white; }
    else
    { 
        mat.color = Color.gray;
        gameObject.layer = 12;
        isChase = false;
        nav.enabled = false;
        anim.SetTrigger("doDie");
    }

 

체력이 0 이 되면

회색빛이 되고

레이어태그가 인덱스넘버12인 EnemyDead가 되고

플레이어 쫓는걸 그만두고

자동이동 ai 를 끄고

죽는 모션을 재생한다는 뜻 

 

 

 

 

+

 

지금껏 만든 Enemy 스크립트 내용 : 

 

 

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Animations;
using UnityEngine.UIElements;

public class Enemy : MonoBehaviour
{

    public int maxHealth;
    public int curHealth;
    public Transform target;
    public bool isChase;

    Material mat;
    Rigidbody rigid;
    BoxCollider boxCollider;
    NavMeshAgent nav;
    Animator anim;


    void Awake()
    {
        rigid = GetComponent<Rigidbody>();
        boxCollider = GetComponent<BoxCollider>();

        SkinnedMeshRenderer skinnedMeshRenderer = GetComponentInChildren<SkinnedMeshRenderer>();
        Material[] mats = skinnedMeshRenderer.materials;
        mat = mats[0]; // 첫 번째 Material 할당

        nav = GetComponent<NavMeshAgent>();
        anim = GetComponentInChildren<Animator>();

        Invoke("ChaseStart", 2); // 2초 뒤 ChaseStart 호출
    }


    void ChaseStart()
{
        isChase = true;
        anim.SetBool("isWalk", true);
    }

    void Update()
    {
        if (isChase && nav.isOnNavMesh)
        {
            nav.SetDestination(target.position);
        }
    }


    void FreezeVelocity()
    {
        if (isChase)
        { 
        rigid.linearVelocity = Vector3.zero;
        rigid.angularVelocity = Vector3.zero;
        }  
}


    void FixedUpdate()
    {
        FreezeVelocity();
    }

    void OnTriggerEnter(Collider other)
    {
        if(other.tag == "Melee")
        {
            Weapon weapon = other.GetComponent<Weapon>();
            curHealth -= weapon.damage;
           Vector3 reactVec = transform.position
           - other.transform.position;
            StartCoroutine(OnDamage(reactVec, false));
        }
        else if (other.tag == "Bullet")
            {
            Bullet bullet = other.GetComponent<Bullet>();
            curHealth -= bullet.damage;
           Vector3 reactVec = transform.position
           - other.transform.position;
             Destroy(other.gameObject);
            StartCoroutine(OnDamage(reactVec, false));
        }
    }

    public void HitByGrenade(Vector3 explosionPos)
    {
    curHealth -= 100;
    Vector3 reactVec = transform.position - explosionPos;
    StartCoroutine (OnDamage(reactVec, true));
    }

    IEnumerator OnDamage(Vector3 reactVec,
    bool isGrenade)
    {
        mat.color = Color.red;
        yield return new WaitForSeconds(0.1f);

        if (curHealth > 0)
        { mat.color = Color.white; }
        else
        { 
            mat.color = Color.gray;
            gameObject.layer = 12;
            isChase = false;
            nav.enabled = false;
            anim.SetTrigger("doDie");

            if (isGrenade)
            {
                reactVec = reactVec.normalized;
                reactVec += Vector3.up * 2.2f ;

                rigid.freezeRotation = false;
                rigid.AddForce(reactVec * 4, ForceMode.Impulse);
                rigid.AddTorque(reactVec * 10, ForceMode.Impulse);

            }
            else 
            {
                reactVec = reactVec.normalized;
                reactVec += Vector3.up;
                rigid.AddForce(reactVec * 5, ForceMode.Impulse);
            }


            Destroy(gameObject, 3);
        }

    }


}

 

 

+

 

 

애니메이터 창에서 만약

motion 을 다른 캐릭터 애니메이션을 써 보면?


기본 idle 애니 사용시 다른 캐릭의 idle 애니 사용해봄
  뼈 구조가 같은 오토리그 프로라
모션 공유는 가능

 

 

 

반응형
Posted by 이름이 익명
:

 

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

 

 

 

이번 강의의 플레이어의 마지막 액션 될 수류탄 던지기를 구현해보려 함

수류탄은 필살기 느낌임

 

 

 

아래 에셋에서 수류탄 프리팹을 하이어라키 창으로 드래그해서

수류탄 하나 생성

 

이름은 Throw Grenade

 

미리 준비된 파티클 프리팹을 Throw Grenade 아래 자식오브젝트로 등록

 

(없음 파티클 새로 만들어야 함)

 

수류탄이 터지는 파티클임

loop 에 체크 해제된 파티클임

 

 

파티클에 꼬리를 넣고싶으면 trails 킨 후 

아래 렌더러에 trail material 수정

 

 

리지드 바디와 스피어 콜라이더 추

 

통통 튀어야 하니

피직스 마테리알 추

 

 

 

피직스 마테리알을 스피어 콜라이더에 적용

 

수류탄의 자식 중 메쉬 오브젝트에 trail renderer 추가

 

throw grenade 부모 오브젝트를 layer 설정

playerbullet, yes change children 클릭

 

 

다 만들엇음

throw grenade를 플팹으로 저장

 

프리팹 등록후 position x y z 0 0 0 로 설정

 

프리팹 등록했으면 하이어라키 창에서 지움

 

 

    public GameObject grenadeObj;

플레이어 스크립트 위쪽에 저 문구 추

 

프리팹으로 만든 수류탄을

인스펙터 창에서 지정

 

    bool gDown; // 수류탄키

 

수류탄 버튼을 위한 키보드 입력 만들거임

 

    void GetInput()
    {
        gDown = Input.GetButtonDown("Fire2");
    }
    void Update()
    {
        Grenade();
    }

 

 

edit - project settings - input manager - fire2 키는 mouse 1 , 마우스 우클릭임

 

이 키가 수류탄 던지기가 될 고임

 

 

 

 

 

 

                GameObject instantGrenade 
= Instantiate (grenadeObj, transform.position,
transform.rotation)

instantiate 뜻 :

오브젝트를 파티클처럼

임시로 하나 생성시킨 후 제거하겠다는 뜻

 

 

    void Grenade()
    {
       if (hasGrenades == 0)
       return; // 수류탄 개수가 0개면 발동 안됨
      if (gDown && 
       !isReload && !isSwap)
      {
         Ray ray = followCamera.ScreenPointToRay(Input.mousePosition);
         RaycastHit rayHit;
         if (Physics.Raycast(ray, out rayHit, 100))
         {
         Vector3 nextVec = rayHit.point - transform.position;
         nextVec.y = 7;

         GameObject instantGrenade
         = Instantiate(grenadeObj, transform.position,
         transform.rotation);

         Rigidbody rigidGrenade =instantGrenade.GetComponent<Rigidbody>();
          rigidGrenade.AddForce(nextVec, ForceMode.Impulse);
         rigidGrenade.AddTorque 
         (Vector3.back * 10, ForceMode.Impulse);

         hasGrenades --;
                //인덱스가 유효한지 확인
        if (hasGrenades >= 0 && hasGrenades < grenades.Length) { grenades[hasGrenades].SetActive(false); }

            }
        }
    }

수류탄 투척을 스크립트화 시킨 것

이건 플레이어 스크립트 내부에 적은 거임

 

 

 

         hasGrenades --;
         grenades[hasGrenades].SetActive(false);

 

강의에선 위 처럼 하라 했지만, 빨간 버그걸림

2         hasGrenades --;
                //인덱스가 유효한지 확인
        if (hasGrenades >= 0 && hasGrenades < grenades.Length)
            { grenades[hasGrenades].SetActive(false); }

 

챗봇에게 수정하라 했더니 이렇게 수정함

 

 

 

 

수류탄 프리팹 - open - 자식 오브젝트 중 터지는 파티클은 미리 비활성화 시키기

 

 

 

Grenade 라는 이름의 새 스크립트 생성

 

 

throw grenade 프리팹에 스크립트 적용

 

인스펙터 변수 세 개 생성

 

 

Mesh Obj 는 자식 메쉬 를

Effect Obj 는 자식 폭발 파티클 을

Rigid 는 부모 오브젝트 를 지정

 

 

rigid.Sleep(); / /rigid.velocity=Vector3.zero; 대체코드

rigid velocity vector3 zero 코드는 취소선이 생기는 오류가 있으니

rigid sleep 을 사용함

 

 

+

 

공 모양의 범위 레이 케스트를 활용하여 범위 공격을 구현

 

 

SphereCast

SphereCastAll 

가 있는데 , 

SphereCast  하나만

SphereCastAll   범위내 모두 라는 뜻

 

 

RaycastHit[] rayHits = Physics.SphereCastAll
(transform.position, 15,
Vector3.up, 0f, LayerMask.GetMask("Enemy"));

 

RaycastHit[] rayHits = Physics.SphereCastAll
(시작 위치, 반지름크기,
쏘는방향 여기선 크게 상관없음, 범위 오프셋 거리, 레이어 한정 마스크);

레이 케스트 피직스 엔 5가지 항목이 차례로 있음

시작 위치, 반지름 크기, 쏘는 방향, 범위 오프셋, 물리판정 받는 레이어 지정

 

 

 

    public void HitByGrenade(Vector3 explosionPos)
    {
    curHealth -= 100;
    Vector3 reactVec = transform.position - explosionPos; // 오브젝트 위치와 폭발위치 고려한 반응 계산
    StartCoroutine (OnDamage(reactVec));
    }

Enemy 스크립트에 위 문구 추가함

 

 

 

 

충돌시 뱅그르르 돌아가는 게 어색해서 프리즈 x , z 축 했었음

이걸 풀려면

                rigid.freezeRotation = false;

스크립트에서 이렇게 써주면 x y z 프리즈 로테이션이 풀림

 

 

 

    Destroy(gameObject, 5);

5초 후 자기 자신을 삭제하겠다는 뜻

수류탄은 후폭발 파티클 이펙트 시간도 기다려야 해서 5 초를 설정함

 

 

 

 폭발하며 범위 공격을 가하는 수류탄 구현 완료

 

 

지금까지 

Grenade 스크립트 풀 : 

 

public class Grenade : MonoBehaviour
{

   public GameObject meshObj;
   public GameObject effectObj;
   public Rigidbody rigid;

    void Start()
    {
        StartCoroutine(Explosion());

    }

    IEnumerator Explosion()
    {
    yield return new WaitForSeconds(1f);
    rigid.Sleep(); //rigid.velocity=Vector3.zero; 대체코드
    rigid.angularVelocity = Vector3.zero;
    meshObj.SetActive(false);

    effectObj.SetActive(true);

        RaycastHit[] rayHits = Physics.SphereCastAll
    (transform.position, 3,
    Vector3.up, 0f, LayerMask.GetMask("Enemy"));

      foreach (RaycastHit hitObj in rayHits)
     {
     hitObj.transform.GetComponent<Enemy>().HitByGrenade(transform.position);
     }
    Destroy(gameObject, 5);
    }

}

 

 

Enemy 스크립트 풀 :

 

public class Enemy : MonoBehaviour
{

    public int maxHealth;
    public int curHealth;
    Material mat;

    Rigidbody rigid;
    BoxCollider boxCollider;

    void Awake()
    {
    rigid = GetComponent<Rigidbody>();
    boxCollider = GetComponent<BoxCollider>();
    mat = GetComponent<MeshRenderer>().material;
    }

    void OnTriggerEnter(Collider other)
    {
        if(other.tag == "Melee")
        {
            Weapon weapon = other.GetComponent<Weapon>();
            curHealth -= weapon.damage;
           Vector3 reactVec = transform.position
           - other.transform.position;
            StartCoroutine(OnDamage(reactVec, false));
        }
        else if (other.tag == "Bullet")
            {
            Bullet bullet = other.GetComponent<Bullet>();
            curHealth -= bullet.damage;
           Vector3 reactVec = transform.position
           - other.transform.position;
             Destroy(other.gameObject);
            StartCoroutine(OnDamage(reactVec, false));
        }
    }

    public void HitByGrenade(Vector3 explosionPos)
    {
    curHealth -= 100;
    Vector3 reactVec = transform.position - explosionPos;
    StartCoroutine (OnDamage(reactVec, true));
    }

    IEnumerator OnDamage(Vector3 reactVec,
    bool isGrenade)
    {
        mat.color = Color.red;
        yield return new WaitForSeconds(0.1f);

        if (curHealth > 0)
        { mat.color = Color.white; }
        else
        { 
            mat.color = Color.gray;
            gameObject.layer = 12;

            if (isGrenade)
            {
                reactVec = reactVec.normalized;
                reactVec += Vector3.up * 3 ;

                rigid.freezeRotation = false;
                rigid.AddForce(reactVec * 5, ForceMode.Impulse);
                rigid.AddTorque(reactVec * 15, ForceMode.Impulse);

            }
            else 
            {
                reactVec = reactVec.normalized;
                reactVec += Vector3.up;
                rigid.AddForce(reactVec * 5, ForceMode.Impulse);
            }


            Destroy(gameObject, 2);
        }

    }


}

 

 

플레이어 스크립트 풀 :

 

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 GameObject grenadeObj;
    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 gDown; // 수류탄키
    bool rDown; // 원거리 무기 재장전

    bool iDown;

    bool sDown1;
    bool sDown2;
    bool sDown3;

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

    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();
        Grenade();
        Attack();
        Reload();
        Dodge();
        Swap();
        Interaction(); // 키보드 e 키 눌르면 무기 입수
    }

    void FreezeRotation()
    {
        rigid.angularVelocity = Vector3.zero;
    }
    
    void StopToWall()
    {
     Debug.DrawRay(transform.position, transform.forward*5,
    Color.green);
        isBorder = Physics.Raycast(transform.position,
    transform.forward, 1, LayerMask.GetMask("Wall"));
    }

    void FixedUpdate()
    {
        FreezeRotation();
        Move();
        StopToWall();
    }

    void GetInput()
    {
        hAxis = Input.GetAxisRaw("Horizontal");
        vAxis = Input.GetAxisRaw("Vertical");
        wDown = Input.GetButton("Walk"); // GetButton은 꾹 누름
        jDown = Input.GetButtonDown("Jump");
        fDown = Input.GetButton("Fire1");
        gDown = Input.GetButtonDown("Fire2");
        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;

        if (isDodge)
            moveVec = dodgeVec;

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

        if (!isBorder)
          transform.position += moveVec 
         * speed * (wDown ? 0.2f : 1.0f) * Time.deltaTime;
        // Walk 걸을 때 속도 감소

        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 Grenade()
    {
       if (hasGrenades == 0)
       return; // 수류탄 개수가 0개면 발동 안됨
      if (gDown && 
       !isReload && !isSwap)
      {
         Ray ray = followCamera.ScreenPointToRay(Input.mousePosition);
         RaycastHit rayHit;
         if (Physics.Raycast(ray, out rayHit, 100))
         {
         Vector3 nextVec = rayHit.point - transform.position;
         nextVec.y = 7;

         GameObject instantGrenade
         = Instantiate(grenadeObj, transform.position,
         transform.rotation);

         Rigidbody rigidGrenade =instantGrenade.GetComponent<Rigidbody>();
          rigidGrenade.AddForce(nextVec, ForceMode.Impulse);
         rigidGrenade.AddTorque 
         (Vector3.back * 10, ForceMode.Impulse);

         hasGrenades --;
                //인덱스가 유효한지 확인
        if (hasGrenades >= 0 && hasGrenades < grenades.Length)
            { grenades[hasGrenades].SetActive(false); }

         }
        }
    }

    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;
    }
}
반응형
Posted by 이름이 익명
: