1. Introducción al fondo del juego (tonterías escritas en el frente):
Un día a principios de mayo, vi un cierto sitio web que recomendaba este juego, Pongo. Me veía bastante bien y lo probé con iPad. Después de jugar dos juegos, sentí que era algo bueno, y era bastante satisfactorio, porque era un juego que era un tipo de mano debida. Todos lo saben.
Pero después de un tiempo, descubrí que el juego parecía tener algunos errores en iPad. Me atascaría después de jugar por un tiempo y solo podía obligarlo a retirarse. Fue realmente desgarrador, y el récord todavía estaba esperando ser roto.
¿Qué hacer? La idea de que es mejor jugar que jugar tus propios juegos apareció malvadamente, y luego tiré la almohadilla al desgarrador de mi amigo. En silencio regresé a la computadora y comencé a escribir algo que no podía atascarme.
Tomó unas dos horas escribir el marco básico, y luego lo arrojó a SinaApp y lo probó. Básicamente fue lo mejor para jugar y luego ducharse y acostarse.
Cuando me desperté al día siguiente, pasé un tiempo diseñando la interfaz porque no tenía nada que hacer el fin de semana. Desafortunadamente, yo mismo encontré algunos errores serios, y finalmente me tomé un tiempo para corregirlos.
Finalmente, el juego se llamó "Pongo+" (haga clic en mí para jugar en teléfonos móviles). La computadora no es compatible con el momento. Por cierto, el código fuente se cargó en GitHub y se eliminó el módulo de puntuación de envío.
2. Sitio web de prueba de juego:
Pongo+ (solo móvil): http://mypongo.sinaapp.com/
Github Open Source (Fork es bienvenido a mejorar el juego): https://github.com/chenRason/pongo/blob/gh-pages/index.html
3. Reglas y juego del juego:
Al hacer clic en la pantalla, cambiará la dirección del movimiento del bisel. Al hacer clic en el bisel, cambiará la dirección del bisel una vez, con el propósito de bloquear las bolas pequeñas rodando y evitar que salgan fuera del gran círculo. ¡Cuanto más tiempo sea el tiempo, mejor! ¡Finalmente, puede enviar sus propios puntajes para la clasificación!
4. La tecnología utilizada en el juego:
HTML, CSS, JavaScript, Canvas, PHP
5. Ideas de diseño de juegos:
a) Use lienzo para dibujar la interfaz principal del juego. El fondo es un rectángulo monocromático, cubierto con un círculo grande, y se dibujan un círculo pequeño y un deflector en el círculo grande. También hay un círculo súper pequeño con un tamaño de 1px en el medio del deflector (para la detección de colisiones).
b) Hay 8 direcciones de movimiento de círculos pequeños: superior, inferior, izquierda, derecha, superior izquierda, inferior izquierda, superior derecha y inferior derecha.
c) Solo hay dos direcciones de movimiento del deflector, en el sentido de las agujas del reloj y en sentido antihorario.
d) La detección de colisiones no implica el uso del motor, sino que hace que el juicio de distancia sea basado en el pequeño círculo y el súper círculo en el medio del deflector, logrando así una simple detección de colisiones.
e) Se determina la dirección del rebote después de la bola chocada, y el conocimiento general se usa para enumerarlo, y hay 8 situaciones en total.
6. dificultades en la implementación del juego:
a) Detección de colisión.
b) Tiempo de espacio libre de Timer SetInterval y si es claro y exhaustivo.
c) La relación entre la longitud del ciclo del temporizador y la experiencia del juego.
d) Problemas de fluidez del juego causados por el rendimiento diferente de los dispositivos Android e iOS.
7. Problemas existentes con el juego:
a) Dado que la detección de colisiones es comparar la distancia central entre dos círculos e implica el uso de temporizadores, debido al intervalo de temporizador extremadamente corto, docenas de colisiones han ocurrido detrás de una colisión vista a simple vista. Esto hará que la dirección de rebote real de la pelota sea diferente del teorema físico real. Después de la optimización, la probabilidad de ocurrencia es baja, pero no se ha evitado. Por lo tanto, algunos jugadores descubrirán que si el círculo no golpea el centro del deflector con precisión, puede hacer que el juego falle.
b) Debido a que las funciones son demasiado detalladas, de baja eficiencia y el uso de temporizadores, la experiencia de juego en Android es diferente de iOS u otros dispositivos móviles (en general, iOS se debe a Android).
c) La lista de clasificación no ha logrado actualizaciones automáticas en tiempo real. (La base de datos aún no se usa)
8. Vista previa de la interfaz del juego:
(La Figura 1 es la primera edición, la Figura 2 ha eliminado el botón, la Figura 3 es la edición final y la Figura 4 es la lista de clasificación)
Figura 1
Figura 2
Figura 3
9. Parte del código fuente del juego JavaScript:
La copia del código es la siguiente:
var ifingame = 0;
var maxgrade = 0, grado = 0;
VAR Grado1, Grado2;
Var Apodo;
var gamespeed = 1.4; // Velocidad de la pelota
Var LineSpeed = Math.pi/95; // Velocidad de la línea de pista
VAR CrashDistanceFaild = -7; // Parámetros de detección de colisión
VAR CrashDistancesucc = 15
var fantanjuli = 7;
var ThemlexGradeline = 12.1;
función getcookie1 (apodo)
{
if (document.cookie.length> 0)
{
c_start = document.cookie.indexof (apodo + "=")
if (c_start! =-1)
{
c_start = c_start + nickname.length + 1;
c_end = document.cookie.indexof (",", c_start);
if (c_end ==-1)
c_end = document.cookie.length;
return unescape (document.cookie.substring (c_start, c_end));
}
}
devolver ""
}
función getcookie2 (mymaxgrade)
{
if (document.cookie.length> 0)
{
c_start = document.cookie.indexof (mymaxgrade + "=")
if (c_start! =-1)
{
c_start = c_start + mymaxgrade.length + 1;
c_end = document.cookie.indexof (";", c_start);
if (c_end ==-1)
c_end = document.cookie.length;
return unescape (document.cookie.substring (c_start, c_end));
}
}
devolver ""
}
funciones setcookie (apodo, valor, mymaxgrade, maxgrade, expiredays)
{
var exdate = new Date ()
exDate.SetDate (exDate.getDate ()+expiredays)
document.cookie = Nickname + "=" + Escape (Value) + "," + mymaxgrade + "=" + escape (maxgrade) + ((expiredays == null)? "": "; expiries =" + exDate.TogMtString ());
}
función checkcookie ()
{
Nickname = GetCookie1 ('Nickname');
maxgrade = parseInt (getcookie2 ('mymaxgrade'));
if (isnan (maxgrade) == verdadero)
{
maxgrade = 0;
}
if (Nickname! = Null && Nickname! = "")
{
alerta ('bienvenido'+apodo+'back!'+'/n'+"Si te gusta, por favor compártelo ~");
}
demás
{
Nnopname = solicit ('Ingrese su apodo: (el nombre es demasiado largo y se mostrará incompleto)', "")
if (Nickname! = Null && Nickname! = "")
{
var maxgradestring = maxgrade.toString ();
Setcookie ('Nombre', Apodo, 'MyMaxGrade', Maxgradestring, 365);
}
}
}
var objPane = document.getElementById ("panel");
var ctxpane = objpane.getContext ("2d");
CTXPANE.TRANSLATE (150,150); // Traducción del punto central del lienzo requerido
función sendmail ()
{
if (grado2> themaxgradeline)
var max_grade = grado2;
window.location.href = 'index.php? max_grade ='+max_grade+'& nick_name ='+Nickname;
/* {
<? Php
$ grado = $ _ get ['max_grade'];
$ Nickname = $ _ get ['nick_name'];
$ mail = new Saemail ();
$ ret = $ mail-> Quicksend ('[email protected]', $ grado, $ apodo, '[email protected]', 'mypongo');
$ mail-> clean ();
?>
}*/
alerta (apodo+"Su calificación es:"+grado2+"enviado correctamente ~");
}
var gamedirection = {
Shang: 1,
Xia: 5,
Zuo: 7,
Tú: 3,
Zuoshang: 8,
Zuoxia: 6,
YouShang: 2,
Yoxia: 4,
reloj: 0,
Anticlock: 9,
};//dirección
Var Canvas = {
Ancho: 300,
Altura: 300,
};//lienzo
var bigcircle = {// parámetro Big Circle
x: 0, // El valor coordinado del eje x del centro del círculo
y: 0, // El valor coordinado del eje y del centro del círculo
r: 150, // El radio del círculo
C: 'RGB (255,255,255)',
}; // Dayuan
var smallcircle = {// parámetro SmallCircle
x: 0, // El valor coordinado del eje x del centro del círculo
y: 0, // El valor coordinado del eje y del centro del círculo
r: 12, // El radio del círculo
C: 'RGB (204,105,106)',
Dirección: Gamedirection.xia,
}; // Círculo pequeño
VAR LINE = {// Parámetros de la línea de deflectación
x: 0, // El valor coordinado del eje x del centro del círculo
y: 0, // El valor coordinado del eje y del centro del círculo
r: 150, // El radio del arco
Inicio: (Math.Pi/2-Math.Pi/16),
Fin: (Math.Pi/2+Math.pi/16),
C: 'RGB (55,55,55)',
Dirección: Gamedirection.antantlock,
}; // Línea de seguimiento
var dot = {// Parámetros del punto de seguimiento
x: (bigcircle.r*math.cos (line.start+math.pi/16)), // usa el círculo grande como origen
Y: (bigcircle.r*Math.sin (Line.Start+Math.pi/16)),
R: 1,
} // Punto de seguimiento
función Changelinedirection ()
{
if (line.direction == Gamedirection.Clock)
{
line.direction = Gamedirection.antantlock;
}
demás
{
line.direction = Gamedirection.Clock;
}
}
función getDistance () {
Var Distancia = Math.Sqrt ((SmallCircle.x)*(SmallCircle.x)+(SmallCircle.y)*(SmallCircle.y));
distancia de retorno;
} // Vuelve a la distancia cuadrada entre la bola pequeña y el gran Circle Center GetDistance ()
función ifgameover () {// juzga si está fuera de los límites
if ((getDistance () - bigcircle.r)> 5)
devolver verdadero;
demás
devolver falso;
} // juzga si el juego termina ifgameover ()
función ifcrash () {// Detección de colisión
var dx = dot.x-smallcircle.x;
var dy = dot.y-smallcircle.y;
var dd = math.sqrt (dx*dx+dy*dy);
if (dd <crashDistancesucc)
devolver verdadero;
demás
devolver falso;
} // Detección de colisión ifcrash ()
función randomback ()
{
var x = math.floor (math.random ()*3);
Switch (SmallCircle.Direction) {
Case Gamedirection.shang:
{
interruptor (x)
{
Caso 0:
SmallCircle.Direction = Gamedirection.xia;
smallcircle.y = smallcircle.y+fantanjuli;
romper;
Caso 1:
SmallCircle.Direction = Gamedirection.zuoxia;
SmallCircle.x = SmallCircle.x-Fantanjuli;
smallcircle.y = smallcircle.y+fantanjuli;
romper;
Caso 2:
SmallCircle.Direction = Gamedirection.youxia;
smallcircle.x = smallcircle.x+fantanjuli;
smallcircle.y = smallcircle.y+fantanjuli;
romper;
por defecto:
romper;
}romper;
}
Caso Gamedirection.xia:
{
interruptor (x)
{
Caso 0:
SmallCircle.Direction = Gamedirection.shang;
SmallCircle.y = SmallCircle.y-Fantanjuli;
romper;
Caso 1:
SmallCircle.Direction = Gamedirection.zuoshang;
SmallCircle.x = SmallCircle.x-Fantanjuli;
SmallCircle.y = SmallCircle.y-Fantanjuli;
romper;
Caso 2:
SmallCircle.Direction = Gamedirection.Youshang;
smallcircle.x = smallcircle.x+fantanjuli;
SmallCircle.y = SmallCircle.y-Fantanjuli;
romper;
por defecto:
romper;
}romper;
}
Case Gamedirection.zuo:
{
interruptor (x)
{
Caso 0:
SmallCircle.Direction = Gamedirection.Tiu;
smallcircle.x = smallcircle.x+fantanjuli;
romper;
Caso 1:
SmallCircle.Direction = Gamedirection.Youshang;
smallcircle.x = smallcircle.x+fantanjuli;
SmallCircle.y = SmallCircle.y-Fantanjuli;
romper;
Caso 2:
SmallCircle.Direction = Gamedirection.youxia;
smallcircle.x = smallcircle.x+fantanjuli;
smallcircle.y = smallcircle.y+fantanjuli;
romper;
por defecto:
romper;
}romper;
}
Caso Gamedirection.Ti usted:
{
interruptor (x)
{
Caso 0:
SmallCircle.Direction = Gamedirection.zuo;
SmallCircle.x = SmallCircle.x-Fantanjuli;
romper;
Caso 1:
SmallCircle.Direction = Gamedirection.zuoxia;
SmallCircle.x = SmallCircle.x-Fantanjuli;
smallcircle.y = smallcircle.y+fantanjuli;
romper;
Caso 2:
SmallCircle.Direction = Gamedirection.zuoshang;
SmallCircle.x = SmallCircle.x-Fantanjuli;
SmallCircle.y = SmallCircle.y-Fantanjuli;
romper;
por defecto:
romper;
}romper;
}
Case Gamedirection.zuoshang:
{
interruptor (x)
{
Caso 0:
SmallCircle.Direction = Gamedirection.youxia;
smallcircle.x = smallcircle.x+fantanjuli;
smallcircle.y = smallcircle.y+fantanjuli;
romper;
Caso 1:
SmallCircle.Direction = Gamedirection.xia;
smallcircle.y = smallcircle.y+fantanjuli;
romper;
Caso 2:
SmallCircle.Direction = Gamedirection.Tiu;
smallcircle.x = smallcircle.x+fantanjuli;
romper;
por defecto:
romper;
}romper;
}
Case Gamedirection.zuoxia:
{
interruptor (x)
{
Caso 0:
SmallCircle.Direction = Gamedirection.Youshang;
smallcircle.x = smallcircle.x+fantanjuli;
SmallCircle.y = SmallCircle.y-Fantanjuli;
romper;
Caso 1:
SmallCircle.Direction = Gamedirection.shang;
SmallCircle.y = SmallCircle.y-Fantanjuli;
romper;
Caso 2:
SmallCircle.Direction = Gamedirection.Tiu;
smallcircle.x = smallcircle.x+fantanjuli;
romper;
por defecto:
romper;
}romper;
}
Caso Gamedirection. Youshang:
{
interruptor (x)
{
Caso 0:
SmallCircle.Direction = Gamedirection.zuoxia;
SmallCircle.x = SmallCircle.x-Fantanjuli;
smallcircle.y = smallcircle.y+fantanjuli;
romper;
Caso 1:
SmallCircle.Direction = Gamedirection.zuo;
SmallCircle.x = SmallCircle.x-Fantanjuli;
romper;
Caso 2:
SmallCircle.Direction = Gamedirection.xia;
smallcircle.y = smallcircle.y+fantanjuli;
romper;
por defecto:
romper;
}romper;
}
Caso Gamedirection.Ouxia:
{
interruptor (x)
{
Caso 0:
SmallCircle.Direction = Gamedirection.zuoshang;
SmallCircle.x = SmallCircle.x-Fantanjuli;
SmallCircle.y = SmallCircle.y-Fantanjuli;
romper;
Caso 1:
SmallCircle.Direction = Gamedirection.zuo;
SmallCircle.x = SmallCircle.x-Fantanjuli;
romper;
Caso 2:
SmallCircle.Direction = Gamedirection.shang;
SmallCircle.y = SmallCircle.y-Fantanjuli;
romper;
por defecto:
romper;
}romper;
}
por defecto:
{
romper;
}
}
} // La pelota revertida aleatoriamente randomback ()
función smallcircledirection ()
{
Switch (SmallCircle.Direction) {// Muévete según la dirección de la pelota
Case Gamedirection.shang:
{
smallcircle.y = smallcircle.y-gamespeed;
grado ++;
if (grado> maxgrade)
{
maxgrade = grado;
newRecoder ();
}
admite ();
romper;
}
Caso Gamedirection.xia:
{
smallcircle.y = smallcircle.y+gamespeed;
grado ++;
if (grado> maxgrade)
{
maxgrade = grado;
newRecoder ();
}
admite ();
romper;
}
Case Gamedirection.zuo:
{
smallcircle.x = smallcircle.x-gamespeed;
grado ++;
if (grado> maxgrade)
{
maxgrade = grado;
newRecoder ();
}
admite ();
romper;
}
Caso Gamedirection.Ti usted:
{
smallcircle.x = smallcircle.x+gamespeed;
grado ++;
if (grado> maxgrade)
{
maxgrade = grado;
newRecoder ();
}
admite ();
romper;
}
Case Gamedirection.zuoshang:
{
smallcircle.x = smallcircle.x-gamespeed*0.8;
smallcircle.y = smallcircle.y-gamespeed*0.8;
grado ++;
if (grado> maxgrade)
{
maxgrade = grado;
newRecoder ();
}
admite ();
romper;
}
Case Gamedirection.zuoxia:
{
smallcircle.x = smallcircle.x-gamespeed*0.8;
smallcircle.y = smallcircle.y+gamespeed*0.8;
grado ++;
if (grado> maxgrade)
{
maxgrade = grado;
newRecoder ();
}
admite ();
romper;
}
Caso Gamedirection. Youshang:
{
smallcircle.x = smallcircle.x+gamespeed*0.8;
smallcircle.y = smallcircle.y-gamespeed*0.8;
grado ++;
if (grado> maxgrade)
{
maxgrade = grado;
newRecoder ();
}
admite ();
romper;
}
Caso Gamedirection.Ouxia:
{
smallcircle.x = smallcircle.x+gamespeed*0.8;
smallcircle.y = smallcircle.y+gamespeed*0.8;
grado ++;
if (grado> maxgrade)
{
maxgrade = grado;
newRecoder ();
}
admite ();
romper;
}
por defecto:
{
romper;
}
}
} // La pelota mueve SmallCircledirection ()
/*Dibuja el círculo inferior*/
ctxpane.beginpath (); // dayuan
ctxpane.arc (bigcircle.x, bigcircle.y, bigcircle.r, 0, math.pi*2, true);
ctxpane.fillstyle = bigcircle.c;
ctxpane.fill ();
ctxpane.cloSepath ();
/*Dibuja la línea de seguimiento inferior*/
ctxpane.beginpath ();
ctxpane.linewidth = 6;
ctxpane.strokestyle = line.c;
ctxpane.arc (line.x, line.y, line.r, line.start, line.end, falso);
ctxpane.stroke ();
ctxpane.cloSepath ();
función tapme () // tapme
{
ctxpane.beginpath ();
ctxpane.strokestyle = "rgb (255,222,195)";
ctxpane.font = "80px papyrus";
ctxpane.stroketext ('tap',-95,30);
ctxpane.fillstyle = "rgb (255,205,105)";
ctxpane.font = "35px papyrus";
ctxpane.fillText ('me', 70,30);
ctxpane.cloSepath ();
}
función newRecoder ()
{
ctxpane.beginpath ();
ctxpane.fillstyle = "rgb (255,0,0)";
ctxpane.font = "18px papyrus";
ctxpane.fillText ("¡nuevo!", 58,80);
ctxpane.cloSepath ();
}
function addone ()
{
grado1 = (grado/150) .tofijo (1);
grado2 = (maxgrade/150) .tofixed (1);
var dice1 = "ahora";
var dice2 = "mejor"
ctxpane.beginpath ();
ctxpane.strokestyle = "rgb (250,222,185)";
ctxpane.font = "60px papyrus";
ctxpane.stroketext (grado1, -45, -60);
ctxpane.stroketext (grado2, -45,100);
ctxpane.fillstyle = "rgb (255,0,100)";
ctxpane.font = "15px papyrus";
ctxpane.fillText (digamos1,58, -60);
ctxpane.fillstyle = "rgb (255,0,100)";
ctxpane.font = "15px papyrus";
ctxpane.fillText (digamos 2,58,100);
ctxpane.cloSepath ();
}
función Movetest () {
if (ifgameover ())
{
ifingame = 0;
if (maxgrade> parseInt (getcookie2 ('mymaxgrade'))))
{
Setcookie ('Nickname', Nickname, 'MyMaxGrade', maxgrade.ToString (), 365);
}
ClearInterval (temporizador);
tapme ();
}
demás
{
if (ifcrash ())
{
randomback ();
}
ctxpane.clearrect (-150, -150,300,300); // Borrar la pantalla
ctxpane.beginpath (); // dayuan
ctxpane.arc (bigcircle.x, bigcircle.y, bigcircle.r, 0, math.pi*2, true);
ctxpane.fillstyle = bigcircle.c;
ctxpane.fill ();
ctxpane.cloSepath ();
if (line.direction == Gamedirection.Clock) // Línea de seguimiento en sentido horario
{
line.start = line.Start + LineSpeed;
line.end = line.end +lineSpeed;
ctxpane.beginpath ();
ctxpane.linewidth = 4;
ctxpane.strokestyle = line.c;
ctxpane.arc (line.x, line.y, line.r, line.start, line.end, falso);
ctxpane.stroke ();
ctxpane.cloSepath ();
}
if (line.direction == gamedirection.antantlock) // Seguimiento de antihorario
{
line.start = line.Start - líneas de línea;
line.end = line.end -linespeed;
ctxpane.beginpath ();
ctxpane.linewidth = 4;
ctxpane.strokestyle = line.c;
ctxpane.arc (line.x, line.y, line.r, line.start, line.end, falso);
ctxpane.stroke ();
ctxpane.cloSepath ();
}
dot.x = bigcircle.r*math.cos (line.start+math.pi/32) // Punto de seguimiento
dot.y = bigcircle.r*math.sin (line.start+math.pi/32)
ctxpane.beginpath (); // punto de seguimiento en línea
ctxpane.arc (dot.x, dot.y, dot.r, 0, math.pi*2, true);
ctxpane.fillstyle = smallcircle.c;
ctxpane.fill ();
ctxpane.cloSepath ();
SmallCircledirection (); // SmallCircledirection (); //
ctxpane.save ();
ctxpane.beginpath ();
ctxpane.arc (smallcircle.x, smallcircle.y, smallcircle.r, 0, math.pi*2, true);
ctxpane.fillstyle = smallcircle.c;
ctxpane.fill ();
ctxpane.cloSepath ();
ctxpane.restore ();
}
} // función principal
////////////////////////////////////////
tapme ();
temporizador var;
function startGame () {// Inicie el juego
if (ifingame == 0)
{
ifingame = 1;
grado = 0;
var xx = math.floor (math.random ()*8);
/* interruptor (xx)
{
Caso 0:
SmallCircle.Direction = Gamedirection.shang;
romper;
Caso 1:
SmallCircle.Direction = Gamedirection.xia;
romper;
Caso 2:
SmallCircle.Direction = Gamedirection.zuo;
romper;
Caso 3:
SmallCircle.Direction = Gamedirection.Tiu;
romper;
Caso 4:
SmallCircle.Direction = Gamedirection.zuoshang;
romper;
Caso 5:
SmallCircle.Direction = Gamedirection.zuoxia;
romper;
Caso 6:
SmallCircle.Direction = Gamedirection.Youshang;
romper;
Caso 7:
SmallCircle.Direction = Gamedirection.youxia;
romper;
por defecto:
romper;
}*/
SmallCircle.Direction = Gamedirection.xia;
smallcircle.x = smallcircle.y = 0;
line.start = Math.pi/2-Math.pi/26;
line.end = math.pi/2+math.pi/26;
line.direction = Gamedirection.antantlock;
ClearInterval (temporizador);
temporizador = setInterval (MovTest, 10);
}
} // Inicie el juego StartGame ()
función opentop ()
{
Window.location = "http://pongotop.sinaapp.com";
}
10. Escritado al final
Esto es puramente un auto-entretenimiento. El tercer día después de escribir, estaba ocupado presentando currículums para encontrar una pasantía y no tuve tiempo de cuidarlo. Lo tiré a mi círculo de amigos para que mis amigos jugaran. Este mes ha pasado y veo este juego nuevamente. Siento que no debería haber muerto así. No tengo ninguna habilidad y lo hago muy bien. Por lo tanto, espero que este artículo pueda ayudar a algunos amigos interesados en Pongo. Además, espero que si algún experto en este campo me puede dar consejos si lo ve, me da la bienvenida a dejar un mensaje para todas las dudas y consejos. ¡Gracias!