HTML5 画布中的碰撞检测。也优化了

2024-01-01

我正在制作一个平台游戏,但我的碰撞检测有问题。我创建了一个在屏幕/地图上绘制图块的函数。在该函数中是我的碰撞检测,当仅绘制一个图块时它工作正常,但是当我用三个图块创建“楼梯”时,第一个图块无法正常工作。玩家只是被“推”到图块上。侧面检测不起作用。其他瓷砖工作得很好。

这是碰撞检测和图块绘制的代码:

//Function that allows you to draw a tile
function drawTile(type, x, y, collision){
    var tileImg = new Image();
    tileImg.onload = function(){
        ctx.drawImage(tileImg, x, y)
    };
    tileImg.src = "images/" + type + ".png";

    if (collision){
        //Worst collision detection ever.
        if((player_x + player_width == x) && (player_y + player_height > y)){
            canMoveRight = false;
        }else if((player_x == x + 32) && (player_y + player_height > y)){
            canMoveLeft = false;
        }else if((player_y + player_height > y) && (player_x + player_width >= x) && (player_x + player_width <= x + 64)){
            player_y = y - player_height;
        }else{
            canMoveRight = true;
            canMoveLeft = true;
        }
    }
}

//Draw the map
function drawMap(){
    drawTile("block", 96, 208, true);
    drawTile("block", 128, 208, true);
    drawTile("block", 128, 176, true);
};

正如你所看到的,碰撞检测代码有点糟糕。因此,如果您向我展示更好的制作方法,那就太好了。

如果您需要了解某事,请直接说。 :)


我知道这对您来说听起来像是可怕的开销,但您应该考虑使用场景图来处理屏幕上的所有碰撞检测、绘制甚至单击事件。

场景图基本上是一个表示 1-父到 n-子关系的树形数据结构(注意:每个 HTML 页面的 DOM 也是一个场景图)

因此,为了解决这个问题,您将有一个称为“节点”或其他名称的基本接口或抽象类,代表场景图中每个节点都必须实现的接口。同样,它就像 dom 中的元素一样,它们都有 CSS 属性、事件处理方法和位置修饰符。

Node:

{
    children: [],

    update: function() {
        for(var i = 0; i < this.children.length; i++) {
            this.children[i].update();
        }
    },

    draw: function() {
        for(var i = 0; i < this.children.length; i++) {
            this.children[i].draw();
        }
    }
}

现在,您可能知道,如果您在 DOM 中的每个元素、位置或其他元素移动一个元素,所有子元素都会自动转到其父元素的新位置,这一行为是通过转换继承实现的,为了简单起见,我'我不会显示 3D 变换矩阵,而只是一个平移(x,y 偏移)。

Node:

{
    children: [],
    translation: new Translation(),


    update: function(worldTranslation) {
        worldTranslation.addTranslation(this.translation);

        for(var i = 0; i < this.children.length; i++) {
            this.children[i].update(worldTranslation);
        }

        worldTranslation.removeTranslation(this.translation);
    },

    draw: function() {
        for(var i = 0; i < this.children.length; i++) {
            this.children[i].draw();
        }
    }
}

转型:

{
    x: 0,
    y: 0,

    addTranslation: function(translation) {
        this.x += translation.x;
        this.y += translation.y;
    },

    removeTranslation: function(translation) {
        this.x -= translation.x;
        this.y -= translation.y;
    }
}

(请注意,我们不会实例化新的翻译对象,因为在全局翻译上添加/删除值更便宜)

现在,您的 worldTranslation(或 globalTranslation)具有节点可以从其父级继承的所有偏移量。

在开始碰撞检测之前,我将展示如何使用这种技术绘制精灵。 基本上,您将用位置图像对填充绘图循环中的数组

Node:

{
    children: [],
    translation: new Translation(),

    image: null, //assume this value is a valid htmlImage or htmlCanvas element, ready to be drawn onto a canvas
    screenPosition: null, //assume this is an object with x and y values like {x: 0, y: 0}

    update: function(worldTranslation) {
        worldTranslation.addTranslation(this.translation);

        this.screenPosition = {x: worldTranslation.x, y: worldTranslation.y};

        for(var i = 0; i < this.children.length; i++) {
            this.children[i].update(worldTranslation);
        }

        worldTranslation.removeTranslation(this.translation);
    },

    draw: function(spriteBatch) {

        spriteBatch.push({
            x: this.screenPosition.x,
            y: this.screenPosition.y,
        });

        for(var i = 0; i < this.children.length; i++) {
            this.children[i].draw(spriteBatch);
        }
    }
}

渲染函数:

function drawScene(rootNode, context) {
    //clear context here

    var spriteBatch = [];
    rootNode.draw(spriteBatch);

    //if you have to, do sorting according to position.x, position.y or some z-value you can set in the draw function

    for(var i = 0; i < spriteBatch.length; i++) {
        var sprite = spriteBatch[i];

        context.drawImage(sprite.image, sprite.position.x, sprite.position.y);
    }
}

现在我们已经对从场景图中绘制图像有了基本的了解,我们开始进行碰撞检测:

首先我们需要我们的节点有 BoundryBoxes:

Box:

{
    x: 0,
    y: 0,

    width: 0,
    height: 0,

    collides: function(box) {
        return !(
                ((this.y + this.height) < (box.y)) ||
                (this.y > (box.y + box.height)) ||
                ((this.x + this.width) < box.x) ||
                (this.x > (box.x + box.width))
            );
    }
}

在 2D 游戏中,我更喜欢让边界框不在其所在节点的坐标系中,而不是让它知道它的位置Absolute价值观。 通过CSS解释: 在CSS中,您可以设置边距和填充,这些值不依赖于页面,而是它们仅存在于设置了这些值的元素的“坐标系”中。所以它就像有position:absolute和left:10px vs margin-left:10px; 2D 的好处是我们不需要一直冒泡场景图来查找检测,而且我们不必从当前坐标系计算盒子 -> 进入世界坐标系 -> 返回每个节点进行碰撞检测。

Node:

{
    children: [],
    translation: new Translation(),

    image: null,
    screenPosition: null,

    box: null, //the boundry box

    update: function(worldTranslation) {
        worldTranslation.addTranslation(this.translation);

        this.screenPosition = {x: worldTranslation.x, y: worldTranslation.y};

        this.box.x = worldTranslation.x;
        this.box.y = worldTranslation.y;
        this.box.width = this.image.width;
        this.box.height = this.image.height;

        for(var i = 0; i < this.children.length; i++) {
            this.children[i].update(worldTranslation);
        }

        worldTranslation.removeTranslation(this.translation);
    },

    collide: function(box, collisions) {
        if(this.box.collides(box)) {
            collisions.push(this);
        }

        //we will optimize this later, in normal sceneGraphs a boundry box asures that it contains ALL children
        //so we only will go further down the tree if this node collides with the box
        for(var i = 0; i < this.children.length; i++) {
            this.children[i].collide(box, collisions);
        }
    },

    draw: function(spriteBatch) {

        spriteBatch.push({
            x: this.screenPosition.x,
            y: this.screenPosition.y,
            image: this.image,
        });

        for(var i = 0; i < this.children.length; i++) {
            this.children[i].draw(spriteBatch);
        }
    }
}

碰撞功能:

function collideScene(rootNode, box) {
    var hits = [];

    rootNode.collide(box, hits);

    for(var i = 0; i < hits.length; i++) {
        var hit = hits[i];

        //your code for every hit
    }
}

Note:并非每个节点都必须代表一个图像,您可以创建节点以仅向其子节点添加翻译,就像创建一个没有视觉效果的 DIV 容器来排列一堆对象,而只需编辑一个对象的位置。一个角色可以包括:

CharacterRoot (Translation Only)
    CharacterSprite (Image)
    Weapon (Image)
    Shield (Image)

现在这些只是游戏中使用的场景管理的一些基础知识,您可以在谷歌上搜索我在这里使用的许多术语,进行一些进一步的优化,并随意提出任何问题。

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

HTML5 画布中的碰撞检测。也优化了 的相关文章

随机推荐

  • php 文件上传错误 1 ​​- 使用 php_ini 的正确方法是什么?

    我在将 pdf 上传到服务器时遇到问题 upload max filesize 为 2M 文件大于此值 约为 4M 我在这里发现了一个与我的问题类似的帖子 即使 upload max size 大于文件大小 FILE 上传大文件也会出现错误
  • 如何远程关闭 Java RMI 服务器

    我有一个非常简单的 Java RMI 服务器 如下所示 import java rmi import java rmi server public class CalculatorImpl extends UnicastRemoteObje
  • C++ 算术提升标头的用途

    我一直在使用一组模板来确定 C 中给定两种基本类型的正确提升类型 这个想法是 如果您定义自定义数字模板 您可以使用它们来确定返回类型 例如基于传递给模板的类的operator 函数 例如 Custom numeric class templ
  • 如何创建多维数组?

    我想创建具有级别 组和项目的多维数组 为什么不创建自定义对象的数组 class A int t int b List a test new ArrayList a test add new A a a
  • 理解 phpcurl_multi_exec

    我试图理解curl multi exec 我在这里复制了一部分手动示例 所以我想知道它是如何工作的 我猜第一个循环发送http请求 但随后是一个循环内的循环 使用带有看似未记录的标志的函数 我想并行下载 70 个网址 http www ph
  • 何时使用函数式 setState

    在过去的几天里 我一直在学习 React 查看了一些关于编写不同元素的不同方式的教程和解释 不过 有一个我一直很好奇的 setState更新 覆盖函数state组件的属性 例如 假设我有一个包含以下内容的课程 class Photos ex
  • RxJs switchMap 与 Angular HttpClient

    我有一个用例 每当触发新请求时 任何已经在进行中的 http 请求都应该被取消 忽略 For eg 当请求 1 响应时间太长 网络连接速度变慢时 会出现请求 例如 2 在这种情况下 2 从服务器获得非常快的响应 即使 1 返回 HTTP 响
  • 如何以 REST方式返回随机项?

    我的设计公开了两种资源 Images Tags 我希望客户能够通过他们的标签请求随机图像 例如 给我标记有 纽约 和 冬天 的随机图像 在这种情况下 RESTful 设计会是什么样子 为了总结评论中的所有讨论 并且不改变我最初的建议 这就是
  • php静态方法问题

    这两段代码有什么区别 class something static function doit echo hello world something doit 和相同但没有 static 关键字 class something functi
  • 如何设置网格视图列的高度?

    我是flutter新手 没有太多经验 我正在尝试使用 flutter 开发一个 android 应用程序 这是我以前的应用程序设计 我还能够成功地在颤动中制作网格视图 但列高是问题所在 他们中的任何人都可以帮助我解决我的颤振代码吗 clas
  • Hadoop 2.4.1 和适用于 Hadoop 的 Google Cloud Storage 连接器

    我正在尝试使用 Google 的 Cloud Storage Connector for Hadoop 在 Hadoop 上运行 Oryx https cloud google com hadoop google cloud storage
  • 如何通过jquery隐藏没有id/class的div?

    如何使用 jQuery 隐藏第一个 第二个 div Div 无法获取 id class div p ssssssss p div div p ttttttttt gt p div div p fffff p div 隐藏第一个 div 元素
  • Form1_KeyDown 不起作用

    private void Form1 KeyDown object sender KeyEventArgs e if listBox1 Items Contains e KeyCode listBox1 Items Remove e Key
  • 在 C++ 中添加双精度数和复数

    考虑一下这段代码 include
  • 通过使用网格视图中的图像按钮在代码隐藏中调用函数

    我有一个ImageButton在一个GridView在 aspx 上clicking this ImageButton我必须调用一个函数 这就是我尝试的方法 但该函数没有被调用 aspx页面内的代码
  • 使用自动布局检索子视图的正确位置

    我想以编程方式将视图放置在故事板中创建的所有子视图的中心 在故事板中 我有一个视图 在一个垂直 StackView 内部 它具有填充全屏的约束 分布 等间距 在垂直堆栈视图内部 我有 3 个水平堆栈视图 约束高度 100 超级视图的尾随和前
  • VS2008:项目现在需要很长时间才能打开

    昨天下午 Visual Studio 2008 突然开始需要很长时间才能打开 ASP NET Web 窗体 项目 几个月后没有出现任何问题 这个问题似乎是在我尝试将 CrystalDecisions Enterprise dll 和 Cry
  • 在 R Shiny 中使用并行 foreach 制作进度条

    我正在尝试添加一个进度条ocr data 在 for 循环中 但是当串联使用时 该过程非常慢 我正在尝试利用并行来加快生成表的过程ocr data x 代码粘贴在这里 ui lt fluidPage Application title ti
  • 如何在Android上的文本视图中对长文本进行文本换行

    在我的应用程序中 我有包含长文本的文本视图 我需要像 android 模拟器 gt 联系人 gt 电话 屏幕上没有联系人拨号盘 那样的文本换行 but in my application i get text wrapping as fol
  • HTML5 画布中的碰撞检测。也优化了

    我正在制作一个平台游戏 但我的碰撞检测有问题 我创建了一个在屏幕 地图上绘制图块的函数 在该函数中是我的碰撞检测 当仅绘制一个图块时它工作正常 但是当我用三个图块创建 楼梯 时 第一个图块无法正常工作 玩家只是被 推 到图块上 侧面检测不起