-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.html
311 lines (277 loc) · 15.1 KB
/
index.html
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
<!DOCTYPE html>
<!-- MDN 재단의 '순수한 자바스크립트를 이용한 2D 벽돌깨기 게임' 문서 참조
https://developer.mozilla.org/ko/docs/Games/Tutorials/%EC%88%9C%EC%88%98%ED%95%9C_%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A5%BC_%EC%9D%B4%EC%9A%A9%ED%95%9C_2D_%EB%B2%BD%EB%8F%8C%EA%B9%A8%EA%B8%B0_%EA%B2%8C%EC%9E%84 -->
<!-- 추후 재확인 필요한 부분은 (!) 앞에 달아 주석처리해놓음 -->
<html>
<head>
<meta charset="utf-8">
<title>Sangsu's first game - Blockbreak</title>
<link href="https://fonts.googleapis.com/css?family=Noto+Sang+KR|Black+Han+Sans|Nanum+Gothic|Nanum+Pen+Script|Lobster&display=swap" rel="stylesheet">
<!-- 복수의 커스텀 폰트 연결 / 'Nanum Gothic', 'Nanum Pen Script', 'Lobster' from Google Fonts -->
<link rel="stylesheet" type="text/css" href="style.css">
<!-- CSS 서식 다른 HTML 파일에서 공통적으로 사용할 수 있도록 별도로 분리하여 연결-->
</head>
<body>
<div id="versions">
<a href="ver_phaser2/index.html">벽돌깨기 게임 (Phaser 버전)</a>
</div>
<div id="title">
<h1>벽돌깨기 게임 #1</h1>
</div>
<div id="description">
<p>패드를 움직여 공을 튕기고 벽돌을 부수는 단순한 게임입니다.</p>
<p>키보드의 ←, → 버튼을 누르거나, 마우스로 조작하시면 됩니다.</p>
<p>공이 패드에 닿을 때마다 속도가 빨라지며,</p>
<p>상자 아래에 닿으면 게임오버입니다.</p>
<p>재밌게 플레이하세요 :)</p>
</div>
<div id="difficulty_change">
난이도
<button class="diff_change_button" onClick="difficulty_change('harder')">어렵게</button>
<button class="diff_change_button" onClick="difficulty_change('easier')">쉽게</button>
</div>
<!-- 난이도 조절 버튼 추가 -->
<!-- 클릭 시 JavaScript 함수 동작하는 부분 참고 : https://stackoverflow.com/questions/15455289/changing-variable-by-html-button -->
<canvas id="myCanvas" width="600" height="600" style="margin-bottom:40px">
<!-- canvas 태그는 게임이 렌더되는 공간을 생성 -->
</canvas>
<script>
// JavaScript 코드를 작성하는 위치. 대문자/소문자 구분 잘할 것.
var canvas = document.getElementById("myCanvas");
// canvas 변수에 <canvas> element에 대한 참조를 저장
var ctx = canvas.getContext("2d");
// ctx 변수에 캔버스에 그리는 데 사용되는 도구인 2D rendering context를 저장
var x = canvas.width/2;
var y = canvas.height-30;
// 공의 시작 위치 설정을 위한 위한 x, y 변수 설정
var dxArray = [-2.5, -2, -1.5, 1.5, 2, 2.5]
var dx = dxArray[Math.floor(Math.random()*dxArray.length)];
// 매 시작시 공의 진행 방향을 무작위로 설정
var dy = -2;
// 매회 공의 위치 변화를 위한 dx, dy 변수 설정
var ballRadius = 10;
var ballRadius2 = ballRadius*Math.sqrt(2)/2;
// 공의 반지름 변수 ballRadius 설정
// 상하좌우 충돌감지에는 ballRadius를 사용하고, 대각선 방향 충돌 (패드와 공과의 충돌) 감지를 위해 BallRadius2 설정: 원 중심에서 45도 각도로 뻗었을 때의 x, y 좌표와의 거리
// 참고 : Math.squrt(), MDN web docs (https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt)
// 참고 : Basic Equation of a Circle, Math Open Reference (https://www.mathopenref.com/coordbasiccircle.html)
var ballSpeed = 0.3;
// 패드 충돌 시 증가하는 속도 변수 설정
var angleArray = [-0.2,-0.1,0.1,0.2]
var colorArray = ["#003333", "#993366", "#993300", "#006699"];
var paddleWidth = 80;
var paddleHeight = 12;
var paddleX = (canvas.width-paddleWidth)/2;
var paddleY = (canvas.height-paddleHeight-6);
// 공을 치기 위한 paddle의 너비, 높이, 시작 x, y좌표 설정
var leftPressed = false;
var rightPressed = false;
// 키보드 ← → 버튼이 눌렸는지 확인하기 위한 변수. 처음에는 둘 다 눌리지 않았으므로 false 값으로 설정.
var brickRowCount = 4;
var brickColumnCount = 5;
var brickWidth = 80;
var brickHeight = 20;
var brickPadding = 30;
var brickOffsetTop = 30;
var brickOffsetLeft = 40;
// 벽돌 배열의 행과 열의 수, 벽돌의 너비와 높이, 각 벽돌 사이 및 벽돌과 캔버스 모서리와의 간격 변수 설정
var bricks = [];
for(var c=0; c<brickColumnCount; c++) {
bricks[c] = [];
for(var r=0; r<brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0, status: 1 };
// 공이 벽돌을 치지 않은 기본 상태를 status 1로 설정
}
}
// c열과 r행을 갖는 bricks 배열을 만든 후, 각 벽돌의 x, y 좌표를 설정
// ※ ++는 변수를 1 증가시키는 증감 연산자 (a++와 ++a는 차이가 있음 : a++는 다른 연산을 한 후 1을 증가시키고, ++a는 1 증가시킨 후 다른 연산을 함. 참고: https://www.everdevel.com/JavaScript/operator.php)
var score = 0;
// 점수기록을 위한 변수 설정
var score_change = 5;
// 벽돌 하나 파괴 시 점수 증가분 변수 설정
var interval = setInterval(draw, 10);
// setinterval(a,b) : a 함수를 b 간격(단위 ms : 1/1000초)으로 반복실행
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = "#fffafa"
ctx.fill();
ctx.closePath();
}
// 공을 그리고 색칠하는 함수
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, paddleY, paddleWidth, paddleHeight);
ctx.fillStyle = "#fafafa";
ctx.fill();
ctx.closePath();
}
// paddle을 그리고 색칠하는 함수
function drawBricks() {
for(var c=0; c<brickColumnCount; c++) {
for(var r=0; r<brickRowCount; r++) {
if(bricks[c][r].status == 1) {
var brickX = (c*(brickWidth+brickPadding))+brickOffsetLeft;
var brickY = (r*(brickHeight+brickPadding))+brickOffsetTop;
// 벽돌들의 x, y 좌표를 다르게 해주는 변수 설정
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
/*
if (x+ballRadius2 >= paddleX && x-ballRadius2 <= paddleX+paddleWidth && y+ballRadius2+dy >= paddleY) {
var randomcolor = colorArray[Math.floor(Math.random() * colorArray.length)];
}
*/
// 패들에 닿을 경우 벽돌 색상 바뀌는 구문 연구 필요
ctx.closePath();
ctx.fillStyle = "#d3d3d3"
ctx.fill();
}
}
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
drawPaddle();
drawScore();
drawDifficulty();
blockCollisionDetection();
drawBricks();
// (!) drawPaddle()을 추가하면 공의 색상 변환이 정상 작동하지 않았음. ctx.fillStyle()이 canvas 위의 모든 도형에 대해 색상을 적용하기 때문으로 추정됨.
// (!)) drawPaddle()에서 ctx.fillStyle()을 제외하면 모서리에 닿았을 때 색상이 다시 바뀌나, 문제는 paddle도 같이 바뀜. 공만 바뀌게 하는 방법 알아보기.
x += dx;
y += dy;
if (x >= canvas.width-ballRadius || x <= ballRadius) {
dx = -dx;
}
if (y <= ballRadius) {
dy = -dy;
} else if (y > canvas.height-ballRadius2) {
alert("Game Over! Last Score: "+score);
// (!) 알림창이 두 개가 연속으로 뜸. 하단 모서리 닫기 직전, 그리고 닿았을 때의 두 번.
document.location.reload();
clearInterval(interval)
// 크롬에서 오류 안 나게 함
}
// + 공의 '테두리'가 캔버스 하단 모서리에 닿았을 때 "Game Over!" 알림창을 출력하고 알림창을 없애면 페이지를 다시 불러와 게임을 다시 시작하는 조건문
// 공의 '테두리'(정확히는 공의 중심의 x, y좌표에서 ballRadius를 +-한 x, y 값의 집합)가 캔버스의 모서리에 닿았을 때 방향을 바꿔 튕겨나오게 하는 조건문
if(x+ballRadius2 >= paddleX && x-ballRadius2 <= paddleX+paddleWidth && y+ballRadius2 >= paddleY && y-ballRadius2 <= paddleY+paddleHeight) {
if(x-dx+ballRadius2 < paddleX || x-dx-ballRadius2 > paddleX+paddleWidth) {
dx = -dx;
} else if(x-dx+ballRadius2 >= paddleX || x-dx-ballRadius2 <= paddleX+paddleWidth) {
dy = -dy;
if(dx > 0 && Math.abs(dx) <= 8) {
dx += ballSpeed+angleArray[Math.floor(Math.random()*angleArray.length)];
} else if(dx < 0 && Math.abs(dx) <= 8) {
dx -= ballSpeed+angleArray[Math.floor(Math.random()*angleArray.length)];
}
// 공이 패드와 닿았을 때 dx의 변화를 무작위화하여 반사 각도가 달라지게 함
if(dy > 0 && Math.abs(dy) <= 6) {
dy += ballSpeed;
} else if(dy < 0 && Math.abs(dy) <= 6) {
dy -= ballSpeed;
}
// 공이 패드와 닿았을 때 공의 속도를 빠르게 함
}
}
// 공의 '테두리'가 패들 모서리에 닿았을 때, 공의 x 좌표가 paddle의 너비 사이에 있다면 튕겨내는 조건문
if(rightPressed && paddleX < canvas.width - paddleWidth) {
paddleX += 8;
} else if(leftPressed && paddleX > 0) {
paddleX += -8;
}
// ← →를 누를 때마다 paddle의 X 좌표를 7씩 바꿈
// 계속 누르면 화면 밖을 나가게 되므로 paddle의 X 좌표가 캔버스 안에 위치할 때만 좌표를 바꾸도록 && (and) 논리연산자 추가
}
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
// 키보드 중 어떤 키가 눌리면 "keydown" 이벤트가 발생되며, keyDownHandler() 함수 실행. 키에서 손을 떼면 "keyup" 이벤트가 발생하고 keyUpHandler() 함수 실행
document.addEventListener("mousemove", mouseMoveHandler, false);
// 마우스를 움직이면 "mousemove" 이벤트가 활성화되며, mouseMoveHandler() 함수 실행
function keyDownHandler(e) {
if(e.keyCode == 39) {
rightPressed = true;
} else if(e.keyCode == 37) {
leftPressed = true;
}
}
function keyUpHandler(e) {
if(e.keyCode == 39) {
rightPressed = false;
} else if(e.keyCode == 37) {
leftPressed = false;
}
}
// →를 누르면 rightPressed를 true로 떼면 false로 바꾸고, ←를 누르면 leftPressed를 true로 떼면 false로 바꾸는 함수
// (!) 두 함수 모두 e 변수로 표시되는 이벤트를 파라미터로 사용함 (무슨 뜻인지 이해하지 못했음)
// JavaScript 키보드 Keycode는 https://keycode.info/ 참조
function mouseMoveHandler(e) {
var relativeX = e.clientX - canvas.offsetLeft;
// 마우스 커서의 x 좌표 (e.clientX) - 화면 가장자리와 캔버스 가장자리의 간격을 게임 내에서의 마우스커서의 상대적 x 좌표를 의미하는 relativeX 변수로 설정
if(relativeX-paddleWidth/2 > 0 && relativeX+paddleWidth/2 < canvas.width) {
paddleX = relativeX - paddleWidth/2;
} else if(relativeX-paddleWidth/2 < 0) {
paddleX = 0;
} else if(relativeX+paddleWidth/2 > canvas.width) {
paddleX = canvas.width-paddleWidth;
}
}
// 마우스패드의 상대적 좌표가 캔버스의 좌우 사이에 있으면 마우스커서의 좌표에서 paddle 너비의 절반의 왼쪽에 paddle이 그려지는 x 좌표를 지정해주는 if 조건문 설정
// (exercise) 기존 if 조건문은 마우스를 가장자리로 움직였을 때 paddle의 중심이 가장자리에 닿을때까지 움직이는데, paddle의 모서리와 캔버스 가장자리가 닿을때까지만 움직이게 수정할 것
function blockCollisionDetection() {
for(var c=0; c<brickColumnCount; c++) {
for(var r=0; r<brickRowCount; r++) {
var b = bricks[c][r];
if(b.status == 1) {
if(x+dx+ballRadius2 >= b.x && x+dx-ballRadius2 <= b.x+brickWidth && y+dy+ballRadius2 >= b.y && y+dy-ballRadius2 <= b.y+brickHeight) {
if(x-dx+ballRadius2 < b.x || x-dx-ballRadius2 > b.x+brickWidth) {
dx = -dx;
} else if(x-dx+ballRadius2 >= b.x || x-dx-ballRadius2 <= b.x+brickWidth) {
dy = -dy;
}
b.status = 0;
score += score_change;
}
if(score == brickRowCount*brickColumnCount*score_change) {
alert("Congratulations! You win! Total Score: "+score);
// 모든 벽돌을 다 부수면 게임 이겼다는 메시지가 나오는 if 조건문 추가
document.location.reload();
clearInterval(interval)
// 크롬에서 오류 안 나게 함
}
// 공이 벽돌에 충돌했을 때, 즉 공의 x 좌표가 벽돌의 x 좌표와 너비 사이에 있을 때, 그리고 공의 y 좌표가 벽돌의 y 좌표와 높이 사이에 있을 때 방향 전환하는 if 조건문 추가
// 공이 벽돌에 충돌했을 때 status를 0으로 만드는 조건문 추가 (충돌 시 0의 status가 되어, 그리지 않게 됨)
// 공이 벽돌에 충돌했을 때 score를 1씩 올리는 코드 추가
// 벽돌의 윗모서리 아래모서리 부딪히면 정상적으로 작동하나, 옆면으로 들어갈 경우 안에서 겁나 튕겨댐. 양 옆에 충돌할 경우도 판단할 것
}
}
}
}
// 각 벽돌들에 대해 충돌을 감지하는 함수 설정. 모든 벽돌을 b 변수로 설정.
function drawScore() {
ctx.font = "20px Lobster";
ctx.fillStyle = "#d3d3d3";
ctx.fillText("Score: "+score, canvas.width - 8, 22)
ctx.textAlign = "left"
}
// 점수를 나타내는 함수 정의
function drawDifficulty() {
ctx.font = "20px Lobster";
ctx.fillStyle = "#d3d3d3";
ctx.fillText("Difficulty: "+(paddleWidth-180)/(-20)+" / 8", 8, 22)
ctx.textAlign = "right"
}
// 난이도(패드 너비)를 나타내는 함수 정의
function difficulty_change(choice) {
if(choice == "harder" && paddleWidth > 20) {
paddleWidth -= 20;
} else if(choice == "easier" && paddleWidth < 160) {
paddleWidth += 20;
}
}
draw();
</script>
</body>
</html>