使用JavaScript實現(xiàn)一個物理模擬
1. 碰撞檢測
首先就是碰撞檢測,在游戲中這是最基礎的效果,下述栗子中將會實現(xiàn)檢測兩個矩形物體是否發(fā)生碰撞。
function isColliding(rect1, rect2) {
return (
rect1.x < rect2.x + rect2.width &&
rect1.x + rect1.width > rect2.x &&
rect1.y < rect2.y + rect2.height &&
rect1.height + rect1.y > rect2.y
);
}
// 測試碰撞檢測
const rect1 = { x: 5, y: 5, width: 50, height: 50 };
const rect2 = { x: 20, y: 20, width: 50, height: 50 };
console.log(isColliding(rect1, rect2)); // 輸出:true
2. 碰撞響應
正常情況下,當兩個物體發(fā)生碰撞時,我們需要計算它們的碰撞響應。在這里,就簡單地反轉它們的速度來模擬彈性碰撞。
function resolveCollision(obj1, obj2) {
const tempVelocity = obj1.velocity;
obj1.velocity = obj2.velocity;
obj2.velocity = tempVelocity;
}
// 測試碰撞響應
let obj1 = { velocity: { x: 5, y: 0 } };
let obj2 = { velocity: { x: -3, y: 0 } };
if (isColliding(rect1, rect2)) {
resolveCollision(obj1, obj2);
}
console.log(obj1.velocity, obj2.velocity); // 輸出:{ x: -3, y: 0 } { x: 5, y: 0 }
3. 摩擦力
摩擦力會減緩物體在接觸表面上的移動。在這個示例中,我們使用一個簡單的摩擦系數(shù)來模擬摩擦力對速度的影響。
function applyFriction(velocity, friction) {
velocity.x *= friction;
velocity.y *= friction;
}
// 測試摩擦力
let velocity = { x: 10, y: 0 };
const friction = 0.95; // 摩擦系數(shù)
applyFriction(velocity, friction);
console.log(velocity); // 輸出:{ x: 9.5, y: 0 }
4. 空氣阻力
空氣阻力會減緩物體在空中的移動。我們可以通過減小速度的百分比來模擬這種效果.
function applyDrag(velocity, drag) {
velocity.x *= (1 - drag);
velocity.y *= (1 - drag);
}
// 測試空氣阻力
let velocity = { x: 10, y: 0 };
const drag = 0.05; // 空氣阻力系數(shù)
applyDrag(velocity, drag);
console.log(velocity); // 輸出:{ x: 9.5, y: 0 }
5. 物體破壞
首先當物體的速度超過一定閾值時,我們可以假設物體已經(jīng)被破壞。這里我們定義一個簡單的函數(shù)來判斷物體是否應該被破壞,并在破壞發(fā)生時改變其狀態(tài)。
function checkAndApplyDamage(obj, threshold) {
const speed = Math.sqrt(obj.velocity.x ** 2 + obj.velocity.y ** 2);
if (speed > threshold) {
obj.isDestroyed = true;
}
}
// 測試物體破壞
let obj = { velocity: { x: 50, y: 0 }, isDestroyed: false };
const damageThreshold = 30; // 破壞閾值
checkAndApplyDamage(obj, damageThreshold);
console.log(obj.isDestroyed); // 輸出:true
完整案例
俗話說光說不練假把式,所有的都是為最終的效果服務的,最后我們將上述的所有效果整合起來,實現(xiàn)一個簡單的demo效果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>物理模擬</title>
<style>
canvas {
border: 1px solid black;
}
#controls {
margin-top: 10px;
}
.info {
margin-right: 10px;
}
</style>
</head>
</body>
<canvas id="simulation" width="500" height="300"></canvas>
<div id="controls">
<div class="info">
<label for="friction">摩擦力:</label>
<input type="range" id="friction" min="0" max="1" step="0.01" value="0.99">
<span id="frictionValue">0.99</span>
</div>
<div class="info">
<label for="drag">風阻:</label>
<input type="range" id="drag" min="0" max="0.1" step="0.001" value="0.01">
<span id="dragValue">0.01</span>
</div>
<div class="info">
<label for="threshold">臨界值:</label>
<input type="range" id="threshold" min="0" max="500" step="1" value="25">
<span id="thresholdValue">25</span>
</div>
<button id="restartBtn">重啟模擬</button>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
// 獲取canvas和context
const canvas = document.getElementById('simulation');
const ctx = canvas.getContext('2d');
// 定義物體構造函數(shù)
function GameObject(x, y, width, height, velocity) {
this.initialX = x;
this.initialY = y;
this.width = width;
this.height = height;
this.initialVelocity = { ...velocity };
this.velocity = { ...velocity };
this.isDestroyed = false;
}
// 添加方法到GameObject原型
GameObject.prototype = {
reset() {
this.x = this.initialX;
this.y = this.initialY;
this.velocity = { ...this.initialVelocity };
this.isDestroyed = false;
},
isColliding(other) {
return (
this.x < other.x + other.width &&
this.x + this.width > other.x &&
this.y < other.y + other.height &&
this.height + this.y > other.y
);
},
resolveCollision(other) {
if (!this.isDestroyed && !other.isDestroyed) {
console.log('打印碰撞前的速度:', Math.sqrt(this.velocity.x ** 2 + this.velocity.y ** 2), Math.sqrt(other.velocity.x ** 2 + other.velocity.y ** 2));
const tempVelocity = this.velocity;
this.velocity = other.velocity;
other.velocity = tempVelocity;
console.log('打印碰撞后的速度:', Math.sqrt(this.velocity.x ** 2 + this.velocity.y ** 2), Math.sqrt(other.velocity.x ** 2 + other.velocity.y ** 2));
}
},
update(friction, drag, threshold) {
if (this.isDestroyed) return;
// 應用摩擦力和空氣阻力
this.velocity.x *= friction;
this.velocity.y *= friction;
this.velocity.x *= (1 - drag);
this.velocity.y *= (1 - drag);
// 更新位置
this.x += this.velocity.x;
this.y += this.velocity.y;
// 檢查破壞
const speed = Math.sqrt(this.velocity.x ** 2 + this.velocity.y ** 2);
if (speed > threshold) {
this.isDestroyed = true;
}
},
draw() {
ctx.fillStyle = this.isDestroyed ? 'red' : 'blue';
ctx.fillRect(this.x, this.y, this.width, this.height);
}
};
// 實例化物體,確保設置了 x 和 y
const obj1 = new GameObject(100, 100, 50, 50, { x: 5, y: 0 });
const obj2 = new GameObject(300, 100, 50, 50, { x: -5, y: 0 });
// 設置物理參數(shù)
let friction = 0.99;
let drag = 0.01;
let destructionThreshold = 25;
// 獲取控件元素
const frictionInput = document.getElementById('friction');
const dragInput = document.getElementById('drag');
const thresholdInput = document.getElementById('threshold');
const frictionValueDisplay = document.getElementById('frictionValue');
const dragValueDisplay = document.getElementById('dragValue');
const thresholdValueDisplay = document.getElementById('thresholdValue');
const restartBtn = document.getElementById('restartBtn');
// 更新參數(shù)值的函數(shù)
function updateParameters() {
friction = parseFloat(frictionInput.value);
drag = parseFloat(dragInput.value);
destructionThreshold = parseFloat(thresholdInput.value);
}
// 監(jiān)聽滑動條的變化
frictionInput.addEventListener('input', function () {
frictionValueDisplay.textContent = this.value;
});
dragInput.addEventListener('input', function () {
dragValueDisplay.textContent = this.value;
});
thresholdInput.addEventListener('input', function () {
thresholdValueDisplay.textContent = this.value;
});
// 動畫循環(huán)函數(shù)
let animationFrameId;
function animate() {
animationFrameId = requestAnimationFrame(animate);
// 清除畫布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 更新和繪制物體
obj1.update(friction, drag, destructionThreshold);
obj2.update(friction, drag, destructionThreshold);
obj1.draw();
obj2.draw();
// 碰撞檢測和響應
if (obj1.isColliding(obj2)) {
obj1.resolveCollision(obj2);
}
}
// 啟動動畫
animate();
// 重新開始動畫的函數(shù)
function restartAnimation() {
// 取消當前的動畫幀請求
cancelAnimationFrame(animationFrameId);
// 重置物體狀態(tài)
obj1.reset();
obj2.reset();
// 更新參數(shù)
updateParameters();
// 重新開始動畫
animate();
}
// 綁定重啟按鈕事件
restartBtn.addEventListener('click', restartAnimation);
})
</script>
<body>
總結
上述的案例我們實現(xiàn)了簡單的物理模擬效果,只是給大家一個思路,畢竟萬物皆可JS嘛,但是如果大家想要在項目中使用的話,就需要更加擬真和復雜的算法,本文只是帶大家大概的了解一下它的實現(xiàn)方式。
大家快去試試吧,俗話說眼過千遍,不如手過一遍,說不定大家在實現(xiàn)的過程中,會找到更加方便簡易的實現(xiàn)方法。
以上就是使用JavaScript實現(xiàn)一個物理模擬的詳細內容,更多關于JavaScript物理模擬的資料請關注腳本之家其它相關文章!
相關文章
Javascript根據(jù)指定下標或對象刪除數(shù)組元素
刪除數(shù)組元素在工作中經(jīng)常會用到,本文講解一下Javascript根據(jù)下標刪除數(shù)組元素的方法,需要了解的朋友可以參考下2012-12-12
javascript 網(wǎng)站常用的iframe分割
就是一個頁面使用兩個iframe來調用內容,實現(xiàn)頁面導航,更容易控制,可控制性好2008-06-06
微信小程序實現(xiàn)列表滾動頭部吸頂?shù)氖纠a
這篇文章主要介紹了微信小程序實現(xiàn)列表滾動頭部吸頂?shù)氖纠a,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07

