html5 canvas弹性碰撞方块

2023-11-30

我重新问这个问题是因为我在上一个问题中没有明确说明我想要什么。

有谁知道如何使用矩形在 Canvas 中进行弹性碰撞或处理碰撞?或者能给我指出正确的方向吗?

我创建了一个具有多个正方形的画布,并希望每个正方形在接触时发生偏转。

这是我整理的一个快速小提琴,显示在黑色缓冲画布上http://jsfiddle.net/claireC/Y7MFq/10/

第 39 行是我开始碰撞检测的地方,第 59 行是我尝试执行它的地方。我将有超过 3 个方块四处移动,并希望它们在相互接触时偏转

var canvas = document.getElementById("canvas"),
    context = canvas.getContext("2d");
context.fillStyle = "#FFA500";
    context.fillRect(0, 0, canvas.width, canvas.height);

var renderToCanvas = function (width, height, renderFunction) {
    var buffer = document.createElement('canvas');
    buffer.width = width;
    buffer.height = height;
    renderFunction(buffer.getContext('2d'));
    return buffer;
};

var drawing = renderToCanvas(100, 100, function (ctx) {
ctx.fillStyle = "#000";
    ctx.fillRect(0, 0, canvas.width, canvas.height);


});

var drawing2 = renderToCanvas(100, 100, function (ctx) {
ctx.fillStyle = "blue";
    ctx.fillRect(0, 0, canvas.width, canvas.height);


});

var x = 0, 
 y = 0,
x2 = 200,
y2 = 10,
vx = .80,
vy = .80,
vx2 = .80,
vy2 = .80;



    function collides(rectA, rectB) {
      return !(rectA.x + rectA.width < rectB.x2 ||
       rectB.x2 + rectB.width < rectA.x ||
       rectA.y + rectA.height < rectB.y2 ||
       rectB.y2 + rectB.height < rectA.y);
      }; 

    function executeFrame() {
        x+=vx;
        y+=vy;
        x2+=vx2;
        y2+=vy2;

        if( x < 0 || x > 579) vx = -vx; 
        if( y < 0 || y > 265) vy = -vy;

        if( x2 < 0 || x2 > 579) vx2 = - vx2; 
        if( y2 < 0 || y2 > 233) vy2 = - vy2;

        if(collides(drawing, drawing2)){
            //move in different direction
        };

        context.fillStyle = "#FFA500"; 
        context.fillRect(0, 0, canvas.width, canvas.height);
        context.drawImage(drawing, x, y);
        context.drawImage(drawing2, x2, y2);


        requestAnimationFrame(executeFrame);
    }

    //start animation
    executeFrame();

矩形碰撞检测

进行矩形碰撞检测可能比看起来更复杂。

这不仅仅是弄清楚两个矩形是否相交或重叠,而且我们还需要知道它们碰撞的角度以及它们移动的方向,以便正确地偏转它们,理想情况下将“速度”传递给彼此(质量/能量)等等。

我在这里介绍的方法将执行以下步骤:

  • 首先进行简单的相交检测以查明它们是否发生碰撞。
  • 如果相交:计算两个矩形之间的角度
  • 将一组主矩形划分为圆的四个区域,其中区域 1 位于右侧,区域 2 位于底部,依此类推。
  • 根据区域,检查矩形移动的方向,是否根据检测到的区域向另一个矩形偏转。

在线演示

这里有更高速度的版本

检测交叉点并计算角度

检测交点和角度的代码如下,其中r1 and r2这里是具有属性的对象x, y, w and h.

function collides(r1, r2) {

    /// classic intersection test
    var hit = !(r1.x + r1.w < r2.x ||
               r2.x + r2.w < r1.x ||
               r1.y + r1.h < r2.y ||
               r2.y + r2.h < r1.y);

    /// if intersects, get angle between the two rects to determine hit zone
    if (hit) {
        /// calc angle
        var dx = r2.x - r1.x;
        var dy = r2.y - r1.y;

        /// for simplicity convert radians to degree
        var angle = Math.atan2(dy, dx) * 180 / Math.PI;
        if (angle < 0) angle += 360;

        return angle;
        
    } else
        return null;
}

该函数将返回一个angle or null然后我们用它来确定循环中的偏转(即:在我们的例子中,角度用于确定击中区域)。这是必要的,以便它们朝正确的方向反弹。

为什么要打区域?

Example scenario

只需进行简单的相交测试和偏转,您就可能面临盒子偏转的风险,如右图所示,这对于 2D 场景来说是不正确的。您希望方框继续朝左侧没有影响的方向移动。

确定碰撞区域和方向

以下是我们如何确定要反转的速度矢量(提示:如果您想要更多physical正确的偏转你可以让矩形“吸收”其他矩形的一些速度,但我不会在这里介绍):

var angle = collides({x: x, y: y, w: 100, h: 100},    /// rect 1
                     {x: x2, y: y2, w: 100, h: 100}); /// rect 2

/// did we have an intersection?
if (angle !== null) {

    /// if we're not already in a hit situation, create one
    if (!hit) {
        hit = true;

        /// zone 1 - right
        if ((angle >= 0 && angle < 45) || (angle > 315 && angle < 360)) {
            /// if moving in + direction deflect rect 1 in x direction etc.
            if (vx > 0) vx = -vx;
            if (vx2 < 0) vx2 = -vx2;

        } else if (angle >= 45 && angle < 135) { /// zone 2 - bottom
            if (vy > 0) vy = -vy;
            if (vy2 < 0) vy2 = -vy2;

        } else if (angle >= 135 && angle < 225) { /// zone 3 - left
            if (vx < 0) vx = -vx;
            if (vx2 > 0) vx2 = -vx2;

        } else { /// zone 4 - top
            if (vy < 0) vy = -vy;
            if (vy2 > 0) vy2 = -vy2;
        }
    }
} else
    hit = false;  /// reset hit when this hit is done (angle = null)

差不多就是这样了。

The hit使用标志是为了当我们受到撞击时,我们将“情况”标记为撞击情况,这样我们就不会得到内部偏转(例如,这可能会在高速时发生)。只要我们在命中设置为真后得到一个角度,我们仍然处于相同的命中情况(无论如何理论上)。当我们收到 null 时,我们会重置并准备好迎接新的命中情况。

另外值得一提的是,这里的主要矩形(我们检查其侧面)是第一个矩形(在本例中为黑色)。

两个以上的矩形

如果您想放入两个以上的矩形,那么我建议在矩形本身方面采用与此处使用的不同的方法。我建议创建一个长方形object它在位置、大小、颜色方面是独立的,并且还嵌入了更新速度、方向和绘画的方法。例如,矩形对象可以由执行清除并调用对象的更新方法的主机对象来维护。

要检测碰撞,您可以使用这些对象迭代数组,以找出哪个矩形与当前正在测试的矩形发生碰撞。这里重要的是你“标记”(使用标志)一个已经测试过的矩形,因为碰撞中总是至少有两个,如果你测试 A,然后测试 B,你最终将在不使用用于跳过每帧碰撞“伙伴”对象测试的标志。

综上所述

Note:这里没有介绍一些特殊情况,例如精确角上的碰撞,或者矩形被困在一个边缘和另一个矩形之间(您也可以使用上面提到的命中标志进行边缘测试)。

我没有优化任何代码,但试图使其尽可能简单,以使其更容易理解。

希望这可以帮助!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

html5 canvas弹性碰撞方块 的相关文章

随机推荐

  • Haskell 中的大规模设计? [关闭]

    Closed 这个问题需要多问focused 目前不接受答案 Locked 这个问题及其答案是locked因为这个问题是题外话 但却具有历史意义 目前不接受新的答案或互动 设计 构造大型函数式程序 尤其是在 Haskell 中 的好方法是什
  • 获取字符串列表的哈希值,无论顺序如何

    我想写一个函数GetHashCodeOfList 它返回字符串列表的哈希码 无论顺序如何 给定 2 个具有相同字符串的列表应该返回相同的哈希码 ArrayList list1 new ArrayList list1 Add String1
  • 将多个 XML 转换为 JSON 列表

    我想使用 PowerShell 脚本将具有相同 XML 属性格式的多个 XML 文件转换为 JSON 文件 这个想法是创建一个 JSON 列表 其中每个项目都是 XML 文件的 JSON 表示形式 可行吗 输入和输出的示例 Input Fi
  • 尝试在托管 bean 构造函数中访问 @EJB bean 时出现 NullPointerException

    我有一个 EJB 服务 Stateless public class SomeService 我想将其注入到 viewscoped bean 中并用它进行初始化 ManagedBean ViewScoped public class Vie
  • (ListView?)-像 Windows 资源管理器中那样进行控制

    我想知道是否有任何方法可以在插入设备时制作一个类似于 Windows 资源管理器自动启动的控件 我原以为这可能是一个或多或少经过修改的列表视图控件 但我无法通过谷歌找到任何东西 我还检查了许多 CodeProject 页面 有谁知道我在哪里
  • 将应用了主题对话框的 Activity 定位在特定的 x、y 位置

    我想将对话框放置在屏幕上的特定位置 从顶部开始 10px 从let开始 5px 我确实应用了主题并添加了android scrollX android scrollY 但似乎不起作用 有什么解决办法吗 下面是我的样式 xml
  • 在 Vue.js 中,在哪里放置代码来设置 Firebase 身份验证状态持久性?

    Overview 我正在 Quasar Vue js 和 Firebase 中构建一个需要对用户进行身份验证的 Web 应用程序 我想要实现的目标 一个非常常见的功能 即使用户关闭浏览器 选项卡后仍保持登录状态 可能的解决方案 我知道我可以
  • HTTP 错误 429:python geopy 的请求过多

    我有一个问题 我不知道如何解决 我想迭代一个文件 在其中将坐标转换为地理位置地址 代码工作正常 但在迭代文件中的一定数量的行后 就会出现问题 from future import print function from geopy geoc
  • javascript中的createElement设置“onchange”属性

    我正在尝试动态添加带有 onchange 属性的新输入字段 var f document getElementById dN fieldsd create insert new el document createElement input
  • 执行生成器表达式最Pythonic的方法是什么?

    Python 越来越多的功能变成了 惰性可执行文件 比如生成器 表达式和其他类型的迭代器 然而 有时 我发现自己想要滚动一个单行 for 循环 只是为了执行一些操作 让循环实际执行的最Pythonic的事情是什么 例如 a open num
  • 将 onclick 添加到所有电话链接

    我有一个 WordPress 网站 一切都很完美 没有任何问题 我唯一想弄清楚如何做的是跟踪每当有人点击带有以下内容的电话链接 a href Click here to call us now at 1 800 222 1111 a 问题是
  • 优化分页渲染

    一边看着Pagination 就出现了渲染复杂页面的问题 这API例子 et al 通常指定一个pageFactory每次调用时都会简单地构造一个新的控件 的确 剖析下面的示例在分页时显示出最小的内存压力 并立即收集了一系列新实例 如果日益
  • 用于检查 iPhone 与 USB 的连接的应用程序

    我想知道 iPhone 是否以编程方式连接到 USB 到目前为止 通过谷歌搜索我发现http developer apple com programs mfi 我真的需要这个 api 来查明 iPhone 是否连接到 USB 设备吗 我只想
  • 如何使用 Cloud Composer 下载和访问文件?

    我有一些与文件相关的用例 我不确定如何使用 Cloud Composer 最好地完成这些用例 我应该如何最好地完成这些 1 我需要使用私钥 pem 文件来访问SFTP服务器 该文件应该存储在哪里以及如何访问它 在本地 Airflow 中 我
  • 检查升高的进程状态?

    我想找到一种方法来查明进程是否以提升的方式运行或不使用 Powershell 使用案例 能够以本地域用户的身份以提升的权限运行控制面板任务 例如添加或删除程序 任何帮助将不胜感激 Start add or remove as admin s
  • Meteor - 更新到 v0.9.1 后出现许多错误

    我已经在 Meteor 项目上工作了一个月 昨天我被要求更新到 Meteor v0 9 1 版本 之后 当我使用启动我的应用程序时meteor 它会在客户端控制台上抛出很多错误 他们来了 Uncaught TypeError undefin
  • 在从 stdin 读取的 while 循环中捕获 EOF 字符[重复]

    这个问题在这里已经有答案了 下面的代码从stdin逐个字符地传输 直到遇到 EOF 符号 CTRL D 但是 当我执行 CTRL D 命令时 它不会将其处理为 EOF 字符 include
  • PostgreSQL - 获取每个 GROUP BY 组中列的最大值的行

    我正在处理一个 Postgres 表 称为 lives 其中包含包含 time stamp usr id transaction id 和 lives remaining 列的记录 我需要一个查询 该查询将为我提供每个 usr id 的最近
  • Facebook Graph API tagged_places 返回空数组

    我正在尝试获取用户标记地点但数组总是返回空 如果我通过图形浏览器并生成一个令牌 那么我会得到预期的结果 但使用应用程序中生成的访问令牌我会返回一个空数组 我要求用户标记地点许可 我知道由于此许可要求 我的应用程序必须接受审查 但我现在只是使
  • html5 canvas弹性碰撞方块

    我重新问这个问题是因为我在上一个问题中没有明确说明我想要什么 有谁知道如何使用矩形在 Canvas 中进行弹性碰撞或处理碰撞 或者能给我指出正确的方向吗 我创建了一个具有多个正方形的画布 并希望每个正方形在接触时发生偏转 这是我整理的一个快