1. Introduction to the game background (nonsense written in the front):
One day in early May, I saw a certain website recommending this game, Pongo. I looked pretty good and tried it with iPad. After playing two games, I felt that it was a good thing, and it was quite satisfying, because it was a game that was a type of owed hand. Everyone knows it.
But after a while, I found that the game seemed to have some bugs on iPad. I would get stuck after playing for a while and I could only force it to retreat. It was really heartbreaking, and the record was still waiting to be broken.
what to do? The idea that it is better to play games than to play your own games appeared evilly again, and then I threw the pad to my friend's heartbreaking. I silently returned to the computer and started writing something that I couldn't get stuck.
It took about two hours to write down the basic framework, and then threw it into Sinaapp and tried it. It was basically the best to play and then take a shower and go to bed.
When I woke up the next day, I spent some time designing the interface because I had nothing to do on the weekend. Unfortunately, I found some serious bugs myself, and finally took some time to correct them.
Finally, the game was named "Pongo+" (click on me to play on mobile phones). The computer is not supported for the time being. By the way, the source code was uploaded on Github and the submission score module was removed.
2. Game trial website:
Pongo+ (mobile only): http://mypongo.sinaapp.com/
github open source (fork is welcome to make the game better): https://github.com/ChenReason/pongo/blob/gh-pages/index.html
3. Game rules and gameplay:
Clicking on the screen will change the direction of the bezel movement. Clicking on the bezel will change the direction of the bezel once, with the purpose of just blocking the small balls rolling around and preventing them from running out of the big circle. The longer the time, the better! Finally, you can submit your own scores for ranking!
4. The technology 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, covered with a large circle, and a small circle and a baffle are drawn on the large circle. There is also a super small circle with a size of 1px in the middle of the baffle (for collision detection).
b) There are 8 directions of movement of small circles: upper, lower, left, right, upper left, lower left, upper right and lower right.
c) There are only two directions of movement of the baffle, clockwise and counterclockwise.
d) Collision detection does not involve the use of the engine, but rather makes the distance judgment based on the small circle and the super circle in the middle of the baffle, thereby achieving simple collision detection.
e) The direction of rebound after the ball collided is determined, and the general knowledge is used to list it, and there are 8 situations in total.
6. Difficulties in game implementation:
a) Collision detection.
b) Timer setInterval clearance timing and whether it is clear and thorough.
c) The relationship between the length of the timer cycle and the gaming experience.
d) Game fluency problems caused by the different performance of Android and IOS devices.
7. Existing problems with the game:
a) Since collision detection is to compare the center distance between two circles and involves the use of timers, due to the extremely short timer interval, dozens of collisions have actually occurred behind a collision seen by the naked eye. This will cause the actual rebound direction of the ball to be different from the actual physical theorem. After optimization, the probability of occurrence is low, but it has not been avoided. Therefore, some players will find that if the circle does not hit the center of the baffle accurately, it may lead to the game failure.
b) Because the functions are too verbose, low-efficiency, and the use of timers, the gaming experience on Android is different from iOS or other mobile devices (in general, iOS is due to Android).
c) The ranking list has not achieved automatic real-time updates. (The database won't be used yet)
8. Game interface preview:
(Figure 1 is the first edition, Figure 2 has removed the button, Figure 3 is the final edition, and Figure 4 is the ranking list)
Figure 1
Figure 2
Figure 3
9. Part of the source code of the game JavaScript:
The code copy 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;//Track line speed
var crashdistancefaild=-7;//collision detection parameters
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('mymaxgrade'));
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('Please enter your nickname: (The name is too long and will be displayed incomplete)',"")
if (nickname!=null && nickname!="")
{
var maxgradestring=maxgrade.toString();
setCookie('nickname',nickname,'mymaxgrade',maxgradestring,365);
}
}
}
var objpane=document.getElementById("pane");
var ctxpane=objpane.getContext("2d");
ctxpane.translate(150,150);//Required Canvas Center Point Translation
function sendmail()
{
if(grade2>themaxgradeline)
var max_grade=grade2;
window.location.href='index.php?max_grade='+max_grade+'&nick_name='+nickname;
/* {
<?php
$grade=$_GET['max_grade'];
$nickname=$_GET['nick_name'];
$mail = new SaeMail();
$ret = $mail->quickSend( '[email protected]' , $grade , $nickname ,'[email protected]' , 'mypongo' );
$mail->clean();
?>
}*/
alert(nickname+"Your grade 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 = {//Big circle parameter
x : 0, //The x-axis coordinate value of the center of the circle
y: 0, //The y-axis coordinate value of the center of the circle
r: 150, //The radius of the circle
c: 'rgb(255,255,255)',
};//Dayuan
var smallcircle = {//Smallcircle parameter
x : 0, //The x-axis coordinate value of the center of the circle
y: 0, //The y-axis coordinate value of the center of the circle
r: 12, //The radius of the circle
c: 'rgb(204,105,106)',
direction : gamedirection.xia,
};//Small circle
var line = {// Parameters of the baffle line
x : 0, //The x-axis coordinate value of the center of the circle
y: 0, //The y-axis coordinate value of the center of the circle
r: 150 , //The radius of the arc
start:(Math.PI/2-Math.PI/16),
end :(Math.PI/2+Math.PI/16),
c: 'rgb(55,55,55)',
direction: gamedirection.anticlock,
};//Tracking line
var dot = {//Tracking point parameters
x:(bigcircle.r*Math.cos(line.start+Math.PI/16)), // Use the big 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 to the square distance between the small ball and the large circle center getdistance()
function ifgameover(){//Judge whether it is out of bounds
if((getdistance() - bigcircle.r)>5)
return true;
else
return false;
}//Judge whether the game ends ifgameover()
function ifcrash(){//collision detection
var dx = dot.x-smallcircle.x;
var dy = dot.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;
}
}
}//The ball randomly reversed randomback()
function smallcircledirection()
{
switch (smallcircle.direction){//Move according to 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;
}
}
}//The ball moves smallcircledirection()
/*Draw the bottom circle*/
ctxpane.beginPath();//Dayuan
ctxpane.arc(bigcircle.x,bigcircle.y,bigcircle.r,0,Math.PI*2,true);
ctxpane.fillStyle = bigcircle.c;
ctxpane.fill();
ctxpane.closePath();
/*Draw the bottom tracking 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',70,30);
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 says1="now";
var says2="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(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();
}
function movetest(){
if(ifgameover())
{
ifingame=0;
if(maxgrade>parseInt(getCookie2('mymaxgrade')))
{
setCookie('nickname',nickname,'mymaxgrade',maxgrade.toString(),365);
}
clearInterval(timer);
tapme();
}
else
{
if(ifcrash())
{
randomback();
}
ctxpane.clearRect(-150,-150,300,300);//Clear the screen
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) //Tracking line 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)//Tracking counterclockwise
{
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();//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();
}
}//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(movtest,10);
}
}//Start the game startgame()
function opentop()
{
window.location="http://pongotop.sinaapp.com";
}
10.Written at the end
This is purely a self-entertainment. On the third day after writing, I was busy submitting resumes to find an internship and I didn’t have time to take care of it. I threw it into my circle of friends for my friends to play. This month has passed and I watch this game again. I feel that it shouldn’t have died just like this. I don’t have any skills and do it very well. Therefore, I hope that this article can help some friends who are interested in Pongo. In addition, I hope that if any expert in this field can give me advice if he sees it, he welcomes me to leave a message for all doubts and advice. Thank you!