301 Days (redux)

Another metric leap year of gamedev experiments and such

Day 18 - Who Do You Hate?

Sep 22, 2015 - 3 minute read - OldDaysste-reez-muvi

Peek into the AI for some battle insights.

More detailed violence

We can watch the NPCs take damage and eventually expire, but it’s not exactly super-clear where the damage is coming from. If we poke into the server code to see what’s going on:

DragonsSpine/GameObjects/GameLiving/NPC/AI/AI.cs GitLab
2641
Command.ParseCommand(npc, "poke", npc.mostHated.Name);
DragonsSpine/GameObjects/GameLiving/NPC/AI/AI.cs GitLab
2647
Command.ParseCommand(npc, "shoot", npc.mostHated.Name);
DragonsSpine/GameObjects/GameLiving/NPC/AI/AI.cs GitLab
2662
Command.ParseCommand(npc, "kick", npc.mostHated.Name);
DragonsSpine/GameObjects/GameLiving/NPC/AI/AI.cs GitLab
2666
Command.ParseCommand(npc, "kill", npc.mostHated.Name);
DragonsSpine/GameObjects/GameLiving/NPC/AI/AI.cs GitLab
2674
Command.ParseCommand(npc, "jumpkick", npc.mostHated.Name + "/Ki Yah!/!");

Ok, I think we see a pattern. If we know the NPC’s mostHated, we can be pretty sure that’s who the NPC is attacking. Luckily we’ve already added it to the LiveNPC table in the database, so it’s just a matter of surfacing it.

Assets/DragonsSpine/DAL/DBNPC.cs GitLab
127
128
129
130
npc.hasMostHated = !(dr.IsNull(dtNPCs.Columns["mostHatedId"]));
if (npc.hasMostHated) {
    npc.mostHatedId = Convert.ToInt32(dr["mostHatedId"]);
}
Assets/Managers/NpcManager.cs GitLab
129
130
npcScripts[npc.worldNpcID].hasMostHated = npc.hasMostHated;
npcScripts[npc.worldNpcID].mostHatedId = npc.mostHatedId;

Then we make an angry red arrow to point at the object of most hatred:

Wow what a hateful arrow. Wow what a hateful arrow.

We need a way to find the position of our foe:

Assets/Managers/NpcManager.cs GitLab
174
175
176
177
178
179
public Vector3 locateNpc(int npcId) {
    if (npcScripts.ContainsKey(npcId))
        return npcScripts[npcId].transform.position;
    else
        return Vector3.zero;
}

And now we can display our hatred:

Assets/NpcScript.cs GitLab
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// display hatred
if (hasMostHated) {
    if (myHatred == null) {
        myHatred = Instantiate(hatredPrefab,
                               Vector3.zero,
                               Quaternion.identity) as Transform;
        myHatred.parent = this.transform;
        myHatred.localPosition = new Vector3(0f, 0f, 0.2f);
        myHatredRend = myHatred.GetComponent<Renderer>();
    }
    hatedLocation = npcManager.locateNpc(mostHatedId);

    Vector3 hatredDir = hatedLocation - transform.position;
    hatredDir.z = 0f;
    hatredDir = hatredDir.normalized;

    Quaternion qDir = new Quaternion();
    qDir.SetLookRotation(hatredDir, new Vector3(0f, 0f, 1f));
    myHatred.rotation = qDir;

    myHatred.localScale = new Vector3(0.3f, 1f, 1f);
    myHatredRend.enabled = toBeSeen;
} else {
    if (myHatred != null) {
        myHatredRend.enabled = false;
    }
}
Lots of hating going on in the temple today Lots of hating going on in the temple today

Cohatred

That’s cool, but the Update method in NpcScript is getting crowded. Can’t we do this in a cool, coroutine-ish way?

Assets/NpcScript.cs GitLab
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public bool HasMostHated {
    get { return hasMostHated; }
    set {
        if (hasMostHated != value) {
            hasMostHated = value;
            StopCoroutine("Hatred");
            StartCoroutine("Hatred");
        }
    }
}
public int MostHatedId {
    get { return mostHatedId; }
    set {
        if (mostHatedId != value) {
            mostHatedId = value;
            StopCoroutine("Hatred");
            StartCoroutine("Hatred");
        }
    }
}
Assets/NpcScript.cs GitLab
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
IEnumerator Hatred () {
    while (hasMostHated && !presumedDead && hits > 0f) {
        if (myHatred == null) {
            myHatred = Instantiate(hatredPrefab,
                                   Vector3.zero,
                                   Quaternion.identity) as Transform;
            myHatred.parent = this.transform;
            myHatred.localPosition = new Vector3(0f, 0f, 0.2f);
            myHatredRend = myHatred.GetComponent<Renderer>();
        }
        hatedLocation = npcManager.locateNpc(mostHatedId);
        
        Vector3 hatredDir = hatedLocation - transform.position;
        hatredDir.z = 0f;
        hatredDir = hatredDir.normalized;
        
        Quaternion qDir = new Quaternion();
        qDir.SetLookRotation(hatredDir, new Vector3(0f, 0f, 1f));
        myHatred.rotation = qDir;
        
        myHatred.localScale = new Vector3(0.3f, 0.1f, 1f);
        Vector3 startPos = transform.position + hatredDir * transform.localScale.x * 0.5f;
        Vector3 endPos = hatedLocation - hatredDir * transform.localScale.x * 0.5f;
        startPos.z += 0.2f; endPos.z += 0.2f;
        myHatred.position = Vector3.Lerp(startPos, endPos, Time.time % 1f);
        myHatredRend.enabled = toBeSeen;

        yield return null;
    } 
    if (myHatred != null) {
        myHatredRend.enabled = false;
    }
}

And while we’re at it, we take advantage of the added efficiency to animate the arrows a bit.

Look how popular the priest is! Look how popular the priest is!

More to come

Day 18 code - client