728x90

이 글은 인프런 "솔리디티 깨부수기" 강의를 듣고 정리한 글 입니다. 

 

[지금 무료] 솔리디티 깨부수기 | D_One - 인프런

D_One | 이 강의를 통해서, 스마트 컨트랙 제작을 위한 솔리디티 언어를 배울수 있습니다., 코딩이 처음인 분들도 OK! 처음 배우는 솔리디티, 쉽게 시작해보세요. 강의 주제 📖 [사진] 이 강의에서

www.inflearn.com


Solidity 저장 방법 

solidity 는 storage, memory, calldata ,stack 4개의 저장 영역으로  나뉘어 있다.

 

Storage

 대부분의 함수 밖의 변수들, 함수 자체가 저장되며, 영속적으로 저장이 되어 스마트 컨트롤 배포시 저장된 함수와 변수의 정보들 블록에 들어가게 된다.

블록체인에서 블록에 정보가 들어간다는 것은 영속적인 의미를 가지게 되며, 모든 노드들이 해당 블록을 다운 받아야 하기 때문에 가스 비용이 비싸진다.

 

Memory

스토리지와 반대되는 개념으로 함수 내의 변수값, 함수의 파라미터, 리턴값, 레퍼런스 타입이 주로 저장이 된다. 

그러나, 메모리는 메모리에 저장된 함수가 작동할 때만 유효하기 때문에 storage 처럼 영속적이지 않기 때문에 가스 비용이 비교적 스토리지보다 싸다.

 

Calldata 

주로 external function 의 파라미터에서 사용된다. 함수를 실행할 때 파라메터의 값 자체를 calldata로 받을 수 있으며 값은 변경할 수 없고 읽기만 가능하다. 

 

Stack

EVM (Ethereum Virtual Machine) 에서 stack data를 관리할때 쓰는 영역이다. 해당 영역은 1024Mb로 제한적으로 저장해 사용할 수 있다. 예를 들어, 함수를 실행할 때 로컬 변수와 같은 것들을 잠시 기억할 때 EVM이 사용한다. 

 


Function string

 

string은 기본 데이터 타입이 아니기 때문에 레퍼런스 타입이라고 볼 수 있다. 왜냐면 배열에 의해서 string이 연관되기 때문이다. string을 쓸 때 memory 키워드를 넣어줘야 한다.

// SPDX-License-Identifier  GPL-30
pragma solidity >= 0.7.0 < 0.9.0;

contract lec7 {
    
    function get_string(string memory _str) public pure returns( string memory){
        return _str;
    }
}

 

 

위의 예제에서 string 옆에 memory 키워드를 넣고, string 리턴값에도 memory를 지정해야한다. 

 

function get_uint(uint256 memory _ui) public pure returns(uint256 memory){
	return _ui;
}

다른 예제로 get_uint 함수에서 메모리로 지정을 했는데, 이런 기본 데이터 타입인 경우 메모리가 디폴트 되기 때문에 따로 지정하지 않아도 된다. 

 

그리고, pure를 사용하는 이유는, 스토리지에 저장된 변수 값을 읽을 필요가 없기 때문에, pure은 파라미터 받은걸 바로 리턴하기 때문에 사용한다.

728x90

 이 글은 인프런 [따라하면서 배우는 고박사의 유니티 기초] 강의를 수강한 후 정리한 글 입니다.


 

[지금 무료] 따라하면서 배우는 고박사의 유니티 기초 | 고박사 - 인프런

고박사 | 유니티로 게임을 개발하고 싶은 초보자를 대상으로 하며, 유니티 설치부터 2D/3D 게임 개발에 필요한 기초 지식까지 자세하게 설명합니다. (강의에 사용되는 모든 리소스는 영상 상단의

www.inflearn.com


FBX Model 개요

이번 실습에서 사용하는 3D FBX 파일은 Asset Store에서 "Unity-Chan! Model" 검색 후 사용한다.

https://assetstore.unity.com/packages/3d/characters/unity-chan-model-18705

에셋을 추가하면 package manager에서 다운로드 후 프로젝트 뷰에 임포트 할 수 있다.

 

외부에서 가져온 FBX Model Asset은 Model, Rig, Animation, Materials 4개의 tab 정보를 설정하고 이를 통해 여러 요소들을 게임에 활용하게 된다.


FBX Model - Model

 

Scene

  • 광원과 카메라 임포트 여부, 모델 크기 등 씬과 관련된 옵션 설정
  • Scale Factor : 원본 모델의 크기 설정
  • Improt BlendShapre : 블렌드 셰이프를 메시와 함께 Import

Meshes

  • 메시 압축, 최적화 등 메시와 관련된 옵션 설정
  • Mesh Compression : 메시 파일 크기를 줄일 압축 레벨 설정
  • Generate Colliders : 메시 콜라이더를 사용할지 여부 (충돌 처리)

 

Geometry

  • UV와 노말 처리 등을 위한 지오메트리 관련 옵션 설정
  • Normals : 노말 벡터를 계산할지, 어떻게 계산할지 설정

FBX Model - Rig

 

리깅(Rigging)

모델의 뼈대를 만들어 심거나 뼈대를 할당하여, 모델이 애니메이션 될 수 있는 상태로 만드는 것

Animation Type

  • None : 애니메이션이 없을 때 (지형, 건물, 아이템 등)
  • Legacy : Unity 3.x 이전 버전에서 사용 (Animation 컴포넌트)
  • Generic : 정점 애니메이션 (Animator 컴포넌트) 
  • Humanoid : 본 애니메이션 (Animator 컴포넌트) 
  • Avatar Definition : 아바타 정의를 가져올 위치 선택
  • Skin Weights : 하나의 버텍스에 영향을 줄 수 있는 최대 뼈대 수
  • Optimize Game Object : 게임오브젝트의 Transform 계층 구조를 Avatar와 animation 컴포넌트에서 제거 ( 성능 향상)
 

 

Optimize Game Object

 

Extra Transforms to Expose

  • Optimize Game Object 체크됬을 때만 활성화되는 메뉴
  • 체크되는 transform은 게임 오브젝트의 자식 오브젝트로 출력

예시로 오른손 무기를 쥐어 캐릭터가 애니메이션 할 때, 무기가 오른손 위치를 따라다니게 하려면 오른손에 해당하는 transform은 최적화하지 않고 남겨둬야 한다.

Optimize Game Object 결과

 

 

FBX Model - Rig [Animation Type: Generic]

 

FBX Model - Rig [Animation Type: Humanoid ]

 

Avatar Configure - Mappiing

각 부위의 Transform이 연결되어 있는 모습을 확인하고 수정할 수 있다. 

Avatar Configure - Muscle & Setting

근육 정보를 수정할 수 있다.


FBX Model - Animation

FBX Model모델의 애니메이션을 설정하는 탭으로 애니메이션 정보가 없는 FBX모델의 경우 다음과 같이 정보가 뜨지 않는다.

 

 

애니메이션 정보가 있으면 압축 타입을 설정하는 FBX 에셋정보

하나의 FBX에 여러 동작이 담겨있을 떄 동작을 나누는 clips, Clip 정보, 미리보기가 있다.

하나의 clip 상세 정보


FBX Model - Materials

Materials 및 텍스처 접근 방법을 정의하고 모델이 사용되는 Meterials를 설정할 수 있다.

 
unitychan 모델 파일을 Hierarchy View로 드래그하면 모델의 회전 정보, 카메라 위치 조정하여 가까이서 볼 수 있다. 

FBX Model - Mecanim Animation System

외부에서 가져온 FBX 모델은 Hierarchy View로 드래그 앤 드롭했을 때 캐릭터, 지형 등 화면에 보이는 오브젝트로 활용된다.

애니메이션 재생하기 위한 과정을 살펴 보면 FBX 자식으로 애니메이션과 아바타 마스크를 가지게 된다.

애니메이션은 애니메이션 컨트롤러에 등록하여 원하는 여러 상황에 애니메이션이 재생될 수 있도록 설정하게 된다.

설정 완료된 애니메이션 컨트롤러와 아바타 마스크를 컴포넌트로 사용되는 애니메이터 컨포넌트에 등록하게 된다. 

 

Animator Compoent

 

Animator Controller Asset

현재 애니메이터에서 재생 가능한 애니메이션(clip)들을 관리하는 상태 머신으로 project view > + > Animator controller로 파일 생성할 수 있으며, 애니메이터 수정은 window > animator view에서 수정한다. 

 

Animator View에 등록된 애니메이션 상태

 

Animator Asset (*.anim)

하나의 애니메이션 동작에 대한 설정(걷기, 왼손을 휘둘러 공격, 점프 대기 등)을 할 수 있다. 

 

player라는 애니메이터 컨트롤러를 생성한 뒤 unitychan에 애니메이터 컨트롤러를 등록한다. 

Animator View를 열고 create state > empty로 idle이라는 상태를 만든다. 이후 Motion에 WAIT00 애니메이션을 등록한다. 


애니메이션 전환 - Play()

달리는 애니메이션을 추가한다. 

원하는 타이밍에 애니메이션을 전환하는 스크립트를 작성한다. 

PlayerController.cs

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    private Animator animator;

    private void Awake()
    {
        animator = GetComponent<Animator>();
    }

    private void Update()
    {
        if ( Input.GetKeyDown(KeyCode.I) )
        {
            animator.Play("Idle");
        }
        else if ( Input.GetKeyDown(KeyCode.R) )
        {
            animator.Play("RUN00_F");
        }
    }
}

스크립트를 컴포넌트에 등록하면 R 버튼을 누르면 Player가 뛰고 I버튼을 누르면 멈추게 된다. 


 


애니메이션 전환 - 상태전이

Make Transition으로 상태전이를 설정할 수 있으며 화살표가 상태 전이를 나타낸다. 화살표의 진행 방향으로만 애니메이션 전환이 가능하며 조건을 설정하지 않는 경우 애니메이션 1회 재생후 자동으로 애니메이션이 전환된다.

 

조건을 이용한 상테 전이 제어

파라미터를 생성하여 상태 전이의 컨디션을 리스트에 등록해 애니메이션 전환을 위한 조건으로 사용된다. 

 

상태 전이 설명

왜 조건을 추가해도 moveSpeed가 안나오는지 모르겠다..

 

728x90

이 글은 인프런 [따라하면서 배우는 고박사의 유니티 기초] 강의를 수강하고 정리한 글입니다. 

 

[지금 무료] 따라하면서 배우는 고박사의 유니티 기초 | 고박사 - 인프런

고박사 | 유니티로 게임을 개발하고 싶은 초보자를 대상으로 하며, 유니티 설치부터 2D/3D 게임 개발에 필요한 기초 지식까지 자세하게 설명합니다. (강의에 사용되는 모든 리소스는 영상 상단의

www.inflearn.com


Off Mesh Link 개요

Off Mesh Link란, 사다리와 같이 수직으로 올라가거나 내려오는 길로 절벽 사이를 뛰어서 넘어가거나 절벽 아래로 떨어지는 길과 같이 메시가 끊어져 있는 곳을 이동할 수 있게 설정하는 것이다.

 

Off Mesh Link는 자동 설정과 수동 설정이 있다.

자동 Off Mesh Link (절벽, 낭떠러지)

장점:

  • 게임월드에 배치된 많은 오브젝트의 Off Mesh Link를 한꺼번에 설정할 수 있다.

단점:

  • 낙하 높이와 점프 거리를 하나만 설정할 수 있기 때문에 다양한 지형을 세세하게 설정하는 것이 불가능하다.
  • 또한 위로 올라가는 off mesh link 설정이 불가능하다.

수동 Off Mesh Link (사다리)

장점:

  • 지형에 따라 세세한 설정이 가능하다.
  • 사다기/암벽과 같이 위로 올라가는 off mesh link 설정이 가능하다.

단점:

  • off mesh link로 연결이 필요한 모든 부분을 직접 설정해야 한다.
 

자동 Off Mesh Link 생성

  1. 자동 Off Mesh Link를 설정할 오브젝트 선택
  2. Navigation View > Object tab > Generate OffMeshLinks 체크
  3. 낙하 높이, 점프 거리 설정
  4. Bake 버튼을 눌러 Navigation Mesh 데이터 생성
1번, 2번
3번, 4번
 

Navigation Mesh 데이터 생성 후에 높이가 4 이하인 곳에 절벽을 뛰어내릴 수 있도록 Off Mesh Link가 체크되어 있는 오브젝트들만 절벽 뛰어 넘거나 떨어질 수 있다.

 

수동 Off Mesh Link 생성

  1. Off MEsh Link로 연결되는 두 지점으로 사용할 게임 오브젝트 생성 (※ 게임 오브젝트 위치가 Navigation Mesh의 이동 경로 내에 포함되어야 정상적인 이동 가능)
  2. 두 지점 오브젝트를 가지고 있는 게임 오브젝트에 Off Mesh Link 컴포넌트 추가
  3. Off Mesh Link 컴포넌트의 start와 en 변수에 1에서 생성한 게임 오브젝트 등록

 

생성한 두 오브젝트를 LadderObject의 자식으로 설정하고, transform을 초기화한 다음, scale를 0.4로 설정한다.

 

두 오브젝트는 transform외에 다른 컴포넌트들은 끈다.

 

사다리를 구성하고 있는 LadderObject의 컴포넌트에 Off Mesh Link 컴포넌트를 추가하고, 생성한 Start와 End 의 게임오브젝트를 등록한다. 실행하면 사다리를 통해 이동이 가능한 것을 확인할 수 있다. 


구역 설정

 구역을 설정하는 것은 게임 월드에 다양한 지형 존재하는 경우에 어떤 지형을 가로질러 가는 것이 빠를지, 느릴지 명시하기 위하여 구역을 설정하게 된다. 

A에서 B로 이동한다면 이동 경로는 위의 예제와 같을 것이다. 하지만 동일한 지역에서 02구역을 water라고 지정하면 이동 비용을 water의 10배인 10으로 설정한다면 경로는 바뀌게 된다. 

구역은 Nevigation View의 Area 탭에서 설정할 수 있다.

 

user3에 climb라는 새로운 구역을 생성하고 비용을 5로 설정한다.

LadderObject의 Off Mesh Link 컴포넌트의 Navigation Area 변수를 climb로 설정한다.

 

 
 

Off Mesh Link 컴포넌트를 가지고 있지 않은 일반 오브젝트의 경우에는 게임오브젝트 선택 후 Navigation View > Object tab > Navigation Area 변수 설정으로 할 수 있다.

 

 

새로운 구역이 생기면 이동하는 오브젝트의 Area Mask의 구역이 추가되어 있는지 확인해야 하며 체크되지 않은 구역은 이동이 불가능하다.

 


Off Mesh Link 액션 설정

Player 오브젝트에 NevMeshAgent 컴포넌트에 있는 Auto Traverse Off Mesh Link가 체크 해제되어 있으면 Off Mesh Link를 만나면 오브젝트가 멈추게 된다.

 

Off Mesh Link에 따라 이동을 다르게 수행하도록 스크립트를 작성한다.

using System.Collections;
using UnityEngine;
using UnityEngine.AI;

public class OffMeshLinkClimb : MonoBehaviour
{
	[SerializeField]
	private	int				offMeshArea = 3;	// 오프메시의 구역 (Climb)
	[SerializeField]
	private	float			climbSpeed = 1.5f;	// 오르내리는 이동 속도
	private	NavMeshAgent	navMeshAgent;

	private void Awake()
	{
		navMeshAgent = GetComponent<NavMeshAgent>();
	}

	IEnumerator Start()
	{
		while ( true )
		{
			// IsOnClimb() 함수의 반환 값이 true일 때 까지 반복 호출
			yield return new WaitUntil(() => IsOnClimb());

			// 올라가거나 내려오는 행동
			yield return StartCoroutine(ClimbOrDescend());
		}
	}

	public bool IsOnClimb()
	{
		// 현재 오브젝트의 위치가 OffMeshLink에 있는지 (true / false)
		if ( navMeshAgent.isOnOffMeshLink )
		{
			// 현재 위치에 있는 OffMeshLink의 데이터
			OffMeshLinkData linkData = navMeshAgent.currentOffMeshLinkData;
			
			// 설명 : navMeshAgent.currentOffMeshLinkData.offMeshLink가
			// true이면 수동으로 생성한 OffMeshLink
			// false이면 자동으로 생성한 OffMeshLink

			// 현재 위치에 있는 OffMeshLink가 수동으로 생성한 OffMeshLink이고, 장소 정보가 "Climb"이면
			if ( linkData.offMeshLink != null && linkData.offMeshLink.area == offMeshArea )
			{
				return true;
			}
		}

		return false;
	}

	private IEnumerator ClimbOrDescend()
	{
		// 네비게이션을 이용한 이동을 잠시 중지한다
		navMeshAgent.isStopped = true;

		// 현재 위치에 있는 OffMeshLink의 시작/종료 위치
		OffMeshLinkData linkData = navMeshAgent.currentOffMeshLinkData;
		Vector3 start = linkData.startPos;
		Vector3 end   = linkData.endPos;

		// 오르내리는 시간 설정
		float climbTime	  = Mathf.Abs(end.y - start.y) / climbSpeed;
		float currentTime = 0.0f;
		float percent	  = 0.0f;

		while ( percent < 1 )
		{
			// 단순히 deltaTime만 더하면 무조건 1초 후에 percent가 1이 되기 때문에
			// climbTime 변수를 연산해서 시간을 조절한다
			currentTime += Time.deltaTime;
			percent = currentTime/climbTime;
			// 시간 경과(최대 1)에 따라 오브젝트의 위치를 바꿔준다
			transform.position = Vector3.Lerp(start, end, percent);

			yield return null;
		}

		// OffMeshLink를 이용한 이동 완료
		navMeshAgent.CompleteOffMeshLink();
		// OffMeshLink 이동이 완료되었으니 네비게이션을 이용한 이동을 다시 시작한다
		navMeshAgent.isStopped = false;
	}
}

사다리와 같이 오르내리는 Off Mesh Link를 처리하기 위한 스크립트를 작성한다. 

Player 오브젝트에 “OffMeshLinkClimb" 적용하고 컴포넌트 변수를 설정한다. 사다리를 올라갈 때 이전보다 느린 속도로 설정한 속도에 의해 올라가는 것을 확인할 수 있다.

절벽과 같이 뛰어넘는 Off Mesh Link 처리를 위해 OffMeshLinkJump 스크립트

using System.Collections;
using UnityEngine;
using UnityEngine.AI;

public class OffMeshLinkJump : MonoBehaviour
{
	[SerializeField]
	private	float			jumpSpeed = 10.0f;	// 점프 속도
	[SerializeField]
	private	float			gravity = -9.81f;	// 중력 계수
	private	NavMeshAgent	navMeshAgent;

	private void Awake()
	{
		navMeshAgent = GetComponent<NavMeshAgent>();
	}

	IEnumerator Start()
	{
		while ( true )
		{
			// IsOnJump() 함수의 반환 값이 true일 때 까지 반복 호출
			yield return new WaitUntil(() => IsOnJump());

			// 점프 행동
			yield return StartCoroutine(JumpTo());
		}
	}

	public bool IsOnJump()
	{
		if ( navMeshAgent.isOnOffMeshLink )
		{
			// 현재 위치에 있는 OffMeshLink의 데이터
			OffMeshLinkData linkData = navMeshAgent.currentOffMeshLinkData;
			
			// 설명 OffMeshLinkType은 Manual=0, DropDown=1, JumpAcross=2로
			// 자동으로 생성한 OffMeshLink의 속성 구분을 위해 사용(1, 2)

			// 현재 위치에 있는 OffMeshLink의 OffMeshLinkType이 JumpAcross이면
			if ( linkData.linkType == OffMeshLinkType.LinkTypeJumpAcross ||
				 linkData.linkType == OffMeshLinkType.LinkTypeDropDown )
			{
				return true;
			}
		}

		return false;
	}

	IEnumerator JumpTo()
	{
		// 네비게이션을 이용한 이동을 잠시 중지한다
		navMeshAgent.isStopped = true;

		// 현재 위치에 있는 OffMeshLink의 시작/종료 위치
		OffMeshLinkData linkData = navMeshAgent.currentOffMeshLinkData;
		Vector3 start = transform.position;
		Vector3 end   = linkData.endPos;

		// 뛰어서 이동하는 시간 설정
		float jumpTime	  = Mathf.Max(0.3f, Vector3.Distance(start, end) / jumpSpeed);
		float currentTime = 0.0f;
		float percent	  = 0.0f;
		// y 방향의 초기 속도
		float v0 = (end-start).y - gravity;

		while ( percent < 1 )
		{
			// 단순히 deltaTime만 더하면 무조건 1초 후에 percent가 1이 되기 때문에
			// jumpTime 변수를 연산해서 시간을 조절한다
			currentTime += Time.deltaTime;
			percent = currentTime/jumpTime;
			// 시간 경과(최대 1)에 따라 오브젝트의 위치(x, z)를 바꿔준다
			Vector3 position = Vector3.Lerp(start, end, percent);
			// 시간 경과에 따라 오브젝트의 위치(y)를 바꿔준다
			// 포물선 운동 : 시작위치 + 초기속도*시간 + 중력*시간제곱
			position.y = start.y + (v0 * percent) + (gravity * percent * percent);
			// 위에서 계산한 x, y, z 위치 값을 실제 오브젝트에 대입
			transform.position = position;

			yield return null;
		}

		// OffMeshLink를 이용한 이동 완료
		navMeshAgent.CompleteOffMeshLink();
		// OffMeshLink 이동이 완료되었으니 네비게이션을 이용한 이동을 다시 시작한다
		navMeshAgent.isStopped = false;
	}
}

Player 오브젝트에 OffMeshLinkJump 스크립트 적용하고, 컴포넌트 변수를 설정한다.

 

실행하면 절벽 지나갈 떄 포물선 운동으로 점프하는 것을 확인할 수 있다. 

 


이동 가능한 장애물 설정

Navigation Mesh Data는 Bake를 이용하여 미리 구워두기 때문에 게임 내에서 오브젝트 이동 오브젝트의 좌표를 기준으로 Mesh 데이터가 새로 생성되지 않는다.

이동 가능한 장애물의 경우에 NavMeshObstacle 컴포넌트를 이용하여 실시간으로 Navigation Mesh Data 관리하게 된다.

이동하는 장애물 설정을 위해 cube 오브젝트를 생성하고 다음과 같이 설정한다.

이동 경로로 사용할 빈 오브젝트 2개를 만들고 다음과 같이 설정한다.

지정된 경로를 자동으로 이동하는 “SimplePatrol” 스크립트 

using UnityEngine;

public class SimplePatrol : MonoBehaviour
{
	[SerializeField]
	private	Transform[]	paths;				// 순찰 경로
	private	int			currentPath = 0;	// 현재 목표지점 인덱스
	private	float		moveSpeed = 3.0f;	// 이동 속도

	private void Update()
	{
		// 이동 방향 설정 : (목표위치-내위치).정규화
		Vector3 direction = (paths[currentPath].position - transform.position).normalized;
		// 오브젝트 이동
		transform.position += direction * moveSpeed * Time.deltaTime;

		// 목표 위치에 거의 도달했을 때
		if ( (paths[currentPath].position - transform.position).sqrMagnitude < 0.1f )
		{
			// 목표 위치 변경 (순찰 경로 순환)
			if ( currentPath < paths.Length-1 ) currentPath ++;
			else								currentPath = 0;
		}
	}
}

이동하는 장애물 오브젝트에 스크립트를 컴포넌트로 등록하고 path01, pah02를 등록한다.

Patrolcube가 머무는 자리를 이동할 수 없는 구역으로 경로에 해당되지 않는 것을 확인할 수 있다.

Nav Mesh Obstacle 컴포넌트를 추가하고 설정을 체크한다. PatroCube 오브젝트가 머무는 위치가 이동할 수 없는 경로 표현되는 것이 확인된다.

 


3인칭 카메라 제어

카메라의 이동 회전을 제어하는 스크립트 CameraController 작성한다.

using UnityEngine;

public class CameraController : MonoBehaviour
{
	[SerializeField]
	private	Transform	target;				// 카메라가 추적하는 대상
	[SerializeField]
	private	float		minDistance = 3;	// 카메라와 target의 최소 거리
	[SerializeField]
	private	float		maxDistance = 30;	// 카메라와 target의 최대 거리
	[SerializeField]
	private	float		wheelSpeed = 500;	// 마우스 휠 스크롤 속도
	[SerializeField]
	private	float		xMoveSpeed = 500;	// 카메라의 y축 회전 속도
	[SerializeField]
	private	float		yMoveSpeed = 250;	// 카메라의 x축 회전 속도
	private	float		yMinLimit = 5;		// 카메라 x축 회전 제한 최소 값
	private	float		yMaxLimit = 80;		// 카메라 x축 회전 제한 최대 값
	private	float		x, y;				// 마우스 이동 방향 값
	private	float		distance;			// 카메라와 target의 거리

	private void Awake()
	{
		// 최초 설정된 target과 카메라의 위치를 바탕으로 distance 값 초기화
		distance = Vector3.Distance(transform.position, target.position);
		// 최초 카메라의 회전 값을 x, y 변수에 저장
		Vector3 angles = transform.eulerAngles;
		x = angles.y;
		y = angles.x;
	}

	private void Update()
	{
		if ( target == null ) return;	// target이 존재하지 않으면 실행 하지 않는다

		// 오른쪽 마우스를 누르고 있을 때
		if ( Input.GetMouseButton(1) )
		{
			// 마우스를 x, y축 움직임 방향 정보
			x += Input.GetAxis("Mouse X") * xMoveSpeed * Time.deltaTime;
			y -= Input.GetAxis("Mouse Y") * yMoveSpeed * Time.deltaTime;
			// 오브젝트의 위/아래(x축) 한계 범위 설정
			y = ClampAngle(y, yMinLimit, yMaxLimit);
			// 카메라의 회전(Rotation) 정보 갱신
			transform.rotation = Quaternion.Euler(y, x, 0);
		}

		// 마우스 휠 스크롤을 이용해 target과 카메라의 거리 값(distance) 조절
		distance -= Input.GetAxis("Mouse ScrollWheel") * wheelSpeed * Time.deltaTime;
		// 거리는 최소, 최대 거리를 설정해서 그 값을 벗어나지 않도록 한다
		distance = Mathf.Clamp(distance, minDistance, maxDistance);
	}

	private void LateUpdate()
	{
		if ( target == null ) return;	// target이 존재하지 않으면 실행 하지 않는다

		// 카메라의 위치(Position) 정보 갱신
		// target의 위치를 기준으로 distacne만큼 떨어져서 쫓아간다
		transform.position = transform.rotation * new Vector3(0, 0, -distance) + target.position;
	}
	
	private float ClampAngle(float angle, float min, float max)
	{
		if ( angle < -360 )	angle += 360;
		if ( angle > 360 )	angle -= 360;

		return Mathf.Clamp(angle, min, max);
	}
}
 

작성한 스크립트를 컴포넌트로 등록하고 player 오브젝트를 target으로 등록한다. 그러면 player가 움직일 때 카메라가 쫓아다니는 것을 확인할 수 있다.

마우스 오른쪽 버튼을 눌러 마우스를 움직이면 카메라가 회전하는 것도 확인할 수 있다. 휠 스크롤로 줌인 줌아웃을 할 수 있다.

728x90

이 글은 인프런 [따라하면서 배우는 고박사의 유니티 기초] 강의를 수강하고 정리한 글입니다. 

 

[지금 무료] 따라하면서 배우는 고박사의 유니티 기초 | 고박사 - 인프런

고박사 | 유니티로 게임을 개발하고 싶은 초보자를 대상으로 하며, 유니티 설치부터 2D/3D 게임 개발에 필요한 기초 지식까지 자세하게 설명합니다. (강의에 사용되는 모든 리소스는 영상 상단의

www.inflearn.com


Navigation Mesh

Navigation Mesh는 바닥, 장애물과 같은 게임월드의 정보를 미리 저장해두고 에이전트 이동 시 저장된 정보를 바탕으로 장애물을 피해 목표 위치까지 이동하는 경로 탐색을 제공한다.

이를 위해 게임 월드에 Navigation Mesh를 설정하고 이동 에이전트에게 NavMeshAgent 컴포넌트를 기반으로 이동하도록 한다. 또한 offMeshLink 나 NavMeshObstacle 컴포넌트도 사용한다.

 

인프런 강의 자료가 아닌 유튜브에 있는 게임월드 패키지를 다운받아 진행하도록 한다.

 

3DGameBasic02-01.zip

 

drive.google.com

 

게임 월드 prefab을 Hierarchy View로 이동하여 배치한다. 


Navigation View

 

현재 2022.3..22f1 버전에서 Navigation View는 보이지 않기 때문에 강의와는 다르게 다운을 받아야 한다. Window > Package Manager > Unity Registry > AI Navigation 들어가서 AI Navigation 을 설치한다.

Navigation이 잘 보인다. 

 

Agents

네비게이션 메시 정보를 바탕으로 움직이는 이전트에 대한 설정 (NavMeshAgent 컴포넌트)

Agent Type

에이전트 속성으로 “+”로 새로운 에이전트 속성을 추가할 수 있다.

Agnet 정보

  • Name : Agent Type에 보여지는 이름
  • Radius : 에이전트의 반지름
  • Height : 에이전트의 높이(키)
  • Step Height : 오르내릴 수 있는 계단의 높이
  • Max Slope : 올라갈 수 있는 경사 각도

 

 

Areas

네비게이션 메시로 사용되는 오브젝트들의 구역 설정

Name

구역 이름으로 기본으로 Walkable(이동 가능), Not Walkable(이동 불가능), Jump(뛰기)가 제공되고, User 3부터 원하는 구역을 추가로 설정할 수 있다.

Cost

구역과 함께 등록. 이동하는데 소요되는 비용 (1 이상) 경로를 탐색할 때 Cost 정보를 기준으로 최단거리를 찾게 된다.

Cost가 2인 Jump는 Cost가 1인 Walkable을 지나갈 때보다 2배의 시간이 걸린다는 뜻으로 사용된다. (실제 오브젝트의 물리적인 이동 속도가 느려 지는 것이 아닌 경로를 계산할 때 활용된다)

 

 

Bake

네비게이션 메시 데이터를 생성

Baked Agent Size

  • Agent Radius : 에이전트가 지나갈 수 있는 반지름
  • Agent Height : 에이전트가 아래로 지나갈 수 있는 높이
  • Max Slope : 에이전트가 올라갈 수 있는 경사 각도
  • Step Height : 에이전트가 오르내릴 수 있는 계단의 높이
  •  

Generated Off Mesh Links

  • 오프 메시 링크는 올라가기 힘든 언덕, 사다리, 절벽 등을 연결해서 이동 가능하게 만드는 옵션
  • Drop Height : 이동할 수 있는 절벽 아래의 높이
  • Jump Distance : 뛰어서 넘을 수 있는 절벽 거리

Bake 버튼

Navigation에 설정된 옵션들을 바탕으로 네비게이션 정보를 데이터로 굽는다

 
 
 
 

Object

현재 씬(Scene)에 있는 오브젝트 설정 (하나 or 다수)

Scene Filter

현재 씬에서 원하는 오브젝트만 선택해서 볼 수 있다. (Mesh Renderer 컴포넌트, Terrain 컴포넌트 선택 가능)

선택된 오브젝트

  • Navigation Static : 네비게이션 메시로 사용할지 설정
  • Generate OffMeshLinks : 자동으로 Off Mesh를 생성할지 설정
  • Navigation Area : 해당 오브젝트의 구역 설정 (설정되는 구역에 따라 해당 오브젝트의 Cost가 설정됨)
 

 


Navigation Mesh 데이터 생성

게임 오브젝트의 Navigation static 설정하는 방법으로 원하는 게임 오브젝트 선택 후 Inspector View의 static > Navigation static 선택한 후 Object tab에 있는 Navigation Static 변수를 선택한다. 

Navigation View > Bake 에서 Bake 버튼을 누르면 현재 설정된 정보를 바탕으로 Navigation Mesh 데이터가 생성된다.

 

Scene view를 보면 하늘색으로 표시되는 부분이 이동할 수 있는 이동 경로이고, 하늘색 없으면 이동 불가능한 구역이다. Navigation View가 활성화 되어 있어야 Navigation Mesh가 출력된다.

또한 맵에 새로운 바닥 혹은 장애물을 배치했다면 Navigation Static 설정과 Bake를 다시 진행해야한다. 

 


경사각(Max Slope)과 계단 높이(Step Height) 설정

bake tab에서 Max slope가 45로 설정되어 있어서 경사각 45도까지는 이동할 수 있다. 그렇기 때문에 slope02 경로가 이어지지 않았다. 

 

 Step height는 오르내릴 수 있는 계단의 최대 높이이다. 현재 Step Height는 0.4로 인접한 두 계단 사이의 높이가 0.4 이하 차이가 나는 것 까지만 오를 수 있어 오른쪽의 경우 이동 경로가 생성되지 않았다. 

플레이어 오브젝트 생성

플레이어 오브젝트를 생성하고자 capsule 오브젝트를 추가한 뒤에 색상 설정을 위해 meterial 생성한다.

 

Player 오브젝트의 설정은 아래와 같이 했다.

 
 

 


Navigation Mesh 기반의 캐릭터 이동

player 오브젝트에 NavMeshAgent 컴포넌트를 등록한다. 

NavMeshAgent component

네비게이션 메시 정보를 기반으로 이동하는 에이전트 

 

 

에이전트 이동 (Steering)

  • Speed : 이동 속도
  • Angular Speed : 방향을 바꿀 때의 회전 속도
  • Acceleration : 가속도 (정지 상태에서 이동속도가 될 때까지 적용)
  • Stopping Distance : 목적지가 이 값까지 가까워지면 이동을 멈추게 된다
  • Auto Braking : 목적지에 가까워지면 멈추는 기능
  • (목적지에 도착해도 에이전트를 멈추지 않을 때 사용)
  • (여러 목적지를 계속 탐색하는 Patrol에 주로 사용)

장애물 회피 (Obstacle Avoidance)

  • Radius : 장애물을 회피할 때 에이전트의 반지름
  • Height : 에이전트의 높이
  • Quality : 장애물과 충돌 수준 (None이면 뚫고 지나간다)
  • Priority : 장애물과 충돌했을 때의 우선순위 (낮을 수록 높다)
  • (이동 중인 두 에이전트의 모든 조건이 동일할 때 Priority가
  • 낮은 에이전트가 더 우선권을 가지고 경로를 탐색하게 된다)

경로 탐색 (Path Finding)

  • Auto Traverse Off Mesh Link : 오프 메시 링크가 있을 경우
  • 자동으로 탐색해서 찾아갈지 설정
  • Auto Repath : 이동 중에 경로 탐색을 다시 할지 설정
  • (true : 이동 중에 장애물 등으로 막혔을 때 자동으로 재 계산)
  • Area Mask : 해당 에이전트의 이동 가능한 구역 지정

이동을 제어하는 스크립트 Movement3D

using System.Collections;
using UnityEngine;
using UnityEngine.AI;

public class Movement3D : MonoBehaviour
{
	[SerializeField]
	private	float		 moveSpeed = 5.0f;
	private	NavMeshAgent navMeshAgent;

	private void Awake()
	{
		navMeshAgent = GetComponent<NavMeshAgent>();
	}

	public void MoveTo(Vector3 goalPosition)
	{
		// 기존에 이동 행동을 하고 있었다면 코루틴 중지
		StopCoroutine("OnMove");
		// 이동 속도 설정
		navMeshAgent.speed = moveSpeed;
		// 목표지점 설정 (목표까지의 경로 계산 후 알아서 움직인다)
		navMeshAgent.SetDestination(goalPosition);
		// 이동 행동에 대한 코루틴 시작
		StartCoroutine("OnMove");
	}

	IEnumerator OnMove()
	{
		while ( true )
		{
			// 목표 위치(navMeshAgent.destination)와 내 위치(transform.position)의 거리가 0.1미만일 때
			// 즉, 목표 위치에 거의 도착했을 때
			if ( Vector3.Distance(navMeshAgent.destination, transform.position) < 0.1f )
			{
				// 내 위치를 목표 위치로 설정
				transform.position = navMeshAgent.destination;
				// SetDestination()에 의해 설정된 경로를 초기화. 이동을 멈춘다
				navMeshAgent.ResetPath();

				break;
			}

			yield return null;
		}
	}
}

NavMeshAgent와 같이 유니티에서 만든 인공지능을 사용하기 위해서는 UnityEngine.AI가 필요하다.

  • NavMeshAgnet.SetDestination(Vector3 position) : position을 목표지점으로 설정 (목표지점만 설정되면 경로를 탐색해서 자동 이동함)
  • float distance = Vector3.Distance(Vector3, Vector3) ;a와 b 두 벡터 사이의 거리 값을 구한다.
  • NavMeshAgnet.destination : SetDestination()으로 설정한 목표지점 Vector3 정보가 저장되어 있다.
  • NavMeshAgent.ResetPath() : 현재 설정되어 있는 이동 경로를 초기화하여 이동을 멈추게 한다 .

Physics.Raycast()를 이용한 오브젝트 선

우리가 바라보는 화면은 2차원 모니터이고, 게임 세상은 3차원으로 이루어져 있다. 즉, 2차원(2D)을 통해 3차원(3D)의 오브젝트를 제어해야 한다.

구현 원리

  1. 카메라로부터 플레이어가 클릭한 마우스 위치를 관통하는 광선을 생성하여 발사한다.
  2. 현재 카메라에 보이는 화면을 관통해 뻗어나가는 광선은 우리가 지정한 길이에 도달하거나 오브젝트에 부딪히면 멈춘다.
  3. 오브젝트에 부딪혀 멈추게 되면 부딪힌 오브젝트의 정보를 반환한다.(광선에 부딪혔다 = 마우스로 선택한 오브젝트이다.)

플레이어를 제어하는 스크립트 PlayerController

using UnityEngine;

public class PlayerController : MonoBehaviour
{
	private	Movement3D movement3D;

	private void Awake()
	{
		movement3D = GetComponent<Movement3D>();
	}

	private void Update()
	{
		// 마우스 왼쪽 버튼을 눌렀을 때
		if ( Input.GetMouseButtonDown(0) )
		{
			RaycastHit hit;
			// Camera.main : 태그가 "Camera"인 오브젝트 = "Main Camera"
			// 카메라로부터 마우스 좌표(Input.mousePosition) 위치를 관통하는 광선 생성
			// ray.origin : 광선의 시작 위치
			// ray.direction : 광선의 진행 방향
			Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

			// Physics.Raycast() : 광선을 발사해서 부딪히는 오브젝트를 검출
			// (광선에 부딪히는 오브젝트가 있으면 true 반환)
			// ray.origin 위치로부터 ray.direction 방향으로 무한한 길이(Mathf.Infinity)의 광선 발사
			// 광선에 부딪히는 오브젝트의 정보를 hit에 저장
			if ( Physics.Raycast(ray, out hit, Mathf.Infinity) )
			{
				// hit.transform.position : 부딪힌 오브젝트의 위치
				// hit.point : 광선과 오브젝트가 부딪힌 세부 위치
				
				// hit.point를 목표위치로 이동!
				movement3D.MoveTo(hit.point);
			}
		}
	}
}
 

 

 
카메라가 게임월드를 전체적으로 볼 수 있도록 카메라의 위치와 회전값을 수정한 뒤에 실행한다. 선택한 위치로 player 오브젝트가 이동하지만 이동경로가 없는 사다리나 높은 경사면, 절벽 등을 선택하는 경우 근처까지만 이동하고 더 이상 이동하지 못한다. 
728x90

이 글은 인프런 [따라하면서 배우는 고박사의 유니티 기초] 강의를 수강하고 정리한 글입니다. 

 

[지금 무료] 따라하면서 배우는 고박사의 유니티 기초 | 고박사 - 인프런

고박사 | 유니티로 게임을 개발하고 싶은 초보자를 대상으로 하며, 유니티 설치부터 2D/3D 게임 개발에 필요한 기초 지식까지 자세하게 설명합니다. (강의에 사용되는 모든 리소스는 영상 상단의

www.inflearn.com

 

CharacterController 컴포넌트

CharacterController 컴포넌트는 유니티에서 제공하는 컴포넌트로 3차원 게임에서 사람 형태의 캐릭터 움직임과 관련된 제어를 위해 사용하는 컴포넌트이다.

캡슐(Capsule) 형태의 collider이 포함되어 있기 때문에 따로 Collider 컴포넌트를 추가하지 않아도 충돌 처리 및 물리처리가 가능하다.

캐릭터의 움직임을 위해 제작된 컴포넌트이기 때문에 오브젝트가 이동할 수 있는 경사각도, 올라갈 수 있는 계단 높이와 같은 정보를 설정할 수 있으며, center, radius, height 정보를 설정해 collider을 설정할 수 있다. 

  • Slope Limit : 올라갈 수 있는 경사 한계 각
  • Step Offset : 설정 값보다 낮은 높이의 계단(그 외 오브젝트)을 오를 수 있다
  • Center : Capsule 충돌 범위의 중심점
  • Radius : Capsule 충돌 범위의 반지름 (x, z)
  • Height : Capsule 충돌 범위의 높이 (y)

 


게임 월드 구성, 플레이어 캐릭터 제작

영상 하단에 있는 게임월드 패키지를 프로젝트 내부로 가져온다. prefab 폴더에 GameWorld prefab을 Hirearchy view로 드래그한다.

 

한 눈에 볼 수 있도록 main camera를 다음과 같이 바꾸어 준다.

 

player로 사용할 3D 오브젝트를 생성한다.(GameObject > 3D Object > Capsule)

player의 색상을 변경할 때 3D에서는 Project View > “+” > Material로 meterial Asset을 생성하고 색상과 이미지를 정하고 오브젝트에 등록해야한다. 

 

설정이 완료되었다면 플레이어 오브젝트의 Mesh renderer 컴포넌트에서 등록을 해준다.

플레이어 오브젝트에 character controller 컴포넌트를 추가하고 capsule collider를 지워준다. scene view에서 확인을 해보면 캡슐 범위의 충돌 범위가 capsule collider에 의하여 설정된 것을 확인할 수 있다.


플레이어 캐릭터 이동

캐릭터 컨트롤러를 기반으로 이동을 제어하는 Movement3D 스크립트를 작성한다. 

using UnityEngine;

public class Movement3D : MonoBehaviour
{
	[SerializeField]
	private	float		moveSpeed = 5.0f;	// 이동 속도
	private	Vector3		moveDirection;		// 이동 방향

	private	CharacterController	characterController;

	private void Awake()
	{
		characterController = GetComponent<CharacterController>();
	}

	private void Update()
	{

		characterController.Move(moveDirection * moveSpeed * Time.deltaTime);
	}

	public void MoveTo(Vector3 direction)
	{
		moveDirection =  direction;
	}

}

플레이어를 제어하는 스크립트, PlayerController 를 생성한다. 

using UnityEngine;

public class PlayerController : MonoBehaviour
{
	private	Movement3D			movement3D;

	private void Awake()
	{
		movement3D = GetComponent<Movement3D>();
	}

	private void Update()
	{
		// x, z축 방향으로 이동
		float x = Input.GetAxisRaw("Horizontal");	// 방향키 좌/우 움직임
		float z = Input.GetAxisRaw("Vertical");		// 방향키 위/아래 움직임
		
		movement3D.MoveTo(new Vector3(x, 0, z));

	}
}

 

생성한 두 스크립트를 플레이어 오브젝트에 컴포넌트에 추가한다. 방향키의 방향에 따라 오브젝트가 움직이는 것을 확인할 수 있다. 


중력 적용

위에 플레이어 오브젝트의 y 축을 올렸을 때, 중력에 의해 떨어지지 않고 공중에서 이동을 하는데 캐릭터 컨트롤러 컴포넌트는 충돌 처리가 가능할 뿐 중력은 적용되지 않기 때문이다. 중력 적용을 위해 직접 스크립트를 통해 입력을 해야한다.

우선 Movement3D 스크립트 코드로 가서 중력 계수를 추가하였고, 업데이트 함수를 확인하면 characterController.isGrounded == false 즉 캐릭터의 발 위치의 충돌을 체크하여 충돌이 된다면 true, 아니라면 flase값을 나타내는 변수로 캐릭터가 땅에 닿아있을 때를 확인하는 변수로 사용하기 때문에 false로 설정했다.

공중에 있을 떄 moveDirection.y += gravity * Time.deltaTime; 를 통해 y축 이동 방향에 gravity * Time.deltaTime을 계속 더해준다. gravity가 음수이기에 y축 이동 방향은 계속 감소하게 되고, 계속 아래로 떨어지려고 하는 중력을 받는 것이 적용되게 될 것이다. 

private	float		gravity = -9.81f;	// 중력 계수

```

private void Update()
	{
		if ( characterController.isGrounded == false )
		{
			moveDirection.y += gravity * Time.deltaTime;
		}

		characterController.Move(moveDirection * moveSpeed * Time.deltaTime);
	}
public void MoveTo(Vector3 direction)
{
    moveDirection	= new Vector3(movedis.x, moveDirection.y, movedis.z);
}

플레이어 캐릭터 점프

[SerializeField] private float jumpForce = 3.0f; // 뛰어 오르는 힘

뛰는 힘을 변수를 생성하고 Jump To 함수를 public으로 선언하여 외부에서 호출하는 용도로 제작하였다. 

public void JumpTo() 
{ 
	if ( characterController.isGrounded == true ) 
	{ 
    	moveDirection.y = jumpForce; 
    } 
}

 

이 함수가 호출되면 characterController.isGrounded 값이 true일 때, 즉 캐릭터가 바닥에 있으면 moveDirection y값에 jumpforce가 대입한다. 즉 y축 방향이 3이 되어 위로 점프 된다.

update에 작성했던 위 코드에 의해 위로 점프를 하지만 y축 이동 방향 값이 서서히 감소하게 되어 0이 되는 시점이 점프 최고점이 될 것이고 다시 음수에 의해 떨어지게 된다.

// y축 방향으로 뛰어오름
		if ( Input.GetKeyDown(jumpKeyCode) )
		{
			movement3D.JumpTo();
		}

스페이스바를 눌렀을 때 movement3D에 있는 JumpTo() 함수를 호출해서 점프하게끔 설정하였다.

 


경사면 이동과 계단 이동

Slope Limit 변수는 경사 한계 각도를 설정할 수 있으며 현재 45도로 설정되어 있어 45도까지 경사를 오를 수 있다. 

Step Offset 변수는 인접한 계단 사이의 높이가 해당 변수 이하일 때 계단을 오를 수 있다. 

GameWorld에 경사면이 총 2개가 있고 하나는 39도 경사라 오를 수 있지만 다른 하나는 52도여서 올라갈 수 없다. 

인접한 계단의 높이 차가 0.4로 플레이어의 step offset 값을 0.4로 변경하면 계단에 올라갈 수 있다.


1인칭 카메라 제어

먼저 camera 오브젝트가 플레이어를 쫓아다니도록 player의 자식으로 설정한다. 그리고 카메라 transeform 정보를 초기화한 뒤에 y축을 0.6으로 바꾼다.

scene 뷰에서 카메라가 player 오브젝트 안에 있는 것을 확인할 수 있었다.

카메라의 회전을 설정하기 위한 스크립트를 하나 작성한다.

using UnityEngine;

public class CameraController : MonoBehaviour
{
	private	float	rotateSpeedX = 3;
	private	float	rotateSpeedY = 5;
	private	float	limitMinX = -80;
	private	float	limitMaxX = 50;
	private	float	eulerAngleX;
	private	float	eulerAngleY;

	public void RotateTo(float mouseX, float mouseY)
	{
		// 마우스를 좌/우로 움직이는 mouseX 값을 y축에 대입하는 이유는
		// 마우스를 좌/우로 움직일 때 카메라도 좌/우를 보려면 카메라 오브젝트의
		// y축이 회전되어야 하기 때문
		eulerAngleY += mouseX * rotateSpeedX;
		// 같은 개념으로 카메라가 위/아래를 보려면 카메라 오브젝트의 x축이 회전!
		eulerAngleX -= mouseY * rotateSpeedY;

		// x축 회전 값의 경우 아래, 위를 볼 수 있는 제한 각도가 설정되어 있다
		eulerAngleX = ClampAngle(eulerAngleX, limitMinX, limitMaxX);

		// 실제 오브젝트의 쿼터니온 회전에 적용
		transform.rotation = Quaternion.Euler(eulerAngleX, eulerAngleY, 0);
	}

	private float ClampAngle(float angle, float min, float max)
	{
		if ( angle < -360 )	angle += 360;
		if ( angle > 360 )	angle -= 360;

		// Mathf.Clamp()를 이용해 angle이 min <= angle <= max을 유지하도록 함
		return Mathf.Clamp(angle, min, max);
	}
}

RotateTo() 함수는 외부에서 마우스의 x,y축 움직임을 가져와 카메라의 로테이션에 적용해 카메라를 회전시키는 함수이다.

마우스를 좌/우로 움직일 때 카메라도 좌/우를 보려면 카메라 오브젝트의 y축이 회전해야하기 때문에 마우스를 좌/우로 움직이는 mouseX 값을 y축에 대입한다. 

		// 같은 개념으로 카메라가 위/아래를 보려면 카메라 오브젝트의 x축이 회전!
		eulerAngleX -= mouseY * rotateSpeedY;

위에 함수 코드 중에 위에 부분에서 더하지 않고 빼는 이유는 마우는 아래로 가는 것이 -이기 때문에 빼준다. 

 

playercontroller에서 카메라 제어하는 부분을 구현하도록 하자면, CamerController에 있는 Rotate To 함수를 호출하기 위해서 변수를 생성한다. 

[SerializeField]
private	CameraController	cameraController;

private void Update()
	{
		// x, z축 방향으로 이동
		float x = Input.GetAxisRaw("Horizontal");	// 방향키 좌/우 움직임
		float z = Input.GetAxisRaw("Vertical");		// 방향키 위/아래 움직임
		
		movement3D.MoveTo(new Vector3(x, 0, z));

		// y축 방향으로 뛰어오름
		if ( Input.GetKeyDown(jumpKeyCode) )
		{
			movement3D.JumpTo();
		}

		// 카메라 x, y축 회전
		float mouseX = Input.GetAxis("Mouse X");	// 마우스 좌/우 움직임
		float mouseY = Input.GetAxis("Mouse Y");	// 마우스 위/아래 움직임

		cameraController.RotateTo(mouseX, mouseY);
	}

위의 두 변수 값이 CameraController의 Rotate To 함수의 매개변수값으로 전달되면 마우스가 움직였을 때 Rotate To() 함수가 호출되어 실제 카메라가 회전하게 된다.

 

Movement3D 스크립트에서 카메라를 제어하는 부분을 추가하여 방향키를 눌러 이동 시 현재 카메라가 비추고 있는 전방 방향으로 이동할 수 있도록 설정한다. 

public void MoveTo(Vector3 direction)
	{
		// 카메라가 바라보고 있는 방향을 기준으로 방향키에 따라 이동할 수 있도록 함
		Vector3 movedis = cameraTransform.rotation * direction;
		moveDirection	= new Vector3(movedis.x, moveDirection.y, movedis.z);
	}

	public void JumpTo()
	{
		if ( characterController.isGrounded == true )
		{
			moveDirection.y = jumpForce;
		}
	}
}

 

이렇게 설정하면 게임 실행시 마우스가 움직이는 곳을 카메라가 비추고 캐릭터가 잘 움직이는 것을 확인할 수 있다.

+ Recent posts