Using JavaScript to achieve web version of Pongo design ideas and source code sharing

  • 2020-03-30 03:20:27
  • OfStack

1. Introduction to the game background (nonsense written in the front) :

      One day in early may, I saw a web recommended this game, Pongo, looked good and played it with an ipad. After playing two games, I felt that it was wrong, because it was a hand game, you know.

      But not for a while to find the game in the ipad seems to have some bugs, play for a while will be stuck and then can only be forced back, really anxious, the record is still waiting to break.

      How to do? The idea of playing games as evil as playing their own games appeared again, and then put the pad to a friend to abuse the heart, I silently returned to the computer began to write their own not card.

      About two hours, write out the basic frame, then throw it into sinaapp and try it out.

      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 detected some serious bugs and finally spent some time to change them.

2. Website of game trial:

Pongo + (limited to mobile terminal) : http://mypongo.sinaapp.com/

Making open source (welcome fork to make the game better) : https://github.com/ChenReason/pongo/blob/gh-pages/index.html

3. Game rules:

Clicking on the screen will change the direction of the baffle, and once the baffle is clicked, the baffle will change its direction accordingly, in order to stop the ball from going out of the circle. The longer the better! Finally, you can submit your own results 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, covered with a large circle, and then draw a small circle and baffle on the circle. In the middle of the baffle there is a super small circle with the size of 1px (for collision detection).

      B) there are altogether 8 directions of motion of the small circle: up, down, left, right, upper left, lower left, upper right and lower right.

      C) the baffle moves in only two directions, clockwise and counterclockwise.

      D) collision detection does not involve the use of the engine, but is judged by the distance between the small circle and the super small circle in the middle of the baffle, so as to achieve simple collision detection.

      E) determine the rebound direction of the ball after the collision, and use common sense to list 8 cases.

6. Difficulties in game implementation:

      A) collision detection.

      B) the clearing time of the timer setInterval and whether it is clear and thorough.

      C) the relationship between timer cycle length and game experience.

      D) the problem of game fluency caused by the different performance of Android and IOS devices.

7. Existing problems of the game:

      A) because of the collision detection is two circle of center distance, and involves the use of the timer, thus caused by the timer interval is very short in the back of the naked eye can see a collision actually there have been dozens of collision, which can lead to bounce ball the last actual direction and realistic physics theorem is different, after optimization, the probability is low, but still could not avoid, so some players will find small round if not very accurate to bump in the middle of the damper is may cause the failure of the game.

      B) the game experience on Android is not the same as that on iOS or other mobile devices (generally speaking, iOS is due to Android) due to the verbose and inefficient functions and the use of timers.

      C) the leaderboard is not automatically updated in real time. (database not yet available)

8. Preview of the game interface:

(figure 1 is the initial version, figure 2 has removed the button, figure 3 is the final version, and figure 4 is the leaderboard)

< img border = 0 SRC = "/ / files.jb51.net/file_images/article/201406/201406160919102.png" >

Figure 1

< img border = 0 SRC = "/ / files.jb51.net/file_images/article/201406/201406160919103.png" >

Figure 2

< img border = 0 SRC = "/ / files.jb51.net/file_images/article/201406/201406160919104.png" >

Figure 3

< img border = 0 SRC = "/ / files.jb51.net/file_images/article/201406/201406160919105.png" >

9. Game JavaScript part of the source code:



var ifingame=0;
var maxgrade=0,grade=0;
var grade1,grade2;
var nickname; 
var gamespeed=1.4;//Ball speed
var linespeed=Math.PI/95; //Tracking line velocity
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('mymaxgrade'));
       if(isNaN(maxgrade)==true)
       {
       maxgrade=0;
      }
 if (nickname!=null && nickname!="")
   {
        alert(' welcome '+nickname+' Come back !'+'n'+" Please share if you like ~");
    }
 else 
   {
    nickname=prompt(' Please enter your nickname : (too long names on the list will show incomplete oh) ',"")
    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);//Essential canvas center pan & cake;
function sendmail()
     {
            if(grade2>themaxgradeline)
            var max_grade=grade2;
            window.location.href='index.php?max_grade='+max_grade+'&nick_name='+nickname;
        
            alert(nickname+" Your achievements are: "+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,
 };// The direction of 
var canvas={
 width : 300,
 height: 300,
 };// The canvas 

var bigcircle = {//Great circle parameters
        x : 0,    //The x-coordinate of the center of the circle
        y : 0,    //The y-coordinate of the center of the circle
        r : 150,  //The radius of the circle
  c : 'rgb(255,255,255)',   
    };// Great circle 
var smallcircle = {//Small circle parameters
        x : 0,    //The x-coordinate of the center of the circle
        y : 0,    //The y-coordinate of the center of the circle
        r : 12,  //The radius of the circle
  c : 'rgb(204,105,106)',
  direction :  gamedirection.xia,
    };// Small round 

var line = {//Parameters of baffle line
 x : 0,    //The x-coordinate of the center of the circle
    y : 0,    //The y-coordinate of the center of the circle
    r : 150 ,  //Radius of an arc
    start:(Math.PI/2-Math.PI/16),
    end : (Math.PI/2+Math.PI/16),
 c : 'rgb(55,55,55)',
 direction: gamedirection.anticlock,
 };//Track line
var dot = {//Tracking point parameter
 x : (bigcircle.r*Math.cos(line.start+Math.PI/16)),//Let's take the big circle as the origin
 y : (bigcircle.r*Math.sin(line.start+Math.PI/16)),
 r : 1,
 }//Trace 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 distance between the ball and the center of the circle squared getdistance()

function ifgameover(){//Determine if the boundary is crossed
 if((getdistance() - bigcircle.r)>5)
 return true;
 else
 return false;
 } //Determine if the game is over 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;  
    }
   } 
}//Random backward randomback()
function smallcircledirection()
{
 switch (smallcircle.direction){    //It moves 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;  
    }
   } 
}//Small ball movement smallcircledirection()

ctxpane.beginPath(); // Great circle 
ctxpane.arc(bigcircle.x,bigcircle.y,bigcircle.r,0,Math.PI*2,true);
ctxpane.fillStyle = bigcircle.c;
ctxpane.fill();
ctxpane.closePath();

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 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(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(); // Great 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)   //Track 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)         //Track counter 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) //Trace point
dot.y=bigcircle.r*Math.sin(line.start+Math.PI/32)
ctxpane.beginPath();// online Trace point
ctxpane.arc(dot.x,dot.y,dot.r,0,Math.PI*2,true);
ctxpane.fillStyle = smallcircle.c;
ctxpane.fill();
ctxpane.closePath(); 
smallcircledirection();// Small round 
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();
 }
}//The main function
    ///////////////////////////////////////////
tapme();
var timer;
function startgame(){//Start the game
    if(ifingame==0)
    {
        ifingame=1;
        grade=0;
        var xx=Math.floor(Math.random()*8);
        
    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";
    }

Write at the end


Related articles: