- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我正在开发一个简单的碰撞检测演示,其中仅包含一堆在窗口中弹跳的对象。 (目标是查看游戏一次可以处理多少个对象而不会丢帧。)
存在重力,所以物体要么移动要么与墙壁碰撞。
天真的解决方案是 O(n^2):
foreach Collidable c1:
foreach Collidable c2:
checkCollision(c1, c2);
这很糟糕。因此,我设置了 CollisionCell
对象,它维护有关屏幕一部分的信息。这个想法是每个 Collidable
只需要检查其单元格中的其他对象。对于 60 像素 x 60 像素的单元格,这产生了近 10 倍的改进,但我想进一步插入它。
分析器显示,代码将 50% 的时间花在每个单元格用来获取其内容的函数上。在这里:
// all the objects in this cell
public ICollection<GameObject> Containing
{
get
{
ICollection<GameObject> containing = new HashSet<GameObject>();
foreach (GameObject obj in engine.GameObjects) {
// 20% of processor time spent in this conditional
if (obj.Position.X >= bounds.X &&
obj.Position.X < bounds.X + bounds.Width &&
obj.Position.Y >= bounds.Y &&
obj.Position.Y < bounds.Y + bounds.Height) {
containing.Add(obj);
}
}
return containing;
}
}
程序的 20% 的时间花在了该条件语句上。
这里是调用上述函数的地方:
// Get a list of lists of cell contents
List<List<GameObject>> cellContentsSet = cellManager.getCellContents();
// foreach item, only check items in the same cell
foreach (List<GameObject> cellMembers in cellContentsSet) {
foreach (GameObject item in cellMembers) {
// process collisions
}
}
//...
// Gets a list of list of cell contents (each sub list = 1 cell)
internal List<List<GameObject>> getCellContents() {
List<List<GameObject>> result = new List<List<GameObject>>();
foreach (CollisionCell cell in cellSet) {
result.Add(new List<GameObject>(cell.Containing.ToArray()));
}
return result;
}
现在,我必须遍历每个单元格 - 甚至是空单元格。也许这可以以某种方式改进,但我不确定如何在不以某种方式查看单元格的情况下验证单元格是否为空。 (也许我可以在一些物理引擎中实现类似于 sleep 对象的东西,如果一个对象将静止一段时间,它就会进入休眠状态,并且不会包含在每一帧的计算中。)
我可以做些什么来优化它? (此外,我是 C# 的新手 - 还有其他明显的文体错误吗?)
当游戏开始滞后时,物体往往会挤得相当紧,因此不会有太多运动。也许我可以以某种方式利用这一点,编写一个函数来查看给定对象的当前速度,它是否可以在下一次调用 Update()
UPDATE 1 我决定维护一个列表,列出上次更新时发现在单元格中的对象,并首先检查它们是否仍在单元格中。此外,我维护了 CollisionCell
变量的一个 area
,当单元格被填满时我可以停止查看。这是我的实现,它使整个演示变慢了很多:
// all the objects in this cell
private ICollection<GameObject> prevContaining;
private ICollection<GameObject> containing;
internal ICollection<GameObject> Containing {
get {
return containing;
}
}
/**
* To ensure that `containing` and `prevContaining` are up to date, this MUST be called once per Update() loop in which it is used.
* What is a good way to enforce this?
*/
public void updateContaining()
{
ICollection<GameObject> result = new HashSet<GameObject>();
uint area = checked((uint) bounds.Width * (uint) bounds.Height); // the area of this cell
// first, try to fill up this cell with objects that were in it previously
ICollection<GameObject>[] toSearch = new ICollection<GameObject>[] { prevContaining, engine.GameObjects };
foreach (ICollection<GameObject> potentiallyContained in toSearch) {
if (area > 0) { // redundant, but faster?
foreach (GameObject obj in potentiallyContained) {
if (obj.Position.X >= bounds.X &&
obj.Position.X < bounds.X + bounds.Width &&
obj.Position.Y >= bounds.Y &&
obj.Position.Y < bounds.Y + bounds.Height) {
result.Add(obj);
area -= checked((uint) Math.Pow(obj.Radius, 2)); // assuming objects are square
if (area <= 0) {
break;
}
}
}
}
}
prevContaining = containing;
containing = result;
}
UPDATE 2 我放弃了最后一种方法。现在我正在尝试维护一个可碰撞对象池(孤儿
),并在找到包含它们的单元格时从中删除对象:
internal List<List<GameObject>> getCellContents() {
List<GameObject> orphans = new List<GameObject>(engine.GameObjects);
List<List<GameObject>> result = new List<List<GameObject>>();
foreach (CollisionCell cell in cellSet) {
cell.updateContaining(ref orphans); // this call will alter orphans!
result.Add(new List<GameObject>(cell.Containing));
if (orphans.Count == 0) {
break;
}
}
return result;
}
// `orphans` is a list of GameObjects that do not yet have a cell
public void updateContaining(ref List<GameObject> orphans) {
ICollection<GameObject> result = new HashSet<GameObject>();
for (int i = 0; i < orphans.Count; i++) {
// 20% of processor time spent in this conditional
if (orphans[i].Position.X >= bounds.X &&
orphans[i].Position.X < bounds.X + bounds.Width &&
orphans[i].Position.Y >= bounds.Y &&
orphans[i].Position.Y < bounds.Y + bounds.Height) {
result.Add(orphans[i]);
orphans.RemoveAt(i);
}
}
containing = result;
}
这只会产生边际改善,而不是我正在寻找的 2 倍或 3 倍。
UPDATE 3 我再次放弃了上述方法,并决定让每个对象保持其当前单元格:
private CollisionCell currCell;
internal CollisionCell CurrCell {
get {
return currCell;
}
set {
currCell = value;
}
}
这个值得到更新:
// Run 1 cycle of this object
public virtual void Run()
{
position += velocity;
parent.CellManager.updateContainingCell(this);
}
CellManager 代码:
private IDictionary<Vector2, CollisionCell> cellCoords = new Dictionary<Vector2, CollisionCell>();
internal void updateContainingCell(GameObject gameObject) {
CollisionCell currCell = findContainingCell(gameObject);
gameObject.CurrCell = currCell;
if (currCell != null) {
currCell.Containing.Add(gameObject);
}
}
// null if no such cell exists
private CollisionCell findContainingCell(GameObject gameObject) {
if (gameObject.Position.X > GameEngine.GameWidth
|| gameObject.Position.X < 0
|| gameObject.Position.Y > GameEngine.GameHeight
|| gameObject.Position.Y < 0) {
return null;
}
// we'll need to be able to access these outside of the loops
uint minWidth = 0;
uint minHeight = 0;
for (minWidth = 0; minWidth + cellWidth < gameObject.Position.X; minWidth += cellWidth) ;
for (minHeight = 0; minHeight + cellHeight < gameObject.Position.Y; minHeight += cellHeight) ;
CollisionCell currCell = cellCoords[new Vector2(minWidth, minHeight)];
// Make sure `currCell` actually contains gameObject
Debug.Assert(gameObject.Position.X >= currCell.Bounds.X && gameObject.Position.X <= currCell.Bounds.Width + currCell.Bounds.X,
String.Format("{0} should be between lower bound {1} and upper bound {2}", gameObject.Position.X, currCell.Bounds.X, currCell.Bounds.X + currCell.Bounds.Width));
Debug.Assert(gameObject.Position.Y >= currCell.Bounds.Y && gameObject.Position.Y <= currCell.Bounds.Height + currCell.Bounds.Y,
String.Format("{0} should be between lower bound {1} and upper bound {2}", gameObject.Position.Y, currCell.Bounds.Y, currCell.Bounds.Y + currCell.Bounds.Height));
return currCell;
}
我认为这会让它变得更好——现在我只需要遍历可碰撞对象,而不是所有可碰撞对象 * 单元格。取而代之的是,游戏现在慢得可怕,使用我的上述方法只能提供其性能的 1/10。
探查器表明现在主要的热点是一种不同的方法,并且为对象获取邻居的时间非常短。该方法与以前相比没有变化,所以我可能比以前更常调用它...
最佳答案
它在该函数上花费了 50% 的时间,因为您经常调用该函数。优化一个功能只会对性能产生增量改进。
或者,只需少调用函数!
您已经通过设置空间分区方案开始了这条道路(查找 Quadtrees 以查看您的技术的更高级形式)。
第二种方法是将 N*N 循环分解为增量形式并使用CPU 预算。
您可以为每个需要在帧时间内(更新期间)执行操作的模块分配 CPU 预算。碰撞是这些模块之一,人工智能可能是另一个。
假设您希望以 60 fps 的速度运行游戏。这意味着您有大约 1/60 s = 0.0167 s 的 CPU 时间在帧之间刻录。不,我们可以在我们的模块之间拆分那些 0.0167 秒。让我们给碰撞 30% 的预算:0.005 秒。
现在您的碰撞算法知道它只能花费 0.005 秒的时间工作。因此,如果它用完了时间,它将需要推迟一些任务以备后用——您将使算法增量。实现这一点的代码可以很简单:
const double CollisionBudget = 0.005;
Collision[] _allPossibleCollisions;
int _lastCheckedCollision;
void HandleCollisions() {
var startTime = HighPerformanceCounter.Now;
if (_allPossibleCollisions == null ||
_lastCheckedCollision >= _allPossibleCollisions.Length) {
// Start a new series
_allPossibleCollisions = GenerateAllPossibleCollisions();
_lastCheckedCollision = 0;
}
for (var i=_lastCheckedCollision; i<_allPossibleCollisions.Length; i++) {
// Don't go over the budget
if (HighPerformanceCount.Now - startTime > CollisionBudget) {
break;
}
_lastCheckedCollision = i;
if (CheckCollision(_allPossibleCollisions[i])) {
HandleCollision(_allPossibleCollisions[i]);
}
}
}
现在不管碰撞代码有多快,它都会尽可能快地完成,不会影响用户的感知性能。
好处包括:
关于C# XNA : Optimizing Collision Detection?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2343789/
我用 Java 创建了一个简单的 2d 物理引擎,它使用圆形射弹和直墙。目前,每一帧,每个射弹的位置都会提前velocity * (1/fps) .速度随后更新,每帧一次。碰撞检测使用先前和当前位置以
我有一个 CollisionHandler 类,它有 OnCollisionEnter 方法。游戏中的每个 collideable 实体都有 CollisionHandler 作为组件。因此,当 2
我目前正在编写像 Breakout 这样的游戏,我想知道如何正确地将球从表面上弹开。 我采用了将速度旋转 90 度的幼稚方式,即: [vx, vy] -> [-vy, vx] 哪个(不出所料)效果不佳
这个问题一定存在于某个地方,但我不确定它会被称为什么。 在高度图之后,存在直角三角形地形。每个三角形的每个点都从高度图中获取其高度 (y)。 如果角色位于一组特定的坐标,您可以计算出他们在哪个三角形中
我正在做一个2D滚动大理石的项目。我可以检测出大理石何时撞到了盒子的角落,但是我无法弄清楚大理石在撞到角落时应该有什么行为。我需要弄清楚如何重新放置球,使其不与盒子重叠。通过框到框的碰撞,就像将框彼此
是否可以为实体定义更准确的自定义形状碰撞框而不是矩形?请让我知道是否可行以及如何实现? 最佳答案 当然可以,并且有多种可能的解决方案: Box2D: 是一个很棒的物理引擎,可作为 ImpactJS 的
我正在开发一款 2D 游戏,其中包含高速射弹,这些射弹会撞击高速(并且可能旋转得非常快)的多边形目标对象。我一直在试验和寻找适合我的强大碰撞检测算法。 如果快速旋转不是一个因素(即 0 或慢速旋转),
我正在制作一款平台游戏,其中有玩家和一些 Prop 。为了检查碰撞,我使用了 matterjs collisionactive 函数: this.matter.world.on("collisiona
我已经习惯于使用矩形进行碰撞检测,现在我有点难住了。我正在处理类似菱形的形状,在过去的几个小时里,我一直在试图找出如何检查碰撞。 我尝试检查第一个对象的四个点是否在第二个对象的点内,但这只是一个框(我
最初,两个半径为 R1 和 R2 的非接触球体处于静止状态。 然后在时间 = 0 时分别给它们两个加速度 a1 和 a2。查明他们是否会接触。它们的初始位置分别表示为 (x1,y1,z1) 和 (x2
我目前正在学习使用 LWJGL 和 OpenGL 的 ThinMatrix 3d 游戏开发教程。我正在尝试在我的游戏中实现碰撞检测。我已经准备好检测 AABB 与 AABB 碰撞的代码,但似乎无法弄清
如何在 3D 空间中有效地找到无限线和 AABB 之间的最近点? 我有一个简单的解决方案,它涉及找到距离 AABB 的所有 12 个边缘最近的点,并选择最接近的对,这很有效,但性能不是很好。我的用例需
凹面多边形之间有什么好的检测算法吗?我将不胜感激,到目前为止,我只找到了用于检测凸多边形之间的算法。 最佳答案 您可能会发现this论文很有趣。 关于collision-detection - 凹多边
我在哪里可以阅读更多关于游戏或具有交互性的应用程序的基本 2d 碰撞检测的信息? 编辑:Canvas 游戏的 javascript 怎么样? 最佳答案 要知道的最重要的定理是分离轴定理 (SAT)。基
我最近遇到了 this video并想实现类似的东西。基本方法很明确:我必须对图像进行阈值处理并检查虚拟对象是否与剩余前景发生碰撞。 我不想自己实现物理,而是想使用像 Box2D 这样的引擎。但是,如
我似乎无法让海龟在相互接触时死亡。只有当他们在同一个补丁上时,我才能杀死他们。有这样的功能吗? to killturtles if contact? [die] end 最佳答案 没有,但假设你的乌龟
OBB具有位置(x,y),速度(x,y)和方向(Matrix)。给定定期更新,OBB必须彼此冲突,返回被认为成功的举动的一部分。 我已经看过GPWiki上的Polygon测试-http://gpwik
你究竟是如何实现碰撞检测的?所涉及的费用是多少?不同的平台(c/c++、java、cocoa/iphone、flash、directX)对计算碰撞有不同的优化。最后,是否有库可以为我做这件事,或者我可
我正在尝试在我的物理引擎中赋予形状圆度/曲率。 我知道在数学上定义曲线的各种方法;例如贝塞尔曲线、椭圆等。但是我不确定哪种方法最适合在我的物理引擎中使用,因为必须考虑每种方法的速度、构造可行性和灵 a
我希望您对这个问题提出建议... 为简单起见,我将只考虑 x 轴。 对位置 10 的对象进行成像,其宽度也是 10 个单位,每秒向前移动 100 个单位,并且由于低帧,每次更新时它应该移动 80 个单
我是一名优秀的程序员,十分优秀!