GitHub_Trending/ap/app-ideas游戏物理:碰撞检测与运动模拟

GitHub_Trending/ap/app-ideas游戏物理:碰撞检测与运动模拟

【免费下载链接】app-ideas A Collection of application ideas which can be used to improve your coding skills. 【免费下载链接】app-ideas 项目地址: https://gitcode.com/GitHub_Trending/ap/app-ideas

引言:游戏物理的核心价值

在游戏开发领域,物理模拟是创造沉浸式体验的关键技术。无论是简单的2D小游戏还是复杂的3D大作,精确的碰撞检测和真实的运动模拟都是游戏可玩性的基石。本文将深入探讨app-ideas项目中涉及的游戏物理实现,为您提供从理论到实践的完整指南。

您是否曾遇到过这些问题?

  • 游戏物体碰撞时出现穿墙现象
  • 物理运动看起来不自然、不真实
  • 性能问题导致游戏卡顿
  • 复杂的碰撞检测逻辑难以实现

通过本文,您将掌握:

  • 基础物理概念与数学原理
  • 多种碰撞检测算法实现
  • 运动模拟的最佳实践
  • 性能优化技巧
  • 实际项目应用案例

物理基础概念

运动学基本公式

在游戏物理中,我们需要理解几个核心的运动学公式:

物理量公式描述
位移s = v₀t + ½at²物体在时间t内的位移
速度v = v₀ + at物体在时间t后的速度
加速度a = (v - v₀)/t物体的加速度

向量数学基础

class Vector2 {
    constructor(x = 0, y = 0) {
        this.x = x;
        this.y = y;
    }
    
    // 向量加法
    add(other) {
        return new Vector2(this.x + other.x, this.y + other.y);
    }
    
    // 向量减法
    subtract(other) {
        return new Vector2(this.x - other.x, this.y - other.y);
    }
    
    // 标量乘法
    multiply(scalar) {
        return new Vector2(this.x * scalar, this.y * scalar);
    }
    
    // 向量点积
    dot(other) {
        return this.x * other.x + this.y * other.y;
    }
    
    // 向量长度
    magnitude() {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    }
    
    // 单位向量
    normalize() {
        const mag = this.magnitude();
        return mag > 0 ? new Vector2(this.x / mag, this.y / mag) : new Vector2();
    }
}

碰撞检测算法

1. 轴对齐包围盒(AABB)碰撞检测

AABB是最简单且高效的碰撞检测方法,适用于矩形物体。

function checkAABBCollision(rect1, rect2) {
    return rect1.x < rect2.x + rect2.width &&
           rect1.x + rect1.width > rect2.x &&
           rect1.y < rect2.y + rect2.height &&
           rect1.y + rect1.height > rect2.y;
}

2. 圆形碰撞检测

function checkCircleCollision(circle1, circle2) {
    const dx = circle1.x - circle2.x;
    const dy = circle1.y - circle2.y;
    const distance = Math.sqrt(dx * dx + dy * dy);
    return distance < circle1.radius + circle2.radius;
}

3. 分离轴定理(SAT)复杂形状碰撞

function projectVertices(vertices, axis) {
    let min = Infinity;
    let max = -Infinity;
    
    for (const vertex of vertices) {
        const projection = vertex.x * axis.x + vertex.y * axis.y;
        min = Math.min(min, projection);
        max = Math.max(max, projection);
    }
    
    return { min, max };
}

function checkSATCollision(poly1, poly2) {
    const axes = [];
    
    // 获取所有边的法线作为分离轴
    for (let i = 0; i < poly1.vertices.length; i++) {
        const p1 = poly1.vertices[i];
        const p2 = poly1.vertices[(i + 1) % poly1.vertices.length];
        const edge = new Vector2(p2.x - p1.x, p2.y - p1.y);
        const normal = new Vector2(-edge.y, edge.x).normalize();
        axes.push(normal);
    }
    
    for (let i = 0; i < poly2.vertices.length; i++) {
        const p1 = poly2.vertices[i];
        const p2 = poly2.vertices[(i + 1) % poly2.vertices.length];
        const edge = new Vector2(p2.x - p1.x, p2.y - p1.y);
        const normal = new Vector2(-edge.y, edge.x).normalize();
        axes.push(normal);
    }
    
    // 检查所有分离轴
    for (const axis of axes) {
        const proj1 = projectVertices(poly1.vertices, axis);
        const proj2 = projectVertices(poly2.vertices, axis);
        
        if (proj1.max < proj2.min || proj2.max < proj1.min) {
            return false; // 存在分离轴,无碰撞
        }
    }
    
    return true; // 所有轴都有重叠,发生碰撞
}

运动模拟实现

基于时间的运动模拟

class PhysicsObject {
    constructor(position, velocity = new Vector2(), acceleration = new Vector2()) {
        this.position = position;
        this.velocity = velocity;
        this.acceleration = acceleration;
        this.mass = 1;
        this.restitution = 0.8; // 弹性系数
        this.friction = 0.98;   // 摩擦系数
    }
    
    update(deltaTime) {
        // 更新速度
        this.velocity = this.velocity.add(this.acceleration.multiply(deltaTime));
        
        // 应用摩擦力
        this.velocity = this.velocity.multiply(this.friction);
        
        // 更新位置
        this.position = this.position.add(this.velocity.multiply(deltaTime));
        
        // 重置加速度
        this.acceleration = new Vector2();
    }
    
    applyForce(force) {
        // F = ma → a = F/m
        this.acceleration = this.acceleration.add(force.multiply(1 / this.mass));
    }
}

碰撞响应处理

function resolveCollision(obj1, obj2, normal, depth) {
    // 相对速度
    const relativeVelocity = obj2.velocity.subtract(obj1.velocity);
    const velocityAlongNormal = relativeVelocity.dot(normal);
    
    // 如果物体正在分离,不处理碰撞
    if (velocityAlongNormal > 0) return;
    
    // 计算冲量标量
    const restitution = Math.min(obj1.restitution, obj2.restitution);
    let impulseScalar = -(1 + restitution) * velocityAlongNormal;
    impulseScalar /= (1 / obj1.mass + 1 / obj2.mass);
    
    // 应用冲量
    const impulse = normal.multiply(impulseScalar);
    obj1.velocity = obj1.velocity.subtract(impulse.multiply(1 / obj1.mass));
    obj2.velocity = obj2.velocity.add(impulse.multiply(1 / obj2.mass));
    
    // 位置校正(防止物体嵌入)
    const percent = 0.8; // 通常使用80%的渗透深度
    const slop = 0.01;   // 允许的渗透容差
    const correction = normal.multiply(
        Math.max(depth - slop, 0) / (1 / obj1.mass + 1 / obj2.mass) * percent
    );
    
    obj1.position = obj1.position.subtract(correction.multiply(1 / obj1.mass));
    obj2.position = obj2.position.add(correction.multiply(1 / obj2.mass));
}

性能优化策略

空间分割技术

class SpatialHashGrid {
    constructor(cellSize, width, height) {
        this.cellSize = cellSize;
        this.grid = new Map();
        this.width = width;
        this.height = height;
    }
    
    getCellKey(x, y) {
        const cellX = Math.floor(x / this.cellSize);
        const cellY = Math.floor(y / this.cellSize);
        return `${cellX},${cellY}`;
    }
    
    insert(object) {
        const keys = this.getObjectCells(object);
        for (const key of keys) {
            if (!this.grid.has(key)) {
                this.grid.set(key, new Set());
            }
            this.grid.get(key).add(object);
        }
        object.gridCells = keys;
    }
    
    getObjectCells(object) {
        const cells = new Set();
        const bounds = object.getBounds();
        
        const minX = Math.floor(bounds.left / this.cellSize);
        const maxX = Math.floor(bounds.right / this.cellSize);
        const minY = Math.floor(bounds.top / this.cellSize);
        const maxY = Math.floor(bounds.bottom / this.cellSize);
        
        for (let x = minX; x <= maxX; x++) {
            for (let y = minY; y <= maxY; y++) {
                cells.add(`${x},${y}`);
            }
        }
        
        return Array.from(cells);
    }
    
    getNearbyObjects(object) {
        const nearby = new Set();
        for (const key of object.gridCells) {
            const cellObjects = this.grid.get(key);
            if (cellObjects) {
                for (const obj of cellObjects) {
                    if (obj !== object) {
                        nearby.add(obj);
                    }
                }
            }
        }
        return Array.from(nearby);
    }
    
    update(object) {
        // 移除旧位置
        for (const key of object.gridCells) {
            const cell = this.grid.get(key);
            if (cell) {
                cell.delete(object);
            }
        }
        
        // 插入新位置
        this.insert(object);
    }
}

碰撞检测优化流程

mermaid

实际项目应用

Boole-Bot游戏中的物理实现

基于app-ideas中的Boole-Bot游戏项目,我们可以实现一个完整的物理引擎:

class BooleBotPhysics {
    constructor(arenaWidth, arenaHeight) {
        this.arenaWidth = arenaWidth;
        this.arenaHeight = arenaHeight;
        this.bots = [];
        this.collisionPairs = [];
        this.spatialGrid = new SpatialHashGrid(50, arenaWidth, arenaHeight);
    }
    
    addBot(bot) {
        this.bots.push(bot);
        this.spatialGrid.insert(bot);
    }
    
    update(deltaTime) {
        // 更新所有机器人的位置
        for (const bot of this.bots) {
            if (bot.active) {
                bot.update(deltaTime);
                this.handleArenaBoundaries(bot);
                this.spatialGrid.update(bot);
            }
        }
        
        // 检测和处理碰撞
        this.detectCollisions();
        this.resolveCollisions();
    }
    
    handleArenaBoundaries(bot) {
        // 边界碰撞检测和响应
        const radius = bot.radius;
        
        if (bot.position.x - radius < 0) {
            bot.position.x = radius;
            bot.velocity.x = -bot.velocity.x * bot.restitution;
        } else if (bot.position.x + radius > this.arenaWidth) {
            bot.position.x = this.arenaWidth - radius;
            bot.velocity.x = -bot.velocity.x * bot.restitution;
        }
        
        if (bot.position.y - radius < 0) {
            bot.position.y = radius;
            bot.velocity.y = -bot.velocity.y * bot.restitution;
        } else if (bot.position.y + radius > this.arenaHeight) {
            bot.position.y = this.arenaHeight - radius;
            bot.velocity.y = -bot.velocity.y * bot.restitution;
        }
    }
    
    detectCollisions() {
        this.collisionPairs = [];
        
        for (const bot of this.bots) {
            if (!bot.active) continue;
            
            const nearbyBots = this.spatialGrid.getNearbyObjects(bot);
            for (const otherBot of nearbyBots) {
                if (bot !== otherBot && otherBot.active) {
                    const collision = this.checkCircleCollision(bot, otherBot);
                    if (collision.colliding) {
                        this.collisionPairs.push({
                            bot1: bot,
                            bot2: otherBot,
                            normal: collision.normal,
                            depth: collision.depth
                        });
                    }
                }
            }
        }
    }
    
    resolveCollisions() {
        for (const pair of this.collisionPairs) {
            this.resolveCollision(pair.bot1, pair.bot2, pair.normal, pair.depth);
            
            // 处理布尔逻辑碰撞结果
            this.handleBooleanCollision(pair.bot1, pair.bot2);
        }
    }
    
    handleBooleanCollision(bot1, bot2) {
        // 基于布尔逻辑的碰撞处理
        const result1 = this.applyBooleanOperation(bot1.operation, bot1.value, bot2.value);
        const result2 = this.applyBooleanOperation(bot2.operation, bot2.value, bot1.value);
        
        if (result1 === 0) bot1.deactivate();
        if (result2 === 0) bot2.deactivate();
    }
    
    applyBooleanOperation(operation, value1, value2) {
        switch (operation) {
            case 'AND': return value1 && value2;
            case 'OR': return value1 || value2;
            case 'XOR': return (value1 && !value2) || (!value1 && value2);
            case 'NOT': return !value1;
            default: return value1;
        }
    }
}

高级主题:连续碰撞检测

对于高速移动的物体,离散碰撞检测可能失效,需要连续碰撞检测(CCD):

function continuousCollisionDetection(obj1, obj2, deltaTime) {
    // 计算相对运动
    const relativeVelocity = obj2.velocity.subtract(obj1.velocity);
    const relativeSpeed = relativeVelocity.magnitude();
    
    if (relativeSpeed === 0) return null;
    
    // 使用射线投射进行连续检测
    const direction = relativeVelocity.normalize();
    const rayOrigin = obj1.position;
    const rayLength = relativeSpeed * deltaTime;
    
    // 执行射线与形状的相交测试
    const intersection = rayCast(obj2, rayOrigin, direction, rayLength);
    
    if (intersection) {
        return {
            time: intersection.time,
            point: intersection.point,
            normal: intersection.normal
        };
    }
    
    return null;
}

function rayCast(object, origin, direction, maxDistance) {
    // 实现射线与物体形状的相交测试
    // 这里以圆形为例
    if (object.shape === 'circle') {
        const toCenter = object.position.subtract(origin);
        const projection = toCenter.dot(direction);
        const closestDistSq = toCenter.dot(toCenter) - projection * projection;
        const radiusSq = object.radius * object.radius;
        
        if (closestDistSq > radiusSq) return null;
        
        const insideDist = Math.sqrt(radiusSq - closestDistSq);
        let t = projection - insideDist;
        
        if (t < 0) t = projection + insideDist;
        if (t < 0 || t > maxDistance) return null;
        
        const hitPoint = origin.add(direction.multiply(t));
        const normal = hitPoint.subtract(object.position).normalize();
        
        return { time: t / maxDistance, point: hitPoint, normal };
    }
    
    // 可以添加其他形状的射线检测
    return null;
}

调试与性能分析

性能监控工具

class PhysicsProfiler {
    constructor() {
        this.stats = {
            updateTime: 0,
            collisionDetectionTime: 0,
            collisionResolutionTime: 0,
            totalObjects: 0,
            collisionCount: 0
        };
        this.frameTimes = [];
    }
    
    startFrame() {
        this.frameStart = performance.now();
    }
    
    endFrame() {
        const frameTime = performance.now() - this.frameStart;
        this.frameTimes.push(frameTime);
        
        // 保持最近60帧的数据
        if (this.frameTimes.length > 60) {
            this.frameTimes.shift();
        }
    }
    
    startCollisionDetection() {
        this.collisionStart = performance.now();
    }
    
    endCollisionDetection(collisionCount) {
        this.stats.collisionDetectionTime = performance.now() - this.collisionStart;
        this.stats.collisionCount = collisionCount;
    }
    
    getAverageFrameTime() {
        if (this.frameTimes.length === 0) return 0;
        return this.frameTimes.reduce((sum, time) => sum + time, 0) / this.frameTimes.length;
    }
    
    getFPS() {
        const avgFrameTime = this.getAverageFrameTime();
        return avgFrameTime > 0 ? 1000 / avgFrameTime : 0;
    }
    
    logStats() {
        console.log(`FPS: ${this.getFPS().toFixed(1)}`);
        console.log(`碰撞检测时间: ${this.stats.collisionDetectionTime.toFixed(2)}ms`);
        console.log(`碰撞次数: ${this.stats.collisionCount}`);
        console.log(`物体数量: ${this.stats.totalObjects}`);
    }
}

最佳实践总结

代码组织建议

mermaid

性能优化清单

  1. 空间分割:始终使用空间分割技术(四叉树、空间哈希网格等)
  2. 分层检测:先进行宽相位检测,再进行窄相位检测
  3. 时间步长:使用固定的时间步长进行物理更新
  4. 对象池:重用物理对象避免内存分配
  5. 惰性计算:只在需要时计算复杂的物理量
  6. 简化形状:使用简单的碰撞形状近似复杂几何体
  7. 批量处理:批量处理类似的物理计算

常见问题解决方案

问题解决方案
物体穿透使用连续碰撞检测(CCD)或增加迭代次数
性能下降优化空间分割,减少不必要的碰撞检测
运动不真实调整物理参数(质量、弹性、摩擦)
数值不稳定使用更稳定的数值积分方法

结语

游戏物理是一个深奥而有趣的领域,从简单的碰撞检测到复杂的物理模拟,每一个细节都影响着游戏的最终体验。通过app-ideas项目中的实际案例,我们看到了如何将理论物理知识转化为可运行的代码。

记住,优秀的物理模拟不仅仅是技术的堆砌,更是艺术与科学的完美结合。在不断迭代和优化中,您将创造出既真实又令人满意的游戏物理体验。

开始您的物理编程之旅吧!从简单的AABB碰撞开始,逐步挑战更复杂的物理模拟,最终打造出属于您自己的物理引擎。

【免费下载链接】app-ideas A Collection of application ideas which can be used to improve your coding skills. 【免费下载链接】app-ideas 项目地址: https://gitcode.com/GitHub_Trending/ap/app-ideas

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值