首先,我是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;
}
}
当下,要么几乎没有时间运行(但是每秒运行很多次),但随着项目的构建和变得更复杂,这将同时处理更多的对象,并且检查冲突的代码将会更详细。
答案 0 :(得分: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坐标。 让辅助方法完成所有这些操作。现在代码清楚地表达了语义:告诉我屏幕上是否与其他生活的,引起碰撞的演员发生任何碰撞。
答案 1 :(得分: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));
答案 2 :(得分: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));
答案 3 :(得分: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表达式来完全选择你想要的Actors。
答案 4 :(得分: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用于“性能原因”。