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 link 2641
Command . ParseCommand ( npc , "poke" , npc . mostHated . Name );
DragonsSpine/GameObjects/GameLiving/NPC/AI/AI.cs link 2647
Command . ParseCommand ( npc , "shoot" , npc . mostHated . Name );
DragonsSpine/GameObjects/GameLiving/NPC/AI/AI.cs link 2662
Command . ParseCommand ( npc , "kick" , npc . mostHated . Name );
DragonsSpine/GameObjects/GameLiving/NPC/AI/AI.cs link 2666
Command . ParseCommand ( npc , "kill" , npc . mostHated . Name );
DragonsSpine/GameObjects/GameLiving/NPC/AI/AI.cs link 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 link 127
128
129
130
npc . hasMostHated = !( dr . IsNull ( dtNPCs . Columns [ "mostHatedId" ]));
if ( npc . hasMostHated ) {
npc . mostHatedId = Convert . ToInt32 ( dr [ "mostHatedId" ]);
}
Assets/Managers/NpcManager.cs link 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:
We need a way to find the position of our foe:
Assets/Managers/NpcManager.cs link 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 link 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 ;
}
}
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 link 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 link 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.
Day 18 code - client