3D Technology For The Web
UFCFS3-30-2 2018 Sept

Previously on 3DTFTW

Aims and Objectives

Step 1 - Entity Class

Organise all the things in your world into specialised types of a thing called ‘Entity’. So Avatar, Obstacle, Environment will all be a kind of Entity.

Create a new class in your javascript file, somewhere near the top.


    class Entity {
    	constructor() {
    	}

    	Update() {
    	}

    	Reset() {
    	}
    }

Make sure it still compiles.

Step 2 - Classes for your Obstacles

Create a new class, ‘Obstacle’ which extends ‘Entity’ this means that if we like we can ask Obstacle to do anything that Entity can do to. We say it is derived from Entity.

This is similar to a specific car with REG ABC 123X being derived from the type ‘Astra’ which in turn is derived from the type ‘Vauxhall’ and ‘Car’ and ‘Vehicle’ and so on.

The Obstacle class should follow the same structure as the Entity class…


    class Obstacle extends Entity {
    	constructor() {
            //	console.log("Obstacle() constructor");
            super();
    	}

    	Reset() {
            super.Reset();
    	}

    	Update() {
            super.Update();
    	}
    }

Notice the super() and super.etc() code, These call functions in the Entity class we defined earlier. Currently its empty. In this way we can make all the types of objects which extend from Entity share code from one root or parent class.

Take the code your columns or boxes or knots or whatever shape you are using, so that they are also wrapped in the obstacle class.

Step 3 - Container and Loop to Update many things

Make sure you have a container into which you store


    var objects = [];

Make a loop and create a number of obstacles. Push the obstacles into the container


    for ( /* make a loop do it a few times, you should look this up online */) {
        var obstacle = new Obstacle();
        objects.push(obstacle);
    }

Add the following to your animate() function and then you will visit the Update method of each object in turn.


    for (obj of objects) {
        obj.Update();
    }

Step 4 - Classes for your other things

Follow a similar pattern to your obstacle with the Environment and Avatar.

You will need to move/adjust your code for each thing.

    class MY_CLASS_NAME_HERE extends Entity {
    	constructor(newMesh) {
    		super();
        }

        Reset() {
            super.Reset();
        }

        Update() {
            super.Update();

        }
    }

If you put a PlaneGeometry into the Environment class, perhaps its methods are empty except for the constructor?

If you put an Avatar class together the Update() method is where you will respond to the keyboard input and move, maybe?

If you want them to be updated every time you animate() then either add them to the objects array or call the update() method explicitly from within the animate() function.

Step 5 - Detecting Collision

You will recall from the lecture slides that distance is related to pythagorus theorum. THis is because the coordinates of the objects can be thought of as the corners of a triangle and the hypotenuse the line between them. Recheck the lecture slides for this content if you are unsure.

The following method will calculate (and return) the distance between the position of the mesh of the current class (via ‘this.’) and any arbitrary coordinates.


    DistanceTo(x, z) {
        // (xA-xB)²+(yA-yB)²+(zA-zB)² < (rA+rB)²

        let dist = Math.abs ( Math.sqrt (
                        ( ( this.mesh.position.x - x ) * ( this.mesh.position.x - x ) ) +
                        ( ( this.mesh.position.z - z ) * ( this.mesh.position.z - z ) )
                    ) );

        //	console.log("DistanceTo() = " + dist);
        return dist;
    }

However use distance to detect collision we need to compare it with the sum of the sizes of the two objects being tested. So add a member variable to each of your avatar and the obstacles which store their sizes.


    IsCollidedWith(that) {
        // size + size > distance
        let collidedWith = (this.size + that.size) > this.DistanceTo(that.mesh.position.x,  that.mesh.position.z);
        //	console.log("IsCollidedWith() " + collidedWith + " " + (this.size + that.size));
        return collidedWith;
    }

you avatar isnt interested in testing collision against one obstacle, but all the obstacles, so we call IsCollidedWith() from within a loop.


    CollidedWithObstacle() {
        for(var n=0; n<objects.length; n++) {
            if ( objects[n].collidable == true ) {
                if ( this.IsCollidedWith(objects[n]) == true ) {
                    return true;
                }
            }
        }
        return false;
    }

Notice we test .collidable first, you must add a member variable .collidable and set it to true in the obstacles if you want them to respond to collision tests with the avatar.

Finally in the Update() method of the avatar, we can in turn call this.CollidedWithObstacle() and detect if we have collided.


    if ( this.CollidedWithObstacle() )
    {
        console.log(" ------ CRASH ------- ");
    }

Exercises