1
+ <!DOCTYPE html>
2
+ < html lang ="es ">
3
+
4
+ < head >
5
+ < meta charset ="UTF-8 ">
6
+ < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
7
+ < title > 10 - Stack Game</ title >
8
+ < style >
9
+ body {
10
+ background : # 000 ;
11
+ margin : 0 ;
12
+ display : grid;
13
+ place-content : center;
14
+ height : 100vh ;
15
+ font-family : system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI' , Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans' , 'Helvetica Neue' , sans-serif;
16
+ }
17
+
18
+ canvas {
19
+ border : 1px solid white;
20
+ }
21
+
22
+ span {
23
+ color : white;
24
+ font-size : 12px ;
25
+ }
26
+ </ style >
27
+
28
+ < script type ="module ">
29
+ const canvas = document . querySelector ( 'canvas' )
30
+ const context = canvas . getContext ( '2d' )
31
+
32
+ const score = document . querySelector ( '#score' )
33
+
34
+ // CONSTANTS
35
+ const MODES = {
36
+ FALL : 'fall' ,
37
+ BOUNCE : 'bounce' ,
38
+ GAMEOVER : 'gameover'
39
+ }
40
+ const INITIAL_BOX_WIDTH = 200
41
+ const INITIAL_BOX_Y = 600
42
+
43
+ const BOX_HEIGHT = 50
44
+ const INITIAL_Y_SPEED = 5
45
+ const INITIAL_X_SPEED = 2
46
+
47
+ // STATE
48
+ let boxes = [ ]
49
+ let debris = { x : 0 , y : 0 , width : 0 }
50
+ let scrollCounter , cameraY , current , mode , xSpeed , ySpeed
51
+
52
+ function createStepColor ( step ) {
53
+ if ( step === 0 ) return 'white'
54
+
55
+ const red = Math . floor ( Math . random ( ) * 255 )
56
+ const green = Math . floor ( Math . random ( ) * 255 )
57
+ const blue = Math . floor ( Math . random ( ) * 255 )
58
+
59
+ return `rgb(${ red } , ${ green } , ${ blue } )`
60
+ }
61
+
62
+ function updateCamera ( ) {
63
+ if ( scrollCounter > 0 ) {
64
+ cameraY ++
65
+ scrollCounter --
66
+ }
67
+ }
68
+
69
+ function initializeGameState ( ) {
70
+ boxes = [ {
71
+ x : ( canvas . width / 2 ) - ( INITIAL_BOX_WIDTH / 2 ) ,
72
+ y : 200 ,
73
+ width : INITIAL_BOX_WIDTH ,
74
+ color : 'white'
75
+ } ]
76
+
77
+ debris = { x : 0 , y : 0 , width : 0 }
78
+ current = 1
79
+ mode = MODES . BOUNCE
80
+ xSpeed = INITIAL_X_SPEED
81
+ ySpeed = INITIAL_Y_SPEED
82
+ scrollCounter = 0
83
+ cameraY = 0
84
+
85
+ createNewBox ( )
86
+ }
87
+
88
+ function restart ( ) {
89
+ initializeGameState ( )
90
+ draw ( )
91
+ }
92
+
93
+ function draw ( ) {
94
+ if ( mode === MODES . GAMEOVER ) return
95
+
96
+ drawBackground ( )
97
+ drawBoxes ( )
98
+ drawDebris ( )
99
+
100
+ if ( mode === MODES . BOUNCE ) {
101
+ moveAndDetectCollision ( )
102
+ } else if ( mode === MODES . FALL ) {
103
+ updateFallMode ( )
104
+ }
105
+
106
+ debris . y -= ySpeed
107
+ updateCamera ( )
108
+
109
+ window . requestAnimationFrame ( draw )
110
+ }
111
+
112
+ function drawBackground ( ) {
113
+ context . fillStyle = 'rgba(0, 0, 0, 0.5)'
114
+ context . fillRect ( 0 , 0 , canvas . width , canvas . height )
115
+ }
116
+
117
+ function drawDebris ( ) {
118
+ const { x, y, width } = debris
119
+ const newY = INITIAL_BOX_Y - y + cameraY
120
+
121
+ context . fillStyle = 'red'
122
+ context . fillRect ( x , newY , width , BOX_HEIGHT )
123
+ }
124
+
125
+ function drawBoxes ( ) {
126
+ boxes . forEach ( ( box ) => {
127
+ const { x, y, width, color } = box
128
+ const newY = INITIAL_BOX_Y - y + cameraY
129
+
130
+ context . fillStyle = color
131
+ context . fillRect ( x , newY , width , BOX_HEIGHT )
132
+ } )
133
+ }
134
+
135
+ function createNewBox ( ) {
136
+ boxes [ current ] = {
137
+ x : 0 ,
138
+ y : ( current + 10 ) * BOX_HEIGHT ,
139
+ width : boxes [ current - 1 ] . width ,
140
+ color : createStepColor ( current )
141
+ }
142
+ }
143
+
144
+ function createNewDebris ( difference ) {
145
+ const currentBox = boxes [ current ]
146
+ const previousBox = boxes [ current - 1 ]
147
+
148
+ const debrisX = currentBox . x > previousBox . x
149
+ ? currentBox . x + currentBox . width
150
+ : currentBox . x
151
+
152
+ debris = {
153
+ x : debrisX ,
154
+ y : currentBox . y ,
155
+ width : difference
156
+ }
157
+ }
158
+
159
+ function updateFallMode ( ) {
160
+ const currentBox = boxes [ current ]
161
+ currentBox . y -= ySpeed
162
+
163
+ const positionPreviousBox = boxes [ current - 1 ] . y + BOX_HEIGHT
164
+
165
+ if ( currentBox . y === positionPreviousBox ) {
166
+ handleBoxLanding ( )
167
+ }
168
+ }
169
+
170
+ function adjustCurrentBox ( difference ) {
171
+ const currentBox = boxes [ current ]
172
+ const previousBox = boxes [ current - 1 ]
173
+
174
+ if ( currentBox . x > previousBox . x ) {
175
+ currentBox . width -= difference
176
+ } else {
177
+ currentBox . width += difference
178
+ currentBox . x = previousBox . x
179
+ }
180
+ }
181
+
182
+ function gameOver ( ) {
183
+ mode = MODES . GAMEOVER
184
+
185
+ context . fillStyle = 'rgba(255, 0, 0, 0.5)'
186
+ context . fillRect ( 0 , 0 , canvas . width , canvas . height )
187
+
188
+ context . font = 'bold 20px Arial'
189
+ context . fillStyle = 'white'
190
+ context . textAlign = 'center'
191
+ context . fillText (
192
+ 'Game Over' ,
193
+ canvas . width / 2 ,
194
+ canvas . height / 2
195
+ )
196
+ }
197
+
198
+ function handleBoxLanding ( ) {
199
+ const currentBox = boxes [ current ]
200
+ const previousBox = boxes [ current - 1 ]
201
+
202
+ const difference = currentBox . x - previousBox . x
203
+
204
+ if ( Math . abs ( difference ) >= currentBox . width ) {
205
+ gameOver ( )
206
+ return
207
+ }
208
+
209
+ adjustCurrentBox ( difference )
210
+ createNewDebris ( difference )
211
+
212
+ xSpeed += xSpeed > 0 ? 1 : - 1
213
+ current ++
214
+ scrollCounter = BOX_HEIGHT
215
+ mode = MODES . BOUNCE
216
+
217
+ score . textContent = current - 1
218
+
219
+ createNewBox ( )
220
+ }
221
+
222
+ function moveAndDetectCollision ( ) {
223
+ const currentBox = boxes [ current ]
224
+ currentBox . x += xSpeed
225
+
226
+ const isMovingRight = xSpeed > 0
227
+ const isMovingLeft = xSpeed < 0
228
+
229
+ const hasHitRightSide =
230
+ currentBox . x + currentBox . width > canvas . width
231
+
232
+ const hasHitLeftSide = currentBox . x < 0
233
+
234
+ if (
235
+ ( isMovingRight && hasHitRightSide ) ||
236
+ ( isMovingLeft && hasHitLeftSide )
237
+ ) {
238
+ xSpeed = - xSpeed
239
+ }
240
+ }
241
+
242
+ document . addEventListener ( 'keydown' , ( event ) => {
243
+ if ( event . key === ' ' && mode === MODES . BOUNCE ) {
244
+ mode = MODES . FALL
245
+ }
246
+ } )
247
+
248
+ canvas . onpointerdown = ( ) => {
249
+ if ( mode === MODES . GAMEOVER ) {
250
+ restart ( )
251
+ } else if ( mode === MODES . BOUNCE ) {
252
+ mode = MODES . FALL
253
+ }
254
+ }
255
+
256
+ restart ( )
257
+ </ script >
258
+ </ head >
259
+
260
+ < body >
261
+ < span > Puntuación: < span id ="score "> 0</ span > </ span >
262
+ < canvas id ="canvas " width ="320 " height ="500 "> </ canvas >
263
+ </ body >
264
+
265
+ </ html >
0 commit comments