5

首先,我是 LINQ 的新手,所以我并不真正了解它的来龙去脉。我正在尝试在某些代码中使用它,根据我的诊断,它似乎与以相同方式使用 for 循环一样快。但是,我不确定这将如何扩展,因为我正在使用的列表可能会急剧增加。

我将 LINQ 用作碰撞检测功能的一部分(仍在使用中),并且我正在使用它来将列表剔除为仅与检查相关的列表。

这是 LINQ 版本:

partial class Actor {
    public virtual bool checkActorsForCollision(Vector2 toCheck) {
        Vector2 floored=new Vector2((int)toCheck.X, (int)toCheck.Y);

        if(!causingCollision) // skip if this actor doesn't collide
            return false;

        foreach(
            Actor actor in
            from a in GamePlay.actors
            where a.causingCollision==true&&a.isAlive
            select a
            )
            if( // ignore offscreen collisions, we don't care about them
                (actor.location.X>GamePlay.onScreenMinimum.X)
                &&
                (actor.location.Y>GamePlay.onScreenMinimum.Y)
                &&
                (actor.location.X<GamePlay.onScreenMaximum.X)
                &&
                (actor.location.Y<GamePlay.onScreenMaximum.Y)
                )
                if(actor!=this) { // ignore collisions with self
                    Vector2 actorfloor=new Vector2((int)actor.location.X, (int)actor.location.Y);

                    if((floored.X==actorfloor.X)&&(floored.Y==actorfloor.Y))
                        return true;
                }

        return false;
    }
}

这是我以前的方法:

partial class Actor {
    public virtual bool checkActorsForCollision(Vector2 toCheck) {
        Vector2 floored=new Vector2((int)toCheck.X, (int)toCheck.Y);

        if(!causingCollision) // skip if this actor doesn't collide
            return false;

        for(int i=0; i<GamePlay.actors.Count; i++)
            if( // ignore offscreen collisions, we don't care about them
                (GamePlay.actors[i].location.X>GamePlay.onScreenMinimum.X)
                &&
                (GamePlay.actors[i].location.Y>GamePlay.onScreenMinimum.Y)
                &&
                (GamePlay.actors[i].location.X<GamePlay.onScreenMaximum.X)
                &&
                (GamePlay.actors[i].location.Y<GamePlay.onScreenMaximum.Y)
                )
                if( // ignore collisions with self
                    (GamePlay.actors[i].isAlive)
                    &&
                    (GamePlay.actors[i]!=this)
                    &&
                    (GamePlay.actors[i].causingCollision)
                    ) {
                    Vector2 actorfloor=
                        new Vector2(
                            (int)GamePlay.actors[i].location.X,
                            (int)GamePlay.actors[i].location.Y
                            );

                    if((floored.X==actorfloor.X)&&(floored.Y==actorfloor.Y))
                        return true;
                }

        return false;
    }
}

目前,要么几乎立即运行(但每秒运行多次),但随着项目的构建和变得更加复杂,这将同时处理更多的对象,并且检查冲突的代码将更加详细.

4

5 回答 5

11

你的代码看起来不错;我不是更改工作代码的忠实拥护者,但是如果您确实想重写它以使其更易于阅读,那么我会这样做:

首先,抽象出谓词“不在屏幕上”。也许让它成为 GamePlay 的一种方法。每次检查坐标是否在边界内的业务是(1)实现细节,以及(2)使您的代码难以阅读。将来您可能会有一些更复杂的机制来确定对象是否在屏幕上。

其次,抽象掉向量地板操作。也许让它成为 Vector 的方法。请注意,此方法应返回一个新向量,而不是改变现有向量。

第三,在向量上做一个相等运算符。

第四,更好地命名方法。谓词应具有“IsFoo”或“HasFoo”的形式。您将其表述为命令,而不是问题。

第五,你根本不需要循环。

第六,说起来很奇怪somebool == true。就说吧somebool。前者的意思是“如果这个布尔是真的”,这是不必要的复杂。

让我们看看这是如何摆脱的:

public virtual bool HasCollisionWithAnyActor(Vector2 toCheck)
{
    // "Easy out": if this actor does not cause collisions then
    // we know that it is not colliding with any actor.
    if (!causingCollision)
      return false;

    Vector2 floored = toCheck.Floor();

    var collidingActors = 
      from actor in GamePlay.actors
      where actor != this
      where actor.causingCollision
      where actor.isAlive
      where GamePlay.IsOnScreen(actor.location)
      where actor.location.Floor() == floored
      select actor;

    return collidingActors.Any();
}

看看这比你的方法版本更容易阅读!这些都不会影响 X 和 Y 坐标。让辅助方法完成所有这些工作。代码现在清楚地表达了语义:告诉我是否与屏幕上其他活着的、导致碰撞的演员发生碰撞。

于 2013-01-23T16:44:02.410 回答
2

这里的 LINQ 版本比上一个版本更快,因为您忘记创建一个局部变量来存储GamePlay.actors[i]. 然后,如果在循环actors中多次访问数组。for

public virtual bool checkActorsForCollision(Vector2 toCheck)
{
    Vector2 floored = new Vector2((int) toCheck.X, (int) toCheck.Y);

    if (!causingCollision) // skip if this actor doesn't collide
    return false;

    for (int i = 0; i < GamePlay.actors.Count; i++)
    {
        Actor actor = GamePlay.actors[i];
        // ignore offscreen collisions, we don't care about them
        if ((actor.location.X > GamePlay.onScreenMinimum.X) &&
            (actor.location.Y > GamePlay.onScreenMinimum.Y) &&
            (actor.location.X < GamePlay.onScreenMaximum.X) &&
            (actor.location.Y < GamePlay.onScreenMaximum.Y))
        {

            if ((actor.isAlive) && (actor != this) &&
                (actor.causingCollision)) // ignore collisions with self
            {
                Vector2 actorfloor =
                  new Vector2((int) actor.location.X,
                              (int) actor.location.Y);
                if ((floored.X == actorfloor.X) &&
                    (floored.Y == actorfloor.Y))
                  return true;
            }
        }
    }
    return false;
}

现在,LINQ 的性能一般来说非常好。但在某些情况下,最好使用经典for版本。您必须在代码的易读性和性能之间找到适当的平衡(通过比较 LINQ 版本和for版本)。就我而言,我使用和滥用 LINQ 是因为我真的很喜欢它的语法和它的易读性。

这是一个完整的 LINQ 版本:

return (from actor in GamePlay.actors
        where actor.causingCollision && a.isAlive
        where actor != this
        where (actor.location.X > GamePlay.onScreenMinimum.X) &&
              (actor.location.Y > GamePlay.onScreenMinimum.Y) &&
              (actor.location.X < GamePlay.onScreenMaximum.X) &&
              (actor.location.Y < GamePlay.onScreenMaximum.Y)
        select new Vector2((int)actor.location.X,
                           (int)actor.location.Y)).Any(actorfloor => (floored.X == actorfloor.X) &&
                                                                     (floored.Y == actorfloor.Y));
于 2013-01-23T13:17:33.710 回答
1

我认为在这里使用 LINQ 没有任何问题。如果您想真正知道是否正在提高性能,您应该使用 Diagnostics.StopWatch 进行一些测量

http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.elapsedticks.aspx

此外,您可以使用更多这样的 LINQ 来使函数更加紧凑。

return (from actor in (from a in GamePlay.actors
                                   where a.CausingCollision == true && a.IsAlive
                                   select a)
                    where (actor.location.X > GamePlay.onScreenMinimum.X) && (actor.location.Y > GamePlay.onScreenMinimum.Y) && (actor.location.X < GamePlay.onScreenMaximum.X) && (actor.location.Y < GamePlay.onScreenMaximum.Y)
                    where actor != this
                    select new Vector2((int) actor.location.X, (int) actor.location.Y)).Any(actorfloor => (floored.X == actorfloor.X) && (floored.Y == actorfloor.Y));
于 2013-01-23T13:17:55.787 回答
1

我不确定我是否理解这个问题,但总的来说,我认为使用 LINQ 总是一个更好的解决方案,而不是创建额外的循环和 if/else 调用。

仅供参考:您也可以使用 LINQ-Methodes 代替关键字,例如:

foreach(Actor actor in from a in GamePlay.actors
                           where a.causingCollision == true 
                           && a.isAlive
                           select a)
{
  //...
}

是相同的:

foreach(Actor actor in GamePlay.actors.Where( a => a.causingCollision == true && a.isAlive))
{
  //...
}

两种情况下的返回都是过滤的 IEnumerable。我个人的看法是这些方法更容易阅读和理解。在 Where(...) 中,您可以放置​​一个完整的 lambda 表达式来完全选择您想要的 Actor。

于 2013-01-23T13:18:27.583 回答
1

LINQ 进行了相当优化,其输出与手动编写 fors 相同。顺便说一句,扩展方法更具可读性。您的示例将如下所示:

public virtual bool checkActorsForCollision(Vector2 toCheck)
{
    Vector2 floored = new Vector2((int)toCheck.X, (int)toCheck.Y);

    // skip if this actor doesn't collide
    if (!causingCollision)
    {
        return false;
    }

    return GamePlay.actors.Where( actor =>    
            (actor.causingCollision == true) 
            && actor.isAlive
            // ignore offscreen collisions, we don't care about them
            && (actor.location.X > GamePlay.onScreenMinimum.X) 
            && (actor.location.Y > GamePlay.onScreenMinimum.Y)          
            && (actor.location.X < GamePlay.onScreenMaximum.X) 
            && (actor.location.Y < GamePlay.onScreenMaximum.Y)
            // ignore collisions with self
            && (actor != this) )
        .Select( actor => new Vector2((int)actor.location.X, (int)actor.location.Y) )
        .Any( actorFloor => 
            (floored.X == actorfloor.X) 
            && (floored.Y == actorfloor.Y));
}

如果您没有分析您的应用程序,则没有理由不出于“性能原因”使用 LINQ。

于 2013-01-23T13:25:20.343 回答