유니티로 3D 다뤄본적이 별로 없기에 유튜브 강의 하나를 그대로 따라하려 한다.
https://www.youtube.com/watch?v=WkMM7Uu2AoA&list=PLO-mt5Iu5TeYkrBzWKuTCl6IUm_bA6BKy
유니티 강의를 많이 올려주시는 <골드메탈> 유튜브분의 영상인데 4년전 영상이지만
따라 만들어보면 실력이 많이 늘것 같아 시도해보려한다.
추가 및 수정한 스크립트
Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField] float speed; //캐릭터의 속도
[SerializeField] float jumpPower; //캐릭터의 점프파워
float hAxis; //수평선 축 value값
float vAxis; //수직선 축 value값
bool wDown = false; //Input Manager에서 추가한 Walk(Left Shift)의 키 유무 체크
bool jDown = false; //점프 키 유무 체크
bool isJump = false; //현재 점프 중인지 체크 유무
bool isDodge = false; //현재 회피 중인지 체크 유무
Vector3 moveVec; // hAxis와 vAxis 값을 받아와 3차원 벡터값(방향벡터) 변수
// 회피시 캐릭터 방향전환을 막기위해 생성
Vector3 dodgeVec; //회피할때 적용되는 방향벡터
Animator anim;
Rigidbody rigid;
private void Awake()
{
rigid = GetComponent<Rigidbody>();
anim = GetComponentInChildren<Animator>();
}
private void Update()
{
GetInput();
Move();
Turn();
Jump();
Dodge();
}
private void GetInput()
{
//해당 프로젝트 InputManager를 이용하여 만듬
//GetAxisisRaw() : Axis 값을 정수로 반환하는 함수
//GetButton() : 꾹 누르고 있을때만 호출
hAxis = Input.GetAxisRaw("Horizontal");
vAxis = Input.GetAxisRaw("Vertical");
wDown = Input.GetButton("Walk");
jDown = Input.GetButtonDown("Jump");
}
private void Move()
{
//정규화를 하는 이유? : 대각선 이동시 빨라지기 때문에 크기(속도)를 1로 만들어서 속도를 똑같이 만들어줘야한다.
//쉽게 말하면 방향값이 1로 보정되는 벡터라고 생각하면 된다.
//왜 빨라짐? : hAxis = 1 , vAxis = 1 인경우 대각성 이동시 피타고라스의 법칙으로 root(1^2+1^2) = root(2) = 1.141... 때문이다.
moveVec = new Vector3(hAxis, 0, vAxis).normalized;
//회피 동작 중인경우 회피 방향벡터가 대입되어 방향전환을 못하게 막기 위한 코드
if(isDodge) moveVec = dodgeVec;
//transform 이동은 가끔 물리충돌을 무시하는 경우가 발생 함
//Rigidbody 컴포넌트에서 CollisonDetection을 Continuos로 변경
//Cpu를 더 먹어 최적화는 떨어지지만 정확도는 올라가기 때문
if (!wDown) transform.position += moveVec * speed * Time.deltaTime;
else transform.position += moveVec * speed * 0.3f * Time.deltaTime;
//SetXXXX("애니메이션 이름" , bool 변수) 를 넣어 조건문처럼 사용 할 수 있다.
anim.SetBool("isRun", moveVec != Vector3.zero);
anim.SetBool("isWalk", wDown);
}
private void Turn()
{
//지정된 벡터를 향해서 회전시켜주는 함수 - 3D기능
//현재 플레이어 위치에 방향벡터를 더해서 우리가 나아가는 쪽으로 오브젝트를 회전한다는 뜻
transform.LookAt(transform.position + moveVec);
}
//점프키와 회피키가 같지만 이동키가 눌리고 있을때는 회피, 이동 안할때는 점프를 하게된다.
private void Jump()
{
//방향벡터가 0 이기에 이동 하지 않으므로 점프
if(jDown && moveVec == Vector3.zero && !isJump && !isDodge) //액션 도중에 다른 액션이 실행되지 않도록 조건 추가
{
//Addforce : rigidbody에 포함된 메서드이며 해당 오브젝트에 힘을 가해주는 함수
rigid.AddForce(Vector3.up * jumpPower,ForceMode.Impulse); // Impulse : 힘을 주는 종류 중 하나로, 즉발로 힘을 준다는 의미
anim.SetBool("isJump",true);
anim.SetTrigger("doJump");
isJump = true;
}
}
private void Dodge()
{
//방향벡터가 0 이 아니므로 이동 하는 상태이기 때문에 회피
if (jDown && moveVec != Vector3.zero && !isJump && !isDodge) //액션 도중에 다른 액션이 실행되지 않도록 조건 추가
{
dodgeVec = moveVec; //회피시 현재 이동벡터를 회피벡터에 대입한뒤 Move()함수에서 대신 사용됨
speed *= 2;
anim.SetTrigger("doDodge");
isDodge = true;
Invoke("DodgeOut",0.4f);
}
}
private void DodgeOut()//회피가 끝났을때
{
speed *= 0.5f;
isDodge = false;
}
private void OnCollisionEnter(Collision collision)
{
//충돌처리 함수로 점프 후 착지 확인
if(collision.gameObject.CompareTag("Floor"))
{
anim.SetBool("isJump", false);
isJump = false;
}
}
}
Follow.cs :: 카메라가 등록한 Target(오브젝트)를 따라다니는 기능
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//카메라가 플레이어를 따라가는 기능
public class Follow : MonoBehaviour
{
public Transform target; // 따라갈 오브젝트
public Vector3 offset; // 위치 오프셋(카메라를 배치할 위치 )
// Update is called once per frame
void Update()
{
transform.position = target.position + offset;
}
}
Item.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Item : MonoBehaviour
{
//열거형 변수 enum
public enum Type
{
Ammo, Coin, Grenade, Heart, Weapon
};
public Type type; //아이템 종류 변수
public int value; //아이템 값을 저장할 변수
}
1.정규화(Nomarlize)를 하는이유
벡터는 크기와 방향값을 둘다 가지는 단위이다.
간단히 말하면 정규화라는 것은 해당 벡터의 크기를 1로 변환하여 순수한 방향값을 얻을때 주로 사용된다.
//정규화를 하는 이유? : 대각선 이동시 빨라지기 때문에 크기(속도)를 1로 만들어서 속도를 똑같이 만들어줘야한다.
//쉽게 말하면 방향값이 1로 보정되는 벡터라고 생각하면 된다.
//왜 빨라짐? : hAxis = 1 , vAxis = 1 인경우 대각성 이동시 피타고라스의 법칙으로 root(1^2+1^2) = root(2) = 1.141... 때문이다.
moveVec = new Vector3(hAxis, 0, vAxis).normalized;
해당 코드에서 대각선으로 이동시 캐릭터가 더 빠르게 움직이게 된다.
그렇기 때문에 해당 벡터의 크기를 1로 정규화를 하면 순수한 방향벡터값을 구할 수 있다.
2. 하나의 키로 두가지 동작하기 (점프와 회피) + 회피시 방향전환 못하게 막기
bool isDodge = false; //현재 회피 중인지 체크 유무
Vector3 moveVec; // hAxis와 vAxis 값을 받아와 3차원 벡터값(방향벡터) 변수
// 회피시 캐릭터 방향전환을 막기위해 생성
Vector3 dodgeVec; //회피할때 적용되는 방향벡터
private void GetInput()
{
//해당 프로젝트 InputManager를 이용하여 만듬
//GetAxisisRaw() : Axis 값을 정수로 반환하는 함수
//GetButton() : 꾹 누르고 있을때만 호출
hAxis = Input.GetAxisRaw("Horizontal");
vAxis = Input.GetAxisRaw("Vertical");
wDown = Input.GetButton("Walk");
jDown = Input.GetButtonDown("Jump");
}
private void Move()
{
////////
moveVec = new Vector3(hAxis, 0, vAxis).normalized;
Debug.Log("정규화된 벡터 : " + moveVec);
//회피 동작 중인경우 회피 방향벡터가 대입되어 방향전환을 못하게 막기 위한 코드
if (isDodge) moveVec = dodgeVec;
///////
}
//점프키와 회피키가 같지만 이동키가 눌리고 있을때는 회피, 이동 안할때는 점프를 하게된다.
private void Jump()
{
//방향벡터가 0 이기에 이동 하지 않으므로 점프
if(jDown && moveVec == Vector3.zero && !isJump && !isDodge) //액션 도중에 다른 액션이 실행되지 않도록 조건 추가
{
//Addforce : rigidbody에 포함된 메서드이며 해당 오브젝트에 힘을 가해주는 함수
rigid.AddForce(Vector3.up * jumpPower,ForceMode.Impulse); // Impulse : 힘을 주는 종류 중 하나로, 즉발로 힘을 준다는 의미
anim.SetBool("isJump",true);
anim.SetTrigger("doJump");
isJump = true;
}
}
private void Dodge()
{
//방향벡터가 0 이 아니므로 이동 하는 상태이기 때문에 회피
if (jDown && moveVec != Vector3.zero && !isJump && !isDodge) //액션 도중에 다른 액션이 실행되지 않도록 조건 추가
{
dodgeVec = moveVec; //회피시 현재 이동벡터를 회피벡터에 대입한뒤 Move()함수에서 대신 사용됨
speed *= 2;
anim.SetTrigger("doDodge");
isDodge = true;
Invoke("DodgeOut",0.4f);
}
}
Player.cs에 있는 코드를 가져왔다.
*하나의 키로 두가지 동작하기 (점프와 회피)
해당 프로젝트는 Player의 점프 키와 회피키를 같은것을 사용하고 있다.
if로 조건문을 걸어 각 액션동작 중에 다른 동작을 하지 못하게 막아놨다.
moveVec가 0이라는 것은 GetInput() 함수에서 키 입력이 없기에 0을 리턴한것이고
즉, 이동하지 않았다는 것이기 때문에 moveVec == Vector3.zero 로 조건을 걸어 각 액션시 동작을 다르게 하였다.
* 회피시 방향전환 못하게 막기
moveVec가 아닌 회피할때만 사용하는 방향벡터를 만들면 해결되는 문제이기에
dodgeVec를 생성하고 회피행동시 dodgeVec = moveVec를 선언하고
현재 회피중 일때 (isdodge 가 true) moveVec 를 계속 dodgeVec로 초기화해주는 것이다.
3.이번 프로젝트에서 사용된 모르는 함수들 정리
GetAxisisRaw() : Axis 값을 정수로 반환하는 함수
GetButton() : 꾹 누르고 있을때만 호출
ex)
hAxis = Input.GetAxisRaw("Horizontal");
vAxis = Input.GetAxisRaw("Vertical");
wDown = Input.GetButton("Walk");
jDown = Input.GetButtonDown("Jump");
/*=========================================================*/
SetXXXX("애니메이션 이름" , bool 변수) 를 넣어 조건문처럼 사용 할 수 있다.
ex)
anim.SetBool("isRun", moveVec != Vector3.zero);
anim.SetBool("isWalk", wDown);
/*=========================================================*/
transform.LookAt(Vector3 vec) : 지정된 벡터를 향해서 회전시켜주는 함수 - 3D기능
ex)
//현재 플레이어 위치에 방향벡터를 더해서 우리가 나아가는 쪽으로 오브젝트를 회전한다는 뜻
transform.LookAt(transform.position + moveVec);
/*=========================================================*/
Addforce : rigidbody에 포함된 메서드이며 해당 오브젝트에 힘을 가해주는 함수
ex)
// Impulse : 힘을 주는 종류 중 하나로, 즉발로 힘을 준다는 의미
rigid.AddForce(Vector3.up * jumpPower,ForceMode.Impulse);
Unity 기능 살펴보기
1.InputManager 제어하기
현재는 InputSystem을 사용하여 프로젝트를 개발한다는데 기회가되면 InputSystem을 사용해봐야겠다.
InputSystem을 사용하면 크로스플랫폼 개발에 큰 도움을 준다.
2.테스트 중 캐릭터의 점프가 우주에서 하는것처럼 느껴질때 대처
오브젝트의 RigidBody 를 건드는것이 아닌 ProjectSetting의 중력 값을 조절하면된다.
y값이 작아질수록 중력이 쎄진다.
3.캐릭터가 벽과 충돌할때 벽에 끼이면서 느리게 떨어지는거 대처
해당 문제를 해결하기 위해 3가지 방법을 적용해야한다..
3-1. Rigidbody의 Collision Detection > Continuos는 Static과 충돌할 때 효과적
3-1-1. transform 이동은 가끔 물리충돌을 무시하는 경우가 발생 함 ,
Rigidbody 컴포넌트에서 CollisonDetection을 Continuos로 변경하게 되면 계속 충돌처리를 탐색하게 된다.
이렇게 되면 Cpu 처리비용을 더 먹어 최적화는 떨어지지만 정확도는 올라가기 때문이다.
3-2.서로 두 오브젝트가 충돌할때 유니티가 효과적으로 계산하기 위해서는 두 오브젝트다 Rigidbody를 가지고 있어야함
3-2-1.고정 오브젝트인 경우 is Kinematic을 체크하고 Use Gravity를 체크해제 해주면됨
3.점프하면서 벽에 비벼지며 느리게 떨어지는 것은 Physic Material을 이용하여
마찰력이 없는 벽을 만들어 Collider에 적용
4.Animater의 Parameter 결정 조건
Roop Animation들은 보통 bool 파라미터를 사용한다.
일회성 애니메이션(점프,회피)은 Trigger 파라미터를 사용한다.
Trigger 파라미터는 doXXX로 작성 하였고 스크립트에서 On을 하면 애니메이션이 재생되고
애니메이션이 끝난 후 Off하게 되는 파라미터이다.
5.3D에 오브젝트(부모,자식)배치하기 - 하이어라키(계층 구조의 이해)
부모오브젝트는 (0,0,0) 위치에 배치하고 MeshObject의 위치(0,1.7,0)만 조절하여 땅위로 올린 모습이다.
모든 프로젝트가 이런건 아니지만 3D에서는 Sprite가 아닌 Mesh들을 가지고 3D 오브젝트를 관리하기에
내 나름대로 정리하는게 좋을것 같아서 정리 하였다.
1.Player의 최상위 부모에 Rigid,Collider를 넣어서 물리현상 구현 및 Player 스크립트 동작을 하게 하였다.
2.Mesh Object는 캐릭터의 모습을 보여주는 기능이기에 따로 자식으로 뺸뒤 Animator 컴포넌트를 추가하여 캐릭터의 모습에 애니메이션을 추가하는 기능까지 부여하였고
3.기능적인건은 따로 자식오브젝트로 뺼 수도 잇고(무기를 든다거나,스킬을 가지고 있다거나 등등)
즉, 프로젝트 설계에 따라 부모 -자식 및 기능 적으로 분리를 잘 해야 프로젝트가 수월해진다.
6. Object에 Light 추가하기 + Particle
Transforn . y 값 조절 : 위치를 조절하여 빛의 위치 조정
Light - Intensity : 빛의 세기 조절
Light - Range : 빛의 범위 조절
마지막 움짤을 보면 아이템 밑에 부위에도 빛이 비치는 장면이 있다.
*Particle System 간단 설명
Material을 변경하여 파티클 이미지를 변경 할 수 있다.
Emission(방출)은 파티클의 갯수에 영향을 준다. Time의 숫자가 커질수록 파티클의 갯수가 증가한다.
Shape 기능은 파티클이 어떤 방식으로 뻗어 나갈건지 결정하는 기능이다. Corn 모양일때 원뿔 모양에서 파티클이 뿜어져 나왔고 Donut일때는 사방으로 퍼져 나오는 모습을 볼 수있으며 퍼져나오는 근원의 위치,각도,크기를 조절 할 수 있다.
Color over LifeTime 은 파티클의 색상을 결정하는 기능이다. 해당 이미지를 보면 이해가 될것이다.
Size over LifeTime 은 파티클이 살아있는동안 크기를 변화하는 기능이다.
Unity에서 제공하는 프리셋이 있어 그걸 사용해도 된다. 위의 이미지 처럼 위로 올라갈수록 파티클의 크기가 커지는 모습을 볼 수있다.
2024-09-04(수)
https://www.youtube.com/watch?v=WkMM7Uu2AoA&list=PLO-mt5Iu5TeYkrBzWKuTCl6IUm_bA6BKy
: 에셋 다운 + 플레이어 이동 및 카메라 설정
https://www.youtube.com/watch?v=eZ8Dm809j4c
: 점프 / 회피 로직 구현
https://www.youtube.com/watch?v=u2DLOay5oO8
: 아이템 생성 및 파티클 / 라이트 컴포넌트 조작 (12:20초까지 수강)
'내일배움캠프_Unity_6기 > TIL(Today I Learend)' 카테고리의 다른 글
TIL : 2024-09-06(금) (0) | 2024.09.06 |
---|---|
TIL : 2024-09-05(목) (0) | 2024.09.05 |
TIL : 2024-09-03(화) (0) | 2024.09.03 |
TIL : 2024-09-02(월) :: 옵저버패턴 , C#(Unity) Delegate,Event 예제 (0) | 2024.09.02 |
TIL : 2024-08-29(목) (0) | 2024.08.29 |