为啥游戏空间只有,均匀半空间模式

  将对象根据它们的位置存储茬数据结构中来高效地定位对象。

  游戏让我们能拜访其他世界但这些世界通常和我们的世界没有太多不同。它们通常有和我们宇宙同样的基础物理和可理解性这就是我们为什么会认为这些由比特和像素构建的东西是真实的。

  我们这里注意的虚拟事实是位置遊戏世界有空间感,对象都在空间的某处它用很多种方式证明了这点。最明显的是物理——对象移动碰撞,交互——但是还有其他方式音频引擎也许会考虑声源和玩家的距离,越远的声音响声越小在线交流也许局限在较近的玩家之间。

  这意味着游戏引擎通常需偠回答这个问题“哪些对象在这个位置周围?”如果每帧都需要回答这个问题这就会变成性能瓶颈。

  假设我们在做实时战略游戏双方成百上千的单位在战场上撞在一起。战士需要挥舞刀锋向最近的那个敌人砍去最简单的处理方法是检查每对单位,然后看看它们互相之间的距离:

 
  这里使用的是双重循环每个循环都会遍历战场上的所有单位。这就是意味着每帧进行的检测对数会随着单位数量嘚平方增长每个附加单位都需要和之前所有单位的进行检查。如果有大量单位这就完全失控了。
 
  我们这里碰到的问题是没有指明數组中潜藏的对象顺序为了在某个位置附近找到单位,我们需要遍历整个数组现在,我们简化一下游戏不使用二维的战,想象这昰个一维的战线

  在这种情况下,我们可以通过根据单位在战线上的位置排序数组元素来简化问题一旦我们那样做,我们可以使用潒二分查找之类的东西找到最近的对象而不必扫描整个数组
  这里的经验很明显:如果我们根据位置存储对象在数据结构中,就可以哽快的找到它们这个模式便是将这个思路应用到多维空间上。
 
  对于一系列对象每个对象都有空间上的位置。将它们存储在根据位置组织对象的空间数据结构中让你有效查询在某处或者某处附近的对象。当对象的位置改变时更新空间数据结构,这样它可以继续找箌对象
 
  这是存储活跃的,移动的游戏对象的常用模式也可用于静态美术和世界地理。复杂的游戏中不同的内容有不同的空间分區。
  这个模式的基本要求是一系列有位置的对象而你做了太多的通过位置寻找对象的查询,导致性能下降
 
  空间分区的存在是為了将O(n)或者O(n?) 的操作降到更加可控的数量级。你拥有的对象越多这就越重要。相反的如果n足够小,也许不需要担心这个
  由于这個模式需要通过位置组织对象,可以改变位置的对象更难处理你需要重新组织数据结构来追踪在新位置的对象,这添加了更多的复杂性消耗CPU循环保证这种交易是值得的。
  空间分区也会因为记录划分的数据结构而使用额外的内存就像很多优化一样,它用内存换速喥如果内存比时钟周期更短缺,这回是个错误的选择
 
  模式总会变化——每种实现都略有不同,空间分区也不例外不像其他的模式,它的每种变化都很好地被记录下来了学术界发表文章证明各种变化各自的性能优势。由于我只关注模式背后的观念我会给你展示朂简单的空间分区:固定网格
 
  想象整个战场现在,叠加一张方格大小固定的网格在上面就好像一张网格纸。不是在单独的数组Φ存储我们的对象我们将它们存到网格的格子中。每个格子存储一组单位它们的位置在格子的边界内部。

  当我们处理战斗时我們只需考虑在同一格子中的单位。不是将每个游戏中的单位与其他所有单位比较我们将战场划分为多个小战场,每个格子中的单位都较尐
 
  好了,让我们编码把首先,一些准备工作这是我们的基础Unit类。
 
  每个单位都有位置(2D表示)以及一个指针指向它存在的Grid。我们让Grid成为一个friend类因为,就像将要看到的当单位的位置改变时,它需要和网格做复杂的交互以确保所有事情都正确的更新了。
  这里是网格的表示:
 
  注意每个格子都是一个指向单位的指针下面我们扩展Unit,增加nextprev指针:
  这让我们将对象组织为双向链表洏不是数组。

  每个网格中的指针都指向网格中的元素列表的第一个每个对象都有个指针指向它前面的对象,以及另一个指针指向它後面的对象我们很快会知道为什么要这么做。
 
  我们需要做的第一件事就是保证新单位创建时被放置到了网格中我们让Unit在它的构造函数中处理这个:
  add()方法像这样定义:
 
  除了链表带来的繁琐,基本思路是非常简单的我们找到单位所在的网格,然后将它添加到列表前部如果那已经存在有列表单位了,我们把新单位链接到旧单位的后面
 
  一旦所有的单位都放入网格中,我们可以让它们开始互相交互使用这个新网格,处理战斗的主要方法看上去是这样的:
 
  它在每个网格它上面遍历并调用handleCell()就像你看到的那样,我们真的巳经将战场分割为分离的小冲突每个网格之后像这样处理它的战斗:
 
  除了遍历链表的指针把戏,注意它和我们原先处理战斗的原始方法完全一样它对比每对单位,看看它们是否在同一位置
  不同之处是,我们不必再互相比较战场上所有的单位——只与那些近在┅个格子中的相比较这就是优化的核心。
 
  我们解决了性能问题但同时创建了新问题。单位现在陷在它的格子中如果将单位移出叻包含它的格子,格子中的单位就再也看不到它了但其他单位也看不到它。我们的战场有点过度划分了
  为了解决这点,需要每次單位移动时都做些工作如果它跨越了格子的边界,我们需要将它从原来的格子中删除添加到新的格子中。首先我们给Unit添加一个改变位置的方法:
  显而易见,AI代码可以调用它来控制电脑的单位玩家也可以输入代码调用它来控制玩家的单位。它做的只是交换格子的控制权之后:
 
  这块代码很长但也很直观。第一步检查我们是否穿越了格子的边界如果没有,需要做的事情就是更新单位的位置搞定。
  如果单位已经离开了现在的格子我们从格子的链表中移除它,然后再添加到网格中就像添加一个新单位,它会插入新格子嘚链表中
  这就是为什么我们使用双向链表——我们可以通过设置一些指针飞快地添加和删除单位。每帧都有很多单位移动时这就佷重要了。
 
  这看起来很简单但我们某种程度上作弊了。在我展示的例子中单位在它们有完全相同的位置时才进行交互。西洋棋和國际象棋中这是真的但是对于更加实际的游戏就不那么准确了。它们通常需要将攻击距离引入考虑
  这个模式仍然可以好好工作,與检查位置匹配不同我们这样做:
  当范围被牵扯进来,需要考虑一个边界情况:在不同网格的单位也许仍然足够接近可以相互交互。

  这里B在A的攻击半径内,即使中心点在不同的网格为了处理这种情况,我们不仅需要比较同一网格的单位同时需要比较邻近網格的对象。为了达到这点首先我们让内层循环摆脱handleCell()
 
  现在有函数接受一个单位和一列表的其他单位看看有没有碰撞。让handleCell()使这个函數:
 
  注意我们同样传入了网格的坐标而不仅仅是对象列表。现在这也许和前面的例子没有什么区别,但是我们会稍微扩展一下:
 
  这些新增handleCell()调用在八个邻近格子中的四个格子中寻找这个单位是否与它们有任何对抗如果任何邻近格子的单位离边缘近到单位的攻击半径内,就找到碰撞了
  我们只查询一半的近邻格子,这原因和之前是一样的:内层循环从当前单位之后的单位开始——避免每对单位比较两次考虑如果我们检查全部八个近邻格子会发生什么。
  假设我们有两个在邻近格子的单位近到可以互相攻击就像前一个例孓。这是我们检查全部8个格子会发生的事情:
  1. 当找谁打了A时我们检查它的右边找到了B。所以记录一次A和B之间的攻击
  2. 当找谁打了B时,我們检查它的左边找到了A所以记录第二次A和B之间的攻击。
 
  只检查一半的近邻格子修复了这点检查哪一半倒无关紧要。
  我们还需偠考虑另外的边界情况这里,我们假设最大攻击距离小于一个格子如果我们有较小的小格子和较长的攻击距离,我们也许需要扫描几荇外的近邻格子
 
  空间划分的优秀数据结构相对较少,可以一一列举进行介绍但是,我试图根据它们的本质特性来组织我期望当伱学习四叉树和二分空间查找(BSPs)之类的时,可以帮助你理解它们是如何工作为什么 工作,以帮你选择

划分是层次的还是平面的?

 
  我们的网格例子将空间划分成平面格子的集合相反,层次空间划分将空间分成几个区域然后,如果其中一个区域还包含多个对象洅划分它。这个过程递归进行直到每个区域都有少于最大数量的对象在其中。
    •   更简单平面数据结构更容易想到也更容易实现。

    •   内存使用量确定由于添加新对象不需要添加新划分,空间分区的内存使用量通常在之前就可以确定

    •   在对象改变位置时更新的更赽。当对象移动数据结构需要更新,找到它的新位置使用层次空间分区,可能需要在多层间调整层次结构

    •   能更有效率的处理空嘚区域。考虑之前的例子如果战场的一边是空的。我们需要分配一堆空白格子这些格子浪费内存,每帧还要遍历它们

        由于层次涳间分区不再分割空区域,大的空区域保存在单个划分上不需要遍历很多小空间,那里只有一个大的

    •   它处理密集空间更有效率。這是硬币的另一面:如果你有一堆对象堆在一起无层次的划分很没有效率。你最终将所有对象都划分到了一起就跟没有划分一样。层佽空间分区会自适应地划成小块让你同时只需考虑少数对象。

 

划分依赖于对象集合吗

 
  在示例代码中,网格空间大小事先被固定了我们在格子里追踪单位。另外的划分策略是自适应的——它们根据现有的对象集合在世界中的位置划分边界
  目标是均匀半空间地劃分,每个区域拥有相同的单位数量以获得最好性能。考虑网格的例子如果所有的单位都挤在战场的一个角落里。它们都会在同一格孓中找寻单位间攻击的代码退化为原来的O(n?) 问题。
  •   如果划分与对象无关:

    • 对象可以增量添加添加对象意味着找到正确的划分然后放入,这点可以一次性完成没有任何性能问题。
    •   对象移动的更快通过固定的划分,移动单位意味着从格子移除然后添加到另一个如果划分它们的边界跟着集合而改变,那么移动对象会引起边界移动导致很多其他对象也要移到其他划分。

    •   划分也许不均匀半空間当然,固定的缺点就是对划分缺少控制如果对象挤在一起,你就在空区域上浪费了内存这会造成更糟的性能。

  •   如果划分适应對象集合:

      像BSPs和k-d树这样的空间划分切分世界让每部分都包含接近相同数目的对象。为了做到这点划分边界时,你需要计算每边各囿多少对象层次包围盒是另外一种为特定集合对象优化的空间分区。

    •   你可以保证划分是平衡的这不仅提供了优良的性能表现,还提供了稳定的性能表现:如果每个区域的对象数量保持一致你可以保证游戏世界中的所有查询都会消耗同样的时间。一旦你需要固定帧率这种一致性也许比性能本身更重要。

    •   一次性划分一组对象更加有效率当对象集合影响了边界的位置,最好在划分前都出现所有對象这就是为什么美术和地理更多的使用这种划分。

  •   如果划分与对象无关但层次与对象相关:

      有一种空间分区需要特殊注意,因为它拥有固定分区和适应分区两者的优点:四叉树

      四叉树开始时将整个空间视为单一的划分。如果空间中对象数目超过了临界徝它将其切为四小块。这些块的边界是确定的:它们总是将空间一切为二

      然后,对于四个区域中的每一个我们递归地做相同的倳情,直到每个区域都有较少数目的对象在其中由于我们递归地分割有较多对象的区域,这种划分适应了对象集合但是划分本身没有迻动

      你可以从这里从左向右看到分区的过程:

    •   对象可以增量增加添加新对象意味着找到并添加到正确的区域。如果区域中的對象数目超过了最大限度就划分区域。区域中的其他对象也划分到新的小区域中这需要一些小小的工作,但是工作总量是固定的:你需要移动的对象数目总是少于数目临界值添加对象从来不会引发超过一次划分。

        删除对象也同样简单你从它的格子中移除对象,洳果它的父格子中的计数少于临界值你可以合并这些子分区。

    •   移动对象很快当然,如上所述“移动”对象只是添加和移除,两鍺在四叉树中都很快

    •   分区是平衡的。由于任何给定的区域的对象数目都少于最大的对象数量哪怕对象都堆在一起,你也不会有包含太多对象的分区

 

对象只存储在分区中吗?

 
  你可将空间分区作为在游戏中对象存储的唯一地方或者将其作为更快查找的二级缓存,使用另一个集合包含对象
  •   如果它是对象唯一存储的地方:

    • 这避免了内存开销和两个集合带来的复杂度。当然存储对象一遍总比存两遍来的轻松。同样如果你有两个集合,你需要保证它们同步每当添加或删除对象,都得从两者中添加或删除对象
  •   如果其他集合保存对象:

    •   遍历所有的对象更快。如果所有对象都是“活的”而且它们需要做些处理,也许会发现你需要频繁拜访每个对象而並不在乎它的位置想想看,早先的例子中大多数格子都是空的。访问那些空的格子是对时间的浪费

        存储对象的第二集合给了你矗接遍历对象的方法。你有两个数据结构每种为各种的用况优化。

 
 
  •   在这里我试图不讨论特定的空间分区结构细节来保证这章的高層概况性(而且节约篇幅!),但你的下一步应该是学习一下常见的结构尽管名字很恐怖,它们都令人惊讶的直观常见的有:

 
 

      每種空间分区数据结构基本上都是将一维数据结构扩展成更高维度的数据结构。知道它的相互关系有助于分辨它是不是问题的好解答:

     
     
  • 四叉樹和八叉树是多叉树
  •  
     
     
     
          
}

  “将对象存储在根据位置组織的数据结构中来高效的定位它们”

  游戏使我们能够探寻其它世界,但这些世界和我们的世界往往并无太大差异其中的基本物理規则和确切性常常与我们的世界互通。这也正是这些由像素构成的世界看上去如此真实的原因

  我们在者虚拟现实中将要关注的一点僦是位置。游戏世界具有空间感对象则分布于空间中,这一点从多方面展现出了游戏世界:一个明显的粒子就是物理——对象的移动、碰撞和相互影响但也有其它例子。比如音频引擎会考虑声源和角色的相对位置因而更远的声音要相对安静点。在线聊天可能被限制在附近的玩家之间

  这意味者你的游戏引擎通常要解决这个问题:“对象的附近由什么物体?”如果在每一帧中它不得不对此进行反複检测的话,那么它很容易成为性能的瓶颈

  设想你在开发一个即时战略游戏。对立的阵营的上百个单位将在战场中厮杀勇士们需偠知道攻击那个最近的哪个敌人,最简单的方式就是查看每一对敌人的距离的远近:

  这里我们使用了一个双重循环也就是算法的复雜度为O(n^2),其中n表示战场单位数量这对于我们来说并不是一个很好的处理方式,一旦战场中单位的数量增加这个地方很容易成为性能的瓶颈。

  我们所处的困境在于单位数组无秩序可循为了找到某个位置附近的单位,我们需要遍历全部数组现在我们把它简化一下,峩们将战场想象成一个一维的战场线而不是二维的战场。如图:

在着中国情况下我们将战场上单位按其位置排序,那么我们想找某个位置最近的单位时就可以使用二分查找法这样复杂度就变成了O(lgn),这样就不要遍历整个数组了鉴于此,我们可以把这种想法应用到1维以仩的空间

  对于一组对象而言,每一个对象在空间中都有一个位置将对象存储在一个根据对象的位置来组织的数据结构中,该数据結构可以让你高效的查询位于或靠近某处的对象当对象位置变化时,应该更新空间数据结构以便可以继续这样查找对象

  这是一个鼡来存储活跃的、移动的游戏对象以及静态图像和游戏世界的几何形状等对等的常见模式。复杂的游戏常常有多个空间分区来应对不同类型的存储内容

  该模式的基本要求是你有一组对象,每个对象都具备某种位置信息而你因为要根据位置做大量的查询来查找对象从洏遇到了性能问题。

  空间分区将O(n)或者O(n^2) 复杂度的操作拆解为更易于管理的结构对象越多,模式的价值就越大相反,如果你的对象并鈈多则可能不值得使用这个模式。

  由于该模式要根据对象的位置来组织对象故对象的位置改变就变得难以处理了。你必须重新组織数据结构来跟踪物体的新位置这会增加代码的复杂性和额外的CPU周期开销。你必须确保这么做是值得的

  空间分区会使用额外的内存来保存数据结构。就像许多的优化一样它是以空间换取速度的。如果你的内存比时钟周期更吃紧的话这可能就得不偿失了。

  模式的本质在于它们的变化性——每一个实现都有所不同当然本模式也不例外。虽然它不像其它的模式那样为各种变化都配备了丰富的文檔学术界喜欢发表论文来证明模式在性能上的提升空间。因为我们只关心模式背后的概念所以我只提供最简单的一个空间分区:一个凅定的网格。

  我们把战场的整个区域设想为一个矩形然后把这个矩形等分为m*n块小的矩形区域,就像一张方格纸其中网格中的每一個单元取代一维数组来存储对象。如图:

  我们在处理战斗的时候只考虑同一个单元格内的单位。我们不会将每个单位与游戏中其它單位一一比较取而代之的是,我们已经将战场划分为一堆更小的战场每一个战场里的单位要少很多。

  首先我们做些准备工作下媔是Unit类:

  每一个单元都有一个位置和一个指向其所处的Grid指针。我们将Grid作为友元类就像我们看到的,当一个单位的位置发生改变时峩们不得不对网格进行处理确保一切都正常的更新。

  下面是Grid类的大体实现:

  注意到每一个单元格都有一个指向grid的指针下面我们將用next和prev的指针来扩展Unit:

  这样我们就能够用一个双重链表来组织Unity以取代数组。网格中的每个单元格都会指向单元格之内的Units列表的第一个Unit

  我们需要做的第一件事就是确保单位被创建时就被置入网格中。我们需要在Unit类的构造函数中处理:

  add方法实现如下:

  代码很簡单就是一个双向链表添加节点的逻辑,每次把新的节点添加到链表的前面

   当所有单位都被置入网格中后,我们便让它们相互攻擊在Grid类中,处理战斗函数如下:

  上面的方法遍历了每一个单元格并且注意调用其handleCell()方法。正如你所见我们确实已经将大战场切分荿了一些孤立的小规模冲突。每个单元格处理战斗的函数如下;

  只是一个简单的链表遍历至此,我们不需要遍历所有的代码只要遍历单元格内的对象即可。

  我们已经解决了在同一个单元格内战斗的问题但却有另一个问题:单位现在都呆在单元格里面,如果单位移动到了其它的单元格中该如何处理这个时候我们需要额外的一些处理工作:把这个单位从原先的单元格中移出并加入到新的单元格Φ。

  首先我们在Unit类中添加一个方法来改变它的位置:

  这段代码很简单,它把移动的控制权交给了Grid类Grid类中的移动代码如下:

  很简单的逻辑,首先判断是否移动到了新的单元格如果是则在原先的单元格中删除它,同时加入到新的单元格中否则不做任何处理。这里就体现了我们使用双向列表来组织对象的价值——非常方便的添加和移除对象

   这个似乎看起来简单,但我们在某些地方作弊叻在例子中,当单位出现在完全相同的位置时才会相互作用这对于跳棋和国际象棋时没问题的,但是对于更逼真的游戏来说就不适用叻那些游戏通常要考虑攻击距离。对于这个问题我们可以简单的修改一下代码让模式仍然工作良好。

  当进入攻击范围时我们需偠考虑到一种边界情况:不同单元格中的单位也可以足够靠近从而相互作用。比如单元格共享边附近的单位鉴于此,我们不仅要比较单え格内的对象也要考虑相邻单元格中的单位。我们可以如下做:

  我们把原先的循环拆开在handlecell中处理单元格内的战斗和相邻的4个单元格之间的战斗。在handleUnit中我们只查看了一半的相邻的单元格,这么做的原因时避免重复战斗设想一下,每个单位都查看自己左右的单位那么就会出现这样的情况:A查看右侧发现B并与之战斗,而B查看左侧也会发现A从而有发生一次战斗很明显这两次战斗是重复的,只检查一側则可以避免这个问题

  这里要注意的是当前我们假定攻击范围小于一个单元格,所以只需要遍历相邻的单元格如果攻击范围很大,那么我们可能需要横跨好几行(列)去检查

   关于明确定义的空间分区的数据结构可以列个简表,这里本可逐一探讨但我试图根據它们的本质特征来组织。我希望一旦你接触到四叉树和二叉空间分割(BSP)之类时这将有助于你了解它们的工作过程和原理,并在它们の间择优使用

  在网格例子中,我们将网格分成了一个单一扁平的单元格集合于此相反,层级空间分区是将空间分成几个区域然後,如果这些区域中仍然包含着许多对象就会继续划分。这个递归的过程持续到每个区域的对象数目都少于某个约定的最大对象数量为圵

  •   如果它是扁平的,优点有:
    •   相对简单扁平的数据结构相对来说更易于推理和实现。
    •   内存使用量恒定由于添加新对象鈈需要创建新的分区,所以空间分区使用的内存通常可以提前确定
    •   当对象改变位置时可以更为快速的更新。当一个对象移动时数據结构需要更新以便在新的位置找到对象。使用层级空间分区这可能意味着调整层次结构中的若干层。
  •   如果它是一个层级的分区
    •   它可以更有效地处理空白的空间想象一下,在我们前面的例子中如果战场的一侧是空白的,那么就会产生大量空白的单元格而我們不得不在每帧为它们分配内存并遍历它们。因为层级空间不会细分稀疏区域所以一个大的空白空间仍然是一个单独的分区,而不是大量细小的分区
    •   它在处理稠密区域时更为有效。这是硬币的另一面:如果你有一堆对象成群则在一起非层级分区时低效的。你最终會有一个包含着许多对象的可能根本没有分区的分割。层级分区将会自适应地将其细分成更小地分区使得你一次只需考虑少数几个对潒。

  在我们地示例中网格地间距时预先固定的,并且我们将单位放置进了单元格中其它的分区方案是自适应的,它们根据实际的對象集合及其在世界中的位置来选择分区的边界

  我们的目标是实现一个均衡的分区,每一个分区都有着大致相同的对象个数以获得朂佳的性能以我们的网格为例考虑下,如果所有单位都集中在战场的一个角落那么它们将会处在同一个单元格内,找寻单位间攻击的玳码又会回到原来我们要试图解决的O(n^2)问题这一远点

  •   如果分区依赖于对象
    •   对象可以被逐步地添加。添加一个对象意味着要找到正確地分区并且将对象放置进去所以你可以在不影响性能地情况下一次性完成这个动作。
    •   对象可以快速地移动对于固定分区,移动┅个单位意味着将一个单位从一个分区移出并添加到另一个单元格中如果分区边界本身基于对象集合来改变,那么移动对象会引起边界嘚移动从而可能要将大量的其它对象移动到其它分区。
    •   分区可以不平衡当然,这么做的硬伤在于你对最终呈现的非均匀半空间分區的掌控力会很薄弱如果对象拥挤到一起,那么你会因为在空白区域浪费了内存而令其性能变得很糟糕
  •   如果分区自适应于对象集匼

  像二叉空间分割(BSPs)和k-d树(k-d trees)这样的空间分区方式会递归的将世界分割开来,以使得每部分包含着数目几乎相同的对象要左到这點,在选取要进行分区的层级前你必须计算每个阵营包含的对象数目。边界体积层次结构(Bounding volume hierarchies)是空间分区中的另外一种类型用于优化世堺中的特定对象集合。

  •   你可以确保分区间的平衡着不仅仅带来优秀的性能表现,而且会是持续稳定的表现:如果每个分区有着相同數量的对象你便可以确保对世界中的任意分区的查询时间开销均等。当需要维持稳定的帧速率时这种稳定性比原始性能更为重要。
  •   当整个对象集合进行一次性的分区时更为高效当对象集合影响到边界时,最好在分区之前对所有对象进行审视这就是为什么这种类型的分区越来越多地适用于游戏过程中保持不变的事物诸如美术和静态几何。
  •   如果分区不依赖于对象而层级却依赖于对象。有一个涳间分区特别值得一提因为它同时具备了固定分区和自适应性分区地优良性质:四叉树(quadtrees)。

  四叉树从将整个空间作为一个单一地分區开始如果空间中对象地数目超过了某一个阈值,则空间被切分成四个较小地正方形这些正方形地边界是固定地:它们总是将空间对半切分。然后对于四个正方形中地每一个而言我们重复同样地过程。递归下去知道每一个正方形内部只有少量对象由于我们只是递归哋将高密度对象区域分开,因此这个分区会自适应于对象集合但分区是不会移动的。如下图从左往右阅读,你可以看到分区的过程:

  •    可以逐步的增加对象添加一个新对象意味着要寻找何时的区域并且放置进去。如果对象放入区域时超过了最大数量那么该区域会被继续细分。在区域中的其它对象也会被细分到更细小的区域中去这需要一些工作,但工作量时固定的:你需要移动的对象的数量始终偠比最大的对象数少添加单个对象永远也不会触发一次以上的拆分动作。删除对象同样简单你将对象从它所在区域中移出,如果它的父区域的对象总数低于了一个值那么你就可以合并这些细分的区域。
  •   对象可以快速地移动这个当然,和上面一样“移动”一个對象只是一次添加和一次删除,两者在四叉树模式下速度很快
  •   分区是均衡地。由于任何给定地区域中地对象数目都比最大对象数要尛因此即使对象聚集在一起,也不会存在容纳着大量对象地单一分区

  你可以将分区看作游戏中对象存货地地方,或者你可以只将咜看作是二级缓存相比直接持有对象列表地集合而言,查询能够更快速

  •   如果他是对象唯一存储的地方

  这避免了两个集合的内存开销和复杂性。当然将东西存成一份比两份的代价要小。另外如果你有两个集合,你必须确保两个集合间的同步每次当一个对象被创建或被删除时,将不得不从两者中对其进行添加或者删除

  •   如果存在存储对象的另外一个集合

  遍历所有的对象会更为快速。洳果问题中对象是“存货”的并且它们需要做一些处理则你可能会发向自己要频繁地访问每一个对象,无论对象地位置在哪试想一下,在我们前面地例子中大部分单元格都是空的。遍历网格中所有单元格来找到那些非空单元格都是在浪费时间第二个仅用于存储对象哋集合令你可以直接对全部对象进行遍历。你有两个数据结构其中一个针对每个用例进行了优化。

}

1.画出css两种盒子模型


  
 

 
 
window.onload必须等到页面內包括图片的所有元素和其他外部资源加载完毕后才能执行;
$(document).ready()是DOM结构绘制完毕后就执行不必等到加载完毕。这个时候可能图片还未加载唍成要解决可以使用jquery中的load( )方法
 

 

 

 

 

7.浏覽器兼容性问题以及解决方法

 

 
 
 
 

 

11.写出表达式执行结果

 





















 


























 





 






 






 

最噺版本的谷歌里不支持这个属性了需要通过css3的transform来解决:




只能缩放可以定义宽高的元素,而span是行内元素
设置后宽高也会比例缩放,对布局会产生影响可以使用translate方法改变位置

17.针对移动端1px问题怎么解决

 
出现原因:不同的手机有不同的像素密度导致的。
devicePixelRatio属性:返回当前显示设備的物理像素分辨率与 CSS 像素分辨率的比率









(2)使用box-shadow模拟边框:利用css对阴影处理的方式实现0.5px的效果



优点:代码量少可以满足所有场景
缺点:边框有阴影,颜色变浅

















优点:所有场景都能满足支持圆角(伪元素和本体都需要加border-radius)
缺点:对于已经使用伪元素的元素(例如clearfix),可能需要多层嵌套

18.跳转路由如何传递一个对象过去

 

 

 

 

 

23.简述vue父子通信的方式

 

 

 

26.webpack有个插件可以解决css兼容性问题你知道吗


}

我要回帖

更多关于 均匀半空间 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信