Skip to content

Commit e84c36a

Browse files
committed
Merge branch 'laurenbee-master'
2 parents 8a8cade + b004611 commit e84c36a

File tree

2 files changed

+60
-62
lines changed

2 files changed

+60
-62
lines changed

book/spatial-partition.markdown

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ can start to be a performance bottleneck.
2828

2929
Say we're making a real-time strategy game. Opposing armies with hundreds of
3030
units will clash together on the field of battle. Warriors need to know which
31-
nearby enemy to swing their blade at. The naïve way to handle this is by looking
31+
nearby enemy to swing their blades at. The naïve way to handle this is by looking
3232
at every pair of units and seeing how close they are to each other:
3333

3434
^code pairwise
3535

36-
Here we have a doubly-nested loop where each loop is walking <span
36+
Here we have a doubly nested loop where each loop is walking <span
3737
name="all">all</span> of the units on the battlefield. That means the number of
3838
pairwise tests we have to perform each frame increases with the *square* of the
3939
number of units. Each additional unit we add has to be compared to *all* of the
@@ -54,22 +54,22 @@ In Big-O terms, though, this is still *O(n&sup2;)*.
5454

5555
The problem we're running into is that there's no underlying order to the array
5656
of units. To find a unit near some location, we have to walk the entire array.
57-
Now imagine we simplify our game a bit. Instead of a 2D battle*field*, imagine
57+
Now, imagine we simplify our game a bit. Instead of a 2D battle*field*, imagine
5858
it's a 1D battle*line*.
5959

6060
<img src="images/spatial-partition-battle-line.png" alt="A number line with Units positioned at different coordinates on it." />
6161

6262
In that case, we could make things easier on ourselves by *sorting* the array of
63-
units by their position on the battleline. Once we do that, we can use something
63+
units by their positions on the battleline. Once we do that, we can use something
6464
<span name="array">like</span> a [binary
6565
search](http://en.wikipedia.org/wiki/Binary_search) to find nearby units without
6666
having to scan the entire array.
6767

6868
<aside name="array">
6969

7070
A binary search has *O(log n)* complexity, which means find all battling units
71-
goes from *O(n&sup2;)* to *O(n log n)*. Something like a [*pigeonhole
72-
sort*](http://en.wikipedia.org/wiki/Pigeonhole_sort) could get that down to
71+
goes from *O(n&sup2;)* to *O(n log n)*. Something like a [pigeonhole
72+
sort](http://en.wikipedia.org/wiki/Pigeonhole_sort) could get that down to
7373
*O(n)*.
7474

7575
</aside>
@@ -84,7 +84,7 @@ For a set of **objects**, each has a **position in space**. Store them in a
8484
**spatial data structure** that organizes the objects by their positions. This
8585
data structure lets you **efficiently query for objects at or near a location**.
8686
When an object's position changes, **update the spatial data structure** so that
87-
it can continue to find it.
87+
it can continue to find the object.
8888

8989
## When to Use It
9090

@@ -123,11 +123,11 @@ proposition.
123123

124124
## Sample Code
125125

126-
The nature of patterns is that they *vary*: each implementation will be a bit
127-
different and spatial partitions are no exception. Unlike other patterns,
126+
The nature of patterns is that they *vary* -- each implementation will be a bit
127+
different, and spatial partitions are no exception. Unlike other patterns,
128128
though, many of these <span name="variations">variations</span> are
129129
well-documented. Academia likes publishing papers that prove performance gains.
130-
Since I just care about the concept behind the pattern, I'm going to show you
130+
Since I only care about the concept behind the pattern, I'm going to show you
131131
the simplest spatial partition: a *fixed grid*.
132132

133133
<aside name="variations">
@@ -139,7 +139,7 @@ spatial partitions used in games.
139139

140140
### A sheet of graph paper
141141

142-
Imagine the entire field of battle. Now superimpose a grid of fixed-size squares
142+
Imagine the entire field of battle. Now, superimpose a grid of fixed-size squares
143143
onto it like a sheet of graph paper. Instead of storing our units in a single
144144
array, we put them in the cells of this grid. Each cell stores the list of units
145145
whose positions are within that cell's boundary.
@@ -153,7 +153,7 @@ units.
153153

154154
### A grid of linked units
155155

156-
OK, let's get coding. First, some prep work. Here's our basic unit class:
156+
OK, let's get coding. First, some prep work. Here's our basic `Unit` class:
157157

158158
^code unit-simple
159159

@@ -171,7 +171,7 @@ we'll extend `Unit` with `next` and `prev` pointers:
171171

172172
^code unit-linked
173173

174-
This lets us organize units into a [doubly-linked
174+
This lets us organize units into a [doubly linked
175175
list](http://en.wikipedia.org/wiki/Doubly_linked_list) instead of an array.
176176

177177
<img src="images/spatial-partition-linked-list.png" alt="A Cell pointing to a a doubly linked list of Units." />
@@ -224,7 +224,7 @@ after the new unit.
224224
### A clash of swords
225225

226226
Once all of the units are nestled in their cells, we can let them start hacking
227-
at each other. With this new grid, the main method for handling combat look like
227+
at each other. With this new grid, the main method for handling combat looks like
228228
this:
229229

230230
^code grid-melee
@@ -237,22 +237,22 @@ cell then handles its combat like so:
237237

238238
Aside from the pointer shenanigans to deal with walking a linked list, note that
239239
this is exactly <span name="nested">like</span> our original naïve method for
240-
handling combat: it compares each pair of units to see if they're in the same
240+
handling combat. It compares each pair of units to see if they're in the same
241241
position.
242242

243243
The only difference is that we no longer have to compare *all* of the units in
244-
the battle to each other, just the ones close enough to be in the same cell.
244+
the battle to each other -- just the ones close enough to be in the same cell.
245245
That's the heart of the optimization.
246246

247247
<aside name="nested">
248248

249249
From a simple analysis, it looks like we've actually made the performance
250-
*worse*. We've gone from a doubly-nested loop over the units to a
251-
*triply*-nested loop over the cells and then the units. The trick here is that
250+
*worse*. We've gone from a doubly nested loop over the units to a
251+
*triply* nested loop over the cells and then the units. The trick here is that
252252
the two inner loops are now over a smaller number of units, which is enough to
253253
cancel out the cost of the outer loop over the cells.
254254

255-
However, that does depend a bit on the granularity of our cells: make them too
255+
However, that does depend a bit on the granularity of our cells. Make them too
256256
small and that outer loop can start to matter.
257257

258258
</aside>
@@ -284,7 +284,7 @@ If the unit *has* left its current cell, we remove it from that cell's linked
284284
list and then add it back to the grid. Like with adding a new unit, that will
285285
insert the unit in the linked list for its new cell.
286286

287-
This is why we're using a doubly-linked list: we can very quickly add and remove
287+
This is why we're using a doubly linked list -- we can very quickly add and remove
288288
units from lists by setting a few pointers. With lots of units moving around
289289
each frame, that can be important.
290290

@@ -337,17 +337,16 @@ The cell with the unit is `U`, and the neighboring cells it looks at are `X`.
337337
</aside>
338338

339339
We only look at *half* of the neighbors for the same reason that the inner loop
340-
starts *after* the current unit: to avoid comparing each pair of units twice.
340+
starts *after* the current unit -- to avoid comparing each pair of units twice.
341341
Consider what would happen if we did check all eight neighboring cells.
342342

343343
Let's say we have two units in adjacent cells close enough to hit each other,
344-
like the previous example. If, for each unit, we looked at all eight cells
345-
surrounding it, here's what would happen:
344+
like the previous example. Here's what would happen if we looked at all eight cells surrounding each unit:
346345

347-
1. When we are finding hits for A, we would look at its neighbor on the right
346+
1. When finding hits for A, we would look at its neighbor on the right
348347
and find B. So we'd register an attack between A and B.
349348

350-
2. Then, when we are finding hits for B, we would look at its neighbor on the
349+
2. Then, when finding hits for B, we would look at its neighbor on the
351350
*left* and find A. So we'd register a *second* attack between A and B.
352351

353352
Only looking at half of the neighboring cells fixes that. *Which* half we look
@@ -390,7 +389,7 @@ programmer.
390389
<aside name="simpler">
391390

392391
This is a design point I mention in almost every chapter, and for good
393-
reason: whenever you can, take the simpler option. Much of software
392+
reason. Whenever you can, take the simpler option. Much of software
394393
engineering is fighting against complexity.
395394

396395
</aside>
@@ -425,8 +424,8 @@ programmer.
425424

426425
### Does the partitioning depend on the set of objects?
427426

428-
In our sample code, the grid spacing was fixed beforehand and we slotted units
429-
into cells. Other partitioning schemes are adaptable: they pick partition
427+
In our sample code, the grid spacing was fixed beforehand, and we slotted units
428+
into cells. Other partitioning schemes are adaptable -- they pick partition
430429
boundaries based on the actual set of objects and where they are in the world.
431430

432431
The goal is have a *balanced* partitioning where each region has roughly the
@@ -457,7 +456,7 @@ to solve.
457456

458457
</aside>
459458

460-
* *The partitions can be imbalanced.* Of course the downside of this
459+
* *The partitions can be imbalanced.* Of course, the downside of this
461460
rigidity is that you have less control over your partitions being evenly
462461
distributed. If objects clump together, you get worse performance there
463462
while wasting memory in the empty areas.
@@ -529,14 +528,14 @@ to solve.
529528

530529
* *The partitions are balanced.* Since any given square will have less
531530
than some fixed maximum number of objects, even when objects are
532-
clustered together you don't have single partitions with a huge pile of
531+
clustered together, you don't have single partitions with a huge pile of
533532
objects in them.
534533

535534
### Are objects only stored in the partition?
536535

537536
You can treat your spatial partition as *the* place where the objects in your
538537
game live, or you can consider it just a secondary cache to make look-up faster
539-
while also having another collection that holds the list of objects directly.
538+
while also having another collection that directly holds the list of objects.
540539

541540
* **If it is the only place objects are stored:**
542541

0 commit comments

Comments
 (0)