- Home /
Enemy AI script causing Unity to crash (Javascript to C#)
I figured it'd be best to ask a new question since the issue is in a different script.
I've managed to convert the Enemy AI script to C# and Unity does not show any errors, however when trying to run after attaching the script to anything, the program freezes (both in the editor itself and as a standalone .exe). I can't shake the feeling that I'm just overlooking something simple (I'm guessing I messed up somewhere and made an infinite loop or something similar), but figured it'd be best to ask in case I'm wrong.
using UnityEngine;
using System.Collections;
//We need a character controller for this script to run.
[RequireComponent(typeof (CharacterController))]
public class EnemyAI:MonoBehaviour
{
public float speed = 3.0f;
public float rotationSpeed = 5.0f;
public float shootRange = 15.0f;
public float attackRange = 30.0f;
public float shootAngle = 4.0f;
public float dontComeCloserRange = 5.0f;
public float delayShootTime = .35f;
public float pickNextWaypointDistance = 2.0f;
public Transform target;
private float lastShot = -10.0f;
void Start ()
{
//If we have no target at first, we'll set it to be our player.
if(target == null && GameObject.FindWithTag("Player"))
{
target = GameObject.FindWithTag("Player").transform;
}
StartCoroutine("Patrol");
}
IEnumerator Patrol()
{
var curWayPoint = AutoWaypoint.FindClosest(transform.position);
while(true)
{
var waypointPosition = curWayPoint.transform.position;
//If we're close to a waypoint, we'll go ahead and pick the next one.
if(Vector3.Distance(waypointPosition, transform.position) <
pickNextWaypointDistance)
{
curWayPoint = PickNextWaypoint(curWayPoint);
}
//We'll attack the player if he is in sight.
if(CanSeeTarget())
{
StartCoroutine("AttackPlayer");
}
//Otherwise we'll just continue moving along the waypoints.
MoveTowards(waypointPosition);
}
}
bool CanSeeTarget()
{
//If the target is out of range, we cannot see it.
if(Vector3.Distance(transform.position, target.position) > attackRange)
{
return false;
}
RaycastHit hit;
if(Physics.Linecast(transform.position, target.position, out hit))
{
return hit.transform == target;
}
return false;
}
IEnumerator Shoot()
{
//We need to begin the shooting animation.
animation.CrossFade("shoot", 0.3f);
//We'll wait until half of the animation has played...
yield return new WaitForSeconds(delayShootTime);
//...fire the gun...
BroadcastMessage("Fire");
//...and finally, let the rest of the animation finish.
yield return new WaitForSeconds(animation["shoot"].length - delayShootTime);
}
IEnumerator AttackPlayer()
{
//We'll set up the position the player was last visible.
var lastVisiblePlayerPosition = target.position;
while(true)
{
if(CanSeeTarget())
{
//If our target is dead, we need to quit hunting.
if(target == null)
{
yield return null;
}
//If the target is too far away, we need to give up the hunt.
var distance = Vector3.Distance(transform.position, target.position);
if(distance > shootRange * 3)
{
yield return null;
}
lastVisiblePlayerPosition = target.position;
//If we aren't in the "Don't come closer range" yet, we'll move toward the last known
//position.
if(distance > dontComeCloserRange)
{
MoveTowards(lastVisiblePlayerPosition);
}
else
{
RotateTowards(lastVisiblePlayerPosition);
}
var forward = transform.TransformDirection(Vector3.forward);
var targetDirection = lastVisiblePlayerPosition - transform.position;
targetDirection.y = 0;
var angle = Vector3.Angle(targetDirection, forward);
//We'll start shooting at the player if he's in sight and within range.
if(distance < shootRange && angle < shootAngle)
{
StartCoroutine("Shoot");
}
else
{
StartCoroutine("SearchPlayer", lastVisiblePlayerPosition);
//If at any point the player leaves visibility, we should stop attacking.
if(!CanSeeTarget())
{
yield return null;
}
}
}
}
}
IEnumerator SearchPlayer(Vector3 position)
{
//We'll run at the player, however if we can't see him for 3 seconds, we return to our patrol
//route.
float timeout = 3.0f;
while(timeout > 0.0f)
{
MoveTowards(position);
//We've found the player!
if(CanSeeTarget())
{
yield return null;
}
timeout -= Time.deltaTime;
}
}
IEnumerator RotateTowards(Vector3 position)
{
SendMessage("SetSpeed", 0.0);
var direction = position - transform.position;
direction.y = 0;
if(direction.magnitude < .1)
{
yield return null;
}
//Otherwise we have a target that we can see. As such, we'll rotate
//towards our target.
var targetPoint = target.position;
var targetRotation = Quaternion.LookRotation(targetPoint -
transform.position, Vector3.up);
transform.rotation = Quaternion.Slerp(transform.rotation,
targetRotation, Time.deltaTime * 2.0f);
}
IEnumerator MoveTowards(Vector3 position)
{
var direction = position - transform.position;
direction.y = 0;
if(direction.magnitude < .5)
{
SendMessage("SetSpeed", 0.0);
yield return null;
}
//Otherwise we have a target that we can see. As such, we'll rotate
//towards our target.
var targetPoint = target.position;
var targetRotation = Quaternion.LookRotation(targetPoint -
transform.position, Vector3.up);
transform.rotation = Quaternion.Slerp(transform.rotation,
targetRotation, Time.deltaTime * 2.0f);
//Now we should modify our speed so that we slow down when we aren't facing our target.
var forward = transform.TransformDirection(Vector3.forward);
var speedModifier = Vector3.Dot(forward, direction.normalized);
speedModifier = Mathf.Clamp01(speedModifier);
//Now we actually move our character.
direction = forward * speed * speedModifier;
CharacterController controller = GetComponent<CharacterController>();
controller.SimpleMove(direction * speed);
SendMessage("SetSpeed", speed * speedModifier,
SendMessageOptions.DontRequireReceiver);
}
AutoWaypoint PickNextWaypoint(AutoWaypoint currentWaypoint)
{
//Let's find the waypoint where our character will have to turn the least.
//This is the direction the character is walking.
var forward = transform.TransformDirection(Vector3.forward);
//The closer our two vectors are, the larger the dot product shall be.
var best = currentWaypoint;
float bestDot = -10.0f;
foreach(AutoWaypoint cur in currentWaypoint.connected)
{
var direction = Vector3.Normalize(cur.transform.position - transform.position);
var dot = Vector3.Dot(direction, forward);
if(dot > bestDot && cur != currentWaypoint)
{
bestDot = dot;
best = cur;
}
}
return best;
}
}
I could, and I'm pretty sure I could simplify quite a few things in it. Of course that would ruin the point of this little project, namely converting the Javascript to C#.
Answer by Statement · Dec 26, 2011 at 01:40 PM
In AttackPlayer
you have this code:
while(true)
{
if(CanSeeTarget())
{
// Other code omitted
}
}
If CanSeeTarget
return false, then it will probably return false again after and lock up.
Also Patrol
seems to have a loop that will lock up. Coroutines doesn't work in C# as they do in JS. To branch into a child coroutine of a function you must yield return StartCoroutine(MoveTowards(waypointPosition));
etc. Just calling the coroutine function will not work.
while() loop will lock up the system until they yield or return. Yah, it took me a while to get used to C# Coroutine, I now often write a void-return launching function along with every Coroutine so it just happens no matter what I write.
Thanks, fixing up that code made it so that it didn't freeze upon starting.
Your answer
Follow this Question
Related Questions
Converting Autowaypoint (JS) to C# 1 Answer
The name 'Joystick' does not denote a valid type ('not found') 2 Answers
Distribute terrain in zones 3 Answers
How to access a static variable in c# from js 2 Answers
Reference a JS script in a C# script? 2 Answers