Q.비트연산자는 뭐야?

A.

컴퓨터가 나타내는 제일 작은 단위인 비트(bit = 0 또는 1)

비트 연산자는 bit(0,1)을 이동하거나 논리 연산을 할떄 사용하는 것이다.

논린 연산을 사용하면 이런식의 결과값을 얻을수 있다.

AND : 1000 & 0001 = 0000 
OR : 1000 & 0001 = 1001 
XOR : 1000 & 0001 = 1001 
NOT : 1000 -> 0111 

0010  << 1 = 0100 

0010  >> 1 = 0001

(<< , >> 이동시 처음과 끝 bit 는 0으로 채워진다.)

 

Q.비트연산자는 왜 쓰는거야?

A.

효율적이고 빠른 데이터 처리와 메모리 절약이 목적

Unity에서는 레이어마스크 처리 할 때 사용이 됨

 

사용하는 이유를 정확히 설명하면

1.메모리 효율성 :하나의 정수 값으로 각각의 비트를 개별적인 상태로 관리 할수 있기 때문에

예를 들이 32bit 정수 (4byte = int)는 32개의 서로 다른 상태를 한번에 표현 할 수 있기 때문

2.빠른 성능 : 비트연산은 CPU에 최적화 된 연산이며 덧셋,곱셈 보다 비트 연산이 더 빠르게 수행되기 떄문에

그래픽 처리시 매우 높은 최적화 효율을 보인다.

3.복잡한 조건을 단순하게 표현 : 메모리 효율성과 좀 겹치는 내용이지만, 32개의 정수 단일값으로 다중 상태를 관리하기에 가독성이 매우 좋아짐 

 

Q.레이어마스크는 뭐야?

A.

Unity의 레이어비트마스크(Bit Mask)를 사용하여 여러 레이어를 하나의 값으로 표현하는 방식

오브젝트의 레이어 설정 및 32개의 레이어(int 정수= 32bit)

 

 p.s 비트마스크?

bit의 이진수 표현을 활용하여 나타내는 방법

 

Q.레이어마스크는 왜 쓰는거야?

A.

일반적으로 내 공격이 나한테 맞거나 , 적의 공격이 적에 맞거나 하는건 게임에서는 적용되면 않된다.

(물론 적용되는 게임들도 있다) 이러한 문제를 대처하기 위해 레이어마스크를 사용하는것이다 .

 

1.충돌 검사 : 비트 마스크를 사용하여 특정 레이어에 속한 오브젝트만을 대상으로 충돌 검사 수행이 가능

즉, 태그,이름(문자열)로 검색할 필요가 없으니 최적화에 도움이 됨

2.레이캐스팅 제어 : 레이캐스트가 특정 레이어의 오브젝트에만 반응하도록 비트마스크를 설정 할 수 있음

 

예시1)

객체에서 레이(빛)을 쏴서 부딪히는 객체를 탐지할때 ,장애물 오브젝트는 무시하고 탐색을 하고 싶을때

해당 장애물의 레이어를 제외하여 레이가 해당 장애물 오브젝트에 부딪힐때 투과해서 쏠수 있게 하는기능

레이(빛)이 장애물(8번레이어)에 닿은경우 투과하여 사람(5번 레이어)에 접촉하여 사람 오브젝트의 Data를 얻게됨

해당 내용을 코드로 작성하면 

public int ObstcalseLayer = 5;
public int PeopleLayer = 8;

LayerMask layerMask = 1 << ObstcalseLayer; //장애물(5번레이어)를 비트마스크로 생성후 레이어마스크에 초기화
raycastObj = ~raycastObj; //반전비트를 이용하여 장애물 레이어를 0으로 변경
 
 //레이캐스트에 layerMask를 인자값으로 넣어 장애물 레이어를 투과하여 레이캐스트를 발사
 Physics.Raycast (transform.position, transform.TransformDirection (Vector3.forward), hit, Mathf.Infinity, layerMask);

이렇게 작성된다.

 

3.카메라 렌더링 설정 : 카메라가 특정 레이어의 오브젝트를 렌더링하도록 설정하여, 게임의 시각적 요소를 세밀하게 제어를 가능하게 한다. 

 

Q.레이어마스크를 어떡해 쓰는거야?

A.우선 오브젝트에 있는 레이어를 코드내에서 사용하기위해 비트마스크로 생성해야한다.

 

1 << n // 1을 n번째 비트 위치로 시프트(이동)한다는 의미
//즉 n은 레이어(0~32 layer정수값을 의미), n번쨰 레이어를 나타내는 비트마스크로 생성한다는 의미

//예시
//충돌된 객체의 게임오브젝트에서 레이어를 초기화
int layer =  collision.gameObject.layer;

1 << layer; //호출받아온 레이어를 비트마스크로 생성하여 사용

 

해당 코드처럼 1 << n 을 사용하여 비트마스크로 생성하는것이다. 

 

그럼 비트마스크로 생성까지 완료했으면 그 이후에는 어떡해 해야되나?

해당 비트마스크를 이용하여 검사를 진행하면된다.

 

위에서 설명한 논리 연산자들을 활용하는 것이다.

AND (&): 특정 레이어의 존재 여부 확인할떄 사용
OR (|): 새로운 레이어를 추가할 때 사용
XOR (^):  두 레이어의 차이를 찾을떄 사용 ( 비교하는 대상이 다르면 1 )
NOT (~):  특정 레이어를 제외시킬때 사용 ( 모든비트 반전 )

 

보통 이 논리연산자들 이 특징들을 사용한다. 

 

public LayerMask levelCollisonLayer; //비교할 기준 레이어

private void OnTriggerEnter2D(Collider2D collision)
{
	//LayerMask의 value 프로퍼티는 해당레이어의 비트마스크를 십진수를 반환해준다.
    if (IsLayerMatched(levelCollisonLayer.value, collision.gameObject.layer))
    {
        //레이어가 일치하면 true
    }
	//레이어가 불일치하면 false
}



private bool IsLayerMatched(int value, int layer)
{
	//value는 비트마스크이기 때문에 그대로 사용 , layer는 비트마스크로 생성 
    
    //(value | 1 << layer) : | 연산자로 기준 레이어(value)와 객체와 충돌한 레이어(layer) OR연산
    
    //OR 연산된 비트마스크 가 기준 레이어와 일치할경우 True
    return value == (value | 1 << layer);
}

 

해당 코드는 OR 연산을 충돌된 객체 레이어가 기준 레이어와 일치할 경우 동작하는 코드이다.

OR 연산은 레이어를 추가할때 사용 하는데 , 이런식으로도 원하지 않는 레이어는 제외할수 있게 구성 할 수도 있다.

 


해당 테크닉은 유니티 게임 개발시에 정말 많이 사용될것이기 때문에 반드시 익혀둬야한다.

 

해당 애니메이터를 이용하여 설명

Q.StringToHash 메서드는 뭐야?

A.

Animator Class에 있는 메서드애니메이션의 파라미터의 이름(문자열)을 해시값으로 변경하여

최적화 및 성능 개선에 도움을 주는 메서드이다.

 

Q.Animator의 함수라는건 알겠어, 근데 왜 사용하는거야?

A.위에서 말했듯이 최적화 및 성능개선이라고 했다.

애니메이션의 파라미터를 코드로 사용할때

SetBool("isWalking",true);

이런식으로 문자열을 그대로 넣어서 사용하게 되면 파라미터의 문자열을 서로 비교하는 작업을 하는데

이때 처리비용이 크기 때문에 비효율적이라고 하는것이다.

 

하지만 StringToHash를 사용하여 문자열을 Hash값으로 변경해주면

int(정수)는 한 자리만 체크하므로 최적화에 매우 효율적이기 때문이다.

 

또한 문자열 -> 정수( 21억 범위)로 변홤하여 충돌(중복)이 일어날 확률이 매우 낮다!!!
문자열 -> 정수(Hash값)은 항상 동일한 해시값을 반환하기에 다른 파라미터를 건들일 일도 없다.

 

Q.StringToHash 단점이 있을까?

A.

정수 -> 문자열 반환에는 사용하지 못하는 단점이 있다, StringToHash는 일방향으로 작동하는 메서드이기 때문에

 

 p.s

정수(Hash값) -> 문자열은 항상 동일한 문자열을 반환하지 않기에 충돌이 발생 할 수 있다.

 

 

Q.StringToHash는 어떡해 사용해?

public class TopDownAnimationController : AnimationController
{
	//해당 객체에서 사용할 Class 위에 문자열을 해시값으로 변환한후 정적변수로 선언해놓기
    private static readonly int isWalking = Animator.StringToHash("isWalking");
    private static readonly int isHit = Animator.StringToHash("isHit");
    private static readonly int attack = Animator.StringToHash("attack");
 
 	
    //필요할때 해당 변수값을 가져와서 사용하기
 	private void Move(Vector2 vector)
	{
         animator.SetBool(isWalking);
	}

}

이런식으로 미리 문자열을 해시값으로 변환하여 선언한 뒤 (정적으로 생성하여 컴파일하면서 동시에 생성)

필요할때 해당 해시값 변수를 인자값으로 사용하면 된다.

//코드 일부를 가져왔습니다.

private InputActionAsset inputActions;

inputActions = GetComponent<InputActionAsset>();

 

해당 코드는 InputSystem 패키지를 이용하기 위해 스크립트 에서 InputActionAsset을 호출하는 코드이지만

계속 Null이 들어와 코드가 동작하지 않았다.

 

그래서 구글링 해보고 찾았는데 원인을 찾았다.

 

원인은 InputActionAsset이 컴포넌트가 아닌 ScriptableObject로 관리되기 때문이다.

ScriptableObject는 게임오브젝트에 컴포넌트로 추가될수 없다는 특성을 가지고있다.

 

그러기 때문에 PlayerInput 컴포넌트에 등록되있는  InputActionAsset을 호출해서 쓸 수 없던것이다.

 

그럼 어떡해 호출해야되는것인가?

1. Inspector에서 직접 등록해서 쓰는 방법

2. InputActionAsset 이 아닌 PlayerInput 컴포넌트를 호출하는 방법

private PlayerInput playerInput;
private InputActionAsset inputActions;

void Awake()
{
    playerInput = GetComponent<PlayerInput>();
    inputActions = playerInput.actions; 
}

이렇게 playerInput을 컴포넌트로 호출해서 사용하면된다.

ScriptableObject는 Unity에서 데이터를 저장하고 관리하는 유연한 데이터 컨테이너이다.

 

ScriptableObject의 특징을 나열하면

1.MonoBehaviour 보다 경령화 되었고 GameObject에 컴포넌트로 추가 할 수 없다.

2.유니티 CallBack 중에서 OnEnable(),OnDisable(),OnDestroy()만 호출이 가능하다.

3.인스턴스화를 하지 않기 떄문에 사본이 여러개가 생성되지 않아 메모리 할당 최적화에 용이하다. 

 

json,xml,csv 등으로 관리하던 데이터를 Unity에서 쉽게 사용 할 수 있도록 편의적인 기능으로 제공한 것이 ScriptableObject 이다.

 

그럼 ScriptableObject는 어떡해 사용하는가?

1. ScriptableObject를 어떡해 설정할건지 선언이 필요하다.

파일 폴더링(폴더 분류 및 정리)는 필수이다.

Class 기반이기에 똑같이 .cs 로 작성을 해주면된다.

 

1.CreataAssetMenu 라는 Attribute 를 통해 포멧에 맞춰 작성해주어야 프로젝트 창에서 문제없이 호출하여 생성이 가능해진다.

2.ScriptableObject로 사용할 Class는 ScriptableObject를 상속해줘야 된다.

3.Header라는 Attribute를 통해 Inspector창에 표시될때 항목을 나눠줄 수 있다.

4. ScriptableObject Class 기반이기에 상속이 가능하다 ( RangedAttackSO는 AttackSO를 상속하고있다)

 

2. ScriptableObject를 .cs로 설정을 다했는데 어떡해 생성해?

제대로 .cs를 작성하였다면 , 프로젝트 창에서 생성시에 위에 이미지처럼 제대로 표시될것이다.

RangedAttackSO를 생성하면 Inspector에서 부모 데이터까지 다 Inspector에서 표시되며 쉽게 데이터 값이 수정 할수있다. 

 

3. ScriptableObject를 생성했으면 어떤식으로 코드에서 사용돼?

CharaterStat 클래스에 attackSO를 멤버변수로 가지게 한상태

캐릭터 스텟에 AttackSO 라는 ScriptableObject를 추가하여 사용하며 필요할때 데이터를 가져와서 호출할 수있다.

 

위의 CharaterStat을 SerializeField화 해놓고 Inspector에서 수정 할수 있게 선언
Charater Stats Handler 컴포넌트에 ScriptableObject(PlayerDefaultAttackSO)를 설정하고 스텟값을 설정하는 모습

 

이런식으로 해당 객체에게 능력치 데이터를 선언할 수 있게 된다.

자식 Class로 형변환하여 사용도 가능

투사체를 발사하는 메서드인데 공격에 관련된 데이터를 ScriptableObject에 담아놔서 이렇게 꺼내 쓸수도 있다.

 

4. 스텟 능력치 말고도 쓸 수 있어?

물론이다.

1.다양한 패턴을 분리하여 ScriptableObject로 만들수도있고

2.FSM 구조같은 형태를 만들어 해당 캐릭 모드를 ScritptableObject로 만들수도 있고

3.미연시 게임의 대화 로직 같은 것도 .csv가아닌 ScritptableObject로도 만들 수있다.

 

위에 예시 든것 뿐만아니라 생각해서 활용하면 어떡해든 만들어 사용 할 수 있다. 

게임 개발을 하면 오브젝트를 회전시켜야 될 일이 정말 많다.

회전을 시키면 두가지 개념을 보게 되는데

오일러 각과 쿼터니언이다.

 

오일러 각(Euler Angle)

-3차원 공간의 절대적 좌표를 기준으로 물체의 회전을 측정하는 방법

-X,Y,Z 축을 기준으로 회전하기 때문에 직관적이고 조작이 용이함

-180도가 넘는 회전의 표현이 가능

-3번에 나누어 계산하기 떄문에 비용으 크며 짐벌락 현상이 발생할수 있음 

 

짐벌락 : 두개의 회전 축이 서로 겹쳐지는 현상

https://www.youtube.com/watch?t=97&v=zc8b2Jo7mno&feature=youtu.be

 

쿼터니언(Quaternion)

-방향과 회전을 표현하는 방식

-4차원 복소수를 이용한 회전표현 방법 ( x,y,z,w)로 이루어져 있고 , 각 성분은 축이나 각도가 아니다.

-180도가 넘는 회전을 표현하지 못한다 ( 180도 넘는 표현은 179 + 130 이런식으로 더해서 표현한다)

-각 축을 한번에 계산하여 비용이 적고 짐벌락이 발생하지 않는다.


위의 각 오일러 각 , 쿼터니언의 특징을 알고있으면 게임 개발시 많은 도움을 받을 수있다.

 

유니티는 Inspector 에서 나오는 Rotation(회전)은 vector3로 표현이 된다. 즉, 오일러 각을 의미한다.

하지만 유니티 내부에서는 회전을 계산시에 오일러각이 아닌 쿼터니언을 사용하게된다. 

 

즉, 코드내부에서 회전을 시킬경우 쿼터니언 메서드들을 사용해서 회전을 시켜줘야한다.

 

쿼터니언은 많은 메서드를 가지고 있지만 자주 사용하는것을 몇개 기록하였다.

 

1. Quaternion.Euler(x,y,z) : 오일러각을 쿼터니언으로 변경 


두 벡터의 거리(Vector2 direction)를 인자값으로 아크탄젠트를 이용하여 각도(rotZ)를 반환받은뒤

해당 각도를 이용하여 객체(armPivot)을 회전시키고 있다.


2. Quaternion.LookRotation : 머리를 회전하는 것처럼, 앞과 위를 특정한 방향으로 하는 회전 쿼터니언을 만듬
3. Quaternion,Slerp : 쿼터니언과 다른 쿼터니언 사이 내분점을 알수 있음,
구형보간 기법(Spherical Linear Interpolation)을 활용해서 쿼터니언에 쓰면 더 정확해짐
ex)두 쿼터니언 Q1,Q2가 있을때 30% 지점은 어디일까? 같은 문제를 풀기 유용해짐

 


쿼터니언과 벡터의 곱셈

 

쿼티니언을 활용해 객체의 회전뿐만아닌 벡터의 회전도 가능하다.

즉, 오른쪽으로 이동중인 객체를 위쪽으로 회전하는 구현이 가능하다.

유니티에서는 쿼터니언과 벡터의 곱을 지원하기에 순서만 맞춰서 코드를 작성해주면된다. 

쿼터니언 * 벡터 이 순서대로 계산해주면된다. 

https://0x72.itch.io/dungeontileset-ii

 

16x16 DungeonTileset II by 0x72

A free tileset + animated characters + weapons + others.

0x72.itch.io

타일맵 공부를 위해 해당 에셋으 사용하였습니다.

 

Q.게임 월드에 타일맵을 어떡해 생성해요?

A.하이어라키 창 우클릭 - 2D Object - TileMap - 원하는 타일맵 선택(해당 글에서는 Rectangular)

Rectangular : 흔히 아는 직각 모양의 타일맵

Hexagonal : 문명 게임의 육각형 타일맵을 제공

IsoMetric : 대각선 방향 + 위에서 아래로 바라보는 방향의 타일맵 제공

하이어라키창 우클릭 후 타일맵 생성

 

생성시에 Grid와 그 자식으로 Tilemap이 생성된다.

위의 Tilemap 오브젝트하나의 레이어 처럼 사용 될것이다. 

Grid 컴포넌트
Tilemap컴포넌트 , Tilemap Renderer에서 Order in Layer 를 변경하여 레이어 순서를 변경 가능

 

Q.어떡해 Tile을 생성해?

A.우리가 가지고 있는 Asset을 이용해서 Tile Palette를 만들어줘야한다.

말그대  물감을 짜서 그림그릴떄 사용하는 파렛트를 말하는것이다.

 

Tile Palette 생성은 프로젝트 창 우클릭 - Create - 2D - Tile Palette - 원하는 타일맵 파레트 선택

(해당 글은 Rectangular Palette를 선택)

Tile Palette를 Project 창에 생성된 모습

 

Tile Pallete를 생성 했으면 그 안에 스프라이트(물감)를 넣어야 된다.

 

팔렛트에 스프라이트 타일을 추가하는 방법

 

타일 팔렛트에 추가한 경우 프로젝트 창에 타일 스프라이트들이 추가 되있는 모습

 

>>실수로 스프라이트를 중복으로 넣었거나 잘못 배치한 경우

더보기
현재 똑같은 스프라이트가 2개가 들어가 있는 경우

 

팔렛트 배치를 잘못하거나 중복 스프라이트가 들어가거나 아니면 월드에 타일을 배치할때 잘못했을떄가 있을것이다.

 

그럴떄 당황하지 말고 타일 팔렛트의 펜 도구를 한번 클릭 후

타일 팔렛트의 펜 도구

 

Shift + 지우고 싶은 타일 클릭을 하게 되면 해당 타일이 사라지게 된다.

중복 스프라이트가 지워진 모습

Q.그럼 타일맵 레이어는 어떡해 추가해요?

A.Grid에 자식으로 타일팔렛트 오브젝트를 추가하고, 타일 팔렛트에서 설정하고 배치하면된다.

 

BackDeco를 선택 후 배치하면

Back과 BackDeco가 배치된 범위가 주황색 선으로 표시가 된다.

 

Q.타일에 충돌처리는 어떡해 해요?

A.Tile Collider 컴포넌트를 추가하면된다. 

Collider 레이어를 만들고 해당 TileMap 오브젝트의 Renderer를 비활성화 시키면된다.

 

Collider 타일맵 오브젝트를 생성하고 Tilemap Collider2D 컴포넌트를 추가하면 Scene 뷰에 콜라이더가 표시된다.

 

일반 오브젝트에 적용되는 콜라이더와 똑같이 스프라이트 크기에 맞춰 콜라이더가 생성된다.

물론 offset을 건들여 수정도 가능하다.

 

Render를 비활성화하면 이런 모습이 된다.
벽에 충돌하여 뚫지 못하고 지나가는 모습을 보여준다.

<InputManager>

Unity 작업시 기존부터 사용되어왔던 입력처리 패키지로 비교적 쉽게 사용가능하다는 장점이 있다.

Unity-ProjectSetting에 접근시 기본적으로 설치되어 있는 패키지 중 하나이다.

*Inspect를 통한 간단한 키매핑
    public KeyCode Up;
    public KeyCode Down;
    void Update()
    {
        movement = 0f;
        if(Input.GetKey(Up)) { movement += 1f; }
        if(Input.GetKey(Down)) { movement -= 1f; }
        rigidbody.velocity = new Vector2(0, movement * speed);    
    }

InputManager를 이용하여 키 맵핑과 오브젝트 조작까지 동작하게하는 코드이다.

 

위에 코드 적용시 Inspector에서 이런식으로 키맵핑이 가능해진다.

 

<InputManager 를 지양하게 된이유>

1.다양한 입력 장비 대응 : 현 게임시장은 다양한 플랫폼이 제공되고 있고 , 키맵핑도 매우 자유롭다.
하지만 InputManager는 해당 문제를 대응하기 부적절하다.
2.입력 처리와 실제 로직 실행주체의 분리의 목적
InputManager의 자체 문제는 아니지만, 기능 별로 class를 나누는 작업을 해야 유지 보수 및 확장성이 많이 상승한다.

-> OPP(객체지향프로그래밍) 에서 이런 설계 원칙을 단일책임원칙(SRP)라고 부름

 

위에 이렇게 단점이 있지만 실제로도 간단한 프로젝트를 만들때는 아직도 많이 사용된다.

 

<InputSystem>

위에 말한 InputManager의 단점을 극복하기 위해 새로 나온 패키지이다.

해당 패키지는 Unity 패키지에 포함되어있지 않기에 ProjectManager를 통해서 설치해야한다.

window - packageManager-inputsystem 설치 ( 현재 설치되있는상태라 Remove로 뜸)

 

InputSystem의 추가로 Unity에서 사용하는 모습

Input Action : 해당 동작에 대한 키를 맵핑 데이터를 모아두는 파일

Player Input 컴포넌트 : 자동으로 입력 행동을 처리하고 해당 게임 오브젝트에 메시지를 보내는 역할

 

Player Input의 Behaviour은 무엇인가?

해당 Input 입력 발생시 해당 게임 오브젝트에 메시지 보내는 여러 방식중 하나 라고 생각하면 된다.

https://docs.unity3d.com/Packages/com.unity.inputsystem@1.3/manual/Components.html#notification-behaviors

 

GameObject components for input | Input System | 1.3.0

GameObject components for input The Input System provides two MonoBehaviour components that simplify how you set up and work with input: Component Description PlayerInput Represents a single player, and that player's associated Input Actions. PlayerInputMa

docs.unity3d.com

해당 글음 참고하여 작성하였음

이름 특징
SendMessages  PlayerInput을 컴포넌트로 가지고있는 GameObject에 코드내용을 전달
MonoBehaviour 의 update,start와 같은 이벤트함수처럼 사용이 가능해짐
BroadCastMessages PlayerInput을 컴포넌트로 가지고있는 GameObject와 그 자식들에게 코드내용을 전달
성능적인 면에서 하자가 있기에 잘 사용되진 않음
InvokeUnityEvents UI의 button 컴포넌트처럼 Inspector 에서  Onclick() 이벤트 함수처럼 메서드를 추가해서 사용하는것 
Invoke CSharp Events 위의 기능과 비슷하지만 PlayerInput API에서 코드를 호출하여 사용이 가능하고 , inspector 표시되지 않고 오직 스크립트 코드로 동작한다.

onActionTriggered (collective event for all actions on the player)
onDeviceLost
onDeviceRegained 

해당 메서드들이 API에 등록되있다고 함

 

<InputSystem 사용>

Input Action Asset 에서 맵 추가하는 방법

 

 

그 외에도 기능이 많다. 예를들어 키를 홀드하고있는지 키를 몇번 더블 클릭하는지 설정이 가능하지만 기본적인것만 적어보았다.

Action Type 이름 : 입력을 어떻게 받을 것인지 설명
Value 일반적인 상태에 사용함 , 눌렀을 때, 누르고 있을때, 뗄 떼 등 다양한 상황에 대응이 가능해짐
Button 눌렀을때 발생하는 액션에 활용 되며 , Control 타입이 Button으로 고정 됨
Pass-Through 명확화(Disambiguation)을 거치지 않은 Value.
Value 쓰시면 됩니다.

입력이 들어오면 그 즉시 처리 되기에 중복 입력에 대한 필터링이 없음(액션 상태에 따른 시작,중지)가 이루어지지 않음

연속적인 입력 처리가 필요할때 주로 사용,동일한 입력에서 여러 프레임에 걸쳐 이벤트를 받을 수 있음

언제 사용하면 됨??
축(axis)기반 입력이나 마우스의 움직임 , 복잡한 키 조합
: 축적된 입력을 매 프레임마다 처리해야 하는 경우
ex)조이스틱 축, 마우스 이동 -> 연속적인 입력으로 매 프레임마다 값을 처리해야함
ex2) 멀티 터치 입력 -> 여러 입력을 동시에 수집하고 처리해야됨, 중복입력 필터링이 없기에 모든 입력을 받음

 

<InputSystem 예제>

using UnityEngine.InputSystem;

public void OnMove(InputValue value)
  {
      Debug.Log("OnMove" + value.ToString());
      Vector2 moveInput = value.Get<Vector2>().normalized;
      CallMoveEvent(moveInput);
      //실제 움직이는 처리는 여기서 작업하는게 아님 
      //PlayerMovement에서 작업을 하는것
  }

  public void OnLook(InputValue value)
  {
      //Debug.Log("OnLook" + value.ToString());
      Vector2 newAim = value.Get<Vector2>();
      //마우스 위치를 정규화 해버리면 제대로된 좌표가 반환되지 않음
      Vector2 worldPos = _camera.ScreenToWorldPoint(newAim);//화면 좌표계에 있기에 월드 좌표로 변환
      //벡터의 뺼셈으로 원하는 방향벡터를 계산
      newAim = (worldPos - (Vector2)transform.position).normalized;

      if (newAim.magnitude >= .9f)
      {
          CallLookeEvent(newAim);
      }
  }

 

수업 듣는 InputController의 구조를 살짝 떼왔다. Event Action으로 각 기능의 함수를 동작시키는 형태인데,

InputValue 타입의 변수 인자값을 받아 해당 동작에서 사용하는 간단한 예시이다.

 

하지만 게임 프로젝트 규모가 커질수록 Input Map들도 많아지게되며, map에 직접 접근해서 사용해야되는 경우가 존재한다.

 

ChatGPT - 참고 코드 : 사용시 꼭 읽어보고 사용할것 

using UnityEngine;
using UnityEngine.InputSystem;

public class ActionMapSwitcher : MonoBehaviour
{
    // InputActionAsset은 Unity의 Input Actions Asset 파일을 연결할 때 사용됩니다.
    public InputActionAsset inputActions;

    // Action Maps
    private InputActionMap playerActionMap;
    private InputActionMap uiActionMap;

    private void Awake()
    {
        // InputActionAsset에서 각 Action Map을 가져옴
        playerActionMap = inputActions.FindActionMap("Player");
        uiActionMap = inputActions.FindActionMap("UI");

        // Player Action Map에서 'Move' 액션을 가져와 이벤트 등록
        InputAction moveAction = playerActionMap.FindAction("Move");
        moveAction.started += OnMoveStarted;
        moveAction.performed += OnMovePerformed;
        moveAction.canceled += OnMoveCanceled;

        // UI Action Map에서 'Navigate' 액션을 가져와 이벤트 등록
        InputAction navigateAction = uiActionMap.FindAction("Navigate");
        navigateAction.started += OnNavigateStarted;
        navigateAction.performed += OnNavigatePerformed;
        navigateAction.canceled += OnNavigateCanceled;
    }

    private void OnEnable()
    {
        // 기본적으로 Player Action Map 활성화
        playerActionMap.Enable();
    }

    private void OnDisable()
    {
        // 비활성화 시 모든 액션맵을 비활성화
        playerActionMap.Disable();
        uiActionMap.Disable();
    }

    // 'Move' 액션 관련 콜백 함수들
    private void OnMoveStarted(InputAction.CallbackContext context)
    {
        Debug.Log("Move started");
    }

    private void OnMovePerformed(InputAction.CallbackContext context)
    {
        Vector2 moveInput = context.ReadValue<Vector2>();
        Debug.Log($"Move performed: {moveInput}");
    }

    private void OnMoveCanceled(InputAction.CallbackContext context)
    {
        Debug.Log("Move canceled");
    }

    // 'Navigate' 액션 관련 콜백 함수들
    private void OnNavigateStarted(InputAction.CallbackContext context)
    {
        Debug.Log("Navigate started");
    }

    private void OnNavigatePerformed(InputAction.CallbackContext context)
    {
        Vector2 navigateInput = context.ReadValue<Vector2>();
        Debug.Log($"Navigate performed: {navigateInput}");
    }

    private void OnNavigateCanceled(InputAction.CallbackContext context)
    {
        Debug.Log("Navigate canceled");
    }

    // Action Map 전환 예시
    public void SwitchToUI()
    {
        // Player Action Map 비활성화
        playerActionMap.Disable();

        // UI Action Map 활성화
        uiActionMap.Enable();
        Debug.Log("Switched to UI Action Map");
    }

    public void SwitchToPlayer()
    {
        // UI Action Map 비활성화
        uiActionMap.Disable();

        // Player Action Map 활성화
        playerActionMap.Enable();
        Debug.Log("Switched to Player Action Map");
    }

    private void Update()
    {
        // 예시: Escape 키를 누르면 UI로 전환, 다시 누르면 Player로 전환
        if (Keyboard.current.escapeKey.wasPressedThisFrame)
        {
            if (playerActionMap.enabled)
            {
                SwitchToUI(); // UI로 전환
            }
            else
            {
                SwitchToPlayer(); // Player로 전환
            }
        }
    }
}

 

해당 코드는 게임내에서의 Player와 UI창의 키 맵핑을 다르게 하고 사용하는 모습이다.

Esc 키를 누르면 UI로 전환 한번 더 누르면 Player로 전환하는 방식이다. 

 


결론 : 

1.InputManager는 간단한 프로젝트 진행시 사용하기 좋다.

 

2.InputSystem은 규모가 커지고 다양한 플랫폼에 제공 및 키 맵핑 변경을 한다면 좋은 선택지이다.

 

3.InputSystem의 핵심 개념은 , InputAction , Input Action Asset , Player Input 컴포넌트 이 세가지이다.

 

InputAction : 프로젝트 창에서 생성 가능한 Input 맵핑 데이터가 있는 파일

Input Action Asset : 여러 개의 입력 행동을 그룹화하는 방법, InputAction을 열면 설정창에서 ActionMaps,Actions,AcitonProperites를 수정할 수 있다.

Player Input 컴포넌트 : 입력 행동을 받아 해당 게임오브젝트에 메시지로 전달해주는 역할

인자값이 타입이 다름

Unity로 작업을 할때 충돌처리를 하기 위해서 많이 쓰던 메서드이다.

 

하지만, 수업을 듣는 도중 Unity 패키지의 자동완성으로 나오는 충돌처리 메서드의 인자값의 타입이 다르다는 말을 듣고나서 찾아보니 진짜 달랐다.

그 둘의 차이점을 알고싶어 따로 알아보았다.

 

<OnCollison~(Collison )>

using UnityEngine;

    private void OnCollisionEnter(Collision collision)
    {
        
    }

OnCollison~()메서드는 두개의 물체가 충돌 할때 충돌 지점,충돌한 물체,물리적 힘 등의 정보를 처리해야된다.

Collsion 객체는 충돌관 관련된 위의 정보를 담고 있기에 해당 타입을 사용하는 것이다.

 

Collision Class 내부 모습 , Impulse,Velocity 등등 물리와 관련된 변수가 보인다.

<OnTrigger~(Collider )>

using UnityEngine;

 	private void OnTriggerEnter(Collider other)
 	{
     
 	}

OnTrigger()는 객체들이 충돌 했을때 물리적 충돌은 발생하지 않고 , Collider로 들어온 객체의 Data를 인자값으로 들고온다. 즉 쉽게 말하기 위해 충돌이라고 하는거지 해당 트리거 영역에 객체가 들어왔을때 동작하는 메서드인것이다. 

하지만 물리동작을 하지 않는다고 Rigidbody를 빼놓으면 안된다.

(움직이는 쪽이 물리 시뮬레이션을 하면서 충돌체크를 하기 때문)

 

Collider Class의 내부 모습, 물리 충돌 변수가 존재하지 않는다 그 대신에 isTrigger 변수가 눈에 보인다.

 

결론 : OnCollison() 메서드와 OnTrigger()메서드의 자동완성 인자값이 다르기에 실제로 사용할때 주의하여 사용하자

+ Recent posts