检查拦截AS3中视线的物体的有效方法

时间:2011-05-09 23:12:38

标签: flash actionscript-3 performance

我已经有一段时间了,现在一直试图找出一种有效的方法,看看是否有东西在其他东西的视线范围内。一个很好的例子就是如果能够看到你的话,有一个向你发射火箭的球体 - 但显然不是你在墙后面。

以下是我一般的做法:

function cast(end:GameObject, walls:Array, accuracy:uint=10):Object
{
    var xp:Number = skin.x;
    var yp:Number = skin.y;

    var ang:Number = Math.atan2(end.skin.y - yp, end.skin.x - xp);
    var xvel:Number = Math.cos(ang)*accuracy;
    var yvel:Number = Math.sin(ang)*accuracy;

    var i:uint = 0;
    for(i; i<800/accuracy; i+=accuracy)
    {
        xp += xvel;
        yp += yvel;

        var j:GameObject;
        for each(j in walls)
        {
            if(j.skin.hitTestPoint(xp, yp))
                return {visible:false, x:xp, y:yp};
        }
    }

    return {visible:true};
}

使用它基本上是:

var sight:Object = cast(player, impassable);

if(sight.visible) trace('can see');
else trace('cant see - collision at ' + sight.x + ", " + sight.y);

可行,但正如我们所知,每增加一枚新火箭或无法通过的物体数量增加,这将会非常缓慢。

我假设有一种非常简单有效的方法,我错过了 - 我的意思是,所有的游戏都会(暗黑破坏神等)与数百个不做任何事情的敌人一起做,除非你可见。

想法?

3 个答案:

答案 0 :(得分:4)

  

我的意思是,所有游戏都是这样做的(暗黑破坏神等)   有数百个不做的敌人   什么,除非你可见。

像暗黑破坏神一样的游戏使用基于磁贴的引擎,以减少计算碰撞,视线和AI行为所需的计算次数;基于磁贴的引擎诞生于您对游戏引擎的确切顾虑。

给定绝对坐标,找出任何敌人所在的特定图块并将其转换为地图上的x,y坐标是微不足道的。一旦你拥有了这个区块,就不应该太难以缩小你需要运行的“检查”的数量,以便弄清楚是否有另一个对象。

进一步采用基于磁贴的引擎,寻路在基于磁贴的游戏引擎中也非常有用,可以很容易地完成你的任务;路径距离和/或复杂性可以让您轻松判断2个对象是否可以相互“看到”。 (如果您需要走四十步,或者在迷宫般的路径中,对象彼此不可见)可能会有机会。

基于平铺的引擎彻底减少了您开始考虑的开销问题。

答案 1 :(得分:1)

保存自己必须编写这么多代码并尝试使用Box2d物理引擎进行光线投射,为你做了很多艰苦的工作,你不需要担心速度,因为box2d已经优化但是如果你坚持编码你自己的瓦片肯定是要走的路,也许有一个*寻路移动和布莱恩说射击就足够了。但是,如果您需要考虑cells.gif场景,那么只需更改磁贴搜索的条件。

答案 2 :(得分:0)

对于碰撞,维护某种网格(或空间树,但网格应该足够精细)通常是有意义的,每个单元知道其中的对象,以便您可以直接在特定空间中查找对象。 / p>

然后编写一个例程,检索所有相关的单元以进行碰撞,在这种情况下,检查沿光线的所有单元格。在检索它们时,请对其中的对象执行碰撞检查。

这段代码基本上应该这样做:

function rayCollision(start:Point, dest:Point, grid:CollisionGrid, ignore:* = null):CollisionData {
    var direction:Point = new Point(dest.x - start.x, dest.y - start.y);
    if (direction.length == 0) return null;//just in case
    const limit:Number = direction.length;
    direction.normalize(grid.cellSize);
    var pos:Point = start;
    var cur:Array, last:Array = null, tested:Dictionary = new Dictionary();
    if (!(ignore is Function)) {
        if (ignore is ICollidable) tested[ignore] = true;
        else for each (var entry:* in ignore) tested[entry] = true;//assume it is a collection
    }
    var collision:CollisionData = null;
    while (grid.containsPoint(pos) && (pos.subtract(start).length < limit) {//stop when you're off the grid or out of range
        cur = grid.getCellByPoint(pos);
        if (cur == last) continue;//cell already checked, skip
        last = cur;
        for each (var object:ICollidable in cur) {
            if (tested[object] || (ignore && ignore(object))) continue;//object already checked or should be ignored, skip
            tested[object] = true;
            collision = object.collideWithLine(start, dest);
            if (collision) return collision;
        }
        pos = pos.add(direction);
    }
    return null;
}

CollisionData应该包含坐标和对象。 ICollidable是任何可碰撞的接口,应该单独实现,因为例如线和墙之间的碰撞应该相当容易计算。
实际上,你应该把它变成CollisionGrid的方法,所以你只需要去myGrid.rayCast(start, end, theCaster),你就可以方便地在你想要的任何东西之间投射光线。

好运的细节;)