Collision Detection problems in Voxel Engine (XNA)

Posted by Darestium on Game Development See other posts from Game Development or by Darestium
Published on 2012-01-18T00:57:33Z Indexed on 2012/07/08 21:24 UTC
Read the original article Hit count: 269

Filed under:
|
|
|
|

I am creating a minecraft like terrain engine in XNA and have had some collision problems for quite some time. I have checked and changed my code based on other peoples collision code and I still have the same problem. It always seems to be off by about a block. for instance, if I walk across a bridge which is one block high I fall through it. Also, if you walk towards a "row" of blocks like this:

enter image description here

You are able to stand "inside" the left most one, and you collide with nothing in the right most side (where there is no block and is not visible on this image).

Here is all my collision code:

    private void Move(GameTime gameTime, Vector3 direction)
    {
        float speed = playermovespeed * (float)gameTime.ElapsedGameTime.TotalSeconds;

        Matrix rotationMatrix = Matrix.CreateRotationY(player.Camera.LeftRightRotation);
        Vector3 rotatedVector = Vector3.Transform(direction, rotationMatrix);

        rotatedVector.Normalize();

        Vector3 testVector = rotatedVector;
        testVector.Normalize();

        Vector3 movePosition = player.position + testVector * speed;
        Vector3 midBodyPoint = movePosition + new Vector3(0, -0.7f, 0);
        Vector3 headPosition = movePosition + new Vector3(0, 0.1f, 0);

        if (!world.GetBlock(movePosition).IsSolid &&
            !world.GetBlock(midBodyPoint).IsSolid &&
            !world.GetBlock(headPosition).IsSolid)
        {
            player.position += rotatedVector * speed;
        }

        //player.position += rotatedVector * speed;
    }

...

    public void UpdatePosition(GameTime gameTime)
    {
        player.velocity.Y += playergravity * (float)gameTime.ElapsedGameTime.TotalSeconds;

        Vector3 footPosition = player.Position + new Vector3(0f, -1.5f, 0f);
        Vector3 headPosition = player.Position + new Vector3(0f, 0.1f, 0f);

        // If the block below the player is solid the Y velocity should be zero
        if (world.GetBlock(footPosition).IsSolid ||
            world.GetBlock(headPosition).IsSolid)
        {
            player.velocity.Y = 0;
        }

        UpdateJump(gameTime);
        UpdateCounter(gameTime);
        ProcessInput(gameTime);

        player.Position = player.Position + player.velocity * (float)gameTime.ElapsedGameTime.TotalSeconds;
        velocity = Vector3.Zero;
    }

and the one and only function in the camera class:

    protected void CalculateView()
    {
        Matrix rotationMatrix = Matrix.CreateRotationX(upDownRotation) * Matrix.CreateRotationY(leftRightRotation);
        lookVector = Vector3.Transform(Vector3.Forward, rotationMatrix);

        cameraFinalTarget = Position + lookVector;

        Vector3 cameraRotatedUpVector = Vector3.Transform(Vector3.Up, rotationMatrix);
        viewMatrix = Matrix.CreateLookAt(Position, cameraFinalTarget, cameraRotatedUpVector);
    }

which is called when the rotation variables are changed:

    public float LeftRightRotation
    {
        get { return leftRightRotation; }
        set
        {
            leftRightRotation = value;
            CalculateView();
        }
    }

    public float UpDownRotation
    {
        get { return upDownRotation; }
        set
        {
            upDownRotation = value;
            CalculateView();
        }
    }

World class:

    public Block GetBlock(int x, int y, int z)
    {
        if (InBounds(x, y, z))
        {
            Vector3i regionalPosition = GetRegionalPosition(x, y, z);
            Vector3i region = GetRegionPosition(x, y, z);

            return regions[region.X, region.Y, region.Z].Blocks[regionalPosition.X, regionalPosition.Y, regionalPosition.Z];
        }

        return new Block(BlockType.none);
    }

    public Vector3i GetRegionPosition(int x, int y, int z)
    {
        int regionx = x == 0 ? 0 : x / Variables.REGION_SIZE_X;
        int regiony = y == 0 ? 0 : y / Variables.REGION_SIZE_Y;
        int regionz = z == 0 ? 0 : z / Variables.REGION_SIZE_Z;

        return new Vector3i(regionx, regiony, regionz);
    }

    public Vector3i GetRegionalPosition(int x, int y, int z)
    {
        int regionx = x == 0 ? 0 : x / Variables.REGION_SIZE_X;
        int X = x % Variables.REGION_SIZE_X;

        int regiony = y == 0 ? 0 : y / Variables.REGION_SIZE_Y;
        int Y = y % Variables.REGION_SIZE_Y;

        int regionz = z == 0 ? 0 : z / Variables.REGION_SIZE_Z;
        int Z = z % Variables.REGION_SIZE_Z;

        return new Vector3i(X, Y, Z);
    }

Any ideas how to fix this problem?

EDIT 1:

Graphic of the problem:

enter image description here

EDIT 2

GetBlock, Vector3 version:

    public Block GetBlock(Vector3 position)
    {
        int x = (int)Math.Floor(position.X);
        int y = (int)Math.Floor(position.Y);
        int z = (int)Math.Ceiling(position.Z);

        Block block = GetBlock(x, y, z);

        return block;
    }

Now, the thing is I tested the theroy that the Z is always "off by one" and by ceiling the value it actually works as intended. Altough it still could be greatly more accurate (when you go down holes you can see through the sides, and I doubt it will work with negitive positions). I also does not feel clean Flooring the X and Y values and just Ceiling the Z. I am surely not doing something correctly still.

© Game Development or respective owner

Related posts about XNA

Related posts about c#