Hi, I am now making a simple AI for my zombie game, using a cube as a makshift zombie. But when the zombie is at idle, I want to to move around randomly.
var target : Transform;
var moveSpeed : int = 5;
private var moveRotation : float = 5;
private var canLookAt : boolean = false;
var timeToSwitch : float = 10;
private var canSwitch : boolean = false;
static var isHit : boolean = false;
static var health = 100;
function Update () {
var playerInFront : RaycastHit;
if (Physics.Raycast(transform.position, transform.forward, playerInFront, 100)) {
if (playerInFront.collider.tag == "Player") {
canLookAt = true;
}
}
if (canLookAt || isHit) {
transform.LookAt (target);
transform.Translate(Vector3.forward*moveSpeed*Time.deltaTime);
}
if (health <= 0){
Destroy (gameObject);
}
if (!canLookAt !isHit) {
Shamble();
}
}
function Shamble() {
var ranV = Random.value;
if (ranV <= .5 ) {
yield WaitForSeconds (timeToSwitch);
moveRotation = -50;
}
if (ranV >= .5 ) {
yield WaitForSeconds (timeToSwitch);
moveRotation = 50;
}
transform.eulerAngles = Vector3(0, moveRotation * Time.deltaTime,0);
transform.Translate (Vector3.forward * moveSpeed * Time.deltaTime);
}
This is it so far. The problem, is that I want it to switch the direction after a set amount of time. yield WaitForSeconds is not doing this well, and so the object just fltters around, rather than changing its rotational path. Could anyone tell me how to fix this? Or have a better suggestion for AI? Thanks
Ok, update. I jut replaced it with an idle animation on a character. The problem is that if I have more than 1 zombie in the scene, triggering 1 will cause all of them to react. And when I kill 1, all die (a LOT of ragdolls).
Heres the AI itself:
//This script is applied to the enemy. It works with the GunBasis script and PlayerHealthAndAI script
var ragdoll : Rigidbody;
var target : Transform;
var moveSpeed : float = 5;
var gravity : float = 9.81;
static var canLookAt : boolean = false;
private var moveDirection = Vector3.zero;
private var timeToReturn : float = 5;
static var isHit : boolean = false;
static var canBite : boolean = false;
static var health = 100;
function Start() {
animation["Idle"].layer = 1;
animation["Bite"].layer = 2;
animation["Mope"].layer = 3;
animation.wrapMode = WrapMode.Loop;
}
function FixedUpdate() {
moveDirection.y -= gravity * Time.deltaTime;
}
function Update () {
var playerInFront : RaycastHit;
if (Physics.Raycast(transform.position, transform.forward, playerInFront, 100)) {
if (playerInFront.collider.tag == "Player") {
canLookAt = true;
}
}
if (canLookAt || isHit) {
transform.LookAt (target);
transform.Translate(Vector3.forward*moveSpeed*Time.deltaTime);
animation.Stop("Idle");
animation.Play("Mope");
}
if (health <= 0){
var rag : Rigidbody;
rag = Instantiate (ragdoll,transform.position, transform.rotation);
Destroy (gameObject);
}
if (!canLookAt !isHit) {
animation.Stop("Mope");
animation.Play("Idle");
}
if (canBite) {
animation.wrapMode = WrapMode.Once;
animation.Play("Bite");
animation.wrapMode = WrapMode.Loop;
}
}
And heres the code that goes on the CC:
static var health : int = 100;
private var timeToDie : float = 1.0;
private var timeCanHit : float = 1.5;
private var canHit : boolean = true;
function OnControllerColliderHit (hit : ControllerColliderHit) {
if (hit.gameObject.tag == "ZombieNPC" !SimpleAI.isHit) {
yield WaitForSeconds (timeToDie);
SimpleAI.isHit = true;
SimpleAI.canBite = true;
} else if (hit.gameObject.tag == "ZombieNPC" SimpleAI.isHit canHit) {
SimpleAI.isHit = false;
SimpleAI.canLookAt = false;
canHit = false;
var randomHealth = Random.value;
if (randomHealth > .2) {
health -= 10;
}
if (randomHealth > .4) {
health -= 20;
}
if (randomHealth == .6) {
health -= 5;
}
yield WaitForSeconds (timeCanHit);
canHit = true;
}
}
function Update() {
if (health <= 50) {
Debug.Log ("Player Health is low!");
}
if (health <= 0) {
health = 0;
KillPlayer();
}
}
function KillPlayer() {
Time.timeScale = 0.5;
Debug.Log ("Player is Dead!");
}
Can anyone mix these so I can have more than one zombie? Also, the gun shooting script is involved, but not much. But if you want to see it, ask. :roll:
Try adding in a line of code like var zombie : transform;
then when you make refrences to the zombie use zombie.Whatever. Then drag the zombie into that slot. If that doesn’t work then I don’t know, your script looks fine to me.
Well, my scripts arent fine, there is a lot of bugs and such. But it seams like that would work. Im gonna try…
Ok, it seams to work, But not fully, because I have no idea where to put it. Where shoud it go?
Since you have isHit as a static var, that means there is one instance of the variable. This means every zombie will share the same value for that variable. This could be useful, but not there. First take out any static keywords that you don’t need(probably all of them) then take this out:
if (hit.gameObject.tag == "ZombieNPC" !SimpleAI.isHit) {
yield WaitForSeconds (timeToDie);
SimpleAI.isHit = true;
SimpleAI.canBite = true;
} else if (hit.gameObject.tag == "ZombieNPC" SimpleAI.isHit canHit) {
SimpleAI.isHit = false;
SimpleAI.canLookAt = false;
canHit = false;
var randomHealth = Random.value;
if (randomHealth > .2) {
health -= 10;
}
if (randomHealth > .4) {
health -= 20;
}
if (randomHealth == .6) {
health -= 5;
}
yield WaitForSeconds (timeCanHit);
canHit = true;
}
}
and replace it with this:
if (hit.gameObject.tag == "ZombieNPC") {
var zombieController : SimpleAI = hit.gameObject.GetComponent(SimpleAI);
if(!zombieContoller.isHit) {
yield WaitForSeconds (timeToDie);
SimpleAI.isHit = true;
SimpleAI.canBite = true;
}
else if (zombieController.isHit zombieController.canHit) {
zombieController.isHit = false;
zombieController.canLookAt = false;
canHit = false;
var randomHealth = Random.value;
if (randomHealth > .2) {
health -= 10;
}
if (randomHealth > .4) {
health -= 20;
}
if (randomHealth == .6) {
health -= 5;
}
yield WaitForSeconds (timeCanHit);
canHit = true;
}
}
static var health : int = 100;
private var timeToDie : float = 1.0;
private var timeCanHit : float = 1.5;
private var canHit : boolean = true;
//did we hit a zombie?
function OnControllerColliderHit (hit : ControllerColliderHit) {
if (hit.gameObject.tag == "ZombieNPC") {
var zombieController : SimpleAI = hit.gameObject.GetComponent(SimpleAI);
if(!zombieContoller.isHit) {
yield WaitForSeconds (timeToDie);
SimpleAI.isHit = true;
SimpleAI.canBite = true;
}
else if (zombieController.isHit zombieController.canHit) {
zombieController.isHit = false;
zombieController.canLookAt = false;
canHit = false;
var randomHealth = Random.value;
if (randomHealth > .2) {
health -= 10;
}
if (randomHealth > .4) {
health -= 20;
}
if (randomHealth == .6) {
health -= 5;
}
yield WaitForSeconds (timeCanHit);
canHit = true;
}
}
}
//are we dead?
function Update() {
if (health <= 50) {
Debug.Log ("Player Health is low!");
}
if (health <= 0) {
health = 0;
KillPlayer();
}
}
function KillPlayer() {
Time.timeScale = 0.5;
Debug.Log ("Player is Dead!");
}
Doesnt seem to work. I get an error meaage:
And:
Sorry, I am a bad scripter and debugger… 
Also, a collision with the object isn’t the only way to trigger it. If you shoot it or step in front of it, it will activate as well.
Hmm, still doesnt work. I have no idea what it could be. But I have the same problem with some barrels exploding, I guess I can test it there since it is much more simple. :?
This script is really making me mad. So, if anyone can make a script that plays an idle animation until a static var is true, then follows the player, then if it is a distance from the player, stop following the player, then play a bite animation, subtract random health, then wait, then return to following the player.
I am going to mess with the FPS tutorial AI, but I doubt it can work… :evil:
Ok, the FPS AI works (kind of) and I have a lot of code to change, but now I have a problem. The fire raycast part of my script is not killing the zombie. This is because the zombie now has a character controller rather than a collider. So how would I make it so that the raycast can return a hit with a character controller?
It works. Thanks. But I still have the problem of killing zombies. Here is my code for the shooter:
function LaunchProjectile() {
var hit : RaycastHit;
if (Physics.Raycast(transform.position, transform.forward, hit, 2000)) {
if (hit.collider.tag == "ZombieNPC") {
AI.health -= zombieHealth;
if (AI.health <= 0) {
Destroy (hit.collider.gameObject);
var rag : Rigidbody;
rag = Instantiate (deadZombie, hit.transform.position, hit.transform.rotation);
Debug.Log ("NPC is hit!");
}
}
If i kill a zombie, all other zombies will die in 1 hit. How would I make an instance of each zombie so they have independent health?
Here’s usually the easiest way to have damage be applied. Add a damage variable at the top of your script.
if (Physics.Raycast(transform.position, transform.forward, hit, 2000)) {
if (hit.collider.tag == "ZombieNPC") {
hit.transform.BroadCastMessage("ApplyDamage", damage, SendMessageOptions.DontRequireReceiver);
Debug.Log ("NPC is hit!");
}
}
Then add this code to the zombie.
var health = 20;
var ragdoll : RigidBody;
function ApplyDamage (damage : int) {
health -= damage;
if(health <= 0) {
Die();
}
function Die () {
Instantiate (ragdoll, transform.position, transform.rotation);
Destroy(gameObject);
}
P.S. Haven’t checked for typos. I didn’t see any, but if it gives an error, that is probably why.
Also, like I said earlier, if you don’t want all the zombies to die together then you have to get rid of the static keyword and use a GetComponent then call the variable.
Holy crap it works! And I added a Random.Range for the damage, applied to a variable (the same gun script for every gun). But could you explain exactly BroadcastMessage does? Sorry, I am still learing… 
BroadcastMessage calls the method in the object and every child of the object that has that method in a script attached to it.
Ex.
Let’s say you BroadcastMessage(“DestroyThisObject”, SendMessageOptions.RequireReceiver);
Here’s the object:
Parent → doesn’t have method
—Child1 → doesn’t have method
—Child2 → doesn’t have method
—Child3 → has method
By has method I mean a script attached to the object has that method. So Child3 will call that function. It basically remotely calls the method in another object. The reference page also says it will call it once for every object or child with the method. So if Parent and Child1 has the method, both will call it.
Parent → has method
—Child1 → has method
—Child2 → doesn’t have method
—Child3 → doesn’t have method
Both Parent and Child1 will call the method.
Here’s the reference page.
You can look up SendMessageOptions, but they are fairly self explanatory. Also see SendMessage and SendMessageUpwards. They are similar to BroadcastMessage.
Well, the kill script doesn’t work perfectly. If I kill a zombie, the other one will die in 1 hit. The gun is not a child of the zombie and vice versa by the way.
Now how would I fix that?
I understand that. The collider.BroadcastMessage means we are calling it in the object we hit (the zombie), not the gun so that is fine. Did you get rid of the static vars?
OH, I see. I had the most important var - health - as a static var. I was wondering why because if this doesnt work but my barrel explode does, then WTF? 
Thanks a lot.