301 Days

A year of gamedev experiments.

Day 14 - Anonymous NPCs and Stuff

| Comments

Handling arbitrary NPC names, etc.


We are NPCs. Expect Us.

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.csGitLab
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:


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.csGitLab
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.cslink
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.


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

Day 14 code - client

Comments