碰撞检测

碰撞检测是大部分游戏类型都会用到的技术,是游戏交互的重要基础,在2D游戏中,由于少了一个维度,碰撞检测的实现其实大大简化了。底层数学原理这里不多说,我们高中数学中已经学习过,这里介绍下LibGDX中如何实现碰撞检测。

2D游戏中的碰撞检测

以RPG游戏为例,障碍物拦住玩家的判定,ARPG中玩家攻击怪物的判定,玩家射出的子弹是否击中怪物的判定,这些判定都属于碰撞检测。2D游戏开发中,常用的碰撞检测有这么几种:

  • 矩形的重合检测
  • 圆形的重合检测
  • 多边形重合检测
  • 射线(线段)相交检测

其中,多边形重合检测比较少用,一般使用物理引擎实现,手动实现可以用现成的pnpoly算法,射线(线段)检测能用矩形检测替代,圆形检测一般是圆形和点之间的检测,判断点到圆心的距离就行了,圆形和多边形之间的检测也可以遍历端点实现,非常简单。所以说白了,碰撞检测其实就一种:矩形重合检测,而且LibGDX已经帮我们实现好了。

注:LibGDX能够集成Box2D和Bullet(主要用于3D)物理引擎,适用于例如多边形检测之类的复杂情况,物理引擎的内容将在后续章节学习,本篇笔记主要介绍如何在不使用物理引擎的情况下实现。

矩形碰撞检测

检测两个游戏物体碰撞,其实可以将两个物体简化看作矩形,然后判断两个矩形受否有重叠部分,如果有,认为发生了碰撞,除此之外,点和矩形的碰撞可以看作一种特殊的矩形碰撞。矩形碰撞检测是一种游戏中常用的简化的碰撞检测模型。

Rectangle 矩形

LibGDX中使用Rectangle表示一个矩形,我们对需要碰撞检测的Actor,其实可以给其初始化一个专用于碰撞检测的Rectangle属性。

PlayerActor.java

Rectangle bounds = new Rectangle(getX(), getY(), getWidth(), getHeight());

在Actor中实例化Rectangle对象很简单,传入Actor的坐标、长宽即可,这个矩形可以作为Actor范围的简化表示。

Rectangle实现的重合检测方法主要会用到如下三个:

boolean contains(float x, float y)     某点是否在矩形内
boolean contains(Rectangle rectangle)  某矩形是否在矩形内
boolean overlaps(Rectangle r)          两个矩形是否有重叠部分

检测碰撞

具体到游戏代码中,我们获取目标矩形,可以通过遍历所有Actor来实现。我们可以将生成的所有Actor找个全局变量存储起来,或者直接遍历一个Stage下的Actor

下面是一个例子,假设我们的Stage中,有一个玩家Actor,还有一个敌人Actor,下面代码写在玩家的act()函数中,遍历判断该Stage下所有Actor,是否和玩家发生了碰撞,如果是,打印一条信息。

// 检测是否和敌人有碰撞
Array<Actor> actors = getStage().getActors();
for (Actor ac : actors) {
    if (ac instanceof TestEnemyActor) {
        TestEnemyActor enemy = (TestEnemyActor) ac;
        Rectangle enemyRect = enemy.getBounds();
        if (bounds.overlaps(enemyRect)) {
            System.out.println("碰撞了:[" + bounds + "] [" + enemyRect + "]");
        }
    }
}

注:上面代码可能有一处容易让人误解,Actor类本身没提供getBounds(),上面代码中,我在自己的Actor初始化时,为其设置了一个Rectangle属性,需要时通过actor.getBounds()返回,实际上碰撞检测时现通过Actor的坐标和大小初始化一个Rectangle也可以。Actor提供了一个setBounds()函数,这个函数和Rectangle没关系,它是用来同时设置Actor的位置和大小的。

鼠标点击检测

除了矩形检测,这里再介绍一个稍微不相关的内容。实际上,鼠标点击检测是射线检测的一种,鼠标点击时产生一条射向屏幕里的射线,判断射线是否和游戏对象相交。

但LibGDX是个2D游戏框架,使用的都是正交摄像机,弱化了z轴这个概念,并做了一系列封装。我们判断鼠标是否点击了某个Actor,直接为Actor绑定监听函数就行了,不需要考虑射线检测之类的底层实现,LibGDX会在底层计算点击射线在XY平面上的焦点是否在Actor矩形内。

playerActor.addListener(new ClickListener() {
    @Override
    public void clicked(InputEvent event, float x, float y) {
        System.out.println("玩家被点击了[" + x + " " + y + "]");
    }
});

上面代码中,在playerActor这个实例初始化时,为其绑定一个监听函数。注意,click()回调的xy参数是相对于Actor的,而不是世界坐标的,另外,Actor的位置和大小需要被正确初始化。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。