1.遊戲背景介紹(寫在前面的廢話):
五月初的某天,看到某網推薦了這款遊戲,Pongo,看著還不錯的樣子就用ipad下下來試玩了下,玩了兩局感覺還錯挺過癮的,因為是手欠類遊戲嘛大家懂的。
但沒一會發現遊戲在ipad似乎有些bug,玩一會兒就會卡住然後只能強退了,真是揪心,記錄還等著破呢。
怎麼辦?玩遊戲不如玩自己的遊戲的念頭又邪惡的出現了,然後就把pad丟給了朋友虐心去,我默默回到電腦前開始動手自己寫個不會卡的。
大概兩小時吧,寫出了基本框架,然後丟sinaapp裡試了下效果基本能玩就洗洗睡了。
隔天醒來因為週末沒事就花了些時間設計了下介面,同時不幸自己測出了一些比較嚴重的bug,最後又花了些時間給改掉了。
最後遊戲取名」Pongo 「(手機黨點我即玩),電腦端暫時不支持,並順便在Github上上傳了源碼並去掉了提交成績模組。
2.遊戲試玩網址:
Pongo (僅限行動裝置):http://mypongo.sinaapp.com/
github開源(歡迎fork讓遊戲更好):https://github.com/ChenReason/pongo/blob/gh-pages/index.html
3.遊戲規則玩法:
點擊螢幕會改變擋板的運動方向,點擊一次擋板方向相應改變一次,目的是為了能剛好擋住四處滾動的小球不讓其跑出大圓外。時間越長越好!最後可提交自己的成績進行排名!
4.遊戲所用技術:
HTML、CSS、JavaScript、Canvas、PHP
5.遊戲設計思路:
a)運用Canvas將遊戲的主介面畫出,底部為一單色長方形,上面覆蓋一個大圓,大圓上再繪製小圓及擋板,擋板中部還有一個大小為1px的超級小圓(作實現碰撞檢測)。
b)小圓運動方向一共有8個分別為上、下、左、右、左上、左下、右上、右下。
c)擋板的運動方向只有兩個,順時針和逆時針。
d)碰撞偵測未涉及引擎的使用,而是根據小圓與擋板中間的超級小圓進行距離判斷,從而實現簡陋的碰撞檢測。
e)小球碰撞後反彈的方向確定,利用常識列舉,共8種情況。
6.遊戲實現困難:
a)碰撞偵測。
b)定時器setInterval的清除時機及是否清楚且徹底。
c)定時器週期長短與遊戲體驗的關係。
d)Android與IOS裝置效能不同所導致的遊戲流暢度問題。
7.遊戲現有問題:
a)由於碰撞檢測是比較兩圓的圓心距,且涉及定時器的使用,因此由於定時器間隔極短導致在肉眼所見的一次碰撞背後其實已經發生了數十次碰撞,由此會導緻小球最後實際的反彈方向與現實的物理定理有所不同,經過優化,出現的機率已經較低,但仍未能避免,因此有些玩家會發現小圓若沒有很準地撞在擋板正中央則可能導致遊戲失敗。
b)由於函數過於冗長,運作效率較低,再加上使用定時器,因此在Andorid與iOS或其他行動裝置上的遊戲體驗不盡相同(整體來說iOS由於Android)。
c)排行榜並未實現自動即時更新。 (資料庫還不會用)
8.遊戲介面預覽:
(圖1為初版,圖2去掉了按鈕,圖3為最終版,圖4為排行榜)
圖1
圖2
圖3
9.遊戲JavaScript部分原始碼:
var ifingame=0;
var maxgrade=0,grade=0;
var Grade1,grade2;
var 暱稱;
var gamespeed=1.4;//小球速度
var linespeed=Math.PI/95; //追蹤線速度
var crashdistancefaild=-7;//碰撞偵測參數
var crashdistancesucc=15
var fantanjuli=7; ;
function getCookie1(nickname)
{
if (document.cookie.length>0)
{
c_start=document.cookie=document. ( c_start!=-1)
{
c_start=c_start暱稱.length 1;
c_end=document .indexOf==(",",c_start); c_end=document.cookie.length;
return unescape(document.cookie.substring(c_start,c_) turn ""
}
function getCookie2(mymaxgrade)
{
if (document.cookie.length>0)
{
c_start=document.cookie.indexOf(mymaxgrade "=")
{
c_start=c_start mymaxgrade.length 1;
c_end=document.cookie.indexOf(==;",c_start);
c_end=document.cookie.length;
return unescape(document.cookie.substring(c_start,c_end));
}
(暱稱,值,mymaxgrade,maxgrade,expiredays)
{
var exdate=new Date()
exdate.setDate(exdate.getDate() expiredays)
(值) "," mymaxgrade "=" escape(maxgrade) ((expiredays==null) ? "" : "; expires=" exdate.toGMTString());
}
函數checkCookie()
{
nickname=getCookie1('nickname');
maxgrade=parseInt(getCookie2 ('mymaxgrade'));
maxgrade =0;
}
if (暱稱!=null && 暱稱!="" )
{
alert(''中使「暱稱」為「」暱稱回來了! ''n'“如果喜歡請分享一下哈~”);
}
else
{
暱稱=提示('請輸入你的暱稱中榜但是會顯示名字太長上榜但是會顯示名字太長上榜但是會顯示名字太長不完整哦)',"")
if (nickname!=null &&暱稱!="")
{
var maxgradestr nickname,'mymaxgrade',maxgradestring,365);
}
}
}
var jpane= documget.cy>docum; ("2d");
ctxpane.translate(150,150);//必備物資中心點平移
function sendmail()
{
var max_grade= grade2;
window.location.href='index.php?max_grade=' max_grade '&nick_name=' 暱稱 $grade=$_GET['max_grade' ];
$nickname=$_GET['nick_name'];
$mail = new SaeMail();
$ ret = $mail->quickSend( 'reasonpongo@163.com', $quickSend( 'reasonpongo@163.com', $quickSend( 'reasonpongo@163.com' $nickname ,'reasonpongo@163.com' , 'mypongo' );
$mail->clean();
?>
?>
name "你的成績是: "2年級"提交成功~");
}
var gamedirection={
var gamedirection={
上:1,
var gamedirection={
上:1,
下:左🎜 >你:3,
左上:8,
左下:6,
右:2,
youxia : 4,
時鐘: 0,
反時鐘: 9,
時鐘: 0,
反時鐘: 9. > };//方向
var canvas={
寬度: 300,
高度: 300,
};//畫布
var bigcircle = {//大圓參數
x : 0, //圓心的x軸座標值
> r : 150, //圓的半徑
c : 'rgb(255,255,255)',
};//大圓 x : 0, / /圓心的x軸座標值
y : 0, //圓心上的y軸座標值
r : 12,005,00050,00050,00050,000,005 direction : gamedirection.xia,
};//小圓
var line = {//擋板線的參數
x : 0, //圓心的x : 0, //圓心的y軸座標值
r : 150 , //圓弧的半徑
start:(Math.PI/2-Math.PI/16),
.PI/2 Math.PI/16),
c : 'rgb(55,55,55)',
direction: gamedirection.anticlock,
};//追蹤線
var dot dot = {//追蹤點參數
x : (bigcircle.r*Math.cos(line.start Math.PI/16)),//以大圓為原點
y : (bigcircle.r*Math. sin(line.start Math.PI/16)),
r : 1,
}//追蹤點
function changelinedirection()
{
if(line.direction==gamedirectiondirection==gamedirection .clock)
{
line.direction=gamedirection.anticlock;
}
else
{
line.direction=gam.direction=gam. 🎜>
function getdistance(){
var distance=Math.sqrt((smallcircle.x)*(smallcircle.x ) (smallcircle.y )*(smallcircle.y ));
}//返回小球與大圓中心距離平方getdistance()
function ifgameover(){//判斷是否出界
if((getdistance() - bigcircle.r)>5)
return true;
else
return false;
} //判斷遊戲是否結束ifgameover()
function ifcrash(){ //碰撞偵測6 -smallcircle.x;
var dy = dot.y-smallcircle.y;
var dd=Math.sqrt(dx*dx dy*dy);
if(dd true;
else
return false;
}//碰撞偵測ifcrash()
function randomback()
{
var xMath. ()*3);
switch (smallcircle.direction){
case gamedirection.shang:
case 0:
smallcircle. direction=gamedirection.xia;
case 1:
smallcircle.direction=gamedirection.zuoxia;
-fantanjuli;
smallcircle.y=smallcircle.y fantanjuli;
break;
). xia;
smallcircle.x=smallcircle.x fantanjuli;
smallcircle.y=smallcircle.y fantanjuli;
break;
🎜> }
case gamedirection.xia:
{
switch (x)
. cle.direction=gamedirection.shang;
smallcircle.y=smallcircle.y -fantanjuli;
break;
情形1:
smallc)
smallcircle.y=smallcircle.y-fantanjuli ;
break;
break;
smallcircle.y=smallcircle.y-fantanjuli;
break;
預設:
break;
} break;
}
case gamedirection.zuo:
> case 0:
smallcircle.direction=gamedirection。 ;
smallcircle.x=smallcircle.x fantanjuli;
break;
> smallcircle.x=smallcircle.x fantanjuli;
小議.y=smallcircle.y-fantanjuli;
break;
.情況2:
=smallcircle.x fantanjuli;
smallcircle. y =小圓.y ase gamedirection.you:
{
開關( x)
{
案例0:
smallcircle.direction=gamedirection.zuo; 案例1:
小圓圈。 狀況2:
smallcircle. direction=gamedirection zuoshang;
smallcircle.x=smallcircle.x-fantanjuli;
smallcircle.y=smallcircle.break-fanjuli 預設:
break;
} break;
}
案例gamedirection.zuoshang:
{
{
switch(x)
{
smallcircle.y=smallcircle.yyy fantanjuli;
lcircle.direction=gamedirection.xia;
smallcircle.y =smallcircle .y fantanjuli;
break;
情況2:
. =smallcircle.x fantanjuli;
break;
預設:
規則;
} 規則;
} 規則;
}
> case gamedirection.zuoxia:
{
{
switch(x)
{
案例0:
smallcircle.y=smallcircle.y-fantanjuli; break;
情況1:
smallcircle.direction=gamedirection.shang;
break;
break;
break;
預設:
中斷;
} break;
}
case gamedirection.youshang:
case 0:
smallcircle.direction =遊戲方向zuoxia;
smallcircle.x=smallcircle.x-fantanjuli;
smallcircle.y=smallc.
smallcircle.direction=gamedirection.zuo;
smallcircle.x=smallcircle.x-fantanjuli;
break;
smallcircle.y=smallcircle.y fantanjuli;
休息;
預設:
break;
} break;
switch (x)
{
情形0:
smallcircle.direction=gamedirection.zuoshang;
smallcircle.x= lcircle.x-fantananjuyat>; 🎜> break;
狀況1 :
smallcircle.direction=gamedirection.zuo;
smallcircle.x=smallc 🎜> smallcircle.direction=gamedirection.shang;
smallcircle.y=smallcircle.y-fantanjuli;
break;
預設值:
}
預設值:
{
休息;
}
}
}//小球隨機反向 randomback()
functionsmallcircledirection() case gamedirection.shang:
{
smallcircle.y=smallcircle.y-gamespeed;
newrecoder();
🎜> break;
}
case gamedirection.xia:
{
smallcircle.y=smallcircle.y 等級🎜> {
之後且有等級=等級;
newrecoder();
break;
}
case gamedirection.zuo:
gamespeed ;
等級;
if(等級>maxgrade)
{
newrecoder();
🎜> break;
}
case gamedirection.you:
{
smal if(grade>maxgrade)
{
等級=等級;
newrecoder();
);
break;
}
case gamedirection.zuoshang:
smallcircle. x-gamespeed*0.8;
smallcircle.y=smallcircle.y-gamespeed*0.8;
🎜> maxgrade=等級;
newrecoder ();
break;
}
case gamedirection.zuoxia:
{
. *0.8;
smallcircle.y=smallcircle.y gamespeed*0.8;
等級;
maxgrade=等級;
🎜> }
addone();
case gamedirection.youshang:
{
smallcircle.x=smallcircle.x 片. 🎜> 等級;
if (等級>maxgrade)
{
newrecoder();
case gamedirection.youxia :
{
smallcircle.x=smallcircle.x gamespeed*0.8;
if(等級>maxgrade)
{
maxgrade=grade;
); }
addone();
🎜> 中斷;
}
}
}//小球移動smallcircledirection()
/*畫出底部圓形*/
ctxpane.beginPath(); .x,bigcircle.y,bigcircle.r,0,Math.PI*2,true);
ctxpane.fillStyle = bigcircle.c;
ctxpane.fill ();
ctxpane.closePath() ;
/*畫出底部追蹤線條*/
ctxpane.beginPath();
ctxpane.lineWidth=6;
ctxpane.linesStyle = line .c;
ctxpane.arc(line .x, line.y, line.r, line.start, line.end,false);
ctxpane.中風();
ctxpane.closePath();
function tapme()//tapme
{
ctxpane.beginPath();
ctxpane.strokeStyle="rgb(255,222,195)";
ctxpane .font = "80px 紙莎草',-95,30);
ctxpane.fillStyle="rgb(255,205,105)";
ctxpane.font = "35px 紙莎草" ;
ctxpane.font = "35px 紙莎草" ;
ctxpane.fill,3000 );
ctxpane.closePath();
}
函數newrecoder()
{
ctxpane.beginPath();
ctxpane. ctxpane.beginPath();
ctxpane。 )";
ctxpane.font = "18px Papyrus";
ctxpane.fillText("新!",58,80);
ctxpane.closePath();
> ()
{
grade1=(grade/150).toFixed(1);
grade2=(maxgrade/150).toFixed( 1);
var say1="nownow";
var say2="最佳"
ctxpane.beginPath();
ctxpane.中風樣式="rgb(250,222,185)";
ctxpane. font = "60px Papyrus";
ctxpane. font = "60px Papyrus";
ctxpane. font = "60px Papyrus";
(grade1,-45,-60);
ctxpane.lines(grade2,-45,100);
ctxpane.fillSty. "15px 紙莎草";
ctxpane.fillText(say1,58,-60);
ctxpane.fillStyle="rgb(255,0,100)" ;
font =xppanp. Papyrus";
ctxpane.fillText(say2,58,100);
ctxpane.closePath();
}
函式movetest(){
}
函數movetest(){
}
函數movetest(){
>o(ifggo) > {
ifingame=0;
ifingame=0;
if(maxgrade>parseInt(getCookie String() ,365);
}
clearInterval(timer);
tapme();
}
else randomback() ;
}
ctxpane.clearRect(-150,-150,300,300); //清屏
ctxi.mm/m/mhpm(m); .x,bigcircle.y,bigcircle.r,0,Math.PI*2,true);
ctxpane.fillStyle = bigcircle.c;
ctxpane.fill ();
ctxpane.closePath() ;
if(line.direction==gamedirection.clock) 追蹤// 線順時針
{
line.start=line.start linespeed;
line.end=line.end linespeed;
ctxpane.beginPath();
ctxpane.lineWidth=4;
ctxpane.strokeStyle = line.c;
ctxpane.arc(line. x、line. 、line.end、false);
ctxpane.中風();
ctxpane.closePath();
}
if(line.direction==gamedirection.anticlock) //追蹤逆順時針
{
line.start=line.start - linespeed;
line.end=line.end -linespeed;
ctxpane.beginPath();
ctxpane.lineWidth=4;
ctxpane.strokeStyle = line.c;
ctxpane.closePath();
}
dot.x=bigcircle.r*Math.cos(line.start Math.PI/32) //追蹤點
dot.y=bigcircle.r*Math.sin( line.start Math.PI/32)
ctxpane.beginPath();//線上追蹤點
ctxpane.arc(dot.x,dot.y,dot.r,0,Math.PI*2 ,true);
ctxpane.fillStyle =smallcircle.c;
ctxpane.fill();
ctxpane.closePath();
smallcircledirection();//小圓
ctxpane.save();
ctxpane.beginPath();
ctxpane.arc(smallcircle.x,smallcircle。 Math.PI*2,true);
ctxpane.fillStyle =smallcircle.c;
ctxpane.fill();
ctxpane.closePath();
ctxpane.restore();
}
}//主函數
/////////////////////////////////////////////
tapme ();
var timer;
function startgame(){//開始遊戲
if(ifingame==0)
{
ifingame=1;
*8);
/* switch(xx)
{
caselc case 1:
smallcircle.direction=gamedirection.xia;
break;
狀況2:
smallcircle.direction=gamedirection.zuo;
🎜> break;
狀況4:
smallcircle.direction=gamedirection.zuoshang;
break;
狀況6:
smallcircle.direction=gamedirection.youshang;
break;
狀況7:
smallcircle.irection=gamedirection.
smallc、 break;
} */
smallcircle.direction=gamedirection.xia;
smallcircle.x=smallcircle.y=0;
line.start=Math.PI/2-Math.PI/26;
line.start=Math.PI/2-Math.PI/26; line.direction=gamedirection.anticlock;
clearInterval(timer);
timer=setInterval(movetest,10);
}
}//開始遊戲startgame()
function opentop()
{
🎜>
10.寫在最後
這純屬又是一個自娛自樂,寫後的天因為開始著著著簡歷投簡歷忙實習沒空再管,扔到朋友圈讓第三個朋友玩。看這個遊戲,感覺不該就這樣死掉,本人沒什麼技術,做得很差因此,發布這篇文字希望能幫到一些對pongo感興趣的朋友,再者就是希望如果有這方面的高手看到了能夠賜教,疑惑一切和賜教都歡迎給我留言,謝謝!