摘要:越界是常見的場景,一般會有兩種場景的越界一是整個物體移出區域,二是物體接觸到區域邊界?;舅悸窓z查物體是否越過任意邊界如果發生越界,立即將物體置回邊界反轉物體的速度向量的方向。核心代碼如下完整示例兩圓基于距離的碰撞演示
前言
本系列前幾篇中常出現物體跑到畫布外的情況,本篇就是為了解決這個問題。
閱讀本篇前請先打好前面的基礎。
本人能力有限,歡迎牛人共同討論,批評指正。
假定物體是個圓形,如圖其圓心坐標即是物體的x軸和y軸坐標。
越界是常見的場景,一般會有兩種場景的越界:一是整個物體移出區域,二是物體接觸到區域邊界。我們以畫布邊界為例進行討論,示例中矩形邊界即是:
let top = 0; let bottom = canvas.height; let left = 0; let right = canvas.width;整個物體移出區域
要整個物體離開范圍才算越界,則可得越界條件如下,以下任何一項為true即可判定越界。
// 右側越界 object.x - object.width/2 > right // 左側越界 object.x + object.width/2 < left // 上部越界 object.y + object.height/2 < top // 下部越界 object.y - object.height/2 > bottom物體接觸到區域邊界
物體接觸到區域邊界就算越界,則可得越界條件如下,以下任何一項為true即可判定越界。
// 右側越界 object.x + object.width/2 > right // 左側越界 object.x - object.width/2 < left // 上部越界 object.y - object.height/2 < top // 下部越界 object.y + object.height/2 > bottom越界了該怎么辦
搞明白越界條件后,接下來討論越界之后的處理辦法,一般是一下四種。
將物體移除這是最簡單的處理辦法,屬于整個物體移出區域才算越界的情況。
下面的例子會先批量創建ball,保存在balls數組里,每次動畫循環都會遍歷這個數組,依次輸入draw()函數,改變ball的位置并檢測是否越界。下面只列出draw()函數的代碼。
完整示例:清除越界圓
function draw(ball, pos) { // 依據球的速度改變球的位置 ball.x += ball.vx; ball.y += ball.vy; // 檢查是否越界 if (ball.x - ball.radius > canvas.width || ball.x + ball.radius < 0 || ball.y - ball.radius > canvas.height || ball.y + ball.radius < 0) { // 在數組中清除越界的球 balls.splice(pos, 1); // 打印提示 if (balls.length > 0) { log.value += `Removed ${ball.id} `; log.scrollTop = log.scrollHeight; } else { log.value += "All gone! "; } } // 畫球 ball.draw(context); }將其物體置回邊界內
屬于整個物體移出區域才算越界的情況。
下面的例子也是把創建的ball保存在balls數組里,但ball的初始位置都是畫布中間的下部,如果檢測到有ball越界,則會重置ball的位置。下面只列出draw()函數的代碼。
完整示例:彩色噴泉
function draw(ball) { // 依據球的速度改變球的位置,這里包含了偽重力 ball.vy += gravity; ball.x += ball.vx; ball.y += ball.vy; // 檢測是否越界 if (ball.x - ball.radius > canvas.width || ball.x + ball.radius < 0 || ball.y - ball.radius > canvas.height || ball.y + ball.radius < 0) { // 重置ball的位置 ball.x = canvas.width / 2; ball.y = canvas.height; // 重置ball的速度 ball.vx = Math.random() * 6 - 3; ball.vy = Math.random() * -10 - 10; // 打印提示 log.value = `Reset ${ball.id} `; } // 畫球 ball.draw(context); }屏幕環繞
屬于整個物體移出區域才算越界的情況。
屏幕環繞就是讓同一個物體出現在邊界內的另一個位置,如果一個物體從屏幕左側移出,它就會在屏幕右側再次出現,反之亦然,上下也是同理。
這里比前面的要稍微復雜的判斷物體躍的是那邊的界,偽代碼如下:
if(object.x - object.width/2 > right){ object.x = left - object.widht/2; }else if(object.x + object.width/2 < left){ object.x = right + object.width/2; } if(object.y - object.height/2 > bottom){ object.y = top - object.height/2; }else if(object.y + object.height/2 < top){ obejct.y = bottom + object.height/2; }反彈(粗略版)
這是較復雜的一種情況,屬于物體接觸到區域邊界就算越界的情況。基本思路:
檢查物體是否越過任意邊界;
如果發生越界, 立即將物體置回邊界;
反轉物體的速度向量的方向。
下面的示例是一個ball在畫布內移動,撞到邊界就反彈,反彈核心代碼如下。
完整示例:反彈球(粗略版)
if (ball.x + ball.radius > right) { ball.x = right - ball.radius; vx *= -1; } else if (ball.x - ball.radius < left) { ball.x = left + ball.radius; vx *= -1; } if (ball.y + ball.radius > bottom) { ball.y = bottom - ball.radius; vy *= -1; } else if (ball.y - ball.radius < top) { ball.y = top + ball.radius; vy *= -1; }反彈(完美版)
咋看似乎效果不錯,但仔細想想,我們這樣將物體置回邊界的做法是準確的嗎?
答案是否定的,理想反彈與實際反彈是不同的,如下圖:
從圖中我們可以清除的知道,ball實際上是不太可能會在理想反彈點反彈的,因為如果速度過大,計算位置時ball已經越過“理想反彈點”到達“實際反彈點”,而我們如果只是將ball的x軸坐標簡單粗暴移到邊界上,那還是不可能是“理想反彈點”,也就是說這種反彈方法不準確。
那么,完美反彈的思路就明確了,我們需要找到“理想反彈點”,并將ball置到該點。如果是左右邊越界,則算出"理想反彈點"與“實際反彈點”在y軸上的距離;如果是上下邊越界,則算出"理想反彈點"與“實際反彈點”在x軸上的距離。如圖,思路以左右邊越界為例:
由速度可求得物體的方向弧度angle;
算出"實際反彈點"和“理想反彈點”在x軸上的距離;
依據正切求"實際反彈點"和“理想反彈點”在y軸上的距離;
“理想反彈點”的y軸坐標即是"實際反彈點"加上這段距離。
改造后的核心代碼如下,至于有沒有必要多做這么多運算,這就要權衡性能和精密性了。
完整示例:反彈球(完美版)
if (ball.x + ball.radius > right) { const dx = ball.x - (right - ball.radius); const dy = Math.tan(angle) * dx; ball.x = right - ball.radius; ball.y += dy; vx *= bounce; } else if (ball.x - ball.radius < left) { const dx = ball.x - (left + ball.radius); const dy = Math.tan(angle) * dx; ball.x = left + ball.radius; ball.y += dy; vx *= bounce; } if (ball.y + ball.radius > bottom) { const dy = ball.y - (bottom - ball.radius); const dx = dy / Math.tan(angle); ball.y = bottom - ball.radius; ball.x += dx; vy *= bounce; } else if (ball.y - ball.radius < top) { const dy = ball.y - (top + ball.radius); const dx = dy / Math.tan(angle); ball.y = top + ball.radius; ball.x += dx; vy *= bounce; }碰撞檢測
和越界檢查很像,我們擴展到兩個物體間的碰撞檢測,一般常用的有如下兩種辦法。
基于幾何圖形的碰撞檢測一般是用在檢測矩形的碰撞,原理就是判斷一個物體是否和另一個物體有重疊。
下面直接給出兩個檢測的工具函數。完整示例:
兩個矩形碰撞檢測演示
矩形與點碰撞檢測演示
// 兩個矩形碰撞檢測 function intersects(rectA, rectB) { return !(rectA.x + rectA.width < rectB.x || rectB.x + rectB.width < rectA.x || rectA.y + rectA.height < rectB.y || rectB.y + rectB.height < rectA.y); };
// 矩形與點碰撞檢測 function containsPoint(rect, x, y) { return !(x < rect.x || x > rect.x + rect.width || y < rect.y || y > rect.y + rect.height); };基于距離的碰撞檢測
一般是用在檢測圓形的碰撞,原理就是判斷兩個物體是否足夠近到發生碰撞。
對于圓來說,只要兩個圓心距離小于兩圓半徑之和,那我們就可判定為碰撞。圓心距離可通過勾股定理求得。核心代碼如下:
完整示例:兩圓基于距離的碰撞演示
const dx = ballB.x - ballA.x; const dy = ballB.y - ballA.y; const dist = Math.sqrt(dx ** 2 + dy ** 2); if (dist < ballA.radius + ballB.radius) { log.value = "Hit!"; } else { log.value = ""; }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/54528.html
摘要:越界是常見的場景,一般會有兩種場景的越界一是整個物體移出區域,二是物體接觸到區域邊界?;舅悸窓z查物體是否越過任意邊界如果發生越界,立即將物體置回邊界反轉物體的速度向量的方向。核心代碼如下完整示例兩圓基于距離的碰撞演示 前言 本系列前幾篇中常出現物體跑到畫布外的情況,本篇就是為了解決這個問題。 閱讀本篇前請先打好前面的基礎。 本人能力有限,歡迎牛人共同討論,批評指正。 越界檢測 假...
摘要:科普動量是守恒量。動量守恒定律表示為一個系統不受外力或者所受外力之和為零,這個系統中所有物體的總動量保持不變。動量守恒定律可由機械能對空間平移對稱性推出。在可以忽略碰撞以外的因素時,動量是守恒的。 前言 一路沿著本系列教程學習的朋友可能會發現,前面教程中都盡量避免提及質量的概念,很多運動概念也時刻提醒大家這不是真實的物體運動。因為真實的物體運動其實跟質量都是密不可分的,而且質量的引入自...
摘要:科普動量是守恒量。動量守恒定律表示為一個系統不受外力或者所受外力之和為零,這個系統中所有物體的總動量保持不變。動量守恒定律可由機械能對空間平移對稱性推出。在可以忽略碰撞以外的因素時,動量是守恒的。 前言 一路沿著本系列教程學習的朋友可能會發現,前面教程中都盡量避免提及質量的概念,很多運動概念也時刻提醒大家這不是真實的物體運動。因為真實的物體運動其實跟質量都是密不可分的,而且質量的引入自...
閱讀 3208·2021-09-30 09:48
閱讀 3485·2021-09-22 16:00
閱讀 1062·2019-08-30 13:08
閱讀 3098·2019-08-30 10:53
閱讀 2410·2019-08-29 18:33
閱讀 1583·2019-08-29 12:47
閱讀 896·2019-08-29 12:16
閱讀 1929·2019-08-26 12:02