@@ -28,12 +28,12 @@ can start to be a performance bottleneck.
2828
2929Say we're making a real-time strategy game. Opposing armies with hundreds of
3030units 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
3232at 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
3737name="all">all</span > of the units on the battlefield. That means the number of
3838pairwise tests we have to perform each frame increases with the * square* of the
3939number 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²)*.
5454
5555The problem we're running into is that there's no underlying order to the array
5656of 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
5858it'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
6262In 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
6565search] ( http://en.wikipedia.org/wiki/Binary_search ) to find nearby units without
6666having to scan the entire array.
6767
6868<aside name =" array " >
6969
7070A binary search has * O(log n)* complexity, which means find all battling units
71- goes from * O(n² ; )* 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² ; )* 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
8585data structure lets you ** efficiently query for objects at or near a location** .
8686When 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,
128128though, many of these <span name =" variations " >variations</span > are
129129well-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
131131the 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
143143onto it like a sheet of graph paper. Instead of storing our units in a single
144144array, we put them in the cells of this grid. Each cell stores the list of units
145145whose 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
175175list] ( 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,35 +224,34 @@ after the new unit.
224224### A clash of swords
225225
226226Once 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
228228this:
229229
230230^code grid-melee
231231
232- It walks each cell and then calls ` handleCell() ` on it. As you can see, we
233- really have partitioned the battlefield into little isolated skirmishes. Each
232+ It walks each cell and then calls ` handleCell() ` on it. As you can see, we have partitioned the battlefield into little isolated skirmishes. Each
234233cell then handles its combat like so:
235234
236235^code handle-cell
237236
238237Aside from the pointer shenanigans to deal with walking a linked list, note that
239238this 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
239+ handling combat. It compares each pair of units to see if they're in the same
241240position.
242241
243242The 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.
243+ the battle to each other -- just the ones close enough to be in the same cell.
245244That's the heart of the optimization.
246245
247246<aside name =" nested " >
248247
249248From 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
249+ * worse* . We've gone from a doubly nested loop over the units to a
250+ * triply* nested loop over the cells and then the units. The trick here is that
252251the two inner loops are now over a smaller number of units, which is enough to
253252cancel out the cost of the outer loop over the cells.
254253
255- However, that does depend a bit on the granularity of our cells: make them too
254+ However, that does depend a bit on the granularity of our cells. Make them too
256255small and that outer loop can start to matter.
257256
258257</aside >
@@ -284,7 +283,7 @@ If the unit *has* left its current cell, we remove it from that cell's linked
284283list and then add it back to the grid. Like with adding a new unit, that will
285284insert the unit in the linked list for its new cell.
286285
287- This is why we're using a doubly- linked list: we can very quickly add and remove
286+ This is why we're using a doubly linked list -- we can very quickly add and remove
288287units from lists by setting a few pointers. With lots of units moving around
289288each frame, that can be important.
290289
@@ -337,17 +336,16 @@ The cell with the unit is `U`, and the neighboring cells it looks at are `X`.
337336</aside >
338337
339338We 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.
339+ starts * after* the current unit -- to avoid comparing each pair of units twice.
341340Consider what would happen if we did check all eight neighboring cells.
342341
343342Let'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:
343+ like the previous example. Here's what would happen if we looked at all eight cells surrounding each unit:
346344
347- 1 . When we are finding hits for A, we would look at its neighbor on the right
345+ 1 . When finding hits for A, we would look at its neighbor on the right
348346 and find B. So we'd register an attack between A and B.
349347
350- 2 . Then, when we are finding hits for B, we would look at its neighbor on the
348+ 2 . Then, when finding hits for B, we would look at its neighbor on the
351349 * left* and find A. So we'd register a * second* attack between A and B.
352350
353351Only looking at half of the neighboring cells fixes that. * Which* half we look
@@ -390,7 +388,7 @@ programmer.
390388 <aside name =" simpler " >
391389
392390 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
391+ reason. Whenever you can, take the simpler option. Much of software
394392 engineering is fighting against complexity.
395393
396394 </aside >
@@ -425,8 +423,8 @@ programmer.
425423
426424### Does the partitioning depend on the set of objects?
427425
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
426+ In our sample code, the grid spacing was fixed beforehand, and we slotted units
427+ into cells. Other partitioning schemes are adaptable -- they pick partition
430428boundaries based on the actual set of objects and where they are in the world.
431429
432430The goal is have a * balanced* partitioning where each region has roughly the
@@ -457,7 +455,7 @@ to solve.
457455
458456 </aside >
459457
460- * * The partitions can be imbalanced.* Of course the downside of this
458+ * * The partitions can be imbalanced.* Of course, the downside of this
461459 rigidity is that you have less control over your partitions being evenly
462460 distributed. If objects clump together, you get worse performance there
463461 while wasting memory in the empty areas.
@@ -529,14 +527,14 @@ to solve.
529527
530528 * * The partitions are balanced.* Since any given square will have less
531529 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
530+ clustered together, you don't have single partitions with a huge pile of
533531 objects in them.
534532
535533### Are objects only stored in the partition?
536534
537535You can treat your spatial partition as * the* place where the objects in your
538536game 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 .
537+ while also having another collection that directly holds the list of objects.
540538
541539 * ** If it is the only place objects are stored:**
542540
0 commit comments