x


Raycasting Collision for player movement in a maze

I'm using various methods for movement physics in a Pacman clone, to learn some different techniques (yes i know its probably overkill but its all for the sake of learning ;) ).

I am using 4 Rays for determining where the player can and cannot move, and when they can change direction.

The effect I am aiming for is best explained with an example:

Pacman is in a horizontal corridor which has T junctions at both ends, if the user presses right or left theyll start travelling along, and if they then press up or down this will be stored till they actual reach a valid turning point, then start moving in the new direction. alt text

I have extended the draw of the above rays for debugging purposes, their actual length reaches the edge of the Players Collider.

I have got this mostly working, the only problem I have encountered is that at corners it seems to count it as a valid turning when only one of the Rays has passed the corner; as shown in the picture above, rather than waiting till both Rays are clear and Pacman is fully in the vertical corridor. My position correcting means that when this happens pacman will just halt rather than actually clipping the wall.

here is Update()

void Update()
{
    #region Controls

    // Set what the next direction the player intends to move next
    if (Input.GetButtonDown("Up"))
    {
        nextDirection = Vector3.up;
    }
    if (Input.GetButtonDown("Right"))
    {
        nextDirection = Vector3.right;
    }
    if (Input.GetButtonDown("Down"))
    {
        nextDirection = Vector3.down;
    }
    if (Input.GetButtonDown("Left"))
    {
        nextDirection = Vector3.left;
    }
    // If pressing the direction youre already going in cancel it
    if (nextDirection != Vector3.zero && nextDirection == currDirection)
    {
        nextDirection = Vector3.zero;
    }

    #endregion
    #region Reposition

    // If moving in a direction
    if (currDirection != Vector3.zero)
    {
        // Check if reached a wall
        wallAhead = AnyWalls(currDirection);

        // if im travelling and there are walls ahead stop
        if (wallAhead)
        {
            // correct position to prevent sinking into wall
            if (currDirection == Vector3.left || currDirection == Vector3.right)
            {
                myTransform.position = new Vector3(Mathf.Round(myTransform.position.x), myTransform.position.y);
            }
            if ((currDirection == Vector3.up || currDirection == Vector3.down))
            {
                myTransform.position = new Vector3(myTransform.position.x, Mathf.Round(myTransform.position.y));
            }
            // then stop moving
            nextDirection = Vector3.zero;
            currDirection = Vector3.zero;
        }

        // Move in Direction
        if (currDirection != Vector3.zero)
        {
            myTransform.position = Vector3.Lerp(myTransform.position, myTransform.position + currDirection * speed, Time.deltaTime);
        }

        // Correct minor misplacement
        if (currDirection == Vector3.left || currDirection == Vector3.right)
        {
            myTransform.position = new Vector3(myTransform.position.x, Mathf.Round(myTransform.position.y));
        }
        if ((currDirection == Vector3.up || currDirection == Vector3.down))
        {
            myTransform.position = new Vector3(Mathf.Round(myTransform.position.x), myTransform.position.y);
        }
    }

    #endregion
    #region Turning

    // Has user pressed a direction
    if (nextDirection != Vector3.zero)
    {
        // Check for walls when Player wants to turn
        wallInNextDirection = AnyWalls(nextDirection);

        // If not moving and walls in pressed direction ignore the direction
        if (wallInNextDirection && currDirection == Vector3.zero)
        {
            nextDirection = Vector3.zero;
        }

        // Check if it is possible to change direction
        if (!wallInNextDirection)
        {
            Debug.Log("Turning");
            currDirection = nextDirection;
        }
        else Debug.Log("Cant Turn");
    }

    #endregion
}

And this is the function called to cast rays

bool AnyWalls(Vector3 dir)
{   
    Vector3 origin1 = new Vector3();
    Vector3 origin2 = new Vector3();

    if (dir == Vector3.up | dir == Vector3.down)
    {
        origin1.x = (myTransform.position.x + ( (myCollider.size.x / 2)) - 0.05f);
        origin1.y = myTransform.position.y;
        origin2.x = (myTransform.position.x - ( (myCollider.size.x / 2)) + 0.05f);
        origin2.y = myTransform.position.y;
    }
    else /* Direction == Left / Right */
    {
        origin1.x = myTransform.position.x;
        origin1.y = (myTransform.position.y + ( (myCollider.size.x / 2)) - 0.05f);
        origin2.x = myTransform.position.x;
        origin2.y = (myTransform.position.y - ( (myCollider.size.x / 2)) + 0.05f);
    }

    Debug.DrawRay(origin1,dir * myCollider.size.x);
    Debug.DrawRay(origin2,dir * myCollider.size.x);
    return (Physics.Raycast(origin1, dir, (myCollider.size.x / 2) + 0.1f, kDefaultRaycastLayers) &&
            Physics.Raycast(origin2, dir, (myCollider.size.x / 2) + 0.1f, kDefaultRaycastLayers));
}

As you can see that should return True when both rays are clear. It is likely I have just made some stupid mistake in there, and simply need a fresh pair of eyes to find it.

Again theres probably an approach more suitable for Pacman, but I'm just trying to learn all the different methods of doing things using simple projects.

Thanks for any replies!

Edit 1: Corrected typos and updated the variable names in the Update() snippet to be more easily readable Edit 2: I have included in the Update() snippet some of the Debug messages I am using which output "Can't Turn" and "Turning" these demonstrated to me that it was indeed attempting to turn early when a corner is reached.

more ▼

asked Jul 25 '10 at 08:59 PM

sovalopivia gravatar image

sovalopivia
393 2 6 15

(comments are locked)
10|3000 characters needed characters left

3 answers: sort voted first

Looking at the code briefly, I was unable to find an apparent error in your implementation. The AnyWalls() function looks correct.

However, some thoughts:

Thought #1: Comparing Vectors with == is risky at best, and can result in really annoying bugs. This is due to floating point errors that arise in computations and/or values defined that don't necessarily reflect an exact representable 32-bit float value. Even more subtle, is that 32-bit floats are put in high precision CPU registers, so you can have different results depending on whether the value goes by memory or not during computation.

This can generate VERY weird bugs, because usually what happens, when you don't get the results you expect, is that you add logging to track the value. This potentially alters the results of the computation because the value goes through memory and back into registers at lower precision. When you remove your debug logging, computation is probably performed in high precision registers all the way, which gives subtly different results. While Unity probably implements == operator with some sort of epsilon, you can still end up with it returning false for very similar vectors. I recommend you write a little function to make a coarse equality check, so that you can be confident that it isn't flaky for your needs.

Thought #2: I recommend you always put {} brackets around if/while/else/etc blocks. Not doing so can result in mistakes, resulting in bugs that are usually subtle and/or hard to find.

I'm sorry I couldn't help you more with the specific bug.

more ▼

answered Jul 25 '10 at 10:05 PM

Magnus Wolffelt gravatar image

Magnus Wolffelt
457 9 11 26

Thanks for the advice, I know missing out brackets is a bit questionable, I was trying to cut down the conde length to make it reasonable to embed here ;)

Also how would I go about overloading the == operator for Vector3, in the cleanest way?

Jul 25 '10 at 11:24 PM sovalopivia

Just use a local function to compare:

void IsAlmostEqual(Vector3 v0, Vector3 v1) { return Math.Abs(v0.x - v1.x) < 0.1f && ... (for all components) }

Jul 26 '10 at 05:30 PM Magnus Wolffelt

Didn't think of that, I actually looked into overriding the == operator of the Vector3 class, but i guess thats unnecessary thanks again...

I doubt it will make any difference to this project, the only Vector3 comparisons I'm currently doing are using single unit directional vectors, eg Vector3.up but I will keep this in mind for the future.

Jul 26 '10 at 10:01 PM sovalopivia
(comments are locked)
10|3000 characters needed characters left

There should be a wall if either ray is true not if both are true facepalm

fixed by changing the return line in the AnyWalls() function && to a ||

Before:

return (Physics.Raycast(origin1, dir, (myCollider.size.x / 2) + 0.1f, kDefaultRaycastLayers) &&
        Physics.Raycast(origin2, dir, (myCollider.size.x / 2) + 0.1f, kDefaultRaycastLayers));

After:

return (Physics.Raycast(origin1, dir, (myCollider.size.x / 2) + 0.1f, kDefaultRaycastLayers) ||
        Physics.Raycast(origin2, dir, (myCollider.size.x / 2) + 0.1f, kDefaultRaycastLayers));
more ▼

answered Jul 27 '10 at 01:02 PM

sovalopivia gravatar image

sovalopivia
393 2 6 15

(comments are locked)
10|3000 characters needed characters left

probably a faster way to determin colission like this would be to us an array map of 0s and 1s 0 beign no wall 1 beign a wall

more ▼

answered Oct 13 '10 at 06:10 PM

Vincent 1 gravatar image

Vincent 1
17 1 1 1

I wasn't looking for an ideal method for collision detection this was an exercise for myself on doing raycasting.

Oct 14 '10 at 03:47 PM sovalopivia
(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x5081
x2498
x1529
x81

asked: Jul 25 '10 at 08:59 PM

Seen: 2447 times

Last Updated: Jul 26 '10 at 12:13 AM