- Home /
Enemy AI using raycasting.. **Solved**
HI, I have been looking into all the ways of doing Ai for an enemy character.. What I have decided upon is to use raycasting to position the enemy and let him just randomly wonder about the room, and when he sees you to go into attack mode and chase you. I have a basic attack script working(sort of) but what I want it to do is, if the enemy looses sight of you to go to the last known position he saw you at and go back into Random Wonder mode.
What I have accomplished right now is to have an invisible GO above the enemy which has a smoothLookAt script attached to look directly at the player and cast a ray of 100 length towards the character. If the ray hits the player, he simply follows the player(geting stuck on walls, falling behind, etc.)
for Random Wonder mode, I need the enemy to happily explore the room without getting stuck on any walls.. sort of like those robotic hoovers make their way around your house, but, you get the idea? Anyway, my code I have right now is this:
//EnemylockOn.js
//attatched to the invisible GO above the enemy
var player : Transform;
var LKplayerPos : Transform;
function Update ()
{
var hit : RaycastHit;
if(Physics.Raycast(transform.position,transform.TransformDirection(Vector3.forward) , hit, 50))
{
if(hit.collider.gameObject.tag == "Player")
{
LKplayerPos = player;
print("Player found");
}
else if(hit.collider.tag == "Wall")
{
LKplayerPos = null;
print("Player lost");
}
}
Enemy_ai_v2.player = LKplayerPos;
}
//EnemyAIv2.js
//attatched to the enemy its self.
var speed : float = 6.0;
var rotation : float = 8.0;
private var target : Transform;
private var myTransform : Transform;
static var player : Transform;
function Start()
{
myTransform = transform;
}
function Update()
{
if(player)
{
target = player;
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.LookRotation(target.position - myTransform.position),
rotation * Time.deltaTime);
myTransform.position += myTransform.forward * speed * Time.deltaTime * 2;
}
}
I know what I am asking is very tricky, and that's fine, I have been using Unity for over two years now and I do know my way around the program.
Many, many thanks --TG106
UPDATE
this is my code right now, the enemy has basic RW working and a more precise status switching, also a respawn. Any suggestions are very welcome.
var hits : int;
var rayRange : float;
var speed : float = 6.0;
var rotation : float = 8.0;
var fieldOfViewRange : float = 68.0;
var minPlayerDetectDistance : float = 4.0;
var status : int = 0;
private var csp = false;
private var repos = false;
/////////////////////
// STATUS KEY:
// 0 = sleep
// 1 = chase
// 2 = randomWonder.
/////////////////////
var player : Transform;
var target : Transform;
private var myTransform : Transform;
private var spawnPos : Vector3;
private var rayDirection = Vector3.zero;
static var reset = false;
function Start()
{
myTransform = transform;
spawnPos = transform.position;
status = 0;
// Error checks
if(player == null)
Debug.LogError("Player is not set on " + this.name);
if(target == null)
Debug.LogError("target is not set on " + this.name);
}
function Update()
{
// Setting local variables
var hit : RaycastHit;
var dtt = Vector3.Distance(transform.position, target.position);
var distanceToPlayer = Vector3.Distance(transform.position, player.position);
rayDirection = player.position - transform.position;
// Failsafe
if((status != 2) && (Physics.Raycast(transform.position, transform.forward, hit, 2)))
{
target.position = transform.position;
}
if(transform.position.y <= -20)
{
transform.position = spawnPos;
}
// Catch the player if he is within view
if((Vector3.Angle(rayDirection, transform.forward)) < fieldOfViewRange)
{
if (Physics.Raycast (transform.position, rayDirection, hit, rayRange))
{
if ((hit.transform.tag == "Player") && (hit.transform.tag != "Wall"))
{
target.position = player.position;
csp = true;
}
else
{
csp = false;
}
}
}
// Catch the player if he sneaks up behind
if(Physics.Raycast (transform.position, rayDirection, hit))
{
Debug.DrawRay(transform.position, rayDirection, Color.green);
if((hit.transform.tag != "Player") && (distanceToPlayer <= minPlayerDetectDistance))
{
target.position = player.position;
}
}
// Status setting
if(dtt <= 3 && csp)
{
status = 0;
}
if(dtt > 3 && csp)
{
status = 1;
}
if(dtt <= 3 && !csp)
{
status = 2;
}
// Status Control
if(csp)
hits = 0;
switch(status)
{
case 0:
break;
case 1:
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.LookRotation(target.position - myTransform.position),
rotation * Time.deltaTime);
myTransform.position += myTransform.forward * speed * Time.deltaTime * 2;
break;
case 2:
RandomWonderMode();
break;
default:
Debug.LogError("Unrecognised enemy status " + status);
break;
}
if(reset)
{
Reset();
}
}
function RandomWonderMode()
{
var pp = player.positon;
var mp = transform.position;
var dtt = Vector3.Distance(transform.position, target.position);
var hit : RaycastHit;
if(!csp)
{
if(dtt > 3)
{
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.LookRotation(target.position - myTransform.position),
rotation * Time.deltaTime);
myTransform.position += myTransform.forward * speed * Time.deltaTime;
}
else
{
repos = true;
}
if(Physics.Raycast (transform.position, transform.forward, hit, 2) || repos)
{
var pos = Vector3(Random.Range(-50,50), 1, Random.Range(-50,50));
target.position = pos;
repos = false;
hits ++;
}
if(hits > 100 && !csp)
{
hits = 0;
transform.position = spawnPos;
}
}
}
function Reset()
{
transform.position = spawnPos;
hits = 0;
status = 2;
reset = false;
// Error checks
if(player == null)
{
Debug.LogError("Player is not set on " + this.name);
}
if(target == null)
{
Debug.LogError("target is not set on " + this.name);
}
}
Answer by Owen-Reynolds · Mar 18, 2012 at 05:39 PM
Some comments:
o Don't null out lastKnownPlayerPos
when you see a wall. The entire point of saving LKPPos is for when you finally see a wall. A common trick is to have a "wanderTarget." When you first lose the player, set wanderTarget to LKplayerPos (and when you reach wanderTarget, set the next one randomly.) You'd need a "player seen last frame" var to check for "I just lost him."
o Probably don't look straight ahead to check if you see the player (unless the player is huge.) A lot of people check the angle (between my forward, and world angle from me to player) and then raycast directly to the player, for visibility.
o You don't need an extra gameObject doing the raycast, if you don't want. Your NPC script can just raycast from EmptyChildWhereMyEyesAre
in whatever direction you like.
o transform.TransformDirection(Vector3.forward)
has a shortcut: transform.forward
that can make programs a little easier to read.
Hi, those are good points, if you could just give me an example on what I should do to code the first two points? I have fixed the last two..
Thanks --TG106 ps, thanks for the .forward shortcut. Totally forgot about that :P
Search here for "AI raycast." Not too many complete solutions, but some with the right idea. http://answers.unity3d.com/questions/15735/field-of-view-using-raycasting.html has "raycast to player" and an angle-check, for example.
Thanks alot man ;) got the AI sorted now. You have been a great help, seriously you have. --TG106
Answer by davedx · Mar 18, 2012 at 07:37 PM
You probably want to look into some kind of better path finding. The common algorithm used to path between two points on a flat surface is A*. Unity 3.5 comes with a nice easy to use navigation mesh system, check it out here: http://unity3d.com/support/documentation/Manual/Navmesh%20and%20Pathfinding.html
It's for Pro only unfortunately. Otherwise, implementing A* and its subsystems is excellent practise and great experience to have.
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
FPS Problem Help! 0 Answers
Different enemy types script design 1 Answer
Enemy gets through wall over time, colliders working. 1 Answer