VR default scripts

Hi veryone,

I am developing a VR project and i need to make some changes. Tha problem is that i do not understand the scripts that comes with the base VR program.

camera-controller.js

code

i know it is not a very specific question, but if someone can help me understand what each function does and how it is doing it i will be very glad.
Also this question may be very helpful for people developing for first time VR in playcanvas in the future, as there is not much information about it.

Million thanks!

1 Like

Is there a specific area you want more information on? We should have more documentation about how the starter kit works but in the short term, we can help explain specific areas a bit

Also, which project is this from?

No offense, but they are 400 lines of code, and you seems to be looking for tutoring.

What is your JavaScript level? Did you read the documentation? For example, Playcanvas uses a firing system to allow scripts to communicate between them. If you didn’t know that, you could not understand this line:

this.app.on('controller:teleport', this.onTeleport, this);

My JavaScript level is the one i adquired using this program and yes, i have read much of the documentation while working on this

Yes, I do understand that line: when it receives the signal “controller:teleport”, fired from another script, then it executes the function teleport.

What i do not achieve to understand is what most of those functions do. For example this one:

CameraController.prototype.onMove = function (x, y, dt) {
    this.vec2A.set(x, y);

    this.app.on('object:hovered', this.hovered, this);
    this.app.on('object:notHovered', this.notHovered, this);

    if (this.vec2A.length()) {

            this.vec2A.normalize();

            this.vec2B.x = this.camera.forward.x;
            this.vec2B.y = this.camera.forward.z;
            this.vec2B.normalize();

            var rad = Math.atan2(this.vec2B.x, this.vec2B.y) - (Math.PI / 2);

            var t = this.vec2A.x * Math.sin(rad) - this.vec2A.y * Math.cos(rad);
            this.vec2A.y = this.vec2A.y * Math.sin(rad) + this.vec2A.x * Math.cos(rad);
            this.vec2A.x = t;

            this.vec2A.scale(this.movementSpeed);
            this.entity.translate(this.vec2A.x * dt, 0, this.vec2A.y * dt); 
        } 
   
};

or this one:

CameraController.prototype.onRotate = function (yaw, dt) {
    var now = Date.now();

    if ((now - this.lastRotate) < 200)
        return;

    if (this.lastRotateValue !== 0) {
        if (this.lastRotateValue > 0) {
            if (yaw < this.rotateResetThreshold) {
                this.lastRotateValue = 0;
            } else {
                return;
            }
        } else {
            if (yaw > -this.rotateResetThreshold) {
                this.lastRotateValue = 0;
            } else {
                return;
            }
        }
    }

    if (Math.abs(yaw) > this.rotateThreshold) {
        this.lastRotateValue = Math.sign(yaw);

        this.vec3A.copy(this.camera.getLocalPosition());
        this.entity.translateLocal(this.vec3A);
        this.entity.rotateLocal(0, Math.sign(yaw) * this.rotateSpeed, 0);
        this.entity.translateLocal(this.vec3A.scale(-1));
    }
};

Ok, reading the first example you shared:

CameraController.prototype.onMove = function (x, y, dt) {
    this.vec2A.set(x, y);

    this.app.on('object:hovered', this.hovered, this);
    this.app.on('object:notHovered', this.notHovered, this);

It sets x and y in vec2A instead of creating a new one everytime, more efficient. It may also be being used elsewhere in the class.

this.app.on('object:hovered', this.hovered, this);
this.app.on('object:notHovered', this.notHovered, this);

It has linked 2 methods to the firing system

if (this.vec2A.length()) {

This just checks that vec2A has length different than 0, same as checking if at least x or y is non zero.

            this.vec2A.normalize();

Normalize vec2A. Basically, it makes its lenght = 1

            this.vec2B.x = this.camera.forward.x;
            this.vec2B.y = this.camera.forward.z;
            this.vec2B.normalize();

It sets up x and y for vec2B, and normalizes it. It is using the camera’s coordinate system. This is useful to make vectors that always face where you are looking at, or always to your camera’s left, or whatever.

var rad = Math.atan2(this.vec2B.x, this.vec2B.y) - (Math.PI / 2);

var t = this.vec2A.x * Math.sin(rad) - this.vec2A.y * Math.cos(rad);
this.vec2A.y = this.vec2A.y * Math.sin(rad) + this.vec2A.x * Math.cos(rad);
this.vec2A.x = t;

A bunch of trigonometry. I suggest you to take a paper and draw the arrows. For example, atan2 gives you the angle

this.vec2A.scale(this.movementSpeed);
this.entity.translate(this.vec2A.x * dt, 0, this.vec2A.y * dt); 

Remember vec2A had length = 1 ? well, now you scall it by the speed the class had, and use it to move the entity multiply by dt , which is the time it has passed since last frame.

3 Likes

You will have to reach out to the author of the project as that was not written by the PlayCanvas team.

You can find the author at:

While we can help with specific questions, we can’t really help with how the overall project is put together. For that, you will need to reach out to the author

Wow, that is complex
Thanks a lot!

It is the same as the standard VR sample that comes by default in playcanvas. Here you have the raw sample: PlayCanvas 3D HTML5 Game Engine
As I understand that is created by the playcanvas team, so if you can help me please :pray:

If you have questions about the raw template, that’s fine but https://playcanvas.com/project/873904/overview/vr-interaction-framework has a lot of extra functionality built on top of it by the author.

CameraController.prototype.onMove effectively moves the player in the direction that the camera (user) is looking in based on the gamestick direction.

CameraController.prototype.onRotate allows the user to rotate the camera without physically moving. eg when they are sitting down. This allows them to move in an open source all 360 around them even without turning themselves.

There’s a bit of extra code to ensure that the user is offset from the reference point (the Camera Parent) correctly after rotation.

2 Likes

Yes i just need to understand the raw template.

Sorry but i do not understand what onMove really does if we are using onTeleport to move the player

In VR games/experiences, moving is either done by teleporting the user by pointing at the floor and clicking or with the joystick on one of the handcontrollers.

The starter kit shows how to do both so the developer can keep both or remove one or the other or even both depending on the application being made.

The important part is the setup here:

image

For most 6DoF VR headsets where you can move around the local space, the origin of the guardian area is where the Camera Parent is and the headset position is an offset from the origin of the guardian area.

That offset is applied to the Camera entity as the local position.

Both teleporting and the moving is down by changing the position of the Camera Parent.

1 Like

Oooooh ok, now i understand. Thanks!

Could you also explain me the controller.js please?

Any particular bit? It represents functionality of the hand controller and input controls

1 Like

yeah i woukd like to understand how the ray works and interacts. I understand that happens in the update.

Also i do not understand what enitites with tag “interactive” are being used for. That is in onSelect function

In a nutshell,

Every frame it fires an app wide event (‘object:pick’) and passes itself as the parameter.

In objectPicker.js, it listens to this event and on the function callback, it creates a ray that starts from the controller position and is set in the direction that the controller is pointing in. (Probably the forward direction (-Z))

It goes through all the entities that have been tagged ‘pickable’ and checks for intersection between the ray the axis aligned bounding box of the entities.

It gets the closest intersected entity and fires the ‘hover’ event on the controller scriptType instance.

It also checks if it’s no longer hovering on an entity and fires ‘blur’ if it used to be hovering on an entity.

When the user presses the select button, it does the same thing and then if the entity has a ‘teleportable’ tag, it will teleport the user. If has an ‘interactive’ tag, then it performs some logic on it to make ithe mesh change colour.

2 Likes

Ok, thanks!
One more thing: is there a way to block the ray? I ve been trying with rigidbodys and collisions but nothing, it goes through every object.
It is ok with normal objects if as you said it looks for intersection between pickable object and the ray, but what if we want a wall between the user and the object so the user cannot interact with it if there is an obstacle.
Can something be done to achive this?

Thanks for everything

1 Like

I am also interested in blocking the ray, have you found a solution?

1 Like

As the script does raycasting for any entity that has the pickable tag, you can put any entity in world (that renders something (eg a cube for the wall)) and add the ‘pickable’ tag and it can be used to ‘block’ the raycasting selecting items behind it

As there is no code to do a reaction to entities that just have the pickable tag, it won’t do anything to the entity.

1 Like

Yes, that worked fine with standard objects, but it does not with elements as buttons. You can still click on them through objects even if they have pickable tag