- Home /
Help Understanding Raycast
I am attempting to develop a replacement character controller to use with 2D games in Unity. The default character controller is a capsule and therefore the rounded edges make for annoying behavior near ledges and the mandatory z depth dependent on the size of the capsule does not play well with 2D games in a 3D environment. This one will be a box collider which will allow modifiable character z depths.
I am not using physics (via rigidbodies) in order to have complete control over collision detection. I am attempting to raycast from the edges of the box collider and limiting the ray casting to the direction the player is attempting to move (for performance). For instance if the player was moving right I am casting a ray from the right middle, top-right corner, and bottom-right corners to the right (the direction) at a distance of what the next movement would be. I am simply changing the position of the player's transform if no collision is detected. If a collision is detected I am returning CollisionFlags indicating where the collision took place and not moving the character to the newly desired position.
Currently all the code seems to be working well except the rays always return false. I have verified the following conditions:
My colliders are on the same z plane.
Drawing the ray indeed shows that it is moving in the correct direction and distance.
I have stopped using layers for now to ensure that incorrect use of LayerMask is not the issue.
I have made the colliders on my character and the test ground piece "fat" (changed them from 2D toolkit's default 0.2 to 20 for z-scale.
The player is starting well above the ground piece and is falling into and past the ground piece very slowly.
My code for movement works fine as I can move left and right and the correct Log statements (as seen in my code) display just as they should.
Currently I am not moving the character but I am letting gravity (my gravity) be applied in order to narrow the problem to only one of the conditional statements in my collision detection code.
void checkMovement() {
horizontal = Input.GetAxis ("Horizontal"); vertical = Input.GetAxis ("Vertical"); //...other code here but I can assure you this is being called as it should else if (!isJumping && !grounded) //apply gravity movement.y = -gravitySpeed; movement.z = 0.0f; //never move into the z axis //my code is attempting to mimic the next statement //lastCollision = playerCharController.Move(movement * Time.deltaTime); lastCollision = playerMove(movement * Time.deltaTime); } //Cache all variables for playerMove() public int collisionLayerToIgnore = 0; private float distance = 0.0f; private Vector3 colliderTop = new Vector3(0.0f, 0.0f, 0.0f); private Vector3 colliderBottom = new Vector3(0.0f, 0.0f, 0.0f); private Vector3 colliderLeft = new Vector3(0.0f, 0.0f, 0.0f); private Vector3 colliderRight = new Vector3(0.0f, 0.0f, 0.0f); private Vector3 right = new Vector3(1.0f, 0.0f, 0.0f); private Vector3 left = new Vector3(-1.0f, 0.0f, 0.0f); private Vector3 up = new Vector3(0.0f, 1.0f, 0.0f); private Vector3 down = new Vector3(0.0f, -1.0f, 0.0f); private CollisionFlags playerMove(Vector3 toThisPoint) { CollisionFlags flags = 0; //Not using layer masks right now //collisionLayerToIgnore = 1 << LayerMask.NameToLayer("LevelGeometry"); //Get the x and y values in world space for the edges of the box collider for convenience colliderTop.x = playerCollider.bounds.center.x; colliderTop.y = playerCollider.bounds.center.y + playerCollider.bounds.extents.y; colliderTop.z = playerCollider.bounds.center.z; colliderBottom.x = playerCollider.bounds.center.x; colliderBottom.y = playerCollider.bounds.center.y - playerCollider.bounds.extents.y; colliderBottom.z = playerCollider.bounds.center.z; colliderLeft.x = playerCollider.bounds.center.x - playerCollider.bounds.extents.x; colliderLeft.y = playerCollider.bounds.center.y; colliderLeft.z = playerCollider.bounds.center.z; colliderRight.x = playerCollider.bounds.center.x + playerCollider.bounds.extents.x; colliderRight.y = playerCollider.bounds.center.y; colliderRight.z = playerCollider.bounds.center.z; //Cast rays from the edge middle and corners at a distance //equal to the x value of the given point ignoring all colliders not in collisionLayer. //Adding the vectors representing the centers of two sides results in a vector that //represents the corner between those two sides. //Check diagonal movement if (toThisPoint.x > 0 && toThisPoint.y > 0) { //...some code } else if (toThisPoint.x > 0 && toThisPoint.y < 0) { //...some code } else if (toThisPoint.x < 0 && toThisPoint.y > 0) { //...some code } else if (toThisPoint.x < 0 && toThisPoint.y < 0) { //...some code } //Check strict horizontal movement else if (toThisPoint.x > 0) { //..some code } else if (toThisPoint.x < 0) { //..some code } //Check strict vertical movement //Here is where the magic "isn't" happening else if (toThisPoint.y > 0) { Debug.Log("toThisPoint.y < 0"); distance = -1 * toThisPoint.y; //distance = Mathf.Infinity; Debug.DrawRay(colliderBottom, new Vector3(0.0f, distance, 0.0f), Color.red, 100.0f); //...as you can see I have ignored layermask for now if (Physics.Raycast(colliderBottom, down, distance)//, collisionLayerToIgnore) ||Physics.Raycast(colliderBottom + colliderRight, down, distance)//, collisionLayerToIgnore) ||Physics.Raycast(colliderBottom + colliderLeft, down, distance))//, collisionLayerToIgnore)) flags = flags & CollisionFlags.Below; } //.. up vertical direction code //If no collision will happen then move the player to the new location if (flags == 0) { playerTransform.position = playerTransform.position + toThisPoint; Debug.Log("Collision Flags: None"); } if ((flags & CollisionFlags.Sides) == CollisionFlags.Sides) Debug.Log("Collision Flags: Sides"); else if ((flags & CollisionFlags.Above) == CollisionFlags.Above) Debug.Log("Collision Flags: Above"); else if ((flags & CollisionFlags.Below) == CollisionFlags.Below) Debug.Log("Collision Flags: Below"); return flags; }
Like I said, currently flags always gets set to none but when I don't press left or right control reaches the "toThisPoint.y < 0" Log statement and draws the ray correctly.
I have tried calling the methods from within Update()/FixedUpdate() with the same results. I also tried using a toggle to only used every other Update()/FixedUpdate().
Am I not grasping the function of Physics.Raycasting correctly. I have spend a full night with no sleep researching everything I could find on using the different Physics.Raycast methods and have scoured the forums for answers. If they are out there and I missed them please direct me appropriately.
Any ideas will be much appreciated.
I would advise looking at the Physics.Raycast in the Unity Scripting Documentation for more information.
Answer by Owen-Reynolds · Feb 17, 2013 at 10:43 PM
"Fat" colliders? There's a special rule that rays don't hit anything they start inside of. So maybe the ground is too fat?
Sometimes easier to start with something working. Maybe set up a fresh scene with one object using a very simple script to raycast towards another:
public string hitWho;
RaycastHit H;
if(Physics.Raycast(transform.position, transform.forward, out H, 20) {
hitWho=H.transform.name;
}
else hitWho="";
Running in scene view, turn it and move it around. Add the bottom/sides math for start pos and see if that works... . Eventually you will add one more thing that breaks it, and know exactly what the problem was.
Answer by Shubius · Feb 20, 2013 at 07:34 PM
So for anyone who has issues with getting raycasting to work like I did the solution MAY be simple. Unfortunately all raycasting is disabled be default in the editor between layers (including custom layers!!!). I could not find this in the documentation and only accidently noticed it while changing the gravity for my scene. Just "UNCHECK" the box in the "Ignore Raycast" row representing the layer you would like to allow raycasting to work between and you will be fine.