유니티 절대강좌 챕터4(주인공 캐릭터 제작)
Input.GetAxis("Horizontal") 은 -1.0f ~+1.0f 사이의 연속적인 값을 반환한다. 따라서 바로바로 방향을 바꾸거나 속도를 변경해야할때는 Input.GetAxisRaw("Horizontal")을 사용하면 -1.0f,0.0f,+1.0f 세가지의 값만 반환한다.
정규화벡터
Vector3.forward : Vector3(0,0,1)
Vector3.up :Vector3(0,1,0)
Vector3.right :Vector3(1,0,0)
Vector3.one :Vector3(1,1,1)
Vector3.zero :Vector3(0,0,0)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCtrl : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Debug.Log("h="+h);
Debug.Log("v="+ v);
transform.position += new Vector3(0, 0, 1);
}
}
=
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCtrl : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Debug.Log("h="+h);
Debug.Log("v="+ v);
transform.position += Vector3.forward *1;//1은 속력
}
}
<컴포넌트 캐시 처리>
프로젝트 하면서 collider를 start()에서 GetComponent를 안해도 onTriggerEnter 충돌감지는 되는데 왜 하는지 몰랐었는데
컴포넌트 캐시 처리를 위해서 했던것이었던걸 알았다.
프레임마다 컴포넌트에 접근하는 방식이 바람직하지 않았던것-> Awake함수나 Start함수에서 미리 변수에 할당한 후에 그 변수를 통해 접근하는것이 컴포넌트 캐시 처리이다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCtrl : MonoBehaviour
{
private Transform tr;
// Start is called before the first frame update
void Start()
{
this.tr = GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Debug.Log("h="+h);
Debug.Log("v="+ v);
this.tr.position += Vector3.forward *1;//1은 속력
}
}
Translate함수
함수 원형 : void Translate(Vector3 direction,[Space relativeTo]) // [Space relativeTo]는 월드좌표 기준인지 로컬 좌표 기준으로 이동할지 결정
void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Debug.Log("h="+h);
Debug.Log("v="+ v);
this.tr.Translate(Vector3.forward * 1);
}
Time.deltaTime
같은 소스코드지만 프레임 레이트가 다른 환경에서 실행하면 속도가 달라지는 문제가 발생한다-> Time.deltaTime을 곱하면 해결됨
this.tr.Translate(Vector3.forward* Time.deltaTime *v* moveSpeed);//v는 키보드 입력값
v를 곱함으로 키보드 입력이 없으면 이동하지 않고 up화살표 키를 누르면 전진, down 화살표 키를 누르면 후진한다.

캐릭터 전후 좌우 이동
void Update()
{
//수평
float h = Input.GetAxis("Horizontal");
//수직
float v = Input.GetAxis("Vertical");
Debug.Log("h="+h);
Debug.Log("v="+ v);
Vector3 moveDir = (Vector3.forward * v) + (Vector3.right * h);
this.tr.Translate(moveDir* Time.deltaTime * moveSpeed);//v는 키보드 입력값
}
-> 대각선일때( 키 두개를 동시에 누를때 ) 속도가 빨라짐 대각선은 √ 2=1.414...이기때문에 빨라진것 따라서 길이가 1인 벡터로 변환해야함=> 정규화 벡터
this.tr.Translate(moveDir.normalized* Time.deltaTime * moveSpeed);
※ 참고
벡터의 크기는 Vector3.Magnitude 함수를 이용해 가져올 수 있다.
캐릭터 회전
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCtrl : MonoBehaviour
{
private Transform tr;
public float moveSpeed = 10.0f;
public float turnSpeed = 80.0f;
// Start is called before the first frame update
void Start()
{
this.tr = GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
//수평
float h = Input.GetAxis("Horizontal");
//수직
float v = Input.GetAxis("Vertical");
float r = Input.GetAxis("Mouse X");
//Debug.Log("h="+h);
//Debug.Log("v="+ v);
Vector3 moveDir = (Vector3.forward * v) + (Vector3.right * h);
this.tr.Translate(moveDir.normalized* Time.deltaTime * moveSpeed);//v는 키보드 입력값
tr.Rotate(Vector3.up * turnSpeed * Time.deltaTime * r);
}
}
Mouse X는 마우스를 왼쪽으로 움직이면 음수 값을 반환, 오른쪽으로 움직이면 양수 값을 반환한다,

애니메이션 블렌딩
현재 수행 중인 애니메이션에서 다른 애니메이션으로 변경될 때 부드럽게 연결해주는 기능이 애니메이션 블렌딩이다.
CrossFade함수를 제공함
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCtrl : MonoBehaviour
{
private Transform tr;
public float moveSpeed = 10.0f;
public float turnSpeed = 80.0f;
private Animation anim;
// Start is called before the first frame update
void Start()
{
this.tr = GetComponent<Transform>();
this.anim = this.GetComponent<Animation>();
anim.Play("Idle");
}
// Update is called once per frame
void Update()
{
//수평
float h = Input.GetAxis("Horizontal");
//수직
float v = Input.GetAxis("Vertical");
float r = Input.GetAxis("Mouse X");
//Debug.Log("h="+h);
//Debug.Log("v="+ v);
Vector3 moveDir = (Vector3.forward * v) + (Vector3.right * h);
this.tr.Translate(moveDir.normalized* Time.deltaTime * moveSpeed);//v는 키보드 입력값
tr.Rotate(Vector3.up * turnSpeed * Time.deltaTime * r);
this.PlayerAnim(h, v);
}
void PlayerAnim(float h,float v)
{
if (v >= 0.1f)
{
anim.CrossFade("RunF", 0.25f);//0.25f 동안 애니메이션이 변경
}
else if (v <= -0.1f)
{
anim.CrossFade("RunB", 0.25f);
}
else if (h >= 0.1f)
{
anim.CrossFade("RunR", 0.25f);
}
else if (h <= -0.1f)
{
anim.CrossFade("RunL", 0.25f);
}
else
{
anim.CrossFade("Idle", 0.25f);
}
}
}

마우스 드래그 한 만큼 회전하기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCtrl : MonoBehaviour
{
public enum eAnimState
{
Idle,
RunB,
RunF,
RunL,
RunR
}
private Transform tr;
public float moveSpeed = 10.0f;
public float turnSpeed = 80.0f;
private Animation anim;
private bool isDown;
private Vector3 downPosition;
// Start is called before the first frame update
void Start()
{
this.tr = GetComponent<Transform>();
this.anim = this.GetComponent<Animation>();
//방법1
//anim.Play("Idle");
//방법2
this.anim.clip = this.anim.GetClip(eAnimState.Idle.ToString());
this.anim.Play();
}
// Update is called once per frame
void Update()
{
//수평
float h = Input.GetAxis("Horizontal");
//수직
float v = Input.GetAxis("Vertical");
float r = Input.GetAxis("Mouse X");
//Debug.Log("h="+h);
//Debug.Log("v="+ v);
Vector3 moveDir = (Vector3.forward * v) + (Vector3.right * h);
this.tr.Translate(moveDir.normalized* Time.deltaTime * moveSpeed);//v는 키보드 입력값
//tr.Rotate(Vector3.up * turnSpeed * Time.deltaTime * r);
//마우스 드래그한만큼 회전하기
if (Input.GetMouseButtonDown(0))
{
this.isDown = true;
this.downPosition = Input.mousePosition;
}
else if (Input.GetMouseButtonUp(0))
{
this.isDown = false;
}
if (this.isDown)//isDown==true
{
if (this.downPosition != Input.mousePosition)
{
var rotDir = Mathf.Sign(r);//부호반환함수
this.transform.Rotate(Vector3.up, rotDir * Time.deltaTime * this.turnSpeed);//y축방향으로 회전
this.downPosition = Input.mousePosition;
}
}
this.PlayerAnim(h, v);
}
void PlayerAnim(float h,float v)
{
if (v >= 0.1f)
{
anim.CrossFade("RunF", 0.25f);//0.25f 동안 애니메이션이 변경
}
else if (v <= -0.1f)
{
anim.CrossFade("RunB", 0.25f);
}
else if (h >= 0.1f)
{
anim.CrossFade("RunR", 0.25f);
}
else if (h <= -0.1f)
{
anim.CrossFade("RunL", 0.25f);
}
else
{
anim.CrossFade("Idle", 0.25f);
}
}
}
Mathf.Sign은 float f의 부호를 반환하는 함수이다.
0이나 양수일 경우 1을, 음수일 경우 -1을 반환한다.
Debug.Log(Mathf.Sign(-10)); //출력값: -1
Debug.Log(Mathf.Sign(10)); //출력값: 1
무기장착

위치가 살짝 어긋나있음

리셋 후

9/26
오전: 절대강좌 4강 끝내기, 5장 조금하기
오후: 절대강좌 5장 끝내기
카메라 따라가기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FollowCam : MonoBehaviour
{
//카메라가 따라갈 타겟의 Transform
public Transform targetTr;
//카메라 자신의 Transform
private Transform camTr;
//따라갈 대상으로부터 떨어질 거리
[Range(2.0f, 20.0f)]//다음라인에 선언한 변수(distance)의 입력범위 최소 최대값, 인스펙터뷰에 슬라이드바를 표시
public float distance = 10.0f;
[Range(0.0f, 10.0f)]
public float height = 2.0f;
// Start is called before the first frame update
void Start()
{
camTr = GetComponent<Transform>();
}
// Update is called once per frame
void LateUpdate()
{
camTr.position = targetTr.position + (-targetTr.forward * distance) + (Vector3.up * height);
camTr.LookAt(targetTr.position);
}
}
[Range(min, max)] : 다음라인에 선언한 변수(distance)의 입력범위 최소 최대값, 인스펙터뷰에 슬라이드바를 표시

Update()함수가 아닌 LateUpdate()에 쓴 이유: 플레이어의 update(이동로직)을 완료한 후에 카메라를 이동시키기 위해서
update에 쓴 경우 플레이어의 update먼저 실행 후 카메라의 update가 실행되면 상관이 없지만 이 호출 순서가 보장된것이 아니기 때문에 카메라 떨림 현상이 일어날 수 있다.

카메라의 위치를 이동시킨 후 LookAt함수를 쓰면 타겟을 향해 카메라의 각도를 회전하는 역할을 한다.
부드럽게 따라가는 느낌을 주기 위해 => 주인공 캐릭터가 이동한 후 일정한 시간 가격을 두고 따라가도록한다. => Vector3.Slerp 사용
Vector3.Lerp(선형보간), Vector3.Slerp(구면 선형 보간) : 현재 값을 목푯값으로 변경할 때 갑자기 변경하지 않고 부드럽게 변경시키는 로직에 많이 활용된다.
선형보간은 직선으로 균일한 속도로 이동시키거나 회전시킬 때 사용한다(vector3, Mathf, Quaternion,Color 구조체에서 사용가능), 구면 선형 보간은 직선의 형태가 아닌 구면 형태로 값을 추론한다. 시작점과 종료점은 느리게 증가하고 중간지점은 동일한 시간대비 이동해야할 거리가 크기 때문데 빠르게 이동, 따라서 회전로직에 사용된다.(Quaternion, Vector3구조체에서 사용가능)
Vector3.Slerp(시작 좌표,종료 좌표, t);
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FollowCam : MonoBehaviour
{
//카메라가 따라갈 타겟의 Transform
public Transform targetTr;
//카메라 자신의 Transform
private Transform camTr;
//따라갈 대상으로부터 떨어질 거리
[Range(2.0f, 20.0f)]//다음라인에 선언한 변수(distance)의 입력범위 최소 최대값, 인스펙터뷰에 슬라이드바를 표시
public float distance = 10.0f;
[Range(0.0f, 10.0f)]
public float height = 2.0f;
//반응 속도
public float damping = 10.0f;
// Start is called before the first frame update
void Start()
{
camTr = GetComponent<Transform>();
}
// Update is called once per frame
void LateUpdate()
{
Vector3 pos = targetTr.position + (-targetTr.forward * distance) + (Vector3.up * height);
camTr.position = Vector3.Slerp(camTr.position, pos, Time.deltaTime * damping);
camTr.LookAt(targetTr.position);
}
}

target offset 적용하기
LookAt처리를 해서 시야가 좁은 현상을 해결하기 위해서 사용
public float targetOffset = 2.0f;
void LateUpdate()
{
Vector3 pos = targetTr.position + (-targetTr.forward * distance) + (Vector3.up * height);
camTr.position = Vector3.Slerp(camTr.position, pos, Time.deltaTime * damping);
camTr.LookAt(targetTr.position+(targetTr.up*targetOffset));
}
