不記得哪個黑色星期五,貪吃魚基本完工的時候,產品突然增加需求,要求金幣扔出去後不消失,互相可碰撞,其最終結果還要由伺服器控制(沒錯,至今做的所有遊戲都有幕後**,=w=).
對於碰撞以前只寫過乙個球到處碰牆壁的,小球之間的碰撞倒是沒有接觸,想到他們碰撞過程中的角度變化、速度分配,就不敢往下想了,於是馬上想到box2d這個牛逼哄哄的引擎.
但是,使用物理引擎雖然高效、逼真,但所有碰撞都是不可控,包括最終的落點。所以引擎不能解決這次遇到的需求。
不能用引擎,咱自己寫也不怕,反正當年物理和高數都學得還不錯,嘻嘻、
最後,趁遊戲上線後的空閒時間,整理下碰撞的思路,記錄成本文。
本文的核心**是通過遊戲中的邏輯移動過來,為了方便,所以**組織也和cocos2d的思想類似。
cocos2d中有scene、director、layer層級之分,在demo中也有對應的init、game、ball,既然用到模組層級,當然首選seajs。
遊戲架構
├ css
├ main.css
├ js
├ jquery
├ jquery.js
├ seajs
├ seajs.js
├ ball
├ game
├ util
└ init
└ crashball.html
util
模組封裝的是一些必要的工具函式
ball
模組封裝小球move和drawpredict的類
game
模組控制遊戲進度,管理小球的運動
init
模組是遊戲入口,呼叫game.js
的初始化和開始介面
遊戲難度集中在ball
和game
模組,所以只分析碰撞過程得碰撞檢測和處理。
複雜的碰撞
首先建系很重要。由於我們模擬使用left
和top
來改變座標,所以我們以table
左上角為座標原點,垂直向下為x軸正方向,水平向左為y軸正方向。
邊緣碰撞處理
邊緣碰撞處理較為常規,只需要檢測小球座標和邊緣的相對關係即可:
//左右牆壁
if (ball.x util.w - util.r)
if (ball.x > util.w - util.r) }//
上下牆壁
if (ball.y util.h - util.r)
上述**作用是,檢測小球座標和牆壁大小關係,修改小球運動的角度。
計算角度的原始碼:
angle = math.atan2(topos[0] - frompos[0], topos[1] - frompos[1]);
計算的是點(left, top)和x正方向的角度,即下圖的∠dac.
左右碰撞處理方案是乘以-1
,∠dac是入角,∠dab和∠dac正好差個-1
.
上下碰撞是分正負處理。如上圖中∠eac和∠eab互補,如果反向運動,∠eab和∠cab的變化應該是反向再選擇180°。
最後注意的是,如果小球座標超過邊界座標,則要它座標設為邊界座標,不然就會出現靠著邊界來回撞的bug,前人留下來的bug找了好久,= = .
小球碰撞處理
a. 碰撞檢測是判斷他們的距離和2倍半徑的關係:
vardis = math.sqrt(math.pow(disx, 2) + math.pow(disy, 2));
if (dis <= gap)
b. 如果速度太大或者重繪頻率太小,怎會看到兩個小球互相融入的效果,所以處理第一步是還原碰撞初始狀態。
ball.x -= (gap - dis) * sin;
ball.y -= (gap - dis) * cos;
上面**是修正主動碰的小球的位置,讓他退回剛好碰撞的位置,其實就是下圖的ab之間的距離,相當於球o1從a退到b.
c. 小球斜碰很難分析,我們把他們的速度旋轉到x軸方向。
vx1 = vx * hitcos + vy * hitsin,
vy1 = vy * hitcos - vx * hitsin,
上面的vx是原來速度v在x方向的速度,vy是v在y方向的速度。把原來的速度順時針旋轉兩球圓心弦的角度,以後則不考慮y方向的運動。x方向上動量守恆和能量守恆,即
m * vx10 + m * vx20 = m * vx11 + m * vx21;
1/2 * m * vx10² + 1/2 * m * vx20² = 1/2 * m * vx11² + 1/2 * m * v21²
聯立求解可得碰撞後的速度大小。
然後將速度旋轉回去:
vx = vx1 * hitcos - vy1 * hitsin;
vy = vy1 * hitcos + vx1 * hitsin;
則碰撞後的速度和角度都可以得出:
ball.v = math.sqrt(vx * vx + vy * vy) * (1 - 0); //
(1-0) 變為大小,標量
obj.v = math.sqrt(objvx * objvx + objvy * objvy) * (1 - 0);
ball.angle = math.atan2(vx, vy);
obj.angle = math.atan2(objvx, objvy);
注意atan2(y, x)計算的是(x,y)到(0,0)的角度,我們這裡使用(vx, vy),計算的是(vx, vy)到(0,0)角的餘角。
考慮外力
ball.v = ball.v * (1 - util.loss);
//碰撞邊緣後減速
故迴圈跳出條件是math.round(v) <= 0
if( math.round(ball.v <= 0)
}window.clearinterval(_this.emmiter);
}上式moveingballs.remove(i)移除陣列指定位置的元素,是手動新增的:
array.prototype.remove = function()
**線
**線的運動邏輯和小球一樣,唯一不同的是,使用while而不是setinterval, 這樣**路線就會比我們的小球提前運動到指定位置。
由於**路線和小球運動路徑相同,且比小球提前到達,那麼就能提前知道小球的停止位置,如果判斷到小球到達了不能到達的位置,則可以減小他的速度即可。
**路線的小黃點,是通過
追加到dom父節點上的,所以如果一次碰撞小球太多,則會卡死的現象。
在cocos2d中是使用在layer上drawdot(),幾乎沒有記憶體消耗,所以不存在卡的現象。
如果要應用到是專案中,建議使用canvas 或者 documentfragment處理。
另外在貪吃魚遊戲中,運動的過程是通過加速度和路程來計算每次增加的位置的,運動效果比剛才的每次增加相同dx,dy的方案要好,如果對運動效果要求較高,可以考慮這種方式。
實際需求中,很多情況下我們不能使用現成的引擎或者框架,必須要去自己造輪子。
像剛才的碰撞處理中完全靠數學和物理知識,可見學好基本理論知識的重要性,就像作為程式設計師必需的資料結構和演算法一樣,總有你不知道的哪天就會用上,所以不斷學習這些基礎的東西,才能在用的時候隨機應變。
最後,原始碼放在github上:
基於Unity3D引擎的Android遊戲優化
最近專案進入收尾階段,之前對專案做了很多優化,mesh合併 減少drawcall和模型骨骼以及物理計算,合併材質球,優化 等等,在ios上還好,但是android上,試過幾款手機,從低端到高階,發現效能還是很差,所以又花了幾天來研究摸索,終於把遊戲效能搞定。記錄下來,留作以後參考。1.更 新不透明貼...
基於C 用WinForm實現的2048小遊戲
2048遊戲規則比較簡單,玩家通過上 下 左 右四個方向來控制方塊的移動,每一次移動,所有的方塊都會朝這個方向進行移動,而此時則會在某個隨機空的地區產生乙個新的數字方塊,在移動過程中,若方塊上的數字與移動方向後乙個的方塊上的數字相同,則會碰撞成乙個新的方塊,數字為兩個方塊數字之和。若所有地方都被填滿...
基於組合語言的MVC思想架構2048小遊戲
一 需求分析 在win32環境下,使用mvc思想架構,同時應用多檔案多模組的軟體設計實踐,以masm6.15為主要彙編工具,sublime text 3為 編寫工具,綜合利用多種彙編命令語句,進行2048遊戲設計開發。二 技術路線 2.1 系統架構 程式分為乙個主模組和三個子模組,其中排行榜模組由於...