Alee
Alee

Reputation: 31

Collision walls in three.js

I'm creating a game in three.js. I'm new in this field but I've read a lot of collision documentation. I used raycast to get a collision between my boat contained in the cube and the islands contained in cubes. I tried to solve the problem this way.

collision2 = false;

    var originPoint = MovingCube.position.clone();
    clearText();
    for (var vertexIndex = 0; vertexIndex < MovingCube.geometry.vertices.length; vertexIndex++)
        {       
            var localVertex = MovingCube.geometry.vertices[vertexIndex].clone();
            var globalVertex = localVertex.applyMatrix4( MovingCube.matrix );
            var directionVector = globalVertex.sub( MovingCube.position );

            var ray = new THREE.Raycaster( originPoint, directionVector.clone().normalize() );
            var collisionResults = ray.intersectObjects( collidableMeshList );
            if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() ) {
                appendText(" Hit "),
                collision2 = true;
            }
}

    if ( keyboard.pressed("W") ){
        obj.translateZ( moveDistance ),
    if (collision2==true){
        obj.translateZ( -moveDistance*4 ),
        }
    }
    if ( keyboard.pressed("S") ){
        obj.translateZ( - moveDistance ),;
    if (collision2==true){
        obj.translateZ( moveDistance*4 ),
        }
    }
    if ( keyboard.pressed("A") ){
        obj.rotateOnAxis( new THREE.Vector3(0,1,0), rotateAngle),
    if (collision2==true){
        obj.rotateOnAxis( new THREE.Vector3(0,1,0), -rotateAngle*3),
        }
    }
    if ( keyboard.pressed("D") ){
        obj.rotateOnAxis( new THREE.Vector3(0,1,0), -rotateAngle),
    if (collision2==true){
        obj.rotateOnAxis( new THREE.Vector3(0,1,0), rotateAngle*3),
        }
    }    

Multiply by 3 "moveDistance" and "rotateAngle" so as not to crash my ship in the rays launched by the raycat during the collision. It often works, but sometimes the ship gets stuck or enters the island. I thought about moving the ship a few pixels when it collides with the island based on the face of the cube. Example: Ship collide with the face of the cube on the positive X axis, subtract pixels from the cube position to make the boat move away. But I do not know how to do something like that, it would be a good solution for me. How could I solve the problem of collisions with walls? (I do not want to use any physical engine right now) Thanks for those who will give me help!

screenshot

EDIT:
I would like to point out which side of the cube (island) collided with the ship. If the face is facing the X axis (positive) then move the boat 40 pixels backwards (obj.position = + 40), this for each face. So you can never let the boat in the cube. I was looking at this example that I think could be useful, but I did not understand how to solve it yet.
https://stemkoski.github.io/Three.js/Mouse-Click.html

This is my cube that contains the boat.

var mats2 = [];

    var cubeGeometry = new THREE.CubeGeometry(25,135,121,10,10,10);
    for (var i = 0; i < 6; i ++) {
    mats2.push(new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe:true } ));

}
    MovingCube = new THREE.Mesh( cubeGeometry, mats2 );
    scene.add( MovingCube );
    collidableMeshList0.push(MovingCube);

Upvotes: 1

Views: 4078

Answers (2)

Alee
Alee

Reputation: 31

I think I got a good solution.
Every time a collision occurs, the affected face can be known. Using .face.normal. For example: if the collided face is the face facing the -x axis you will have: x: -1, y: 0, z: 0. If the collided face is the face facing the + x axis you will have: x: +1, y: 0, z: 0. The same principle applies to the other axes. So I solved the following way. Now the boat is no longer stuck in the walls!

var originPoint = MovingCube.position.clone();
clearText();
for (var vertexIndex = 0; vertexIndex < MovingCube.geometry.vertices.length; vertexIndex++)
    {       
        var localVertex = MovingCube.geometry.vertices[vertexIndex].clone();
        var globalVertex = localVertex.applyMatrix4( MovingCube.matrix );
        var directionVector = globalVertex.sub( MovingCube.position );

        var ray = new THREE.Raycaster( originPoint, directionVector.clone().normalize() );
         collisionResults1 = ray.intersectObjects( collidableMeshList );
        if ( collisionResults1.length > 0 && collisionResults1[0].distance < directionVector.length() ) {
            appendText(" Hit "),
            faccia = collisionResults1[0].face.normal;
            if (faccia.x <=-0.9 ){
                obj.position.x = originPoint.x -30,
            }
            if (faccia.x >=0.9 ){
                obj.position.x = originPoint.x +30,
            }
            if (faccia.z <=-0.9 ){,
                obj.position.z = originPoint.z -30,
            }
            if (faccia.z >=0.9 ){
                obj.position.z = originPoint.z +30,
            }

Upvotes: 2

manthrax
manthrax

Reputation: 5016

There are infinite ways to do a half assed job of solving this with raycasts.. but one of the most effective I've made work is to make a simple point mass system, with 3 point masses arranged in a triangle, connected with 3 springs and do a simple verlet style simulation to keep them in a triangular configuration.. you then reconstruct the object matrix by taking the vectors of this triangle, and compose an orthonormal basis and plug that into your three.js object. Then per frame, you apply a gravitational force to your point masses, and if they are below your sea level, apply an arbitrary buoyancy force proportional to the points' depth below sea level. Then for each point mass, you raycast downward from the center of the point, and adjust the point masses position to maintain a distance from the ground height of your height map.

Here's a prototype of something similar I made a long time ago: http://vectorslave.com/webgl/zbox.html (ASWD + Arrow keys to control the chopper)

Upvotes: 1

Related Questions