Skip to content

Commit 4ebb6bc

Browse files
committed
Added spatial partition pattern
1 parent 0411932 commit 4ebb6bc

File tree

9 files changed

+473
-128
lines changed

9 files changed

+473
-128
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
%YAML 1.1
2+
%TAG !u! tag:unity3d.com,2011:
3+
--- !u!21 &2100000
4+
Material:
5+
serializedVersion: 6
6+
m_ObjectHideFlags: 0
7+
m_CorrespondingSourceObject: {fileID: 0}
8+
m_PrefabInstance: {fileID: 0}
9+
m_PrefabAsset: {fileID: 0}
10+
m_Name: Ground
11+
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
12+
m_ShaderKeywords:
13+
m_LightmapFlags: 4
14+
m_EnableInstancingVariants: 0
15+
m_DoubleSidedGI: 0
16+
m_CustomRenderQueue: -1
17+
stringTagMap: {}
18+
disabledShaderPasses: []
19+
m_SavedProperties:
20+
serializedVersion: 3
21+
m_TexEnvs:
22+
- _BumpMap:
23+
m_Texture: {fileID: 0}
24+
m_Scale: {x: 1, y: 1}
25+
m_Offset: {x: 0, y: 0}
26+
- _DetailAlbedoMap:
27+
m_Texture: {fileID: 0}
28+
m_Scale: {x: 1, y: 1}
29+
m_Offset: {x: 0, y: 0}
30+
- _DetailMask:
31+
m_Texture: {fileID: 0}
32+
m_Scale: {x: 1, y: 1}
33+
m_Offset: {x: 0, y: 0}
34+
- _DetailNormalMap:
35+
m_Texture: {fileID: 0}
36+
m_Scale: {x: 1, y: 1}
37+
m_Offset: {x: 0, y: 0}
38+
- _EmissionMap:
39+
m_Texture: {fileID: 0}
40+
m_Scale: {x: 1, y: 1}
41+
m_Offset: {x: 0, y: 0}
42+
- _MainTex:
43+
m_Texture: {fileID: 0}
44+
m_Scale: {x: 1, y: 1}
45+
m_Offset: {x: 0, y: 0}
46+
- _MetallicGlossMap:
47+
m_Texture: {fileID: 0}
48+
m_Scale: {x: 1, y: 1}
49+
m_Offset: {x: 0, y: 0}
50+
- _OcclusionMap:
51+
m_Texture: {fileID: 0}
52+
m_Scale: {x: 1, y: 1}
53+
m_Offset: {x: 0, y: 0}
54+
- _ParallaxMap:
55+
m_Texture: {fileID: 0}
56+
m_Scale: {x: 1, y: 1}
57+
m_Offset: {x: 0, y: 0}
58+
m_Floats:
59+
- _BumpScale: 1
60+
- _Cutoff: 0.5
61+
- _DetailNormalMapScale: 1
62+
- _DstBlend: 0
63+
- _GlossMapScale: 1
64+
- _Glossiness: 0
65+
- _GlossyReflections: 1
66+
- _Metallic: 0
67+
- _Mode: 0
68+
- _OcclusionStrength: 1
69+
- _Parallax: 0.02
70+
- _SmoothnessTextureChannel: 0
71+
- _SpecularHighlights: 1
72+
- _SrcBlend: 1
73+
- _UVSec: 0
74+
- _ZWrite: 1
75+
m_Colors:
76+
- _Color: {r: 0.5283019, g: 0.5283019, b: 0.5283019, a: 1}
77+
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}

Assets/Patterns/19. Spatial Partition/Grid/Ground.mat.meta

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Patterns/19. Spatial Partition/Grid/Scripts/GameController.cs

+52
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,70 @@
55
namespace SpatialPartition.Grid
66
{
77
//Implementation of the Spatial Partion pattern from the book Game Programming Patterns
8+
//The units change color if the can fight each other, and that color remains for some time to see that its working
89
public class GameController : MonoBehaviour
910
{
11+
//Drags
12+
public GameObject battlefieldObj;
13+
14+
public Unit unitPrefab;
15+
16+
public Transform unitParentTrans;
17+
18+
//Private
19+
private Grid grid;
20+
21+
//The number of units we start with
22+
private const int NUMBER_OF_UNITS = 100;
23+
24+
//To keep track of all units so we can move them
25+
private HashSet<Unit> allUnits = new HashSet<Unit>();
26+
27+
1028

1129
void Start()
1230
{
31+
grid = new Grid();
32+
33+
34+
//Make the battlefield the same size as the grid
35+
float battlefieldWidth = Grid.NUM_CELLS * Grid.CELL_SIZE;
1336

37+
battlefieldObj.transform.localScale = new Vector3(battlefieldWidth, 1f, battlefieldWidth);
38+
39+
//The grid starts at origo, so we need to change position as well
40+
battlefieldObj.transform.position = new Vector3(battlefieldWidth * 0.5f, 0f, battlefieldWidth * 0.5f);
41+
42+
43+
//Add units within the grid at random positions
44+
for (int i = 0; i < NUMBER_OF_UNITS; i++)
45+
{
46+
float randomX = Random.Range(0f, battlefieldWidth);
47+
float randomZ = Random.Range(0f, battlefieldWidth);
48+
49+
Vector3 randomPos = new Vector3(randomX, 0f, randomZ);
50+
51+
Unit newUnit = Instantiate(unitPrefab, parent: unitParentTrans) as Unit;
52+
53+
//Init the unit which will also add it to the grid
54+
newUnit.InitUnit(grid, randomPos);
55+
56+
allUnits.Add(newUnit);
57+
}
1458
}
1559

1660

61+
1762
void Update()
1863
{
64+
//Move all units
65+
foreach (Unit unit in allUnits)
66+
{
67+
unit.Move(Time.deltaTime);
68+
}
1969

70+
//Units attack each other
71+
grid.HandleMelee();
2072
}
2173
}
2274
}

Assets/Patterns/19. Spatial Partition/Grid/Scripts/Grid.cs

+55-28
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ public class Grid
99
{
1010
public const int NUM_CELLS = 10;
1111

12-
public const int CELL_SIZE = 20;
12+
public const int CELL_SIZE = 5;
1313

1414
private Unit[,] cells = new Unit[NUM_CELLS, NUM_CELLS];
1515

16-
//If two units are within this distance they can attack each other
17-
private const float ATTACK_DISTANCE = 0.5f;
16+
//How many units do we have on the grid, which should be faster than to iterate through all cells and count them
17+
public int unitCount { get; private set; }
1818

1919

2020

@@ -34,7 +34,7 @@ public Grid()
3434

3535
//Add unit to grid
3636
//This is also used when a unit already on the grid is moving into a new cell
37-
public void Add(Unit newUnit)
37+
public void Add(Unit newUnit, bool isNewUnit = false)
3838
{
3939
//Determine which grid cell it's in
4040
Vector2Int cellPos = ConvertFromWorldToCell(newUnit.transform.position);
@@ -53,23 +53,25 @@ public void Add(Unit newUnit)
5353

5454
nextUnit.prev = newUnit;
5555
}
56+
57+
if (isNewUnit)
58+
{
59+
unitCount += 1;
60+
}
5661
}
5762

5863

5964

60-
//Move a unit on the grid
61-
public void Move(Unit unit, Vector3 newPos)
65+
//Move a unit on the grid = see if it has changed cell
66+
//Make sure newPos is a valid position inside of the grid
67+
public void Move(Unit unit, Vector3 oldPos, Vector3 newPos)
6268
{
63-
//See what cell it was in
64-
Vector2Int oldCellPos = ConvertFromWorldToCell(unit.transform.position);
69+
//See what cell it was in before we assign the new position
70+
Vector2Int oldCellPos = ConvertFromWorldToCell(oldPos);
6571

6672
//See which cell it's moving to
6773
Vector2Int newCellPos = ConvertFromWorldToCell(newPos);
6874

69-
//TODO: Validate if this is a valid cell pos
70-
71-
unit.transform.position = newPos;
72-
7375
//If it didn't change cell, we are done
7476
if (oldCellPos.x == newCellPos.x && oldCellPos.y == newCellPos.y)
7577
{
@@ -114,8 +116,15 @@ public Vector2Int ConvertFromWorldToCell(Vector3 pos)
114116
{
115117
//Dividing coordinate by cell size converts from world space to cell space
116118
//Casting to int converts from cell space to cell index
117-
int cellX = (int)(pos.x / CELL_SIZE);
118-
int cellY = (int)(pos.z / CELL_SIZE); //z instead of y because y is up in Unity's coordinate system
119+
//int cellX = (int)(pos.x / CELL_SIZE);
120+
//int cellY = (int)(pos.z / CELL_SIZE); //z instead of y because y is up in Unity's coordinate system
121+
122+
//Casting to int in C# doesnt work in same way as in C++ so we have to use FloorToInt instead
123+
//It works like this if cell size is 2:
124+
//pos.x is 1.8, then cellX will be 1.8/2 = 0.9 -> 0
125+
//pos.x is 2.1, then cellX will be 2.1/2 = 1.05 -> 1
126+
int cellX = Mathf.FloorToInt(pos.x / CELL_SIZE);
127+
int cellY = Mathf.FloorToInt(pos.z / CELL_SIZE); //z instead of y because y is up in Unity's coordinate system
119128

120129
Vector2Int cellPos = new Vector2Int(cellX, cellY);
121130

@@ -124,6 +133,23 @@ public Vector2Int ConvertFromWorldToCell(Vector3 pos)
124133

125134

126135

136+
//Test if a position is a valid position (= is inside of the grid)
137+
public bool IsPosValid(Vector3 pos)
138+
{
139+
Vector2Int cellPos = ConvertFromWorldToCell(pos);
140+
141+
if (cellPos.x >= 0 && cellPos.x < NUM_CELLS && cellPos.y >= 0 && cellPos.y < NUM_CELLS)
142+
{
143+
return true;
144+
}
145+
else
146+
{
147+
return false;
148+
}
149+
}
150+
151+
152+
127153
//
128154
// Fighting
129155
//
@@ -136,17 +162,17 @@ public void HandleMelee()
136162
{
137163
for (int y = 0; y < NUM_CELLS; y++)
138164
{
139-
HandleCell(new Vector2Int(x, y));
165+
HandleCell(x, y);
140166
}
141167
}
142168
}
143169

144170

145171

146172
//Handles fight for a single cell
147-
private void HandleCell(Vector2Int cellPos)
173+
private void HandleCell(int x, int y)
148174
{
149-
Unit unit = cells[cellPos.x, cellPos.y];
175+
Unit unit = cells[x, y];
150176

151177
//Make each unit fight all other units once in this cell
152178
//It works like this: If the units in the cell are linked like: A-B-C-D
@@ -164,21 +190,21 @@ private void HandleCell(Vector2Int cellPos)
164190
//But we cant check all 8 cells because then some units might fight each other two times, so we only check half (it doesnt matter which half)
165191
//We also have to check that there's a surrounding cell because the current cell might be the border
166192
//This assumes attack distance is less than cell size, or we might have to check more cells
167-
if (cellPos.x > 0 && cellPos.y > 0)
193+
if (x > 0 && y > 0)
168194
{
169-
HandleUnit(unit, cells[cellPos.x - 1, cellPos.y - 1]);
195+
HandleUnit(unit, cells[x - 1, y - 1]);
170196
}
171-
if (cellPos.x > 0)
197+
if (x > 0)
172198
{
173-
HandleUnit(unit, cells[cellPos.x - 1, cellPos.y - 0]);
199+
HandleUnit(unit, cells[x - 1, y - 0]);
174200
}
175-
if (cellPos.y > 0)
201+
if (y > 0)
176202
{
177-
HandleUnit(unit, cells[cellPos.x - 0, cellPos.y - 1]);
203+
HandleUnit(unit, cells[x - 0, y - 1]);
178204
}
179-
if (cellPos.x > 0 && cellPos.y < NUM_CELLS - 1)
205+
if (x > 0 && y < NUM_CELLS - 1)
180206
{
181-
HandleUnit(unit, cells[cellPos.x - 1, cellPos.y + 1]);
207+
HandleUnit(unit, cells[x - 1, y + 1]);
182208
}
183209

184210
unit = unit.next;
@@ -187,13 +213,13 @@ private void HandleCell(Vector2Int cellPos)
187213

188214

189215

190-
//Handles fight for a single unit versus a linked-list of units
216+
//Handles fight for a single unit vs a linked-list of units
191217
private void HandleUnit(Unit unit, Unit other)
192218
{
193219
while (other != null)
194220
{
195221
//Make them fight if they have similar position - use square distance because it's faster
196-
if ((unit.transform.position - other.transform.position).sqrMagnitude < ATTACK_DISTANCE * ATTACK_DISTANCE)
222+
if ((unit.transform.position - other.transform.position).sqrMagnitude < Unit.ATTACK_DISTANCE * Unit.ATTACK_DISTANCE)
197223
{
198224
HandleAttack(unit, other);
199225
}
@@ -208,7 +234,8 @@ private void HandleUnit(Unit unit, Unit other)
208234
private void HandleAttack(Unit one, Unit two)
209235
{
210236
//Insert fighting mechanic
211-
Debug.Log("Two units are fighting! monkaS");
237+
one.StartFighting();
238+
two.StartFighting();
212239
}
213240
}
214241
}

0 commit comments

Comments
 (0)