301 Days

A year of gamedev experiments.

Day 29 - Pooling Worlds

| Comments

Now that Autumn is finally here, I declare this the Pumpkin Spice Blog Post.

Coding

(The actual first thing I did was make the MapManager only pull changed cells on each update, and not to load the map file anymore, but that’s too boring to detail here.)

1. Generic object pooling

First, I converted the NpcManager and MapManager over to using more generic object pools (see below under Learning). Not too exciting, but allows for the next step. The only real wrinkle I ran into, since this method has the not-in-use objects inactive at the GameObject level, is that anything that triggered a coroutine would fail. That required pushing things around a bit so that NPCs were definitely active before I’d try to modify their health or hatred.

Assets/Managers/NpcManager.cslink
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
    } else {
        tempNpc = npcPooler.GetPooledObject().GetComponent<NpcScript>();
        tempNpc.Reset();
        tempNpc.npcManager = this;
        tempNpc.npcId = npc.worldNpcID;
        tempNpc.name = npc.Name;
        SetMaterials(tempNpc);
        tempNpc.toBeSeen = (position.z <= mapManager.zTop + 1);
        npcScripts[npc.worldNpcID] = tempNpc;
    }
    tempNpc.gameObject.SetActive(true);
    tempNpc.cell = cell;
    tempNpc.newPosition = position;
    tempNpc.lastActiveRound = npc.lastActiveRound;
    tempNpc.UpdateMaterial(maxGameRound);
    tempNpc.Hits = npc.Hits;
    tempNpc.HitsFull = npc.HitsFull;
    tempNpc.MostHatedId = npc.mostHatedId;
    tempNpc.HasMostHated = npc.hasMostHated;
}
Assets/Managers/MapManager.cslink
88
89
90
91
92
93
94
if (cellNeedsTransform[c]) {
    // Instantiate this one
    Vector3 position = CLUtils.CellLocToVector3(new CellLoc(cells[c].x, cells[c].y, cells[c].z), 0);

    tempCell = cellPooler.GetPooledObject().GetComponent<CellScript>();
    tempCell.mapManager = this;
    tempCell.newPosition = position;
Assets/lib/ObjectPooler.cslink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// With thanks to Mike Geig and http://unity3d.com/learn/tutorials/modules/beginner/live-training-archive/object-pooling

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class ObjectPooler : MonoBehaviour {

    // public static ObjectPooler current;
    public GameObject pooledObject;
    public int pooledAmount = 400;
    public bool willGrow = true;
    public int currentSize = 0;

    List<GameObject> pooledObjects;

    void Awake() {
        // current = this;
    }

    void Start () {
        pooledObjects = new List<GameObject>();
        for (int i = 0; i < pooledAmount; i++) {
            GameObject obj = (GameObject)Instantiate(pooledObject);
            obj.SetActive(false);
            pooledObjects.Add(obj);
        }
        currentSize = pooledObjects.Count;
    }

    public GameObject GetPooledObject() {
        for (int i = 0; i < pooledObjects.Count; i++) {
            if (!pooledObjects[i].activeInHierarchy) {
                return pooledObjects[i];
            }
        }
        if (willGrow) {
            GameObject obj = (GameObject)Instantiate(pooledObject);
            pooledObjects.Add(obj);
            currentSize = pooledObjects.Count;
            return obj;
        }
        return null;
    }
}

I’m not making the ObjectPooler a singleton because, well, I have two of them. And I keep a count of the objects handy for reference/debugging.

2. Different maps

Until now, we’ve always been pulling map 0, the original Island of Kesmai. I did this initially to keep things simple, but it’s time to open things up. The DB already has the data for every map, we just need to let ourselves read it:

Assets/DragonsSpine/DAL/DBNPC.cslink
104
105
106
107
108
109
110
111
112
public static List<NPCLite> GetAllNpcs(int mapNum)
{
    List<NPCLite> npclist = new List<NPCLite>();

    using (SqlConnection tempConnection = DataAccess.GetSQLConnection())
    using (SqlStoredProcedure sp = new SqlStoredProcedure("prApp_LiveNPC_by_MapID", tempConnection)) {
        sp.AddParameter("@facet", SqlDbType.Int, 4, ParameterDirection.Input, 0);
        sp.AddParameter("@land", SqlDbType.Int, 4, ParameterDirection.Input, 0);
        sp.AddParameter("@map", SqlDbType.Int, 4, ParameterDirection.Input, mapNum);
Assets/DragonsSpine/DAL/DBWorld.cslink
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
        public static int UpdateCellDict(Dictionary<CellLoc, CellLite> cellDict, int mapNum, int sinceRound) {
            int numUpdated = 0;
            CellLoc tmpCellLoc;
            List<CellLite> cellList = GetUpdatedLiveCells(mapNum, sinceRound);
            foreach (CellLite cell in cellList) {
                tmpCellLoc = new CellLoc(cell.x, cell.y, cell.z);
                cellDict[tmpCellLoc] = cell;
                numUpdated += 1;
            }
            return numUpdated;
        }

        public static List<CellLite> GetUpdatedLiveCells(int mapNum, int sinceRound) {
            List<CellLite> cellList = new List<CellLite>();
            int tmpRound = 0;
            using (SqlConnection tempConnection = DataAccess.GetSQLConnection())
            using (SqlStoredProcedure sp = new SqlStoredProcedure("prApp_LiveCell_by_MapID", tempConnection)) {
                sp.AddParameter("@facet", SqlDbType.Int, 4, ParameterDirection.Input, 0);
                sp.AddParameter("@land", SqlDbType.Int, 4, ParameterDirection.Input, 0);
                sp.AddParameter("@map", SqlDbType.Int, 4, ParameterDirection.Input, mapNum);
Assets/Managers/GUIManager.cslink
10
11
12
13
14
15
16
public int mapNum;

void Awake () {
    instance = this;
    mapManager.mapNum = mapNum;
    npcManager.mapNum = mapNum;
}

There’re updates to NpcManager and MapManager to pass the mapNum through, of course.

3.0 Pretty Places


Learning


Playing

  • The Talos Principle continues to deliver on the puzzle front. The narrative also has me somewhat fascinated, and delivering it partially through retro green-on-black text terminals scattered throughout the world is a delicious touch.
  • Puppeteer is visually awesome; I’m not good at it, but love watching the “this is a puppet show on a stage” premise being maintained.

Day 29 code - visualizer

Comments