1
\$\begingroup\$

I'm working on a Minecraft like game engine and recently I've been trying to add good collision response which I have kind of. The collision response is perfect when I try and go through the right side or front side of this cube.

I'm doing a dot product between my player's velocity vector and the normals for each side of the cube (ex: new Vector3f(0.0f,1.0f,0.0f) //Up) and seeing which normal gives the dot product which is the greatest and then I multiply my velocity vector by that normal vector in order to create this cool sliding effect.

This sliding effect only works for the right and front sides of the cube and the rest of the sides have a weird effect which is not sliding at all. Anyway if anyone can help with this it would be very much appreciated :). This is my method for detecting collision which works efficiently. Then there's the method for detecting which way the player should slide which is called velocityDirection. Update: Whenever I try and touch ANY side of the cube (I am printing out the variable BestMatch from the velocityDirection class which is one of the normals from the array of normals) it returns a value of Vector3f(0,1,0) and when I try and move and go through the right side like a ghost it returns Vector3f(-1,0,0) and for the front side it returns Vector3f(0,0,-1), and remember the right and front sides both have perfect collision.

Collision Detection Method:

public static boolean IsCollision(AABB player, AABB block)
      {

            boolean x = player.getMaxX() > block.getMinX() && player.getMinX() < block.getMaxX();


            boolean y = player.getMaxY() > block.getMinY() && player.getMinY() < block.getMaxY();

            boolean z = player.getMaxZ() > block.getMinZ() && player.getMinZ() < block.getMaxZ();



            return (x && y && z);
          }

Sliding Response Method :

public static Vector3f velocityDirection(Vector3f velocityDirection){
            Vector3f normalsArray[] = new Vector3f[]{
                    new Vector3f(0.0f,1.0f,0.0f),  // Up
                    new Vector3f(1.0f,0.0f,0.0f),//Right
                    new Vector3f(0.0f,-1.0f,0.0f), // Down
                    new Vector3f(-1.0f,0.0f,0.0f), //Left
                    new Vector3f(0.0f,0.0f,1.0f), //Front
                   new Vector3f(0.0f,0.0f,-1.0f) //Back


            };
            Vector3f BestMatch = new Vector3f();
            Vector3f TrueVelocity = new Vector3f();
            float max = 0;
            for(int i = 0 ; i < normalsArray.length ; i++){
                float dot = Vector3f.dot(velocityDirection, normalsArray[i]);
                if(dot > max){
                    max = dot;
                    BestMatch = normalsArray[i];

                }
            }
            TrueVelocity = new Vector3f(velocityDirection.x  * BestMatch.x, velocityDirection.y  * BestMatch.y, velocityDirection.z  * BestMatch.z );

            return TrueVelocity;
        }

Camera Class:

public class Camera {

    private static Vector3f position = new Vector3f(0,0,0);
    public static float pitch = 20 ;
    public static float rx, ry, rz;

    public static float velocityX;
    public static float velocityY;
    public static float velocityZ;

    public static float yaw;
    public static float roll;
    private  final static float RUN_SPEED = 20;

    private final static float TURN_SPEED = 150;
    private static float JUMP_POWER = 10;
    private static float currentSpeed = 0;
    public static float currentTurnSpeed = 0;
   public static boolean isMovingX = false;
   public static boolean isMovingY = false;


    public float angleAroundPlayer = 0;
    private static final Vector4f Forward = new Vector4f(0,0,-1,0);
    private static final Vector4f Backward = new Vector4f(0,0,1,0);


   public static boolean isColliding = false;


    public static void moveCamera(AABB player,AABB box){
          calculatePitch();
          if(Keyboard.isKeyDown(Keyboard.KEY_W)){
         isMovingX = true;

              currentSpeed = -RUN_SPEED;


              }
          else if(Keyboard.isKeyDown(Keyboard.KEY_S)){
              currentSpeed = RUN_SPEED;
              isMovingX = true;
          }
          else{
              isMovingX = false;
              currentSpeed = 0;
          }

                if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
                    currentTurnSpeed = -TURN_SPEED;

                } else if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
                    currentTurnSpeed = TURN_SPEED;
                } else {
                    currentTurnSpeed = 0;
                }



                if(Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)){
                 isMovingY = true;
                    velocityY = -JUMP_POWER * DisplayManager.getFrameTimeSeconds();

               }
                else if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
                    isMovingY = true;
                    velocityY = JUMP_POWER * DisplayManager.getFrameTimeSeconds();


                }
                else{
                    isMovingY = false;
                    velocityY = 0;
                }


         yaw -= currentTurnSpeed * DisplayManager.getFrameTimeSeconds();
          Matrix4f rotation = calculateCameraRotationMatrix();
             Vector3f TrueForward = new Vector3f(Matrix4f.transform(rotation,Forward , null));
             Vector3f TrueBackward = new Vector3f(Matrix4f.transform(rotation,Backward , null));
             Vector3f CameraPos = Camera.getPosition();






                    float distance = currentSpeed * DisplayManager.getFrameTimeSeconds();
                velocityX = (float) (distance * Math.sin(Math.toRadians(getRy())));
                velocityZ = (float) (distance * Math.cos(Math.toRadians(getRy())));
                increasePosition(velocityX,velocityY,velocityZ);
                System.out.println(new Vector3f(velocityX, velocityY, velocityZ));
                if(isColliding){
                Vector3f velocityDirection = VectorMath.velocityDirection(new Vector3f(velocityX, velocityY, velocityZ));

                 increasePosition(velocityDirection.x , velocityDirection.y, velocityDirection.z );
          }


         increaseRotation(0, currentTurnSpeed * DisplayManager.getFrameTimeSeconds(), 0);

      yaw %= 360;

    }
    private static Matrix4f calculateCameraRotationMatrix() {
        Matrix4f rotation = new Matrix4f();
        rotation.rotate((float) Math.toRadians(getPitch()), new Vector3f(1, 0, 0));
        rotation.rotate((float) Math.toRadians(getYaw()), new Vector3f(0, 1, 0));
        return rotation;
    }





    public static void increasePosition(float dx, float dy, float dz){
           position.x += dx;
           position.y += dy;
           position.z += dz;
    }
    public static void increaseRotation(float dx, float dy, float dz){
           rx += dx;
           ry += dy;
           rz += dz;

    }





    private static void calculatePitch() {



        float pitchChange = Mouse.getDY() * 0.63f;
        pitch -= pitchChange;
        if (pitch < -40) {
            pitch = -40;
        } else if (pitch > 90) {
            pitch = 90;
        }

}



    public static Vector3f getPosition() {
        return position;
    }

    public static void setPosition(Vector3f position) {
        Camera.position = position;
    }


    public static float getPitch() {
        return pitch;
    }
    public static float getRx() {
        return rx;
    }
    public static float getRy() {
        return ry;
    }
    public static float getRz() {
        return rz;
    }
    public  static float getYaw() {
        return yaw;
    }
    public static float getRoll() {
        return roll;
    }
\$\endgroup\$
  • \$\begingroup\$ Only a brief look first, so I won't put it as an answer, but usually if your only getting right and front sides, likely means that its only considering positive coordinates or vectors, might be missing a multiplication somewhere. \$\endgroup\$ – RNewell122 Aug 27 '17 at 23:28
  • \$\begingroup\$ Nope that's not it, I checked and the velocity vector is returning negative and positive values \$\endgroup\$ – GraphicsPadawan Aug 28 '17 at 0:07
  • \$\begingroup\$ did you check the debugger for the the array ? to see whats happening as it pulled them up ? \$\endgroup\$ – RNewell122 Aug 28 '17 at 0:21
  • \$\begingroup\$ The values in the array stay constant from what I saw \$\endgroup\$ – GraphicsPadawan Aug 28 '17 at 0:32
  • \$\begingroup\$ strange idea but have you tried using doubles instead of floats ? \$\endgroup\$ – RNewell122 Aug 28 '17 at 0:50
0
\$\begingroup\$

Before reading the rest of my waffling, try this

TrueVelocity = new Vector3f(velocityDirection.x  * Mathf.Abs(BestMatch.x), velocityDirection.y  * Mathf.Abs(BestMatch.y), velocityDirection.z  * Mathf.Abs(BestMatch.z) );

You might be multiplying your velocity with negative numbers, which isn't what you want, so take the absolute value with Mathf.Abs()

Anyhow...If I'm reading your code correctly, you're finding the axis that the velocity best aligns with, then "straightening" it. But what if the player is mostly facing the wall, but just rotated just slightly, like this...

enter image description here

In the above diagram we are looking top down on the scene, and the best match for the players direction (or velocity) is the y-axis, but we probably want to slide horizontally.

My first time implementing wall sliding in a FPS was from Mark Overmar's FPS tutorial for GameMaker, it's 10 years old but it's full of great gems. Here's his wall sliding code (modified for clarity).

// undo last movement (because collision)
x = xprevious;
y = yprevious;

// if our velocity is more horizontal, wall slide horizontally
if (abs(hspeed) >= abs(vspeed) &&
    not place_meeting(x+hspeed,y,obj_wall_basic)) {
    x += hspeed;
} else if (abs(vspeed) >= abs(hspeed) &&
    not place_meeting(x,y+vspeed,obj_wall_basic)) {
    y += vspeed; exit;
}
// no sliding
speed = 0; 

Which I now realise doesn't behave as I expect my diagram to...

Also: Are you doing AABB tests against every block in this voxel world? this could get costly. You could improve it by reformulating the ANDs into ORs (with De Morgans theorem) and get early termination (short circuiting), so that when a single comparison is true, the function can return false without having to evaluate all 6 conditions. Since it's more likely that a block is NOT colliding with the player.

return !(player.getMaxX() < block.getMinX() ||
         player.getMaxY() < block.getMinY() ||
         player.getMaxZ() < block.getMinZ() ||
         player.getMinX() > block.getMaxX() ||
         player.getMinY() > block.getMaxY() ||
         player.getMinZ() > block.getMaxZ());
\$\endgroup\$
  • \$\begingroup\$ I added code for camera class to see if it could possibly help. \$\endgroup\$ – GraphicsPadawan Aug 28 '17 at 21:32
  • \$\begingroup\$ Thanks for the aabb code it works perfect, anyway making the the x, y, z values of the TrueVelocity vector always positive makes it more buggy. \$\endgroup\$ – GraphicsPadawan Aug 28 '17 at 22:12
  • \$\begingroup\$ By aabb code I mean the de morgans theorem Reuben \$\endgroup\$ – GraphicsPadawan Aug 29 '17 at 0:21
  • \$\begingroup\$ Could you send me your unity project, so I can hack around myself? Nothing is super obvious from here \$\endgroup\$ – Reuben Crimp Aug 29 '17 at 12:57
  • \$\begingroup\$ ummm this is lwjgl \$\endgroup\$ – GraphicsPadawan Aug 29 '17 at 20:05
0
\$\begingroup\$

I managed to find the solution, It took me about a week and a half to solve this but I have finally solved it. So what I did is I checked which side of the cube the player is touching to determine where and how the player moved, the collision response is not 100% perfect but it's nearly impossible to go through objects and the sliding is pretty cool so I'm happy about that :)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.