[Unity3D] RPG게임 카메라 - 줌 인/아웃, 카메라 회전. [Part 2]
※ 주의
이 글은 아마추어가 개인적으로 생각하여 작성하는 것으로, 이곳에 나오는 내용을 맹신하지 않는것을 당부드립니다.
기능정리
1. 플레이어를 따라오는 카메라.
1.5. 플레이어의 움직임.
2. 마우스 휠 카메라 줌 인,아웃 기능.
3. 마우스 오른쪽 버튼 클릭 후 카메라 회전.
- x,y축 회전
- x축 회전의 한계치 설정.
1.5 플레이어의 움직임.
플레이어부터 움직여 보도록 하자.
카메라가 잘 따라오는지 확인하려면 플레이어가 움직여야 되지 않는가?
Player스크립트를 하나 만들어준다.
이 스크립트에는 2가지 함수가 들어갈 것이다.
1. Run();
2. Rotation();
첫 번째 함수는 플레이어를 움직이게 만드는 함수이고,
두 번째 함수는 플레이어에게 회전을 주어서 원하는 방향으로 이동할수 있게 한다.
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 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class Player : MonoBehaviour { public float Speed; // 움직이는 스피드. private Transform Vec; // 카메라 벡터. private Vector3 MovePos; // 플레이어 움직임에 대한 변수. void Init() { MovePos = Vector3.zero; } void Start() { Vec = GameObject.Find("CameraVector").transform; Init(); } void Update () { Run(); } // 플레이어 움직임. void Run() { int ButtonDown = 0; if (Input.GetKey(KeyCode.LeftArrow)) ButtonDown = 1; if (Input.GetKey(KeyCode.RightArrow)) ButtonDown = 1; if (Input.GetKey(KeyCode.UpArrow)) ButtonDown = 1; if (Input.GetKey(KeyCode.DownArrow)) ButtonDown = 1; // 플레이어가 움직임. 버튼에서 손을 땠을 때 Horizontal, Vertical이 0으로 돌아감으로써 // 플레이어의 회전상태가 다시 원상태로 돌아가지 않게 하기 위해서. if (ButtonDown != 0) Rotation(); else return; transform.Translate(Vector3.forward * Time.deltaTime * Speed * ButtonDown); } // 플레이어 회전. void Rotation() { MovePos.Set(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); // 벡터 셋팅. Quaternion q = Quaternion.LookRotation(Vec.TransformDirection(MovePos)); // 회전 if (MovePos != Vector3.zero) transform.rotation = q; } } | cs |
1. float Speed; 플레이어가 움직이려면 힘이 필요하다. Speed변수를 하나 만들어준다.
2. Transform Vec; 카메라 벡터는, 카메라가 회전한다고 해서 플레이어의 벡터가 달라지는것이 아니므로, 카메라의 벡터를 이용하여 플레이어를 이동시켜주기 위한 변수이다.
3. Vector3 MovePos; 키의 입력을 받아서 플레이어의 벡터를 셋팅해줄 변수이다.
Run() 함수 안에 ButtonDown변수는 키 입력에 반응해 값을 대입 받는다.
키 입력을 받지 않으면 값이 0으로 들어가 플레이어는 움직일수 없게 된다.
반대로 키 입력을 받아 ButtonDown변수에 0이 아닌 값이 들어간다면 플레이어는 움직이게 된다.
그리고, 회전 함수도 함께 수행된다.
ButtonDown변수가 0이 아닐때에만 회전함수를 수행하는 이유는
MovePos에 Horizontal, Vertical로 값을 입력받기 때문이다.
Horizontal은 ←,→에 입력에 따라 -1 ~ +1 의 값이 들어간다.
(※ Vertical은 ↑,↓)
키(Horizontal)를 입력받지 않을 때에는 반환 값이 0으로 들어가는데, 이때 회전 함수가 키 입력(Input.GetKey)에 상관없이 수행된다면 Horizontal에 반환 값 0을 입력받아 플레이어가 다시 원래의 벡터 방향으로 회전하게 된다.
이것을 방지해 주기 위한 처리이다.
1 2 3 4 5 6 7 8 9 10 11 12 | void Run() { int ButtonDown = 0; if (Input.GetKey(KeyCode.LeftArrow)) ButtonDown = 1; if (Input.GetKey(KeyCode.RightArrow)) ButtonDown = 1; if (Input.GetKey(KeyCode.UpArrow)) ButtonDown = 1; if (Input.GetKey(KeyCode.DownArrow)) ButtonDown = 1; Rotation(); transform.Translate(Vector3.forward * Time.deltaTime * Speed * ButtonDown); } | cs |
회전 함수를 예외처리 없이 수행했을 경우.
대각선으로 이동하다가 키에 손을 때면 게속 정면을 바라보게 된다.
다음은 회전함수를 살펴보도록 하자.
MovePos.Set을 활용하여 Horizontal과 Vertical의 입력 값을 셋팅한다.
그리고 쿼터니언(Quaternion)을 활용하여 플레이어에게 회전값을 줄것이다.
Quaternion.LookRotation( Vector3 ); - 이 함수는 인자에 들어온 벡터의 방향으로 물체를 회전시켜준다.
만약 A라는 물체가 B라는 물체를 바라보고 싶을때 B까지의 벡터를 구해 이 함수의 인자로 넣어주면
A는 B를 바라보도록 회전시켜준다.
TransformDirection( Vector3 ); - 이 함수는 인자로 들어온 값을 로컬공간에서 월드공간으로 방향을 변환해준다.
인자로 MovePos를 넣어, CameraVector오브젝트의 벡터를 현재 플레이어의 벡터로 활용하는 것이다.
그뒤 transform.rotation에 회전 값 q를 넣어서 적용시켜주면 적절히 회전하게 된다.
'Unity3D > Project' 카테고리의 다른 글
[Unity3D] RPG게임 카메라 - 줌 인/아웃, 카메라 회전. [Part 4] (3) | 2017.03.08 |
---|---|
[Unity3D] RPG게임 카메라 - 줌 인/아웃, 카메라 회전. [Part 3] (3) | 2017.03.08 |
[Unity3D] RPG게임 카메라 - 줌 인/아웃, 카메라 회전. [Part 1] (0) | 2017.03.06 |
[Unity3D] 캐릭터 움직임, 충돌체크, 물리엔진사용 X [Part 5] (0) | 2017.03.02 |
[Unity3D] 캐릭터 움직임, 충돌체크, 물리엔진사용 X [Part 4] (0) | 2017.03.02 |
[Unity3D] RPG게임 카메라 - 줌 인/아웃, 카메라 회전. [Part 1]
※ 주의
이 글은 아마추어가 개인적으로 생각하여 작성하는 것으로, 이곳에 나오는 내용을 맹신하지 않는것을 당부드립니다.
기능정리
1. 플레이어를 따라오는 카메라.
1.5. 플레이어의 움직임.
2. 마우스 휠 카메라 줌 인,아웃 기능.
3. 마우스 오른쪽 버튼 클릭 후 카메라 회전.
- x,y축 회전
- x축 회전의 한계치 설정.
완성작 미리보기.
1. 플레이어를 따라오는 카메라.
카메라가 플레이어와 아에 멀리 떨어져 있어도 따라오는 카메라를 만들어야 한다.
카메라가 직접 플레이어를 따라가게 만들지 않을것이다.
카메라맨이라는 빈 객체를 만들어서 이 객체가 플레이어를 직접적으로 따라갈 것이고,
카메라는 카메라맨과 일정 거리를 유지한체 따라다닐 것이다.
이러한 번거로운 작업을 할 필요 없이 '카메라를 플레이어의 자식으로 두면 되는거 아닌가요?' 라고 묻는다면
그냥 따라다니기만 할거라면 그렇게 해도 되지만, 우리는 줌 인/아웃 또는 카메라를 회전시킬 것이기 때문에
이러한 과정이 필요하다고 말해두겠다.
물론 이렇게 넣는다고 해서 이러한 과정이 불 가능해 지는것은 아니지만, 매우 귀찮은 코딩이 필요해 지기 때문에
그러한 과정은 피하도록 하겠다.
1. 빈 오브젝트를 만든다. - 이름은 CameraMan
2. 또다시 빈 오브젝트를 만든다. - 이름은 CameraVector
3. 또다시 빈 오브젝트를 만든다. - 이름은 Axis
4. CameraMan에 자식으로 CameraVector를 넣는다.
5. CameraMan에 자식으로 Axis를 넣는다.
6. Axis의 자식으로 Camera를 넣는다. (이 카메라는 메인카메라이다.)
재료를 만들었으니 이제 스크립트를 작성해 보자.
스크립트의 이름은 CameraMan이다.
이 스크립트는 CameraMan오브젝트에 넣으면 된다.
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
|
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraMan : MonoBehaviour {
// 공개
public float MoveSpeed; // 플레이어를 따라오는 카메라 맨의 스피드.
// 비공개
private Transform Target; // 플레이어의 트랜스 폼.
private Vector3 Pos; // 자신의 위치.
void Start()
{
// Player라는 태그를 가진 오브젝트의 transform을 가져온다.
Target = GameObject.FindGameObjectWithTag("Player").transform;
}
// 플레이어를 따라다님.
void Update ()
{
Pos = transform.position;
transform.position += (Target.position - Pos) * MoveSpeed;
}
}
|
cs |
(플레이어의 위치 - 자신의 위치)를 빼주면 현재 카메라맨의 위치에서 플레이어의 벡터(방향)가 구해진다.
그 벡터를 향해 값을 플러스(+) 해주면 카메라맨은 플레이어를 쫓아가게 된다.
* MoveSpeed는 카메라맨이 플레이어를 쫓아가는 스피드다.
※ 플레이어의 Transform을 가져오기위한 설정.
플레이어는 그냥 큐브를 하나 만들어주고 이름을 Player로 바꿔주고,
태그를 Player로 바꿔주면 된다.
그 뒤 실행을 해보면
※ CameraMan스크립트 컴포넌트에서 MoveSpeed를 설정해주지 않으면 카메라맨이 움직이지 않는다.
카메라맨이 플레이어를 따라가지만, 카메라에 비치지 않는것을 확인 할 수 있다.
이것은 당연한 것이다. 우리는 아직 카메라맨과 카메라와의 유지거리를 설정해주지 않았기 때문이다!
카메라의 유지거리를 설정하기 위한 스크립트를 짜보도록 하자.
Axis스크립트.
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
|
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Axis : MonoBehaviour {
// 공개
public float Distance; // 카메라와의 거리.
// 비공개
private Vector3 AxisVec; // 축의 벡터.
private Transform MainCamera; // 카메라 컴포넌트.
void Start()
{
MainCamera = Camera.main.transform;
}
void Update()
{
DisCamera();
}
void DisCamera()
{
AxisVec = transform.forward * -1;
AxisVec *= Distance;
MainCamera.position = transform.position + AxisVec;
}
}
|
cs |
현재 플레이어의 Position == CameraMan의 Position == Axis의 Position은 같다.
왜냐하면 CameraMan이 플레이어를 추적하기 때문이다.
Axis가 같은 이유는 CameraMan의 자식이기 때문이다.
그렇기 때문에 Axis의 전방벡터 (transform.forward)는 플레이어의 전방벡터 이기도 하다.
이러한 전방벡터에 -1을 곱한다는것은 플레이어의 후방벡터의 방향을 뜻한다.
플레이어가 보고있는 방향의 반대 방향으로 Distance 변수의 거리만큼 카메라를 벌려주면
카메라가 멀리서 플레이어를 바라보는것처럼 만들수 있다.
결과
'Unity3D > Project' 카테고리의 다른 글
[Unity3D] RPG게임 카메라 - 줌 인/아웃, 카메라 회전. [Part 3] (3) | 2017.03.08 |
---|---|
[Unity3D] RPG게임 카메라 - 줌 인/아웃, 카메라 회전. [Part 2] (0) | 2017.03.07 |
[Unity3D] 캐릭터 움직임, 충돌체크, 물리엔진사용 X [Part 5] (0) | 2017.03.02 |
[Unity3D] 캐릭터 움직임, 충돌체크, 물리엔진사용 X [Part 4] (0) | 2017.03.02 |
[Unity3D] 캐릭터 움직임, 충돌체크, 물리엔진사용 X [Part 3] (1) | 2017.03.02 |
[Unity3D] 캐릭터 움직임, 충돌체크, 물리엔진사용 X [Part 5]
※ 주의
이 글은 아마추어가 개인적으로 생각하여 작성하는 것으로, 이곳에 나오는 내용을 맹신하지 않는것을 당부드립니다.
메뉴
1. 움직임 적용.
- Horizontal을 이용한 움직임.
2. 중력 적용.
- 점프에 의한 중력.
- 낙하에 의한 중력.
3. 충돌 적용. (레이 캐스트로 충돌체크.)
- 점프로 인한 위쪽블록과의 충돌.
- 낙하로 인한 블록과의 충돌.
- 오른쪽, 혹은 왼쪽으로 움직일때에 벽과 충돌.
4. 점프
- 점프로 인한 충돌체크 및 중력적용.
5. 좌,우 충돌체크.
4. 좌,우 충돌체크.
별로 어려운건 없다.
기존에 만들어두었던 RayCastFire함수를 이용하면 된다.
하지만 몇 가지 추가하거나 바꿔줄 필요는 있다.
현재 x방향으로 레이캐스트를 3개를 쏘고있다.
그리고 이제 할 좌,우 충돌에 대해서도 마찬가지로 3개의 레이캐스트를 쏠 것이다.
for문을 하나 더 만들어서 함수를 호출해버리면 아무래도 프로그램에 부담이걸려 돌아오는 반응에 공백이 생길수
있으므로, for문을 RayCastFire함수 안으로 넣으려고 한다.
그리고 전체적인 구성을 조금 바꿀 것이다.
결과
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour {
// 공개
public float Speed; // 좌, 우로 움직이는 스피드.
public float JumpSpeed; // 점프 할때에 힘.
public float Gravity; // 작용하는 중력.
// 디버그
public float RayTime; // 그려준 레이 지속시간.
public bool isDrawX; // 좌,우 쏘는 레이캐스트 보기.
public bool isDrawY; // 위,아래로 쏘는 레이캐스트 보기.
public Color ColorX; // 레이의 색깔.
public Color ColorY; // 레이의 색깔.
// 비공개
private Vector2 Pos; // 플레이어가 실직적으로 움직일 좌, 우 벡터.
private Vector2 Dir; // 점프 및 낙하 여부의 벡터.
private Vector2 Radius; // 플레이어의 x,y의 반지름.
private float H; // Horizontal.
private bool isGround; // 현재 땅이냐? true 땅이 아니냐? false
// 각종 값 초기화.
void Init()
{
ColorX = Color.blue;
ColorY = Color.red;
Pos = Vector2.zero;
Dir = Vector2.down;
Radius = new Vector2(transform.localScale.x * 0.5f, transform.localScale.y * 0.5f);
H = 0;
isGround = false;
}
void Start()
{
Init();
}
void Update()
{
H = Input.GetAxis("Horizontal");
if (Input.GetButtonDown("Jump") && isGround)
{
Pos.y = JumpSpeed;
Dir = Vector2.up;
}
CollisionCheck();
Move();
}
// 좌, 우 움직임.
void Move()
{
Pos.Set(H * Speed, Pos.y);
transform.Translate(Pos * Time.deltaTime);
}
// 충돌 체크를 위한 레이 캐스트.
void CollisionCheck()
{
RayCastFire(Dir, Radius.y + 0.1f, Color.blue); // 위,아래
RayCastFire(DirX(), Radius.x + 0.1f, Color.yellow); // 좌, 우.
}
// 플레이어 방향 벡터를 결정해 준다. (좌, 우)
Vector2 DirX()
{
if (H < 0)
return Vector2.left;
else if (H > 0)
return Vector2.right;
return Vector2.zero;
}
// 레이캐스트를 발사한다.
void RayCastFire(Vector2 _Dir, float _length, Color _color )
{
if (_Dir == Vector2.zero)
return;
RaycastHit Hit;
for (int i = -1; i < 2; i++)
{
Vector2 MyPos = transform.position;
// 레이 발사위치를 정하는 구문.
if (_Dir == Vector2.right || _Dir == Vector2.left)
MyPos = new Vector2(MyPos.x, MyPos.y + (Radius.y * i)); // 좌,우 충돌 레이.
else
MyPos = new Vector2(MyPos.x + (Radius.x * i), MyPos.y); // 위,아래 충돌 레이.
// 레이를 그려주는 함수.
DrawRay(MyPos, _Dir, _length, _color);
if (Physics.Raycast(MyPos, _Dir, out Hit, _length))
{
// Ray를 발사 했는데 땅에 맞았음.
if (Hit.transform.gameObject.layer == 8)
{
if (_Dir == Vector2.right || _Dir == Vector2.left)
{
H = 0;
return;
}
// 플레이어가 땅에 있으면 함수 종료.
if (isGround)
return;
Transform Target = Hit.transform;
if (_Dir == Vector2.down)
{
// 충돌한 블럭의 위에 정착.
transform.position = new Vector2(transform.position.x, Target.position.y + (Target.localScale.y * 0.5f) + _length);
Pos.y = 0; // 중력이 작용했던 값에 대해 0으로 초기화.
isGround = true; // 이제 땅에 있다고 표시.
return;
}
// 충돌한 블럭 밑으로 이동.
transform.position = new Vector2(transform.position.x, Target.position.y - (Target.localScale.y * 0.5f) - _length);
Pos.y = 0;
break;
}
}
}
if (_Dir == Vector2.right || _Dir == Vector2.left)
return;
// Ray를 발사 했는데 땅에 맞지 않았음.
isGround = false;
Pos.y -= Gravity * Time.deltaTime;
if (Pos.y <= 0)
Dir = Vector2.down;
}
// 레이를 그려준다.
void DrawRay(Vector2 _MyPos, Vector2 _Dir, float _length, Color _color)
{
if (_Dir == Vector2.left || _Dir == Vector2.right)
{
if (isDrawX)
Debug.DrawRay(_MyPos, _Dir * _length, ColorX, RayTime);
}
else
{
if (isDrawY)
Debug.DrawRay(_MyPos, _Dir * _length, ColorY, RayTime);
}
}
}
|
cs |
'Unity3D > Project' 카테고리의 다른 글
[Unity3D] RPG게임 카메라 - 줌 인/아웃, 카메라 회전. [Part 2] (0) | 2017.03.07 |
---|---|
[Unity3D] RPG게임 카메라 - 줌 인/아웃, 카메라 회전. [Part 1] (0) | 2017.03.06 |
[Unity3D] 캐릭터 움직임, 충돌체크, 물리엔진사용 X [Part 4] (0) | 2017.03.02 |
[Unity3D] 캐릭터 움직임, 충돌체크, 물리엔진사용 X [Part 3] (1) | 2017.03.02 |
[Unity3D] 캐릭터 움직임, 충돌체크, 물리엔진사용 X [Part 2] (0) | 2017.03.02 |