Pongo is a very interesting game with low-handed skills. Because of some bugs found during the playing process, I plan to make it myself. after some thought, I will have this article, let's talk about the design ideas and source code for implementing this game. 1. introduction to the game background (nonsense written in front ):
One day in early May, I saw a network recommending this game, Pongo. It looked pretty good and I used the ipad to try it out. After playing two games, I felt that it was quite enjoyable, because it's a hand-held game that everyone understands.
But soon I found that the game seems to have some bugs on the ipad, and it will get stuck after playing for a while and then it will only be able to retreat. It is really worried that the record is still waiting to be broken.
What should I do? The idea of playing a game is worse than having a game of my own. Then I threw pad to my friends, and I silently went back to the computer and started to write a non-card one.
In about two hours, I wrote the basic framework, and then threw it into the sinaapp and tried to get the basic effect.
The next day, I woke up and spent some time designing the interface because I had nothing to do on weekends. Unfortunately, I detected some serious bugs and finally took some time to get rid of them.
Finally, the game is named "Pongo +" (I am playing when the mobile phone party clicks). The computer does not support it for the time being. By the way, the source code is uploaded on Github and the score submission module is removed.
2. Game trial URL:
Pongo + (mobile only): http://mypongo.sinaapp.com/
Github open source (welcome fork for better gaming): https://github.com/ChenReason/pongo/blob/gh-pages/index.html
3. gameplay rules:
Clicking the screen will change the direction of the baffle plate. clicking a baffle plate changes the direction of the screen to block the ball that is rolling around to prevent it from running out of the circle. The longer the time, the better! Finally, you can submit your own scores for ranking!
4. technologies used in the game:
HTML, CSS, JavaScript, Canvas, PHP
5. Game Design ideas:
A) Use Canvas to draw the main interface of the game. The bottom is a monochrome rectangle with a large circle covered on it, and then draw a small circle and bezel on the large circle, there is also a super small circle with a size of 1 px in the middle of the baffle plate (for collision detection ).
B) There are eight incircle motion directions: top, bottom, left, right, top left, bottom left, top right, and bottom right.
C) The baffle has only two directions, clockwise and counterclockwise.
D) The collision detection does not involve the use of the engine. Instead, the collision detection is performed based on the distance between the small circle and the super small circle in the center of the baffle.
E) determine the direction of rebound after a ball collision. Eight cases are listed using common sense.
6. Difficulties in game implementation:
A) collision detection.
B) whether the time for clearing the setInterval timer is clear and thorough.
C) The relationship between the timer cycle and the game experience.
D. Game smoothness problems caused by different performance of Android and IOS devices.
7. Existing game problems:
A) Because collision detection compares the center distance between two circles and involves the use of the timer, therefore, due to the extremely short timer interval, dozens of collisions have actually occurred behind a collision that is visible to the naked eye. As a result, the final rebound direction of the ball is different from the physical Theorem in reality, after optimization, the probability of appearance is low, but it still cannot be avoided. Therefore, some players may find that the game may fail if they do not hit the center of the baffle very accurately.
B) because the functions are too lengthy and the running efficiency is low, coupled with the timer, the gaming experience on the Andorid and iOS or other mobile terminals is different (iOS as a whole due to Android ).
C) rankings are not automatically updated in real time. (The database is not used yet)
8. Game interface preview:
(For the first version, remove the button, for the final version, for the ranking)
9. Some game JavaScript source code:
The Code is as follows:
Var ifingame = 0;
Var maxgrade = 0, grade = 0;
Var grade1, grade2;
Var nickname;
Var gamespeed = 1.4; // ball speed
Var linespeed = Math. PI/95; // Trace Line Speed
Var crashdistancefaild =-7; // collision detection parameter
Var crashdistancesucc = 15
Var fantanjuli = 7;
Var themaxgradeline = 12.1;
Function getCookie1 (nickname)
{
If (document. cookie. length> 0)
{
C_start = document. cookie. indexOf (nickname + "= ")
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 ));
}
}
Return ""
}
Function 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 ));
}
}
Return ""
}
Function setCookie (nickname, value, mymaxgrade, maxgrade, expiredays)
{
Var exdate = new Date ()
Exdate. setDate (exdate. getDate () + expiredays)
Document. cookie = nickname + "=" + escape (value) + "," + mymaxgrade + "=" + escape (maxgrade) + (expiredays = null )? "": "; Expires =" + exdate. toGMTString ());
}
Function checkCookie ()
{
Nickname = getCookie1 ('nickname ');
Maxgrade = parseInt (getCookie2 ('mymaxgrad '));
If (isNaN (maxgrade) = true)
{
Maxgrade = 0;
}
If (nickname! = Null & nickname! = "")
{
Alert ('Welcome '+ nickname +' back! '+' \ N' + "if you like it, please share it ~ ");
}
Else
{
Nickname = prompt ('enter your nickname: (the name is too long, but the list is incomplete )',"")
If (nickname! = Null & nickname! = "")
{
Var maxgradestring = maxgrade. toString ();
SetCookie ('nickname', nickname, 'mymaxgrad', maxgradestring, 365 );
}
}
}
Var objpane = document. getElementById ("pane ");
Var ctxpane = objpane. getContext ("2d ");
Ctxpane. translate (150,150); // required canvas center Translation
Function sendmail ()
{
If (grade2> themaxgradeline)
Var max_grade = grade2;
Window. location. href = 'index. php? Max_grade = '+ max_grade +' & nick_name = '+ nickname;
/*{
$ Grade = $ _ GET ['max _ grad'];
$ Nickname = $ _ GET ['Nick _ name'];
$ Mail = new SaeMail ();
$ Ret = $ mail-> quickSend ('reasonpongo @ 163.com ', $ grade, $ nickname, 'reasonpongo @ 163.com', 'mypongo ');
$ Mail-> clean ();
?>
}*/
Alert (nickname + "your score is:" + grade2 + "submitted successfully ~ ");
}
Var gamedirection = {
Shang: 1,
Xia: 5,
Zuo: 7,
You: 3,
Zuoshang: 8,
Zuoxia: 6,
Youshang: 2,
Youxia: 4,
Clock: 0,
Anticlock: 9,
}; // Direction
Var canvas = {
Width: 300,
Height: 300,
}; // Canvas
Var bigcircle = {// large circle Parameter
X: 0, // x axis coordinate value of the center
Y: 0, // y axis coordinate value of the center
R: 150, // radius of the circle
C: 'The rgb (255,255,255 )',
}; // Large circle
Var smallcircle = {// small circle Parameter
X: 0, // x axis coordinate value of the center
Y: 0, // y axis coordinate value of the center
R: 12, // radius of the circle
C: 'The rgb (204,105,106 )',
Direction: gamedirection. xia,
}; // Small circle
Var line = {// block line parameter
X: 0, // x axis coordinate value of the center
Y: 0, // y axis coordinate value of the center
R: 150, // The Arc Radius
Start :( Math. PI/2-Math.PI/16 ),
End: (Math. PI/2 + Math. PI/16 ),
C: 'rgb (55,55, 55 )',
Direction: gamedirection. anticlock,
}; // Trace Line
Var dot = {// trace point Parameter
X: (bigcircle. r * Math. cos (line. start + Math. PI/16), // use the circle as the origin
Y: (bigcircle. r * Math. sin (line. start + Math. PI/16 )),
R: 1,
} // Tracking point
Function changelinedirection ()
{
If (line. direction = gamedirection. clock)
{
Line. direction = gamedirection. anticlock;
}
Else
{
Line. direction = gamedirection. clock;
}
}
Function getdistance (){
Var distance = Math. sqrt (smallcircle. x) * (smallcircle. x) + (smallcircle. y) * (smallcircle. y ));
Return distance;
} // Return the square getdistance () distance between the center of the ball and the circle ()
Function ifgameover () {// determines whether a field exists.
If (getdistance ()-bigcircle. r)> 5)
Return true;
Else
Return false;
} // Determine whether the game is over ifgameover ()
Function ifcrash () {// Collision Detection
Var dx = dot. The x-smallcircle.x;
Var dy = dot. The y-smallcircle.y;
Var dd = Math. sqrt (dx * dx + dy * dy );
If (dd <crashdistancesucc)
Return true;
Else
Return false;
} // Collision detection ifcrash ()
Function randomback ()
{
Var x = Math. floor (Math. random () * 3 );
Switch (smallcircle. direction ){
Case gamedirection. shang:
{
Switch (x)
{
Case 0:
Smallcircle. direction = gamedirection. xia;
Smallcircle. y = smallcircle. y + fantanjuli;
Break;
Case 1:
Smallcircle. direction = gamedirection. zuoxia;
Smallcircle. x = smallcircle. x-fantanjuli;
Smallcircle. y = smallcircle. y + fantanjuli;
Break;
Case 2:
Smallcircle. direction = gamedirection. youxia;
Smallcircle. x = smallcircle. x + fantanjuli;
Smallcircle. y = smallcircle. y + fantanjuli;
Break;
Default:
Break;
} Break;
}
Case gamedirection. xia:
{
Switch (x)
{
Case 0:
Smallcircle. direction = gamedirection. shang;
Smallcircle. y = smallcircle. y-fantanjuli;
Break;
Case 1:
Smallcircle. direction = gamedirection. zuoshang;
Smallcircle. x = smallcircle. x-fantanjuli;
Smallcircle. y = smallcircle. y-fantanjuli;
Break;
Case 2:
Smallcircle. direction = gamedirection. youshang;
Smallcircle. x = smallcircle. x + fantanjuli;
Smallcircle. y = smallcircle. y-fantanjuli;
Break;
Default:
Break;
} Break;
}
Case gamedirection. zuo:
{
Switch (x)
{
Case 0:
Smallcircle. direction = gamedirection. you;
Smallcircle. x = smallcircle. x + fantanjuli;
Break;
Case 1:
Smallcircle. direction = gamedirection. youshang;
Smallcircle. x = smallcircle. x + fantanjuli;
Smallcircle. y = smallcircle. y-fantanjuli;
Break;
Case 2:
Smallcircle. direction = gamedirection. youxia;
Smallcircle. x = smallcircle. x + fantanjuli;
Smallcircle. y = smallcircle. y + fantanjuli;
Break;
Default:
Break;
} Break;
}
Case gamedirection. you:
{
Switch (x)
{
Case 0:
Smallcircle. direction = gamedirection. zuo;
Smallcircle. x = smallcircle. x-fantanjuli;
Break;
Case 1:
Smallcircle. direction = gamedirection. zuoxia;
Smallcircle. x = smallcircle. x-fantanjuli;
Smallcircle. y = smallcircle. y + fantanjuli;
Break;
Case 2:
Smallcircle. direction = gamedirection. zuoshang;
Smallcircle. x = smallcircle. x-fantanjuli;
Smallcircle. y = smallcircle. y-fantanjuli;
Break;
Default:
Break;
} Break;
}
Case gamedirection. zuoshang:
{
Switch (x)
{
Case 0:
Smallcircle. direction = gamedirection. youxia;
Smallcircle. x = smallcircle. x + fantanjuli;
Smallcircle. y = smallcircle. y + fantanjuli;
Break;
Case 1:
Smallcircle. direction = gamedirection. xia;
Smallcircle. y = smallcircle. y + fantanjuli;
Break;
Case 2:
Smallcircle. direction = gamedirection. you;
Smallcircle. x = smallcircle. x + fantanjuli;
Break;
Default:
Break;
} Break;
}
Case gamedirection. zuoxia:
{
Switch (x)
{
Case 0:
Smallcircle. direction = gamedirection. youshang;
Smallcircle. x = smallcircle. x + fantanjuli;
Smallcircle. y = smallcircle. y-fantanjuli;
Break;
Case 1:
Smallcircle. direction = gamedirection. shang;
Smallcircle. y = smallcircle. y-fantanjuli;
Break;
Case 2:
Smallcircle. direction = gamedirection. you;
Smallcircle. x = smallcircle. x + fantanjuli;
Break;
Default:
Break;
} Break;
}
Case gamedirection. youshang:
{
Switch (x)
{
Case 0:
Smallcircle. direction = gamedirection. zuoxia;
Smallcircle. x = smallcircle. x-fantanjuli;
Smallcircle. y = smallcircle. y + fantanjuli;
Break;
Case 1:
Smallcircle. direction = gamedirection. zuo;
Smallcircle. x = smallcircle. x-fantanjuli;
Break;
Case 2:
Smallcircle. direction = gamedirection. xia;
Smallcircle. y = smallcircle. y + fantanjuli;
Break;
Default:
Break;
} Break;
}
Case gamedirection. youxia:
{
Switch (x)
{
Case 0:
Smallcircle. direction = gamedirection. zuoshang;
Smallcircle. x = smallcircle. x-fantanjuli;
Smallcircle. y = smallcircle. y-fantanjuli;
Break;
Case 1:
Smallcircle. direction = gamedirection. zuo;
Smallcircle. x = smallcircle. x-fantanjuli;
Break;
Case 2:
Smallcircle. direction = gamedirection. shang;
Smallcircle. y = smallcircle. y-fantanjuli;
Break;
Default:
Break;
} Break;
}
Default:
{
Break;
}
}
} // Random reverse randomback ()
Function smallcircledirection ()
{
Switch (smallcircle. direction) {// move in the direction of the ball
Case gamedirection. shang:
{
Smallcircle. y = smallcircle. y-gamespeed;
Grade ++;
If (grade> maxgrade)
{
Maxgrade = grade;
Newrecoder ();
}
Addone ();
Break;
}
Case gamedirection. xia:
{
Smallcircle. y = smallcircle. y + gamespeed;
Grade ++;
If (grade> maxgrade)
{
Maxgrade = grade;
Newrecoder ();
}
Addone ();
Break;
}
Case gamedirection. zuo:
{
Smallcircle. x = smallcircle. x-gamespeed;
Grade ++;
If (grade> maxgrade)
{
Maxgrade = grade;
Newrecoder ();
}
Addone ();
Break;
}
Case gamedirection. you:
{
Smallcircle. x = smallcircle. x + gamespeed;
Grade ++;
If (grade> maxgrade)
{
Maxgrade = grade;
Newrecoder ();
}
Addone ();
Break;
}
Case gamedirection. zuoshang:
{
Smallcircle. x = smallcircle. x-gamespeed * 0.8;
Smallcircle. y = smallcircle. y-gamespeed * 0.8;
Grade ++;
If (grade> maxgrade)
{
Maxgrade = grade;
Newrecoder ();
}
Addone ();
Break;
}
Case gamedirection. zuoxia:
{
Smallcircle. x = smallcircle. x-gamespeed * 0.8;
Smallcircle. y = smallcircle. y + gamespeed * 0.8;
Grade ++;
If (grade> maxgrade)
{
Maxgrade = grade;
Newrecoder ();
}
Addone ();
Break;
}
Case gamedirection. youshang:
{
Smallcircle. x = smallcircle. x + gamespeed * 0.8;
Smallcircle. y = smallcircle. y-gamespeed * 0.8;
Grade ++;
If (grade> maxgrade)
{
Maxgrade = grade;
Newrecoder ();
}
Addone ();
Break;
}
Case gamedirection. youxia:
{
Smallcircle. x = smallcircle. x + gamespeed * 0.8;
Smallcircle. y = smallcircle. y + gamespeed * 0.8;
Grade ++;
If (grade> maxgrade)
{
Maxgrade = grade;
Newrecoder ();
}
Addone ();
Break;
}
Default:
{
Break;
}
}
} // Smallcircledirection ()
/* Draw the bottom circle */
Ctxpane. beginPath (); // large circle
Ctxpane. arc (bigcircle. x, bigcircle. y, bigcircle. r, 0, Math. PI * 2, true );
Ctxpane. fillStyle = bigcircle. c;
Ctxpane. fill ();
Ctxpane. closePath ();
/* Draw the bottom tracing line */
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 ();
Function 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 );
Ctxpane. closePath ();
}
Function newrecoder ()
{
Ctxpane. beginPath ();
Ctxpane. fillStyle = "rgb (255, 0, 0 )";
Ctxpane. font = "18px Papyrus ";
Ctxpane. fillText ("New! ", 58,80 );
Ctxpane. closePath ();
}
Function addone ()
{
Grade1 = (grade/150). toFixed (1 );
Grade2 = (maxgrade/150). toFixed (1 );
Var say1 = "now ";
Var say2 = "best"
Ctxpane. beginPath ();
Ctxpane. strokeStyle = "rgb (250,222,185 )";
Ctxpane. font = "60px Papyrus ";
Ctxpane. strokeText (grade1,-45,-60 );
Ctxpane. strokeText (grade2,-45,100 );
Ctxpane. fillStyle = "rgb (100 )";
Ctxpane. font = "15px Papyrus ";
Ctxpane. fillText (say1, 58,-60 );
Ctxpane. fillStyle = "rgb (100 )";
Ctxpane. font = "15px Papyrus ";
Ctxpane. fillText (say2, 58,100 );
Ctxpane. closePath ();
}
Function movetest (){
If (ifgameover ())
{
Ifingame = 0;
If (maxgrade> parseInt (getCookie2 ('mymaxgrad ')))
{
SetCookie ('nickname', nickname, 'mymaxgrad', maxgrade. toString (), 365 );
}
ClearInterval (timer );
Tapme ();
}
Else
{
If (ifcrash ())
{
Randomback ();
}
Ctxpane. clearRect (-150,-150,300,300); // clear the screen
Ctxpane. beginPath (); // large circle
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) // clockwise
{
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) // trace inverse clockwise
{
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) // tracking point
Dot. y = bigcircle. r * Math. sin (line. start + Math. PI/32)
Ctxpane. beginPath (); // online tracking point
Ctxpane. arc (dot. x, dot. y, dot. r, 0, Math. PI * 2, true );
Ctxpane. fillStyle = smallcircle. c;
Ctxpane. fill ();
Ctxpane. closePath ();
Smallcircledirection (); // small circle
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 ();
}
} // Main Function
//////////////////////////////////////// ///
Tapme ();
Var timer;
Function startgame () {// start the game
If (ifingame = 0)
{
Ifingame = 1;
Grade = 0;
Var xx = Math. floor (Math. random () * 8 );
/* Switch (xx)
{
Case 0:
Smallcircle. direction = gamedirection. shang;
Break;
Case 1:
Smallcircle. direction = gamedirection. xia;
Break;
Case 2:
Smallcircle. direction = gamedirection. zuo;
Break;
Case 3:
Smallcircle. direction = gamedirection. you;
Break;
Case 4:
Smallcircle. direction = gamedirection. zuoshang;
Break;
Case 5:
Smallcircle. direction = gamedirection. zuoxia;
Break;
Case 6:
Smallcircle. direction = gamedirection. youshang;
Break;
Case 7:
Smallcircle. direction = gamedirection. youxia;
Break;
Default:
Break;
}*/
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. anticlock;
ClearInterval (timer );
Timer = setInterval (movetest, 10 );
}
} // Start the game startgame ()
Function opentop ()
{
Window. location = "http://pongotop.sinaapp.com ";
}
10. Write it at the end
This is just a self-entertainment experience. On the third day after writing, I started to submit my resume and seek an internship, so I didn't have to worry about it. I threw it into my circle of friends and let my friends play. After a month, I watched the game again. I felt that it was not supposed to die like this. I had no technology and did a very poor job, therefore, this article hopes to help some friends who are interested in pongo. In addition, I hope that if some experts in this field can enlighten me, I will be welcomed to leave a message for all questions and questions, thank you!