"合成大西瓜"這個遊戲在年前很火熱,還上過微博熱搜,最近便玩了一陣還挺有意思的,因此研究了一下小球碰撞原理,本身親自手寫碰撞算法來實現一個合成大西瓜遊戲.並支持任意大小布局,你想玩多大面積,就拖多大面積,只要面積夠大,認真玩下去,合100個大西瓜均可以.哈哈~~~算法
1.遊戲介紹
遊戲裏面總共有11個水果,兩個相同水果的合成一個較大的水果,最終合成一個大西瓜便不能繼續合下去了:ide
然而博主本身寫的遊戲,本身都合不出一個大西瓜來.函數
若是看起來很模糊,或者看不到視頻,能夠直接去https://www.bilibili.com/video/BV1eh411Y7uV/嗶哩嗶哩直接看.佈局
代碼中邏輯主要以下所示:3d
而水果碰撞計算是裏面較爲複雜的,因此我接下來給你們來說解小球碰撞算法以前,咱們首先來複習下之前學過的向量.視頻
2.向量介紹
咱們如下面向量爲例:blog
那麼此時的向量就是,那麼他們的內容就是(B.x-A.x,B.y-A.y),當咱們對向量取絕對值時,就是求A座標到B座標的長度,也就是:遊戲
斜線長度 = get
3. 單位向量
單位向量就是長度爲1的一個向量.仍是以這個向量爲例(長度爲C):it
若是想獲取的單位向量,那麼他們的內容爲 : ((B.x-A.x)/C, (B.y-A.y) /C)
因此的單位向量就等於1
4. 向量與單位向量點乘
向量與單位向量點乘,是用來獲取向量在單位向量上的投影.
首先向量與向量點乘的公式以下所示:
其中是向量和向量之間的夾角.
假如是單位向量,那麼絕對值就等於1.
因此:
$\vec{a}* \vec{b} = \left | \vec{a} \right | cos\theta$
最終以下圖所示:
紅色的線表示的長度.咱們從俯視圖來看,紅色線不正是向量在向量方向上的投影嗎?
假如兩個向量是收尾相連,那麼角度就是單位向量沿生出來後的角度,以下圖所示:
得出結論:
5.小球碰撞情景
碰撞前以下圖所示:
5.1 獲取v1n和v2n
以前咱們已證實過:向量與單位向量點乘,是用來獲取向量在單位向量上的投影.
因此代碼以下所示:
let distance = Math.sqrt(Math.pow((ball1.pointX - ball2.pointX),2) + Math.pow((ball1.pointY - ball2.pointY),2)); let radius = ball1.r + ball2.r; let dx = ball1.pointX - ball2.pointX let dy = ball1.pointY - ball2.pointY let ex = dx / radius; let ey = dy / radius; // 獲取連心線的單位向量(ex,ey) let v1n = ex * ball1.vx + ey * ball1.vy let v2n = ex * ball2.vx + ey * ball2.vy
5.2 計算碰撞後的速度方向
首先咱們來看下碰撞後以下圖所示:
假如這兩個小球是同樣大, v1n'和v2n'取值就是:
v1n' = v2n
v2n' = v1n
而且根據動量守恆定律和機械能守恆定律得出:
因此最終碰撞函數代碼以下所示:
let distance = Math.sqrt(Math.pow((ball1.pointX - ball2.pointX),2) + Math.pow((ball1.pointY - ball2.pointY),2)); let radius = ball1.r + ball2.r; let dx = ball1.pointX - ball2.pointX let dy = ball1.pointY - ball2.pointY let ex = dx / radius; let ey = dy / radius; // 獲取連心線的單位向量(ex,ey) (單位向量就是長度爲1的一條線) let v1n = ex * ball1.vx + ey * ball1.vy let v2n = ex * ball2.vx + ey * ball2.vy if(v1n >= v2n) return; // 在小球擦肩而過情景中,會描述爲何要加這一句 let v1nn = ball1.cor * ((ball1.mass - ball2.mass) * v1n + 2 *ball2.mass *v2n ) / (ball1.mass +ball2.mass) // 碰撞後公式 let v2nn = ball2.cor * ((ball2.mass - ball1.mass) * v2n + 2 *ball1.mass *v1n ) / (ball1.mass +ball2.mass) let ux = -dy / radius; let uy = dx / radius; let v1t =ux * ball1.vx + uy*ball1.vy let v2t = ux * ball2.vx + uy * ball2.vy ball1.vx = v1nn*ex +v1t*ux; ball1.vy = v1nn*ex +v1t*uy; ball2.vx = v2nn*ex +v2t*ux; ball2.vy = v2nn*ex +v2t*uy;
6. 小球擦肩而過情景
首先咱們來看看下面兩個小球平行移動場景圖:
假如球1和球2在平行移動,那麼他們與連心線的夾角剛好是90°, v1n和v2n則都爲0
假如球1的夾角大於了球2的夾角,那麼就會出現碰撞,以下圖所示:
虛線箭頭速度方向表示球1的夾角大於球2的夾角的時候場景.
而cos的取值方式恰好是在0~180°的時候,角度越大,值越小,因此v1n >=v2n時,則不會碰撞.
7. 小球一直降落在全部小球的正上方情景
效果圖以下所示:
這時候,小球因爲沒有切線上的速度方向,因此在重力加速度下,會慢慢讓小球們堆起來,從而遊戲結束.
因此咱們還要在碰撞後末尾添加如下判斷:
if (v1n == 0 && v1t ==0 && v2t == 0) { // 當v1n爲0,說明小球1靜止不動,而v1t和v2t爲0,說明球1和球2在切線上沒有速度方向,球2位於球1的正上方,此時須要給球2一個vx偏移值,避免小球們堆起來 ball2.vx += 0.1 }
修改後效果圖所下所示:
整個的碰撞算法實現就完成了,其它邏輯就很是依葫蘆畫瓢實現便可,代碼還在上傳中,若是你們還想實現什麼小遊戲,能夠給我留言哦,感興趣的話,我就擼一個出來.