2024년 1월 31일 수요일

Unity - DefaultExecutionOrder





여러 스크립트가 오브젝트안에 있어서 
Start() 함수가 실행되는 순서가 뒤죽박죽인 경우가 있다.

이럴 때 아직 초기화되지 않은 변수를 전달하거나 참조하면
오류가 발생하는데


DefaultExecutionOrder로 실행 순서를 정해준다.



숫자가 작을 수록 우선 순위가 높음






2024년 1월 29일 월요일

Unity - Blend 오류

 


갑자기 잘 작동되던 Blend 파일이 인식이 안되서 오류가 뜨는 문제가 생겼음



그냥 Fbx로 변환하여 해결함

2024년 1월 24일 수요일

Unity - Random.Range

 

UnityEngine.Random 클래스

유니티에서 난수를 생성하려고 할 때 사용하는 클래스이며



범위내에 무작위 숫자를 리턴한다. 

범위가 Int형일 경우에는 0, 1, ...이런식으로 소숫점 부분은 날려버리고




Float 형일 때는 소숫점 7번째 자리까지 리턴한다.



다만 int형의 경우 최대 범위를 포함하지 않으며


Float형은 최대범위 까지 포함하여 리턴하므로 주의해야 한다.

float 형인지 int 형인지 구분하기 위해서 

평소에 float형이면 .0f까지 붙이는 습관을 기르는 것이 좋다고 생각한다.

2024년 1월 22일 월요일

Unity - 모바일 safeArea

 

핸드폰 기종마다 다르지만 아이폰 13처럼 카메라가

화면을 가리는 경우가 가끔 있다.



유니티를 모바일 게임으로 배포하게 될 경우

이처럼 게임이나 영상 시청에 방해되는 요소가 될 수 있으므로

처리해줘야 한다. 

 



Screen.safeArea로 카메라 쪽 가리는 공간의 크기를 구할 수 있다.

 m_topSpace = Screen.safeArea.height / Screen.height

safeArea 높이를 원래 해상도 높이로 나누어 비율을 구하고

이를 월드내 오브젝트의 위치를 옮겨주면 된다.

 



카메라 없는 기종은 그대로고, 있는 기종만 조절 되는 것을 볼 수 있다.

2024년 1월 18일 목요일

Unity - 미로 게임

 

미로 생성을 위한 코드는 여기서 가져옴

[유니티] 절차적 생성을 위한 미로 생성 알고리즘 2 (velog.io)



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
using System.Collections.Generic;
using UnityEngine;
using static UnityEditor.PlayerSettings;
 
public class MazeRunner : MonoBehaviour
{
 
    [SerializeField] private DragToMoveController m_controller;
    [SerializeField] private GameObject m_wall;//미로 안쪽 벽의 프리펩
 
    private bool[,] m_map;
    private int m_mazeWidth = 11;//벽까지 포함한 크기
    private int m_mazeHeight = 17;//벽까지 포함한 크기
    private Vector2Int m_pos = Vector2Int.zero;
    private Vector2Int[] m_direction = { Vector2Int.up, Vector2Int.down, Vector2Int.left, Vector2Int.right };
    private Stack<Vector2Int> m_stackDir = new Stack<Vector2Int>(); //지나온 길의 방향 저장
    private float m_timer = 0.0f;//타이머
    
    private void Start()
    {
        m_timer = 10.0f;
        m_controller.SetMoveSpeed(6.0f);
 
        m_map = new bool[m_mazeWidth, m_mazeHeight];//미로 크기 9x9
        m_pos = new Vector2Int(1, 1);
 
        for (int x = 0; x < m_mazeWidth; x++)
        {
            for (int y = 0; y < m_mazeHeight; y++)
            {
                m_map[x, y] = true; //모든 타일을 벽으로 채움
            }
        }
 
        BuildMaze();
 
        //벽 배치
        for (int x = 1; x < m_mazeWidth-1; x++)
        {
            for (int y = 1; y < m_mazeHeight-1; y++)
            {
                if (m_map[x, y])
                {
                    GameObject obj = Instantiate(m_wall, transform.GetChild(1));
                    obj.transform.position = new Vector3(0.8f * x-4, 0.8f, 0.8f * y-6.8f);
                }
                
            }
        }
    }
 
    // Update is called once per frame
    private void FixedUpdate()
    {
        m_timer -= Time.deltaTime;
        m_controller.UpdateMove();
    }
 
    private void BuildMaze()
    {
        do
        {
            int index = -1; //-1은 갈 수 있는 길이 없음을 의미
 
            RandDirection(); //방향 무작위로 섞음
 
            for (int i = 0; i < m_direction.Length; i++)
            {
                if (CheckWall(i))
                {
                    index = i; //선택한 방향에 길이 없을 경우 방향 배열의 인덱스 저장
                    break;
                }
            }
 
            if (index != -1) //갈 수 있는 길이 있을 경우
            {
                for (int i = 0; i < 2; i++) //길과 길 사이에 벽을 생성하기 위해 3칸을 길로 바꿈
                {
                    m_stackDir.Push(m_direction[index]); //스택에 방향 저장
                    m_pos += m_direction[index]; //위치 변수 수정
                    m_map[m_pos.x, m_pos.y] = false; //타일 생성
                }
            }
            else //갈 수 있는 길이 없을 경우
            {
                for (int i = 0; i < 2; i++) //길을 만들 때 3칸을 만들었기 때문에 뒤로 돌아갈 때도 3칸 이동
                {
                    m_map[m_pos.x, m_pos.y] = false; //완성된 길 의미
                    m_pos += m_stackDir.Pop() * -1; //방향을 저장하는 스택에서 데이터를 꺼낸뒤 -1을 곱해 방향 반전
                }
            }
        }
        while (m_stackDir.Count != 0); //스택이 0이라는 것은 모든 길을 순회했다는 것을 의미하므로 반복문 종료
 
 
    }
 
    private void RandDirection() //무작위로 방향을 섞는 메소드
    {
        for (int i = 0; i < m_direction.Length; i++)
        {
            int randNum = Random.Range(0, m_direction.Length); //4방향 중 무작위로 방향 선택
            Vector2Int temp = m_direction[randNum]; //현재 인덱스에 해당하는 방향과 랜덤으로 선택한 방향을 서로 바꿈
            m_direction[randNum] = m_direction[i];
            m_direction[i] = temp;
        }
    }
 
    private bool CheckWall(int index)
    {
        if ((m_pos + m_direction[index] * 2).x > m_mazeWidth - 2) return false; //2를 곱하는 이유는 길과 길 사이에 벽이 있기 때문
        if ((m_pos + m_direction[index] * 2).x < 0) return false;
        if ((m_pos + m_direction[index] * 2).y > m_mazeHeight - 2) return false;
        if ((m_pos + m_direction[index] * 2).y < 0) return false;
        if (!m_map[(m_pos + m_direction[index] * 2).x, (m_pos + m_direction[index] * 2).y]) return false;
 
        return true;
    }
 
    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player")
            Debug.LogFormat("GameClear in {0}sec", m_timer);
    }
}
 
cs

bool 배열에 미로를 설계하고 이것에 맞춰서

벽 프리펩을 Instantiate 구현해준다.



미로 시작 위치는 정해놨지만 끝부분을 안뚫어놔서  

가끔 막히는 경우가 있다. 이럴 땐

m_map[x, y] = false; 특정 위치의 벽을 없애면 그만이다.



2024년 1월 17일 수요일

Unity - 오브젝트 크기를 화면 해상도로 맞추기

 


사진에서 플레이 버튼을 누르게 되면

가로 방향의 Collider는 화면 밖에 있기 때문에

화면 밖으로 굴러가게 된다.

이를 방지하려면 어떻게 해야 할까



Collider를 위치를 움직여서 화면 밖으로 나가지 못하게 하면 된다.

하지만 모바일 게임 같은 경우 기종에 따라 화면 크기가 제 각각이라

현재 해상도를 가져와서 이를 World Space로 변환 해주고 가로 길이 만큼

위치를 변경해주면 된다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using UnityEngine;
 
class WallCollisions : MonoBehaviour
{
    [Header("Wall Collisions")]
    public GameObject m_left;
    public GameObject m_right;
 
    private float m_camSize = 10.0f;
 
    //테스트용
    private void Start()
    {
        SetWalls();
    }
 
    public void SetOrthographicSize(Camera camera)
    {
        m_camSize = camera.orthographicSize;
    }
 
    //사방면의 벽을 화면 크기에 맞춰서 옮기는 함수
    public void SetWalls()
    {
        float aspect = (float)Screen.width / Screen.height;
        float worldWidth = m_camSize * aspect;
 
        m_left.transform.position = new Vector3(-worldWidth, 00);
        m_right.transform.position = new Vector3(worldWidth, 00);
    }
}
 
cs





아이패드 아이폰 둘다 잘 맞춰진다. 
 



2024년 1월 14일 일요일

Unity - 캐릭터 이동 (Velocity)

이번에 모바일 게임을 만들게 되었는데

캐릭터 이동을 구현할 때 조이스틱으로 이동하지 않고

터치로만 모든것을 할 수 있도록 만들게 되었다.

그래서 터치를 위주로 관리하는 스크립트와 

이동을 구현한 스크립트를 나눠서 설명하겠음





먼저 터치 관리자에서 터치 시작, 끝, 드래그를 나눠서

어느 방향으로 드래그 하였는지,

아직 터치 중인 상태인지, 터치가 막 시작된 상태인지 판별하여

다른 클래스로 전달한다.





방향을 특정하는 변수는 드래그 중이였다가 손을 땟을 때 호출하여

어느 방향으로 손꾸락을 이동하였는지 저장한다.

그리고 다시 손을 댓을 때 초기화





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
using Unity.VisualScripting;
using UnityEngine;
 
public class JumpRopeGame : MonoBehaviour
{
    private Rigidbody m_rigidbody;
    private float m_speed = 3.0f;//이동 속도
    private float m_jumpPower = 300.0f;//점프 거리 
    private bool m_readyJump;//점프가능한 상태인지 확인하는 변수
    private bool m_rayHitted;//레이캐스트 hit한 물체가 있는지 확인하는 변수
 
    private void Start()
    {
        m_rigidbody = GetComponent<Rigidbody>();
    }
 
    private void Update()
    {
 
        float dis = 0.0f;
 
        {//땅과의 거리 측정
            Ray ray = new Ray(transform.position, Vector3.down);//캐릭터 위치에서 y -1방향으로 레이 생성
            RaycastHit hit;
 
            if (Physics.Raycast(ray, out hit))
                dis = ray.origin.y - hit.point.y;
        }
 
        if (dis < 0.51f)
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            Vector3 hitpos;
 
            RaycastHit hit;
            m_rayHitted = Physics.Raycast(ray, out hit);
 
            if (m_rayHitted && TouchManager.instance.IsBegan())//처음 터치한 대상이 플레이어일 때 점프가능한 상태로 만들기
            {//점프 가능한 상태일 때는 플레이어 이동이 안됨
                m_readyJump = hit.transform.tag == "Player" ? true : false;
            }
 
            if (m_readyJump && !TouchManager.instance.IsHolding())//점프 준비 상태에서 손을 땟을 때
            {
                if (TouchManager.instance.IsDragUp())//위로 드래그 했을 경우
                {
                    m_readyJump = false;//준비 상태 해제
                    m_rigidbody.AddForce(new Vector3(0, m_jumpPower, 0));//위로 AddForce
                }
            }
            
            if (m_rayHitted && TouchManager.instance.IsHolding() && !m_readyJump)//터치 누르고 있는 상태일 때 & 점프 준비 상태가 아닐 때
            {
 
                if (hit.transform.tag == "Terrain")
                {
                    hitpos = hit.point;
                }
                else
                {
                    hitpos = transform.position;
                }
 
                hitpos = hitpos - transform.position;
                hitpos.y = 0;
                m_rigidbody.velocity = hitpos.normalized * m_speed;
            }
        }
    }
}
 
cs

다음 캐릭터 이동에서 터치 관리자를 통하여 드래그 방향과

터치 상태를 가져와서 캐릭터 이동을 구현한다.

터치한 부분이 Terrain인 경우 이동할 수 있게 하였으며

캐릭터(구)를 클릭했을 때 점프를 준비하게 만들고 이동못하게 만든다.

이 상태로 플레이어가 아닌 다른 오브젝트를 누르면 준비 상태를 품



 결과


추가로 Rigidbody를 사용하여 이동시킬 때

 Rigidbody와 Character Controller를 동시에 추가하면 안됨




 

2024년 1월 10일 수요일

Unity - 게임 기획 순서

 

팀원 개발 환경 조사


개발 환경 통일
Visual Studio는 년도만





아이디어 수집



게임 세부 사항 구상







와이어 프레임 작성






프로젝트 전체 기능 구분
역할 배정






프로젝트 진행 시 규칙 선언




커밋 룰




코드 컨벤션 (코딩 규칙)






계획 일정 짜기


개발 시작


2024년 1월 4일 목요일

Unity Tutorial - Input System

 

일반적으로 Input.GetKey() 함수등으로 입력을 받아와서

그에 따른 행동을 하거나 값을 변경하여 캐릭터를 이동하거나

점프시켰는데 이번에는 Input System으로 구현해보려고 한다.






먼저 패키지 매니저에서 Input System을 Import 해주고

(주의! 자동으로 껏다 켜짐)




키입력을 지정해 주기 위해 Input Action을 생성해준다.




생성된 액션에서 Map을 생성하면 안에 Action을 생성 가능한데 


컨트롤 타입을 Vector로 지정해주면 아래의 바인딩을 추가가능하다


상하좌우 키를 바인딩하여 Vector2 수치를 스크립트에서 받아와 

캐릭터를 이동시켜야하는데 이는 캐릭터 오브젝트에 Input Action을 끌어다 놓으면 된다.




버튼 이벤트처럼 동작된 Action에 해당하는 함수를 호출하는 방식인데

이는 불필요한 함수 호출을 줄일 수 있어서 좋다




호출될 함수에는 콜백 메시지를 받기 위해 매개변수에 사진처럼 추가해준다.

전달받은 Vector2 값을 변수에 저장하여 이를 캐릭터의 Transform Position이나

Character Controller로 이동 시키면 된다.




+EventSystem은 Canvas를 생성하면 자동으로 생성되는데 

이를 InputSystem으로 교체해야 한다. 






Unity Tutorial - Animation Blend Tree

 

캐릭터가 움직일 때, 앞으로 가는 모션과 옆으로 갈 때는 움직이는 방향에 맞춰서

애니메이션을 지정하면 된다.

그런데 대각선으로 이동할때 해당 방향에 맞는 모션이 없으면 어떻게 할까





이를 위해 블랜드 트리를 사용하여 앞으로 가는 모션과 옆으로가는 모션을

블랜드 하여 대각선으로 가는 모션을 만들 수 있다.




우클릭해서 블랜드 트리 생성




블랜드 트리 내에서 애니메이션을 변수에 맞춰서 애니메이션을 전환해주는데

파라미터 타입은 Float만 된다. 다른 타입은 파라미터로 지정이 안됨





캐릭터가 움직이고 있는 방향을 디그리로 바꿔서

SetFloat함수로 Animator로 전달





아래쪽의 Automate Thresholds를 체크 해제 후

애니메이션들을 끌어다 놓는다.

원하는 파라미터를 지정하고 Threshold칸에 어떤 값일 때

애니메이션이 전환이 될지 적는다.

각 애니메이션 마다 프레임 길이가 다르므로 재생속도도 설정

(왼쪽 걷기 모션 안보여서 둘다 오른쪽으로 해둠)




결과


캐릭터가 이상하게 이동될땐 Animator 컴포넌트에서 Root


c++ thread.h

 c++에서 쓰레드 돌릴려면 thread.h 헤더를 쓰면 되는데 이 친구는 쓰레드가 아직 실행 중인지, 아니면 강제 종료하거나 하는 함수가 없어서 조금 아쉬운 애다. std::thread 는 로컬 변수로 선언하든 new 동적 할당을 하든 start 함...