Hi, I'm stuck in a little problem. I have a code that make a object follow the Player. Now I need this object to ignore any collisions and follow player. And the object will get to the original position it was in after some time. Here's my code, please help. Code (JavaScript): #pragma strict var target : Transform; //the enemy's target var moveSpeed = 3; //move speed var rotationSpeed = 3; //speed of turning var distance = 3; //max distance from object to player var myTransform : Transform; //current transform data of this enemy var startPoint : Vector3; function Start() { target = GameObject.FindWithTag("Player").transform; //target the player startPoint = myTransform.position; //get the start point of the object } function Update () { var dist = Vector3.Distance(target.position, myTransform.position); //get the distance from the player to object myTransform.rotation = Quaternion.Slerp(myTransform.rotation, Quaternion.LookRotation(target.position - myTransform.position), rotationSpeed*Time.deltaTime); //rotate to look at the player var fwd = transform.TransformDirection(Vector3.forward); if(Physics.Raycast(transform.position, fwd, 2)) { print("There is something infront of the object, means, the script works!"); } if(dist > distance) { myTransform.position += myTransform.forward * moveSpeed * Time.deltaTime; //move towards the player } }
How do you mean ignore collisions? Obstacle avoidance (moving around objects)? or Phantom (moving through objects)?
if you cast a ray in front of your character a short distance, and if it is hitting something, check if it's the player and ignore the hit, but if it's not the player, choose a direction, if you use two rays, off to the sides of the character object, you can use that to determine left or right turn. It's important that if a ray is hitting something that's not the player, it overrides any other movement, and the character always walks forward while going where it's going, with respect to slowing and stopping when necessary of course. If you need an example of what I mean don't hesitate to ask. Hope this helps!
So I'm rather novice at this myself, but I'll attempt to provide an answer. Ray casting has a "layer mask" that can ignore layers; this is based upon a principle called "bit shifting". If your layer mask is 0, nothing will be counted as an obstruction. If you want to hit only the default layer, your mask (represented by an int in code) should be 1; if you want to ignore a custom defined layer (let's say the 8th layer), then you should change the bits by saying Code (CSharp): int mask = 1; mask = mask << 8; if (Physics.Raycast(ray, out hit, rayCastDistance, mask)) ... etc This amounts to "ignore layer 1 and layer 8". I hope that this helps. Also, that I'm mostly correct =p
To elaborate, you can select an item in the browser and find the "layer" button on the inspector in the upper right. There you can select and create layers. If you put a collider, or a mesh, or whatever into that layer (it can be in more than one) then you can reference that layer to be ignored by your raycast.
Code (CSharp): if (Physics.Raycast(ray, out hit, rayCastDistance, mask)) ... etc [/QUOTE] What should be the values replaced to "ray, out hit and mask"?
Well, in your case I think you should just add the last parameter to your existing code, e.g. Code (CSharp): if(Physics.Raycast(transform.position, fwd, 2, mask)) You'll still need to configure your layers as described in my last post. That's probably going to involve a bit of trial and error.
The value of mask should start at "1". Any int in Mono is a 32-bit integer, meaning that you can represent 32 different layers. Masks are viewed by the engine as a set of bits. Each bit says whether to count the corresponding layer as an obstacle, where the bits represent 0 for false or 1 for true. As you can see in the layer inspector, the number of the first layer (default layer) is 0. Here is the bit content of an int with value 1: Code (CSharp): 000000000000000000000000000000001 The "1" is at the 0th index. A mask of "1" represents only the default layer, the one corresponding to the 0th bit position. In coding, we start at 0 rather than 1, because we are monkeys. Binary is more complex than I seem to be capable of explaining right now, but it's worth reading up on. So, for your case, the simple explanation is that your mask should probably be 1, meaning that your follower would be blocked by the default layer -- which includes terrain -- or 0, meaning that your follower ignores absolutely everything. Edit: Edited for derp. Beer...
I found this post, which is pretty thorough, and has lots of input from other developers: http://answers.unity3d.com/questions/8715/how-do-i-use-layermasks.html
Nope it's still moving moving through objects. Here's what I have done so far, dont laugh at it I'm just a begginner. Code (JavaScript): #pragma strict var target : Transform; //the enemy's target var moveSpeed = 3; //move speed var rotationSpeed = 3; //speed of turning var distance = 3; //max distance from object to player var myTransform : Transform; //current transform data of this enemy var startPoint : Vector3; //get start position var mask : int = 1; function Start() { target = GameObject.FindWithTag("Player").transform; //target the player startPoint = myTransform.position; //get the start point of the object } function Update () { var dist = Vector3.Distance(target.position, myTransform.position); //get the distance from the player to object myTransform.rotation = Quaternion.Slerp(myTransform.rotation, Quaternion.LookRotation(target.position - myTransform.position), rotationSpeed*Time.deltaTime); //rotate to look at the player var fwd = transform.TransformDirection(Vector3.forward); if(Physics.Raycast(transform.position, fwd, 2)) { print("There is something infront of the object, means, the script works!"); } if(dist > distance) { myTransform.position += myTransform.forward * moveSpeed * Time.deltaTime; //move towards the player } if(Physics.Raycast(transform.position, fwd, 2, mask)); }
Oh, I see. Sorry I wasn't explicit in my explanation =) I meant to say that you should modify line 20 in your code. Your code snippet should look like this: Code (CSharp): var target : Transform; //the enemy's target var moveSpeed = 3; //move speed var rotationSpeed = 3; //speed of turning var distance = 3; //max distance from object to player var myTransform : Transform; //current transform data of this enemy var startPoint : Vector3; //get start position var mask : int = 1; function Start() { target = GameObject.FindWithTag("Player").transform; //target the player startPoint = myTransform.position; //get the start point of the object } function Update () { var dist = Vector3.Distance(target.position, myTransform.position); //get the distance from the player to object myTransform.rotation = Quaternion.Slerp(myTransform.rotation, Quaternion.LookRotation(target.position - myTransform.position), rotationSpeed*Time.deltaTime); //rotate to look at the player var fwd = transform.TransformDirection(Vector3.forward); if(Physics.Raycast(transform.position, fwd, 2, mask)) { print("There is something infront of the object, means, the script works!"); } if(dist > distance) { myTransform.position += myTransform.forward * moveSpeed * Time.deltaTime; //move towards the player } } See the mask in the first IF statement? That's where we use the mask to decide which layers we test. Since almost everything starts in the default layer, this should mean that your follower will not follow through objects unless you've added custom layers or purposely changed the layers of objects in your map. I'm new too, so this is kind of like the whole "deaf leading the blind" thing =) Still, I think this is what your code needs.
Have you added colliders to your obstacles (and your follower)? All the above code should do is keep your follower from "seeing" your character through obstacles. Colliders prevent objects from moving through other objects (that have colliders).
Also, just to be sure, you might try using Physics.LineCast(position_player, position_follower, out hit, mask) instead of Physics.RayCast. It is a more explicit way of attempting to draw a line between two absolute coordinates.
Yeah, obstacles and the follower has colliders they are basically just cubes. xD. And what does "out hit" does? And I wanted the object (follower) to follow player avoiding obstacles. The follower is a AI.
Right, so what we've done so far should work. Another thing to check is whether your scripts are fully compiling. If you open MonoDevelop and hit F8 it will attempt to build. If you get a red x in the top bar it means you have errors and that would prevent your scripts from updating. Sorry, I'm not familiar enough with Unity yet to zero in on a problem =)
Sorry for the late reply, different time zones! Anyhow, I can only provide examples in C#, but the principle is basically the same in JS, since the functions are the same, there's just a few syntax differences. Anyhow, here's the example: Code (CSharp): //ObstacleAvoidanceAI.cs //C# using UnityEngine; using System.Collections; public class ObstacleAvoidanceAI : MonoBehaviour { public float moveSpeed; //speed we walk/run at public float turnSpeed; //speed we turn at private float move; //use to keep track of move velocity private float turn; //used to keep track of turn velocity public float rayAngle; //angle between the two rays private float halfAngle; //for precomputing half of the angle set for the rays public float rayRange; //how far to cast our rays from the character private Rigidbody myTransform; //cache for our transform component private Rigidbody myRigidbody; //cache for our rigidbody component public Transform target; //the target to track towards public void Awake() { myTransform = transform; myRigidbody = rigidbody; halfAngle = rayAngle / 2.0f; } public void FixedUpdate() { if (target == null) { return; } //early out if we have no target turn = 0.0f; //default no turning move = moveSpeed; //default moving forward Ray[] rays = new Ray[] { //rays[0] = left ray new Ray(myRigidbody.position, myRigidbody.rotation * (Quaternion.Euler(0, -halfAngle, 0) * Vector3.forward)), //rays[1] = right ray new Ray(myRigidbody.position, myRigidbody.rotation * (Quaternion.Euler(0, halfAngle, 0) * Vector3.forward)) }; RaycastHit[] hitTargets = new RaycastHit[2]; bool[] hits = new bool[] { //cast and store left ray Physics.Raycast(rays[0], out hitTargets[0], rayRange), //cast and store right ray Physics.Raycast(rays[1], out hitTargets[1], rayRange) } bool cantGoForward = hits[0] && hits[1]; bool cantGo = hits[0] || hits[1]; if (cantGoForward) { turn += turnSpeed } //both rays are hitting, turn any direction (e.g. right) else if (hits[0]) { turn += turnSpeed; } //left is hitting, so turn right else if (hits[1]) { turn -= turnSpeed; } //right is hitting, so turn left if (cantGoForward) { move = 0.0f; } //if both rays are hitting, assume we can't move without turning else if (cantGo) { move *= 0.5f; } //if either ray is hitting, assume we can't move without turning enough else {//we're good to go //direction to target Vector3 direction = target.position - myRigidbody.position; //distance to target float distance = direction.magnitude; //normalize direction to unit length so we can use dot product direction = direction.normalized; //calculate dot products for controlling turn factor float dotForward = Vector3.Dot(direction, myTransform.forward); float dotRight = Vector3.Dot(direction, myTransform.right); if (dotForward < 0.1f) {//target is behind us turn += turnSpeed; //keep turning until they're in front } else {//target is in front of us turn += turnSpeed * dotRight; //right = +1, left = -1; } } //we have our turn and move factors, now do it Vector3 newPosition = myTransform.forward * move; Vector3 newPosition = myRigidbody.position + (newPosition * Time.fixedDeltaTime); Quaternion newRotation = Quaternion.Euler(0, turn * Time.fixedDeltaTime, 0); newRotation = myRigidbody.rotation * (myRigidbody.rotation * newRotation); myRigidbody.MovePosition(newPosition); myRigidbody.MoveRotation(newRotation); } } I hope this helps!
Wow thanks. So when this script is attached the object follows the player while avoiding obstacles? I tried using Nev mesh, though the object avoid obstacles it has difficulties keeping up with player, its trying to come through obstacles which is avoided by nev mesh! xD Anyway, I'll try this when I get back on PC, thanks. And I'll try converting it to JS. Thanks again.
Casting more than two rays will of course produce a more accurate result, two rays should be nice to get the idea behind what is happening though.
I'm getting some errors, since I don't know about C# I don't know what to do. Here's the errors, 1. Follow.cs(53,20): error CS1525: Unexpected symbol `bool' 2. Follow.cs(55,56): error CS1525: Unexpected symbol `}', expecting `;' 3. Follow.cs(86,25): error CS0128: A local variable named `newPosition' is already defined in this scope Here's the code. Code (CSharp): using UnityEngine; using System.Collections; public class Follow : MonoBehaviour { public float moveSpeed; //speed we walk/run at public float turnSpeed; //speed we turn at private float move; //use to keep track of move velocity private float turn; //used to keep track of turn velocity public float rayAngle; //angle between the two rays private float halfAngle; //for precomputing half of the angle set for the rays public float rayRange; //how far to cast our rays from the character private Rigidbody myTransform; //cache for our transform component private Rigidbody myRigidbody; //cache for our rigidbody component public Transform target; //the target to track towards public void Awake() { myTransform = transform; myRigidbody = rigidbody; halfAngle = rayAngle / 2.0f; } public void FixedUpdate() { if (target == null) { return; } //early out if we have no target turn = 0.0f; //default no turning move = moveSpeed; //default moving forward Ray[] rays = new Ray[] { //rays[0] = left ray new Ray(myRigidbody.position, myRigidbody.rotation * (Quaternion.Euler(0, -halfAngle, 0) * Vector3.forward)), //rays[1] = right ray new Ray(myRigidbody.position, myRigidbody.rotation * (Quaternion.Euler(0, halfAngle, 0) * Vector3.forward)) }; RaycastHit[] hitTargets = new RaycastHit[2]; bool[] hits = new bool[] { //cast and store left ray Physics.Raycast(rays[0], out hitTargets[0], rayRange), //cast and store right ray Physics.Raycast(rays[1], out hitTargets[1], rayRange) } bool cantGoForward = hits[0] && hits[1]; bool cantGo = hits[0] || hits[1]; if (cantGoForward) { turn += turnSpeed } //both rays are hitting, turn any direction (e.g. right) else if (hits[0]) { turn += turnSpeed; } //left is hitting, so turn right else if (hits[1]) { turn -= turnSpeed; } //right is hitting, so turn left if (cantGoForward) { move = 0.0f; } //if both rays are hitting, assume we can't move without turning else if (cantGo) { move *= 0.5f; } //if either ray is hitting, assume we can't move without turning enough else {//we're good to go //direction to target Vector3 direction = target.position - myRigidbody.position; //distance to target float distance = direction.magnitude; //normalize direction to unit length so we can use dot product direction = direction.normalized; //calculate dot products for controlling turn factor float dotForward = Vector3.Dot(direction, myTransform.forward); float dotRight = Vector3.Dot(direction, myTransform.right); if (dotForward < 0.1f) {//target is behind us turn += turnSpeed; //keep turning until they're in front } else {//target is in front of us turn += turnSpeed * dotRight; //right = +1, left = -1; } } //we have our turn and move factors, now do it Vector3 newPosition = myTransform.forward * move; Vector3 newPosition = myRigidbody.position + (newPosition * Time.fixedDeltaTime); Quaternion newRotation = Quaternion.Euler(0, turn * Time.fixedDeltaTime, 0); newRotation = myRigidbody.rotation * (myRigidbody.rotation * newRotation); myRigidbody.MovePosition(newPosition); myRigidbody.MoveRotation(newRotation); } }