O. Jueguito 3

Salida

Ábrelo en otra pestaña.

Revísalo en gilpgedit.

1<!DOCTYPE html>
2<html>
3
4<head>
5 <meta charset="utf-8">
6 <meta name="viewport"
7 content="width=device-width">
8 <title>Jueguito 3</title>
9 <style>
10 body {
11 position: fixed;
12 top: 0px;
13 left: 0px;
14 width: 100%;
15 height: 100%;
16 margin: 0;
17 overflow: hidden;
18 }
19
20 .sprite {
21 position: fixed;
22 }
23 </style>
24 <script>
25 //@ts-check
26 /** @abstract */
27 class Jugador
28 extends HTMLElement {
29 /** @param {number} velocidad */
30 izquierda(velocidad) {
31 throw new Error("abstract")
32 }
33 /** @param {number} velocidad */
34 derecha(velocidad) {
35 throw new Error("abstract")
36 }
37 /** @param {number} velocidad */
38 sube(velocidad) {
39 throw new Error("abstract")
40 }
41 /** @param {number} velocidad */
42 baja(velocidad) {
43 throw new Error("abstract")
44 }
45 }
46
47 /** @abstract */
48 class Figura
49 extends HTMLElement {
50 /**
51 * @param {HTMLElement} jugador
52 * el jugador que es
53 * perseguido.
54 */
55 muevete(jugador) {
56 throw new Error("abstract")
57 }
58 }
59
60 /** @interface */
61 class Fabrica {
62 /**
63 * Devuelve el único jugador,
64 * por lo que se considera
65 * Singleton.
66 * @returns {Jugador}
67 */
68 jugador() {
69 throw new Error("interface")
70 }
71 /**
72 * Devuelve un arreglo con las
73 * figuras del juego.
74 * @returns {Figura[]}
75 */
76 figuras() {
77 throw new Error("interface")
78 }
79 }
80
81 class Juego3 {
82 /** @param {Fabrica} fabrica */
83 constructor(fabrica) {
84 this.jugador =
85 fabrica.jugador()
86 this.figuras =
87 fabrica.figuras()
88 this.iniciaX = null
89 this.iniciaY = null
90 this.interval = null
91 this.activo = true
92 }
93
94 inicia() {
95 document.addEventListener(
96 "keydown",
97 evt => this.teclas(evt))
98 document.addEventListener(
99 "touchstart",
100 evt => this.iniciaTouch(evt))
101 document.addEventListener(
102 "touchmove",
103 evt =>
104 this.desplazaTouch(evt))
105 this.interval = setInterval(
106 () => this.mueveFiguras(), 60)
107 }
108
109 mueveFiguras() {
110 for (const f of this.figuras) {
111 f.muevete(this.jugador)
112 }
113 this.detectaColisiones()
114 }
115
116 detectaColisiones() {
117 for (const f of this.figuras) {
118 if (colisiona(
119 this.jugador, f)) {
120 this.termina()
121 break
122 }
123 }
124 }
125
126 termina() {
127 this.activo = false
128 clearInterval(this.interval)
129 this.jugador.innerHTML = "💥"
130 }
131
132 /** @param {KeyboardEvent} evt*/
133 teclas(evt) {
134 if (this.activo) {
135 switch (evt.key) {
136 case "ArrowLeft":
137 this.jugador.izquierda(20)
138 break
139 case "ArrowRight":
140 this.jugador.derecha(20)
141 break
142 case "ArrowUp":
143 this.jugador.sube(20)
144 break
145 case "ArrowDown":
146 this.jugador.baja(20)
147 break
148 }
149 this.detectaColisiones()
150 }
151 }
152
153 /** @param {TouchEvent} evt */
154 iniciaTouch(evt) {
155 if (this.activo) {
156 const toqueInicial =
157 evt.touches[0]
158 this.iniciaX =
159 toqueInicial.clientX
160 this.iniciaY =
161 toqueInicial.clientY
162 }
163 }
164
165 /** @param {TouchEvent} evt */
166 desplazaTouch(evt) {
167 if (this.activo
168 && this.iniciaX
169 && this.iniciaY) {
170 const desplazamiento =
171 evt.touches[0]
172 var desplazamientoX =
173 desplazamiento.clientX
174 var desplazamientoY =
175 desplazamiento.clientY
176 var difX = desplazamientoX -
177 this.iniciaX
178 var difY = desplazamientoY -
179 this.iniciaY
180 if (Math.abs(difX)
181 + Math.abs(difY)
182 > 150) {
183 if (Math.abs(difX)
184 > Math.abs(difY)) {
185 if (difX > 70) {
186 this.jugador.derecha(40)
187 } else {
188 this.jugador.izquierda(40)
189 }
190 } else if (difY > 70) {
191 this.jugador.baja(40)
192 } else {
193 this.jugador.sube(40)
194 }
195 this.detectaColisiones()
196 this.iniciaX = null
197 this.iniciaY = null
198 }
199 }
200 }
201 }
202
203 /**
204 * @param {HTMLElement} e1
205 * @param {HTMLElement} e2
206 * @returns {boolean} true si los
207 * element colisionan.
208 */
209 function colisiona(e1, e2) {
210 const rE1 =
211 e1.getBoundingClientRect()
212 const rE2 =
213 e2.getBoundingClientRect()
214 return (rE1.right >= rE2.left
215 && rE1.left <= rE2.right
216 && rE1.top <= rE2.bottom
217 && rE1.bottom >= rE2.top)
218 }
219
220 customElements.define(
221 "jugador-paloma",
222 class extends Jugador {
223 connectedCallback() {
224 this.classList.add("sprite")
225 this.innerHTML += "🕊"
226 this.style.fontSize = "60px"
227 const raiz =
228 document.documentElement
229 const r =
230 this.getBoundingClientRect()
231 const left =
232 (raiz.clientWidth
233 - r.width) /
234 2
235 const top =
236 (raiz.clientHeight -
237 r.height) /
238 2
239 this.style.left = `${left}px`
240 this.style.top = `${top}px`
241 }
242
243 /**
244 * @param {number} velocidad
245 * @override
246 */
247 sube(velocidad) {
248 const top =
249 this.getBoundingClientRect().
250 top -
251 velocidad
252 this.style.top = `${top}px`
253 }
254
255 /**
256 * @param {number} velocidad
257 * @override
258 */
259 baja(velocidad) {
260 const top =
261 this.getBoundingClientRect().
262 top +
263 velocidad
264 this.style.top = `${top}px`
265 }
266
267 /**
268 * @param {number} velocidad
269 * @override
270 */
271 izquierda(velocidad) {
272 const left =
273 this.getBoundingClientRect().
274 left -
275 velocidad
276 this.style.left = `${left}px`
277 }
278
279 /**
280 * @param {number} velocidad
281 * @override
282 */
283 derecha(velocidad) {
284 const left =
285 this.getBoundingClientRect().
286 left +
287 velocidad
288 this.style.left = `${left}px`
289 }
290 })
291
292 customElements.define(
293 "figura-aguila",
294 class extends Figura {
295 connectedCallback() {
296 this.classList.add("sprite")
297 this.innerHTML = "🦅"
298 this.style.fontSize = "40px"
299 const r =
300 this.getBoundingClientRect()
301 this.style.left =
302 `${r.left}px`
303 this.style.top = `${r.top}px`
304 this.style.bottom = "auto"
305 this.style.right = "auto"
306 }
307
308 /**
309 * @param {HTMLElement} jugador
310 * el jugador que persigue.
311 * @override
312 */
313 muevete(jugador) {
314 const r =
315 this.getBoundingClientRect()
316 const rJ = jugador.
317 getBoundingClientRect()
318 const y2 = rJ.top
319 const y1 = r.top
320 const x2 = rJ.left
321 const x1 = r.left
322 const pendiente = x2 === x1 ?
323 0 :
324 (y2 - y1) / (x2 - x1)
325 const dirección =
326 x2 > x1 ? 1 : -1
327 const x = x1 + dirección * 5
328 const y =
329 pendiente * (x - x1) + y1
330 this.style.left =
331 `${desvía(x)}px`
332 this.style.top =
333 `${desvía(y)}px`
334 }
335 })
336
337 function desvía(i) {
338 return i + 10 -
339 20 * Math.random()
340 }
341 </script>
342</head>
343
344<body>
345 <jugador-paloma></jugador-paloma>
346 <figura-aguila
347 style="right: 0; top: 0;">
348 </figura-aguila>
349 <figura-aguila
350 style="right: 0; bottom: 0;">
351 </figura-aguila>
352 <script>
353 //@ts-check
354 /** @implements {Fabrica} */
355 class MiFabrica {
356 constructor() {
357 /** @type {Jugador} */
358 this.jugadorSingleton =
359 document.querySelector(
360 "jugador-paloma");
361 }
362 /**
363 * Devuelve el único jugador,
364 * por lo que se considera
365 * Singleton.
366 * @returns {Jugador}
367 */
368 jugador() {
369 return this.jugadorSingleton
370 }
371 /**
372 * Devuelve un arreglo con las
373 * figuras del juego.
374 * @returns {Figura[]}
375 */
376 figuras() {
377 return Array.from(
378 document.querySelectorAll(
379 "figura-aguila"))
380 }
381 }
382 const juego =
383 new Juego3(new MiFabrica())
384 juego.inicia()
385 </script>
386</body>
387
388</html>
skip_previous skip_next