1. Introduction à l'arrière-plan du jeu (non-sens écrit à l'avant):
Un jour début mai, j'ai vu un certain site Web recommandant ce jeu, Pongo. J'avais l'air plutôt bien et je l'ai essayé avec iPad. Après avoir joué deux matchs, je sentais que c'était une bonne chose, et c'était assez satisfaisant, car c'était un jeu qui était un type de main dû. Tout le monde le sait.
Mais après un certain temps, j'ai trouvé que le jeu semblait avoir des bogues sur iPad. Je serais coincé après avoir joué pendant un certain temps et je ne pouvais que le forcer à me retirer. C'était vraiment déchirant et le record attendait toujours d'être brisé.
ce qu'il faut faire? L'idée qu'il est préférable de jouer à des jeux que de jouer à vos propres jeux est à nouveau diable, puis j'ai jeté le coussin au déchirant de mon ami. Je suis retourné silencieusement à l'ordinateur et j'ai commencé à écrire quelque chose que je ne pouvais pas rester coincé.
Il a fallu environ deux heures pour noter le cadre de base, puis l'a jeté dans Sinaapp et l'a essayé. C'était fondamentalement le meilleur pour jouer, puis prendre une douche et se coucher.
Quand je me suis réveillé le lendemain, j'ai passé un peu de temps à concevoir l'interface parce que je n'avais rien à faire le week-end. Malheureusement, j'ai moi-même trouvé de sérieux bugs et j'ai finalement pris le temps de les corriger.
Enfin, le jeu a été nommé "Pongo +" (cliquez sur moi pour jouer sur les téléphones mobiles). L'ordinateur n'est pas pris en charge pour le moment. Soit dit en passant, le code source a été téléchargé sur GitHub et le module de score de soumission a été supprimé.
2. Site Web d'essai de jeu:
Pongo + (mobile uniquement): http://mypongo.sinaapp.com/
GitHub Open Source (Fork est invité à améliorer le jeu): https://github.com/chenreason/pongo/blob/gh-pages/index.html
3. Règles de jeu et gameplay:
Cliquez sur l'écran modifiera la direction du mouvement de la lunette. Cliquer sur la lunette changera la direction de la lunette une fois, dans le but de simplement bloquer les petites boules qui roulent et les empêchant de sortir du grand cercle. Plus le temps est long, mieux c'est! Enfin, vous pouvez soumettre vos propres scores pour le classement!
4. La technologie utilisée dans le jeu:
HTML, CSS, JavaScript, toile, PHP
5. Idées de conception de jeu:
A) Utilisez Canvas pour dessiner l'interface principale du jeu. Le fond est un rectangle monochrome, recouvert d'un grand cercle, et un petit cercle et un déflecteur sont dessinés sur le grand cercle. Il y a aussi un super petit cercle avec une taille de 1px au milieu du déflecteur (pour la détection de collision).
b) Il y a 8 directions de mouvement des petits cercles: supérieur, inférieur, gauche, droit, supérieur gauche, inférieur à gauche, supérieur droit et inférieur à droite.
c) Il n'y a que deux directions de mouvement du chicane, dans le sens horaire et dans le sens antihoraire.
d) La détection des collisions n'implique pas l'utilisation du moteur, mais fait plutôt le jugement de distance basé sur le petit cercle et le super cercle au milieu du déflecteur, atteignant ainsi une simple détection de collision.
e) La direction du rebond après la collision du ballon est déterminée, et les connaissances générales sont utilisées pour la répertorier, et il y a 8 situations au total.
6. Difficultés de mise en œuvre du jeu:
a) Détection de collision.
b) Timer SetInterval Clearance Timing et s'il est clair et approfondi.
c) La relation entre la durée du cycle de la minuterie et l'expérience de jeu.
d) Problèmes de maîtrise du jeu causés par les différentes performances des appareils Android et iOS.
7. Problèmes existants avec le jeu:
A) Étant donné que la détection des collisions consiste à comparer la distance centrale entre deux cercles et implique l'utilisation de minuteries, en raison de l'intervalle de minuterie extrêmement court, des dizaines de collisions se sont en fait produites derrière une collision observée par l'œil nu. Cela rendra la direction réelle du rebond de la balle différente du théorème physique réel. Après optimisation, la probabilité d'occurrence est faible, mais elle n'a pas été évitée. Par conséquent, certains joueurs constateront que si le cercle ne frappe pas avec précision le centre du déflecteur, cela peut entraîner l'échec du jeu.
b) Parce que les fonctions sont trop verbeuses, à faible efficacité et à l'utilisation des minuteries, l'expérience de jeu sur Android est différente d'iOS ou d'autres appareils mobiles (en général, iOS est dû à Android).
c) La liste de classement n'a pas atteint des mises à jour automatiques en temps réel. (La base de données ne sera pas encore utilisée)
8. Aperçu de l'interface de jeu:
(La figure 1 est la première édition, la figure 2 a supprimé le bouton, la figure 3 est l'édition finale, et la figure 4 est la liste de classement)
Figure 1
Figure 2
Figure 3
9. partie du code source du jeu javascript:
La copie de code est la suivante:
var ifingame = 0;
var maxgrade = 0, grade = 0;
Var Grade1, grade2;
Var surnom;
Var Gamespeed = 1,4; // vitesse de balle
Var LineSpeed = Math.PI / 95; // Track Line Speed
var crashDistanceFaild = -7; // Paramètres de détection de collision
var crashDistancesUcc = 15
var Fantanjuli = 7;
var themAxraDeline = 12,1;
fonction getcookie1 (surnom)
{
if (document.cookie.length> 0)
{
c_start = document.cookie.indexof (surnom + "=")
if (c_start! = - 1)
{
c_start = c_start + nause.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));
}
}
retour ""
}
fonction getcookie2 (mymaxgrate)
{
if (document.cookie.length> 0)
{
c_start = document.cookie.indexof (mymaxgrate + "=")
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));
}
}
retour ""
}
fonction setcookie (surnom, valeur, mymaxgrate, maxgrade, expiredays)
{
var exDate = new Date ()
exDate.setDate (exDate.getDate () + Expiredays)
document.cookie = surnom + "=" + Escape (valeur) + "," + myMaxrade + "=" + Escape (maxgrade) + ((expiredays == null)? "": "; expires =" + exDate.togMtString ());
}
Fonction Checkcookie ()
{
nautiquer = getcookie1 («surnom»);
maxgrade = parseInt (getcookie2 ('mymaxgrate'));
if (isnan (maxgrade) == true)
{
maxgrade = 0;
}
if (surnom! = null && nulnom! = "")
{
alert ('bienvenue' + surnom + 'back!' + '/ n' + "Si vous l'aimez, veuillez le partager ~");
}
autre
{
nause = invite ('Veuillez entrer votre surnom: (le nom est trop long et sera affiché incomplet)', "")
if (surnom! = null && nulnom! = "")
{
var maxgradestring = maxgrade.toString ();
setcookie («surnom», surnom, «mymaxgrate», maxgradestring, 365);
}
}
}
var objpane = document.getElementById ("volet");
var ctxpane = objpane.getContext ("2d");
CTXPANE.TRANSLATE (150 150); // Traduction de point de centre de toile requise
fonction sendmail ()
{
if (grade2> theaxradeline)
var max_grade = grade2;
window.location.href = 'index.php? max_grade =' + max_grade + '& nick_name =' + nauséère;
/ * {
<? Php
$ grade = $ _ get ['max_grade'];
$ nickname = $ _ get ['nick_name'];
$ mail = new saemail ();
$ ret = $ Mail-> QuickSend ('[email protected]', $ Grade, $ surnom, 'Raisonpongo @ 163.com', 'mypongo');
$ Mail-> Clean ();
?>
} * /
alerte (surnom + "Votre note est:" + grade2 + "soumis avec succès ~");
}
var gamedirection = {
Shang: 1,
Xia: 5,
Zuo: 7,
vous: 3,
Zuoshang: 8,
Zuoxia: 6,
YouShang: 2,
Youxia: 4,
horloge: 0,
Anticloc: 9,
};//direction
var toivas = {
Largeur: 300,
hauteur: 300,
};//toile
var bigCircle = {// Big Circle Paramètre
X: 0, // La valeur de coordonnée de l'axe X du centre du cercle
y: 0, // la valeur de coordonnée de l'axe y du centre du cercle
r: 150, // le rayon du cercle
c: «RVB (255 255 255)»,
}; // dayuan
var smallCircle = {// Paramètre à petit cercle
X: 0, // La valeur de coordonnée de l'axe X du centre du cercle
y: 0, // la valeur de coordonnée de l'axe y du centre du cercle
r: 12, // le rayon du cercle
c: «RVB (204,105,106)»,
Direction: Gamedirection.xia,
}; // petit cercle
Var Line = {// Paramètres de la ligne Baffle
X: 0, // La valeur de coordonnée de l'axe X du centre du cercle
y: 0, // la valeur de coordonnée de l'axe y du centre du cercle
r: 150, // le rayon de l'arc
Démarrer: (Math.pi / 2-Math.pi / 16),
fin: (math.pi / 2 + math.pi / 16),
C: «RVB (55,55,55)»,
Direction: Gamedirection.anticlock,
}; // ligne de suivi
var dot = {// Paramètres de point de suivi
x: (bigcircle.r * math.cos (line.start + math.pi / 16)), // Utilisez le grand cercle comme origine
y: (bigcircle.r * math.sin (line.start + math.pi / 16)),
R: 1,
} // point de suivi
fonction changelinedirerection ()
{
if (line.direction == gamedirection.clock)
{
line.direction = gamedirection.anticlock;
}
autre
{
line.direction = gamedirection.clock;
}
}
fonction getDistance () {
Var Distance = math.sqrt ((smallcircle.x) * (smallCircle.x) + (smallCircle.y) * (smallCircle.y));
Distance de retour;
} // retournez à la distance carrée entre la petite balle et le grand cercle Center GetDistance ()
fonction ifgameover () {// juger s'il est hors limites
if ((getDistance () - bigCircle.r)> 5)
Retour Vrai;
autre
retourne false;
} // juger si le jeu met fin à ifgameover ()
fonction ifcrash () {// Détection de collision
var dx = dot.x-smallCircle.x;
var dy = dot.y-smallcircle.y;
var dd = math.sqrt (dx * dx + dy * dy);
if (dd <crashDistancesUcc)
Retour Vrai;
autre
retourne false;
} // Détection de collision ifcrash ()
fonction Randomback ()
{
var x = math.floor (math.random () * 3);
commutateur (smallCircle.direction) {
Case Gamedirection.shang:
{
commutateur (x)
{
Cas 0:
smallCircle.direction = Gamedirection.xia;
smallCircle.y = smallriccle.y + Fantanjuli;
casser;
Cas 1:
smallCircle.direction = Gamedirection.zuoxia;
smallCircle.x = smallcircle.x-Fantanjuli;
smallCircle.y = smallriccle.y + Fantanjuli;
casser;
Cas 2:
smallCircle.direction = Gamedirection.youxia;
smallCircle.x = smallcircle.x + Fantanjuli;
smallCircle.y = smallriccle.y + Fantanjuli;
casser;
défaut:
casser;
}casser;
}
Case Gamedirection.xia:
{
commutateur (x)
{
Cas 0:
smallCircle.direction = Gamedirection.shang;
smallCircle.y = smallcircle.y-Fantanjuli;
casser;
Cas 1:
smallCircle.direction = gamedirection.zuoshang;
smallCircle.x = smallcircle.x-Fantanjuli;
smallCircle.y = smallcircle.y-Fantanjuli;
casser;
Cas 2:
smallCircle.direction = Gamedirection.youshang;
smallCircle.x = smallcircle.x + Fantanjuli;
smallCircle.y = smallcircle.y-Fantanjuli;
casser;
défaut:
casser;
}casser;
}
Case Gamedirection.zuo:
{
commutateur (x)
{
Cas 0:
smallCircle.direction = Gamedirection.you;
smallCircle.x = smallcircle.x + Fantanjuli;
casser;
Cas 1:
smallCircle.direction = Gamedirection.youshang;
smallCircle.x = smallcircle.x + Fantanjuli;
smallCircle.y = smallcircle.y-Fantanjuli;
casser;
Cas 2:
smallCircle.direction = Gamedirection.youxia;
smallCircle.x = smallcircle.x + Fantanjuli;
smallCircle.y = smallriccle.y + Fantanjuli;
casser;
défaut:
casser;
}casser;
}
Case Gamedirection.ou:
{
commutateur (x)
{
Cas 0:
smallCircle.direction = Gamedirection.zuo;
smallCircle.x = smallcircle.x-Fantanjuli;
casser;
Cas 1:
smallCircle.direction = Gamedirection.zuoxia;
smallCircle.x = smallcircle.x-Fantanjuli;
smallCircle.y = smallriccle.y + Fantanjuli;
casser;
Cas 2:
smallCircle.direction = gamedirection.zuoshang;
smallCircle.x = smallcircle.x-Fantanjuli;
smallCircle.y = smallcircle.y-Fantanjuli;
casser;
défaut:
casser;
}casser;
}
Case Gamedirection.zuoshang:
{
commutateur (x)
{
Cas 0:
smallCircle.direction = Gamedirection.youxia;
smallCircle.x = smallcircle.x + Fantanjuli;
smallCircle.y = smallriccle.y + Fantanjuli;
casser;
Cas 1:
smallCircle.direction = Gamedirection.xia;
smallCircle.y = smallriccle.y + Fantanjuli;
casser;
Cas 2:
smallCircle.direction = Gamedirection.you;
smallCircle.x = smallcircle.x + Fantanjuli;
casser;
défaut:
casser;
}casser;
}
Case Gamedirection.zuoxia:
{
commutateur (x)
{
Cas 0:
smallCircle.direction = Gamedirection.youshang;
smallCircle.x = smallcircle.x + Fantanjuli;
smallCircle.y = smallcircle.y-Fantanjuli;
casser;
Cas 1:
smallCircle.direction = Gamedirection.shang;
smallCircle.y = smallcircle.y-Fantanjuli;
casser;
Cas 2:
smallCircle.direction = Gamedirection.you;
smallCircle.x = smallcircle.x + Fantanjuli;
casser;
défaut:
casser;
}casser;
}
Case Gamedirection.youshang:
{
commutateur (x)
{
Cas 0:
smallCircle.direction = Gamedirection.zuoxia;
smallCircle.x = smallcircle.x-Fantanjuli;
smallCircle.y = smallriccle.y + Fantanjuli;
casser;
Cas 1:
smallCircle.direction = Gamedirection.zuo;
smallCircle.x = smallcircle.x-Fantanjuli;
casser;
Cas 2:
smallCircle.direction = Gamedirection.xia;
smallCircle.y = smallriccle.y + Fantanjuli;
casser;
défaut:
casser;
}casser;
}
Case Gamedirection.youxia:
{
commutateur (x)
{
Cas 0:
smallCircle.direction = gamedirection.zuoshang;
smallCircle.x = smallcircle.x-Fantanjuli;
smallCircle.y = smallcircle.y-Fantanjuli;
casser;
Cas 1:
smallCircle.direction = Gamedirection.zuo;
smallCircle.x = smallcircle.x-Fantanjuli;
casser;
Cas 2:
smallCircle.direction = Gamedirection.shang;
smallCircle.y = smallcircle.y-Fantanjuli;
casser;
défaut:
casser;
}casser;
}
défaut:
{
casser;
}
}
} // La balle a inversé au hasard Randomback ()
fonction SmallCirdedirection ()
{
commutateur (smallCircle.direction) {// se déplacer en fonction de la direction de la balle
Case Gamedirection.shang:
{
smallCircle.y = smallCircle.y-gamespeed;
grade ++;
if (grade> maxgrade)
{
maxgrade = grade;
newrecoder ();
}
addOne ();
casser;
}
Case Gamedirection.xia:
{
smallCircle.y = smallCircle.y + Gamespeed;
grade ++;
if (grade> maxgrade)
{
maxgrade = grade;
newrecoder ();
}
addOne ();
casser;
}
Case Gamedirection.zuo:
{
smallCircle.x = smallCircle.x-GamesPeed;
grade ++;
if (grade> maxgrade)
{
maxgrade = grade;
newrecoder ();
}
addOne ();
casser;
}
Case Gamedirection.ou:
{
smallCircle.x = smallriccle.x + Gamespeed;
grade ++;
if (grade> maxgrade)
{
maxgrade = grade;
newrecoder ();
}
addOne ();
casser;
}
Case Gamedirection.zuoshang:
{
smallCircle.x = smallriccle.x-gamespeed * 0,8;
smallCircle.y = smallriccle.y-gamespeed * 0,8;
grade ++;
if (grade> maxgrade)
{
maxgrade = grade;
newrecoder ();
}
addOne ();
casser;
}
Case Gamedirection.zuoxia:
{
smallCircle.x = smallriccle.x-gamespeed * 0,8;
smallCircle.y = smallCircle.y + Gamespeed * 0,8;
grade ++;
if (grade> maxgrade)
{
maxgrade = grade;
newrecoder ();
}
addOne ();
casser;
}
Case Gamedirection.youshang:
{
smallCircle.x = smallriccle.x + Gamespeed * 0,8;
smallCircle.y = smallriccle.y-gamespeed * 0,8;
grade ++;
if (grade> maxgrade)
{
maxgrade = grade;
newrecoder ();
}
addOne ();
casser;
}
Case Gamedirection.youxia:
{
smallCircle.x = smallriccle.x + Gamespeed * 0,8;
smallCircle.y = smallCircle.y + Gamespeed * 0,8;
grade ++;
if (grade> maxgrade)
{
maxgrade = grade;
newrecoder ();
}
addOne ();
casser;
}
défaut:
{
casser;
}
}
} // La balle déplace SmallCirdedirection ()
/ * Dessiner le cercle inférieur * /
ctxpane.beginpath (); // dayuan
ctxpane.arc (bigCircle.x, bigCircle.y, bigCircle.r, 0, math.pi * 2, true);
ctxpane.fillStyle = bigCircle.c;
ctxpane.fill ();
ctxpane.closepath ();
/ * Dessinez la ligne de suivi inférieure * /
ctxpane.beginPath ();
ctxpane.linewidth = 6;
ctxpane.strokestyle = line.c;
ctxpane.arc (line.x, line.y, line.r, line.start, line.end, false);
ctxpane.stroke ();
ctxpane.closepath ();
fonction 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 ();
}
fonction newrecoder ()
{
ctxpane.beginPath ();
ctxpane.fillStyle = "rgb (255,0,0)";
ctxpane.font = "18px papyrus";
ctxpane.fillText ("New!", 58,80);
ctxpane.closepath ();
}
fonction addOne ()
{
grade1 = (grade / 150) .tofixé (1);
grade2 = (maxgrade / 150) .tofixé (1);
var dit1 = "maintenant";
var dit2 = "meilleur"
ctxpane.beginPath ();
ctxpane.strokestyle = "RGB (250 222,185)";
ctxpane.font = "60px papyrus";
ctxpane.stroketext (grade 1, -45, -60);
ctxpane.stroketext (grade2, -45,100);
CTXPANE.FILLSTYLE = "RGB (255,0,100)";
ctxpane.font = "15px papyrus";
ctxpane.fillText (say1,58, -60);
CTXPANE.FILLSTYLE = "RGB (255,0,100)";
ctxpane.font = "15px papyrus";
ctxpane.fillText (say2,58,100);
ctxpane.closepath ();
}
fonction MoveTest () {
if (ifgameover ())
{
ifingame = 0;
if (maxgrate> parseInt (getcookie2 ('mymaxgrate'))))
{
setcookie («surnom», surnom, «mymaxgrate», maxgrade.toString (), 365);
}
ClearInterval (temporisateur);
tapme ();
}
autre
{
if (ifcrash ())
{
Randomback ();
}
ctxpane.clearrect (-150, -150,300,300); // effacer l'écran
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) // Ligne de suivi dans le sens des aiguilles d'une montre
{
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, false);
ctxpane.stroke ();
ctxpane.closepath ();
}
if (line.direction == gamedirection.anticlock) // suivi dans le sens antihoraire
{
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, false);
ctxpane.stroke ();
ctxpane.closepath ();
}
dot.x = bigCircle.r * math.cos (line.start + math.pi / 32) // point de suivi
dot.y = bigcircle.r * math.sin (line.start + math.pi / 32)
ctxpane.beginpath (); // point de suivi en ligne
ctxpane.arc (dot.x, dot.y, dot.r, 0, math.pi * 2, true);
ctxpane.fillStyle = smallCircle.c;
ctxpane.fill ();
ctxpane.closepath ();
smallCirdedirection (); // smallCirdedirection (); //
ctxpane.save ();
ctxpane.beginPath ();
ctxpane.arc (smallcircle.x, smallcircle.y, smallriccle.r, 0, math.pi * 2, true);
ctxpane.fillStyle = smallCircle.c;
ctxpane.fill ();
ctxpane.closepath ();
ctxpane.Restore ();
}
} // fonction principale
//////////////////////////////////////////////////////////////// /
tapme ();
var temporisateur;
fonction startgame () {// démarrer le jeu
if (ifingame == 0)
{
ifingame = 1;
grade = 0;
var xx = math.floor (math.random () * 8);
/ * commutateur (xx)
{
Cas 0:
smallCircle.direction = Gamedirection.shang;
casser;
Cas 1:
smallCircle.direction = Gamedirection.xia;
casser;
Cas 2:
smallCircle.direction = Gamedirection.zuo;
casser;
Cas 3:
smallCircle.direction = Gamedirection.you;
casser;
Cas 4:
smallCircle.direction = gamedirection.zuoshang;
casser;
Cas 5:
smallCircle.direction = Gamedirection.zuoxia;
casser;
Cas 6:
smallCircle.direction = Gamedirection.youshang;
casser;
Cas 7:
smallCircle.direction = Gamedirection.youxia;
casser;
défaut:
casser;
} * /
smallCircle.direction = Gamedirection.xia;
smallCircle.x = smallriccle.y = 0;
line.start = math.pi / 2-Math.pi / 26;
line.end = math.pi / 2 + math.pi / 26;
line.direction = gamedirection.anticlock;
ClearInterval (temporisateur);
timer = setInterval (movtest, 10);
}
} // Démarrer le jeu startGame ()
fonction openntop ()
{
window.location = "http://pongotop.sinaapp.com";
}
10.Writs à la fin
C'est purement un auto-divertissement. Le troisième jour après l'écriture, j'étais occupé à soumettre des curriculum vitae pour trouver un stage et je n'ai pas eu le temps de m'occuper de cela. Je l'ai jeté dans mon cercle d'amis pour que mes amis jouent. Ce mois-ci est passé et je regarde à nouveau ce match. Je pense que cela n'aurait pas dû mourir comme ça. Je n'ai aucune compétence et je le fais très bien. Par conséquent, j'espère que cet article pourra aider certains amis qui s'intéressent à Pongo. De plus, j'espère que si un expert dans ce domaine peut me donner des conseils s'il le voit, il m'accueille pour laisser un message pour tous les doutes et les conseils. Merci!