GURU_unity_2주차 FPS 게임 제작(1)

2023. 7. 7. 17:11SWU_프로젝트/GURU - unity

오늘부터는 FPSGame(일 인칭 슈팅 게임)을 만드는 방법을 학습할 것이다.

1. 캐릭터 제어(회전, 이동, 점프)

 

1. 캐릭터 제어(회전, 이동, 점프)

에디터 버전을 2020년으로 설정했고, 3D, 프로젝트 이름은 FPSGame1으로 설정하여 Create Project를 하였다.

layout을 tall로 설정하고 위와 같이 작업환경을 만들었다.

 

 

1. 캐릭터 제어 (회전, 이동, 점프)

 

   사용자 입력 키 설정

  • 유니티에서 키보드나 마우스 또는 조이스틱 등의 입력을 담당하는 Input 클래스
  • 유니티 에디터의 [Edit] – [Project Settings - Input] 에서 설정
  • 마우스 드래그 입력 확인(Mouse X, Mouse Y 항목 )

 

게임 기본 환경 구성(바닥만들기)

 

하이어라키 뷰에서 [+] 버튼 - [3D Object] - [Plane]을 선택해 바닥을 설치하고, 이름은 Ground로 수정해준다.

바닥의 사이즈를 조금 키워줬다.

 

이제 바닥 색상 변경을 위한 작업을 할 것이다.

  • Project 뷰의 Assets 폴더에 Material 폴더를 생성한 후 유니티 에디터의 [+]버튼 – [Material]을 선택해 Material을 하나
  • 생성 이름은 ‘Mat_Ground’라고 수정해준다.

 

Albedo에서 흙의 느낌을 주기위해 다음과 같이 수정해봤다.

위 그림과 같이 바닥의 색상 값(RGBA)을 변경하고 Ground 오브젝트에 드래그해서 넣어준다.

 

게임 기본 환경 구성(플레이어 캐릭터만들기)

 

  • 하이어라키 뷰에서 [+] 버튼 - [3D Object] - [Capsule]을 선택해 캡슐 오브젝트를 생성한다.
  • Transform 컴포넌트의 Position을 y축으로 1만큼 위로 올려준다.
  • 캡슐 오브젝트의 이름은 ‘Player’로 변경한다.

카메라 회전 스크립트 만들기

  • Project 뷰에서 Assets 폴더 하위에 Scripts 폴더를 만든 후 [+] 버튼 - [C# Script]를 선택해 새로운 C# 스크립트를 생성 
  • 스크립트의 이름은 ‘CamRotate’로 변경한다.

이제 카메라 회전 스크립트를 만들어보자.

public class CamRotate : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
} // Update is called once per frame
 void Update()
{
// 사용자의 마우스 입력을 받아 물체를 회전시키고 싶다.
// 1. 마우스 입력을 받는다.
     float mouse_X = Input.GetAxis("Mouse X"); // -1~1
     float mouse_Y = Input.GetAxis("Mouse Y"); //-1~1
   // 2. 마우스 입력 값을 이용해 회전 방향을 결정한다.
      Vector3 dir = new Vector3(-mouse_Y, mouse_X, 0);   
//-가 붙는이유는 마우스움직임과 보여지는 방향이 엇갈리기때문이다.
  }
 }
  • CamRotate스크립트에  사용자의 마우스 입력 값을 변수에 저장한다.
  • 마우스 좌우의 입력 값을 y축 회전 값에 대응시키고, 마우스 상하의 입력 값은 x축 회전 값에 대응
  • x축 회전의 경우 양수(+) 방향으로 회전 시 아래쪽으로 회전하고, 음수(-) 방향으로 회전 시 위쪽으로 회전하기 때문에 마우스 상하 입력 값을 반대로 적용

이렇게 작성하면 된다!

 

이렇게 카메라가 날 따라서 잘움직인다.

 

시야를 제한하는 코드를 추가해줄 것이다.

상하 회전을 -90˚ ~ 90˚ 사이로 제한하기 위해 Mathf 클래스의 Clamp() 함수를 사용할 것이다.

  • 유니티 에디터에서 플레이 확인 → 정면을 바라보려고 하면 카메라가 아래로 수그리는 현상 발생한다.
  • 유니티 내부적으로 각도가 0˚보다 작아지면 -1˚가 아닌 359˚ (360˚–1˚)로 자동 연산해주기 때문이다.
void Update()
{
. . . (생략) . . .
// 3. 회전 방향으로 물체를 회전시킨다.
// r = r0 + vt
transform.eulerAngles += dir * rotSpeed * Time.deltaTime;
// 4. x축 회전(상하 회전) 값을 -90도~90도 사이로 제한한다.
Vector3 rot = transform.eulerAngles;
rot.x = Mathf.Clamp(rot.x, -90f, 90f);
transform.eulerAngles = rot;
}

코드는 위 그림과 같다.

  • 문제 해결을 위해 Clamp() 함수를 유니티 내부에서 각도 변환 연산하기 전에 미리 적용하는 방향으로 코드를 수정한다.
  • 먼저 회전 값을 누적할 변수를 선언한다.
  • 새로운 변수에 회전 이동량을 미리 누적한다.
  • 계산된 변수 값을 카메라에 적용하기 전에 먼저 변수 값을 -90˚ ~ 90˚로 제한한다.
  • 계산이 완료된 변수를 이용하여 물체를 회전시킨다.

 

플레이어 회전 스크립트 만들기

 

캐릭터의 좌우 회전을 위한 스크립트도 생성한다.

스크립트 이름은 PlayerRotate.cs로 지정한다.

스크립트는 이와같이 작성하였다.

캡슐모양 플레이어가 왔다갔다 회전하는 것을 확인할 수 있다.

 

 

카메라 이동 스크립트 만들기

우선, 하이어라키창에서 Create Empty를 생성해주고, CamPosition으로 이름을 수정하여준다.

position도 수정하여주었다.

 

플레이어 오브젝트 하위에 카메라가 쫓아다닐 빈 오브젝트를 생성한다.

빈 오브젝트의 이름은 CamPosition으로 설정하고 위치를 그림과 같이 조정한다.

카메라가 쫓아다니게 하는 스크립트 생성 하고 스크립트의 이름은 CamFollow.cs로 설정한다.

 

스크립트를 작성해보자.

카메라의 위치 좌표에 특정한 게임 오브젝트의 위치 좌표를 대입하기위한 코드를 작성한다.

위 그림과 같은 과정으로 진행해준다.

  • 완성된 CamFollow.cs 스크립트를 메인 카메라 오브젝트에 드래그해서 넣어준다.
  • CamPosition 오브젝트를 Target에 드래그해서 넣어준다.
  • 유니티 에디터에서 플레이해보면 카메라가 타깃 위치로 이동하는 것을 확인해준다.

 

플레이어 이동 스크립트 만들기

  • 이전 챕터에서 했던 이동 공식대로 플레이어 오브젝트를 이동하는 코드 작성한다.
  • 스크립트의 이름은 PlayerMove.cs 로 지정한다.

스크립트를 작성해보자.

아래는 플레이어의 움직임을 담은 영상이다.

  • 완성된 PlayerMove.cs 스크립트를 플레이어 오브젝트에 드래그해서 놓아준다.
  • 유니티 에디터에서 플레이해보면 플레이어가 회전해도 이동 방향은 바뀌지 않는 문제가 발생한다.
  • 문제의 원인은 플레이어 캐릭터의 로컬 좌표가 아닌 월드 기준의 월드 좌표로 이동하기 때문이다.
  • 플레이어의 시야 방향(카메라 방향)을 기준으로 전후좌우를 다시 계산하도록 코드를 수정해준다.

// 2-1. 메인 카메라를 기준으로 방향을 변환한다.
dir = Camera.main.transform.TransformDirection(dir);

해결하기 위해서 Transform 클래스의 TransformDirection() 함수를 이용하여 월드 좌표를 로컬 좌표로 변경해준다.

로컬 좌표계의 기준이 될 트랜스폼은 메인 카메라로 지정한다.

 

 

플레이어에 중력 적용하기

  • 플레이어를 선택한 상태에서 인스펙터 뷰에서 [Add Component] - [Physics] – [CharacterContoller]를 선택해 캐릭터 콘트롤러 컴포넌트를 추가한다.
  • 캐릭터 콘트롤러 자체에 충돌 영역이 존재하므로 기존의 콜라이더 컴포넌트는 제거해준다.

이제 스크립트를 작성해보자.

public class PlayerMove : MonoBehaviour
{
// 이동 속도 변수
public float moveSpeed = 7f;
// 캐릭터 콘트롤러 변수
CharacterController cc;
private void Start()
{
// 캐릭터 콘트롤러 컴포넌트 받아오기
cc = GetComponent<CharacterController>();
}
. . . (생략) . . .
}
  • 캐릭터 컨트롤러를 담을 변수를 선언한다.
  • 플레이어가 씬에 생성되면 플레이어에 붙어있는 캐릭터 콘트롤러 컴포넌트를 변수에 저장한다.

  • 중력 값을 지정할 변수 선언한다.
  • 캐릭터의 수평 이동 외에 중력에 의한 수직 이동 값을 추가로 계산하여 적용해준다.
  • 이동 시에는 기존의 p = p0 + vt를 대신하여 캐릭터 콘트롤러의 Move() 함수를 이용한다.

점프 기능 구현

점프할 때 적용할 힘을 저장할 public 변수를 선언한다.

Update부분에는 아래와 같은 코드를 추가해준다.

Jump를 구현하기 위해서는 Space가 사용된다는 것을 Edit에 ProjectSettings를 통해 확인가능하다.

 

  • 점프 테스트를 위해 임시로 씬에 큐브 오브젝트를 하나 생성해준다.
  • 큐브 오브젝트의 이름은 Block으로 설정한다.
  • 그림과 같이 위치과 크기를 조정해준다.
  • 유니티 에디터에서 플레이 했을 때에 점프는 잘 되지만 스페이스 바를 누를 때마다 중복하여 계속 점프가 되는 문제 확인할수 있다.

playermove.cs

  • 문제를 해결하기 위해 현재 점프 중인지를 체크하기 위한 점프 상태 변수를 선언한다.
// 2-2. 만일, 점프 중이었고, 다시 바닥에 착지했다면...
if (isJumping && cc.collisionFlags == CollisionFlags.Below)
{
// 점프 전 상태로 초기화한다.
      isJumping = false;
}
// 2-3. 만일, 키보드 [Spacebar] 키를 입력했고, 점프를 하지 않은 상태라면...
      if (Input.GetButtonDown("Jump") && !isJumping)
{
// 캐릭터 수직 속도에 점프력을 적용하고 점프 상태로 변경한다.
            yVelocity = jumpPower;
             isJumping = true;
}

 

void Update()
{
. . . (생략) . . .
// 2-2. 만일, 점프 중이었고, 다시 바닥에 착지했다면...
    if (isJumping && cc.collisionFlags == CollisionFlags.Below)
{
// 점프 전 상태로 초기화한다.
              isJumping = false;
// 캐릭터 수직 속도를 0으로 만든다.
              yVelocity = 0;
       }
         . . . (생략) . . .
}

시간이 지날수록 수직 속도의 값이 지나치게 커지는 문제를 해결하기 위해 플레이어가 지면에 닿으면 수직 속도를 다시 0으로 초기화하기위해 위 코드를 적용해준다.

 

 

앞으로 FPSGame을 만든다니 조금 설렌다.