301 Days

A year of gamedev experiments.

Day 30 - Intermap Navigation

| Comments

Finally switching between maps on the fly.

Apologies

Sorry for the long delay between posts; epic week at work.


Coding

Beyond map 3

In the table that stores the Cell data for a map, I had foolishly included the landID field (it indicates whether a given map belongs to the Beginners' Game, Advanced Game, or Underworld) which is always the same for a given mapID. Hard-coding landID to zero just gave me the first four maps, so I modified the query in the stored procedure to ignore the field entirely.

DragonsSpine/SQL Scripts/prApp_LiveCell_By_MapID.sqllink
19
20
21
22
23
24
25
26
27
CREATE PROCEDURE [dbo].[prApp_LiveCell_By_MapID]
  @facet smallint,
  @map smallint
AS
BEGIN
  SELECT *
  FROM LiveCell
  WHERE facet=@facet AND map=@map
END

And of course needed to modify the call in the code to match.

Assets/DragonsSpine/DAL/DBWorld.cslink
148
149
150
151
152
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);
    using (DataTable dtCells = sp.ExecuteDataTable()) {

Ditto NPCs. So now we can easily pull from any map we want.

Switching maps

The managers need to handle switching between maps. We’ll start them out on map “-1” and add a setter and coroutine in a by-now familiar pattern.

Assets/Managers/MapManager.cslink
20
21
22
23
24
25
26
27
28
29
30
private int mapNum = -1;
public int MapNum {
    get { return mapNum; }
    set {
        if (mapNum != value) {
            mapNum = value;
            StopCoroutine("LoadMap");
            if (mapNum != -1) StartCoroutine("LoadMap");
        }
    }
}
Assets/Managers/MapManager.cslink
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
IEnumerator LoadMap() {
    lock (cellLock) {
        lastRoundSeen = 0;
        foreach (CellScript cs in cell_objects.Values)
            cs.gameObject.SetActive(false);
        num_cells = 0;
        cell_objects.Clear();
        cellKeyList.Clear();
        cells.Clear();
        cellsKeys.Clear();

        InitialMapLoading();
        nextDBUpdate = Time.time + dbUpdateDelta;
    }
    yield return null;
}

The code that was originally only called once has moved into InitialMapLoading, and we clear everything out (except the object pool and material dictionary) before calling it for the new map. You may notice a lock which needed to be added; in the MapManager we are sharing the various cell structures with the DB access thread, so we need some safety.

Something very similar was done in NpcManager, minus the locks because we’re more safely passing lists around there.

Material Caching

Map-changing performance was initially quite bad on some maps, until I switched the NpcManager over to keeping a dictionary of Materials that persisted.

Assets/Managers/NpcManager.cslink
197
198
199
200
201
202
203
204
205
if (!npcLiveMaterials.ContainsKey(npc.name)) {
    Material npcLiveMaterial = Resources.Load("Materials/" + npc.name, typeof(Material)) as Material;
    if (npcLiveMaterial == null) {
        print("Couldn't find live Material for NPC name: " + npc.name);
        npcLiveMaterial = Resources.Load("Materials/anonymous", typeof(Material)) as Material;
    }
    npcLiveMaterials[npc.name] = npcLiveMaterial;
}
npc.liveMaterial = npcLiveMaterials[npc.name];

I had been doing this in MapManager the whole time, but was kind of surprised at the impact that using it in NpcManager had. I guess calls to Resources.Load are really as expensive as you’d think.

Defining some vantage points

Just to make sure we have a good view of each new map when we switch to it, I manually added an initial camera position for each via the editor. Note that these would change between aspect ratios; I did these for 16:9.

And a keypress to cycle

Assets/Managers/GUIManager.cslink
33
34
35
36
37
38
39
40
41
if (Input.GetKeyDown(KeyCode.M)) {
    mapNum += 1;
    if (mapNum > 12) mapNum = 0;
    if (mapNum == 9) mapNum = 10; // for some reason there's no map 9
    if (mapOverlooks.Length > mapNum)
        mainCamera.MoveToPosition(mapOverlooks[mapNum]);
    mapManager.MapNum = mapNum;
    npcManager.MapNum = mapNum;
}

And some new Materials

For cell types that didn’t exist on the first few maps.

All for a cool gif

(Pauses edited out for your enjoyment. We can’t really load new maps that fast. Yet.)

Well, not just for this cool gif. Now we have all of the plumbing in place to switch maps on the fly, in search of action or to follow a character going through a portal or whatever.


Day 30 code - visualizer Day 30 code - server

Comments