-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.cpp
More file actions
138 lines (120 loc) · 6.9 KB
/
main.cpp
File metadata and controls
138 lines (120 loc) · 6.9 KB
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
/**
* cmake-build-debug/tetris.exe 를 실행해보세요.
*
* 테트리스를 구현해보는 토이 프로젝트를 만들어보면서 확실하게 제 프로그래밍 실력이 증진할 수 있었다고 생각합니다.
* 여러 문제에 맞닥들여보고, 고쳐보면서 테스트 혹은 오류 잡아내기를 얼마나 세심하게 해야하는지를 알 수 있었는데요
* 맨 처음에 콘솔창에 맵을 그려내는것부터 문제를 겪었었습니다.
* 키보드 입력을 감지하면서 콘솔에 맵을 그려내려면 동시에 두 작업을 할 수 있었어야 했는데요.
* 키보드 입력을 감지하는 함수를 쓰면 그 함수에서 키보드 입력을 감지할때까지 대기하는 특성 떄문이였습니다.
* 이 탓에 멀티스레드를 찾아보기도 했었습니다.
* 또 제가 많이 겪어본 Java와는 다르게 C++ 에서는 클래스를 추가하는일이 여간 쉬운일이 아니였습니다.
* 먼저 Cmake의 빌드시스템이 제대로 인식하지 못하는 경우를 처음에 겪었으나 이후 IDEA를 초기화하니 문제가 해결되었고,
* Java와는 다르게 aaa.h 라는 헤더파일을 추가해서 마치 Java의 Interface같은 구조적인 선언부분을 컴파일러에게 알리고 .cpp라는 구현파일에서 내부 동작을 구현한다는 일종의
* 알고리즘을 이해하는것까지 시간이 걸렸습니다. 처음에 왜 이렇게하는지 이해가 쉽지 않았기 때문입니다. ( 선언부분을 컴파일러에게 알림으로써 빌드 타임 최적화를 할 수 있음 )
* 또한 CPP 특유의 직접참조 간접참조 포인터 개념이 점점 들어가기 시작하면서 많은 버그에 걸렸었습니다.
* 예를 들어 Render라는 변수를 인자로 넘겼음에도 변경사항이 적용되지 않아 삽을 팠던적이 있습니다.
* 제가 값복사로 넘겼기 때문인데요. 함수 자체에서 &를 넣어줌으로써 그 객체를 직접 참조한다는 의미를 넣어줘야하는데 이를 인지하지 못하고 있었기 때문입니다.
* CPP의 경험을 늘림으로써 포인터 개념에 한층 가까워 진것 같아 좋았습니다. 더 잘 다룰 수 있는 숙련도또한 갖출 수 있었습니다.
* 또한 이 테트리스를 CPP로 구현한 코드들을 블로그에서 많이많이 찾아 볼 수 있습니다.
* 그런 블로그들의 코드와 이 코드가 뭐가 다르냐 라고 하면
* 당연히 다르다고 할 수 있습니다.
*
* 뭐가 얼마나 효율적이던 간에 저는 모두 클래스로 기능의 역할군마다 부분 분할을 하면서 C++의 객체지향을 실현할 수 있었고 또한
* 기존 블로그 코드들은 직관적이지 않은 절차지향적 코드를 가지고 있기 때문에 코드를 읽는 사람 입장에서 굉장히 불쾌합니다.
* 이 점또한 고려해서 코드를 분할해서 원하는 부분만 볼 수 있게 짰습니다.
* 키 입력을 보고 싶으면 key.h Key.cpp를 보면 되고, Player의 동작을 보고 싶으면 Player.h, Player.cpp를 렌더링 부분을 보고 싶으면 Render.h Render.cpp를 가면 됩니다.
*
* C++과 관련해서 다양한 작업을 해볼 수 있었어서 값진 경험이 됐습니다. 앞으로 언리얼 엔진을 작업할때 많이 도움이 될것 같습니다.
* 언리얼 엔진을 처음 접하고 CPP 클래스를 만났을때 정말 이게 뭔지 하나도 이해가 가지 않았었는데요 ( this->변수 같은 것들 )
* 이제서야 이해할 수 있었습니다.
*
* 게임 개발자로서 한층 더 성장한것 같아 뿌듯합니다.
*/
#include <thread>
#include <Windows.h>
#include "Key.h"
#include "Listener.h"
#include "Player.h"
#include "Render.h"
using namespace std;
bool alive = true;
/**
* 테트리스를 구현하기에 앞서 참고한 자료를 첨부하겠습니다.
* https://velog.io/@science4588/%ED%85%8C%ED%8A%B8%EB%A6%AC%EC%8A%A4-%EA%B0%9C%EB%B0%9C-2%EC%9D%BC%EC%B0%A8
*
* 해당 글을 많이 참고하였는데요, 처음에는 테트리스는 블럭들이 있고 이를 회전하고 이동하는데, 처음에 3차원 배열 int로 구현할까 했습니다.
* 하지만 회전을 구현을 어떻게하지? 라는 생각에 구글링해보고 해당 블로그 글을 모티브로 작성하게 됐습니다.
*
* 해당 글에서는 저와 똑같이 도형당 2차원 ( 총 3차원 ) 으로 구성하려 했으나, 회전에서 막혀서 아예 이분은 하드코딩으로 90, 180, 270, 360을 모두 구현했습니다.
* 예를 들어
* {
* { 2차원 배열(90도) },
* { 2차원 배열(180도) },
* { 2차원 배열(270도) },
* { 2차원 배열(360도) }
* }
*
* 이렇게 나타냅니다. 하지만 저는 하드코딩을 하지 않고 구현은 못할까? 라는 생각에 여러가지를 조사해봤습니다
* 기존 방식에서 문제가 생긴 것은
* 1. 코드가 너무 더러움.
* - 아무래도 2차원 배열을 숫자로 나타내려고 하다보니까 코드가 50줄가까이 들어가며 심한 하드코딩이 되어버려서 가독성을 헤칩니다.
* 2. 가변성이 없음.
* - 만약에 블럭을 많이 추가하면?
* - 만약에 블럭이 바뀌면?
* - 만약에 블럭의 크기들이 바뀌면?
* 이에 대해서 대처하기 껄끄럽습니다.
*
* 이 문제를 해결하기 위해서 비트마스크 방법을 채용한게 있습니다.
* Bitmask를 이용한 테트리스 도형 회전은 7개의 테트리스 도형에 대한 모든 회전(4가지) 정보를 가지고 있는 것으로
* 데이터 양을 줄이고 관리를 편리하게 하기 위해 도형 정보를 bit로 변환해서 저장합니다 (16bit)
*
* 예시로
* 도형 부분을 1, 아닌 부분을 0으로 하면
*
* 0 1 0 0
* 0 1 0 0
* 0 1 1 0
* 0 0 0 0
*
* 즉, L 자 도형은 2 진수로 0100 0100 0110 0000 이 되고
* 16 진수로 4460 이 됩니다.
*
* 여기까지는 좋았습니다
* 제가 원하던 기존 캐싱 방식에서 가독성 부분을 헤치지 않고 간단하게 16진수를 사용하는 이유만 이해하면 흐름자체는 이해하기 쉽기 때문입니다
*
* 그러나 여전히 하드코딩 방식에서 벗어나볼 순 없을까? 란 물음이 생겼고 아예 회전을 알고리즘으로 구현한 사례를 찾을 수 있었습니다.
* Player.cpp rotate 참고
*/
void playerUpdate(Player& player) {
while (true) {
player.update();
Sleep(100);
}
}
void draw(Render& render) {
while (true) {
render.draw();
Sleep(100);
}
}
int main() {
Render render;
render.init();
Chunk chunk(Chunk::pivot_i, Chunk::I, Chunk::i);
Player player(2, 0, render, chunk);
thread playerThread(
playerUpdate,
ref(player)
);
thread drawThread(
draw,
ref(render)
);
Key key(player);
while (alive) {
listen(key);
Sleep(100);
}
alive = false;
return 0;
}