基於Seajs的可控撞球碰撞遊戲

2022-07-04 17:36:07 字數 3730 閱讀 6550

不記得哪個黑色星期五,貪吃魚基本完工的時候,產品突然增加需求,要求金幣扔出去後不消失,互相可碰撞,其最終結果還要由伺服器控制(沒錯,至今做的所有遊戲都有幕後**,=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的初始化和開始介面

遊戲難度集中在ballgame模組,所以只分析碰撞過程得碰撞檢測和處理。

複雜的碰撞

首先建系很重要。由於我們模擬使用lefttop來改變座標,所以我們以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 系統架構 程式分為乙個主模組和三個子模組,其中排行榜模組由於...