301 Days (redux)

Another metric leap year of gamedev experiments and such

Day 14 - Anonymous NPCs and Stuff

Sep 13, 2015 - 4 minute read - OldDaysste-reez-muvi

Handling arbitrary NPC names, etc.


We are NPCs. Expect Us.

Guy Fawkes the Priest, meet Guy Fawkes the Balm Seller, meet Guy Fawkes the Confessor... Guy Fawkes the Priest, meet Guy Fawkes the Balm Seller, meet Guy Fawkes the Confessor...

Time to further simplify life by removing the one-name-at-a-time NPC updates. It’s all well and good for bears and boars and orcs, but each human NPC has a unique name, which may even be autogenerated. On instantiating an NPC, we can just look in the Resources folder for a Material of the same name. If not found, we’ll look for “anonymous”.

Managers/NpcManager.cs GitLab
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
    void Update () {
        if (Time.time > nextDBUpdate) {
            print("Time to read from the DB! " + Time.time);
            nextDBUpdate = Time.time + dbUpdateDelta;
            activeGameRounds.Clear();
            cellsContainingNpcs.Clear();
            UpdateAllNpcs();
            UpdateNpcCohab();
        } 
    }
    
    void UpdateAllNpcs() {
        List<NPC> npcs = DragonsSpine.DAL.DBNPC.GetAllNpcs();
        print("I found " + npcs.Count + " NPCs");
        foreach (NPC npc in npcs) {
            if (npc.lastActiveRound > maxGameRound) maxGameRound = npc.lastActiveRound;
        }
        foreach (NPC npc in npcs) {
            if (!activeGameRounds.Contains(npc.lastActiveRound)) activeGameRounds.Add(npc.lastActiveRound);
            Vector3 cell = new Vector3(npc.X, npc.Y, npc.Z);
            npcLocations[npc.worldNpcID] = cell;
            if (!cellsContainingNpcs.ContainsKey(cell)) 
                cellsContainingNpcs[cell] = new List<int>();
            cellsContainingNpcs[cell].Add(npc.worldNpcID);
            Vector3 position = new Vector3((-1f) * npc.X, (-1f) * npc.Y, (0.1f * npc.Z) + 0.2f);
            if (!npcScripts.ContainsKey(npc.worldNpcID)) {
                NpcScript tempNpc = (NpcScript) Instantiate(npcScript);
                tempNpc.npcId = npc.worldNpcID;
                tempNpc.name = npc.Name;
                npcScripts[npc.worldNpcID] = tempNpc;
            }
            npcScripts[npc.worldNpcID].newPosition = position;
            npcScripts[npc.worldNpcID].lastActiveRound = npc.lastActiveRound;
            SetMaterial(npcScripts[npc.worldNpcID]);
        }
    }

    void SetMaterial(NpcScript npc) {
        Renderer rend = npc.GetComponent<Renderer>();
        Material liveNpc = Resources.Load("Materials/" + npc.name, typeof(Material)) as Material;
        if (liveNpc == null) {
            print("Couldn't find live Material for NPC name: " + npc.name);
            liveNpc = Resources.Load("Materials/anonymous", typeof(Material)) as Material;
        }
        Material exNpc = Resources.Load("Materials/ex" + npc.name, typeof(Material)) as Material;
        if (exNpc == null) {
            print("Couldn't find dead Material for NPC name: " + npc.name);
            exNpc = Resources.Load("Materials/exanonymous", typeof(Material)) as Material;
        }
        if (npc.lastActiveRound >= maxGameRound-1) // one round leeway in case we catch the DB in mid-update
            rend.material = liveNpc;
        else
            rend.material = exNpc;
    }
Not really the way it should be done, since we’re scouring the Materials every time we update an NPC. It does let you add materials while the thing is running, though: That Guy Fawkes is actually a dragon! That Guy Fawkes is actually a dragon!


Keeping Up Appearances

We’ll make the NpcScript responsible for its live and dead materials, and switching between them as appropriate (although NpcManager still kicks things off):

Assets/NpcScript.cs GitLab
40
41
42
43
44
45
    public void UpdateMaterial(int currRound) {
        if (lastActiveRound >= currRound-1) // one round leeway in case we catch the DB in mid-update
            renderer.material = liveMaterial;
        else
            renderer.material = deadMaterial;
    }
Managers/NpcManager.cs GitLab
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
    
            if (!npcScripts.ContainsKey(npc.worldNpcID)) {
                NpcScript tempNpc = (NpcScript) Instantiate(npcScript);
                tempNpc.npcId = npc.worldNpcID;
                tempNpc.name = npc.Name;
                SetMaterials(tempNpc);
                npcScripts[npc.worldNpcID] = tempNpc;
            }
            npcScripts[npc.worldNpcID].newPosition = position;
            npcScripts[npc.worldNpcID].lastActiveRound = npc.lastActiveRound;
            npcScripts[npc.worldNpcID].UpdateMaterial(maxGameRound);
        }
    }

    void SetMaterials(NpcScript npc) {
        npc.liveMaterial = Resources.Load("Materials/" + npc.name, typeof(Material)) as Material;
        if (npc.liveMaterial == null) {
            print("Couldn't find live Material for NPC name: " + npc.name);
            npc.liveMaterial = Resources.Load("Materials/anonymous", typeof(Material)) as Material;
        }
        npc.deadMaterial = Resources.Load("Materials/ex" + npc.name, typeof(Material)) as Material;
        if (npc.deadMaterial == null) {
            print("Couldn't find dead Material for NPC name: " + npc.name);
            npc.deadMaterial = Resources.Load("Materials/exanonymous", typeof(Material)) as Material;
        }
    }
That’s a bit faster and cleaner, even if we lose some of the dynamism (for now). Using the debug, I spent a few hours adding a lot of the materials we’re missing. Though I still don’t have a solution for the generated NPC names.

Sven, the priest, and the ghost confessor sorted, at least. Sven, the priest, and the ghost confessor sorted, at least.

Now I’m off to watch Unity Live Training April 13th, 2015: Creating a Simple Messaging System and maybe learn something for tomorrow.

More to come

Day 14 code - client