Issues with touch camera

Hello,

I’m trying to apply the FPS movement tutorial’s camera logic to work with mobile touch, but there is a problem that I’m not sure how to solve…

On using Chrome’s devtools, the camera movement (currently only on X axis) drags to a certain direction and “accumulates” speed towards it, and then to turn the other way I have to drag the simulated touch all the way to the other side and the process starts again but to the opposite direction instead.

And when doing short drags, there is a visible stutter:

What am I missing?

Link to a fork of the project: https://playcanvas.com/project/778671/overview/mobile-touch-camera-issue

The movement script:

var FirstPersonMovement = pc.createScript('firstPersonMovement');

var mobile;

var mobileX = 0.5;
var mobileY = 0.5;

FirstPersonMovement.attributes.add('camera', {
    type: 'entity',
    description: 'Optional, assign a camera entity, otherwise one is created'
});

FirstPersonMovement.attributes.add('lastTouchPoint', {
    type: 'vec2',
    default: [0, 0],
    description: 'The last coordinates of the last mobile touch.'
});

FirstPersonMovement.attributes.add('power', {
    type: 'number',
    default: 2500,
    description: 'Adjusts the speed of player movement'
});

FirstPersonMovement.attributes.add('lookSpeed', {
    type: 'number',
    default: 0.25,
    description: 'Adjusts the sensitivity of looking'
});
FirstPersonMovement.attributes.add('canLook', {
    type: 'boolean',
    default: true,
    description: 'Can the player look around?'
});

// initialize code called once per entity
FirstPersonMovement.prototype.initialize = function() {
    this.force = new pc.Vec3();
    this.eulers = new pc.Vec3();
    
    touch = this.app.touch;
    
    if (mobile) console.log("mobile detected.");
    
    var app = this.app;
    
    // Listen for mouse move events
    if (!touch)
    {
        app.mouse.on("mousemove", this._onMouseMove, this);

        app.on("stopLooking", this.stopMouseControls, this);

        app.on("startLooking", this.startMouseControls, this);

        // when the mouse is clicked hide the cursor
        app.mouse.on("mousedown", function () {
            app.mouse.enablePointerLock();
        }, this);
    }
    else
    {    
        console.log("registering mobile events");
        
        this.lookSpeed = 0.05;
        
        touch.on(pc.EVENT_TOUCHSTART, this.onTouchStart, this);
        touch.on(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);
        touch.on(pc.EVENT_TOUCHEND, this.onTouchEnd, this);
    }

    // Check for required components
    if (!this.entity.collision) {
        console.error("First Person Movement script needs to have a 'collision' component");
    }

    if (!this.entity.rigidbody || this.entity.rigidbody.type !== pc.BODYTYPE_DYNAMIC) {
        console.error("First Person Movement script needs to have a DYNAMIC 'rigidbody' component");
    }
};

// update code called every frame
FirstPersonMovement.prototype.update = function(dt) {
    // If a camera isn't assigned from the Editor, create one
    if (!this.camera) {
        this._createCamera();
    }
    
    var force = this.force;
    var app = this.app;

    // Get camera directions to determine movement directions
    var forward = this.camera.forward;
    var right = this.camera.right;
       

    // movement
    var x = 0;
    var z = 0;

    if (!mobile)
    {
        // Use W-A-S-D keys to move player
        // Check for key presses
        if (app.keyboard.isPressed(pc.KEY_A) || app.keyboard.isPressed(pc.KEY_Q)) {
            x -= right.x;
            z -= right.z;
        }

        if (app.keyboard.isPressed(pc.KEY_D)) {
            x += right.x;
            z += right.z;
        }

        if (app.keyboard.isPressed(pc.KEY_W)) {
            x += forward.x;
            z += forward.z;
        }

        if (app.keyboard.isPressed(pc.KEY_S)) {
            x -= forward.x;
            z -= forward.z;
        }
    }
    else
    {
        //console.log(mobileX, mobileY);
        
        if (mobileX < 0.3)
        {
            x -= right.x;
            z -= right.z;
        }     
        else if (mobileX > 0.7)
        {
            x += right.x;
            z += right.z;
        }        
        if (mobileY > 0.7)
        {
            x += forward.x;
            z += forward.z;
        }      
        else if (mobileY < 0.3)
        {
            x -= forward.x;
            z -= forward.z;
        }     
    }

    // use direction from keypresses to apply a force to the character
    if (x !== 0 && z !== 0) {
        force.set(x, 0, z).normalize().scale(this.power);
        this.entity.rigidbody.applyForce(force);
    }

    // update camera angle from mouse events
    this.camera.setLocalEulerAngles(this.eulers.y, this.eulers.x, 0);
};

FirstPersonMovement.prototype.stopMouseControls = function ()
{
    this.canLook = false;
};

FirstPersonMovement.prototype.startMouseControls = function ()
{
    this.canLook = true;
};

FirstPersonMovement.prototype._onMouseMove = function (e) {
    if (!this.canLook) return;
    //console.log(e);
    
    // If pointer is disabled
    // If the left mouse button is down update the camera from mouse movement
    if (pc.Mouse.isPointerLocked() || e.buttons[0]) {
        this.eulers.x -= this.lookSpeed * e.dx;
        this.eulers.y -= this.lookSpeed * e.dy;
        
    } 
    this.eulers.y = pc.math.clamp( this.eulers.y, -89.9, 89.9);
};

FirstPersonMovement.prototype.onTouchStart = function (event) {
    //console.log("touchStart");
    event.event.preventDefault();
};

FirstPersonMovement.prototype.onTouchMove = function (event) {
    //console.log("touchMove");
    var touch = event.touches[0];
    var dx = touch.x - this.lastTouchPoint.x;
    var dy = touch.y - this.lastTouchPoint.y;

    this.eulers.x -= this.lookSpeed * dx;
    
    console.log("D: " +  dx,dy);
    console.log("E: " + this.eulers.x, this.eulers.y );
    
    event.event.preventDefault();
};

FirstPersonMovement.prototype.onTouchEnd= function (event) {
    console.log("touchEnd");
    var touch = event.changedTouches[0];
    console.log(touch);
    this.lastTouchPoint.set(touch.x, touch.y);
    event.event.preventDefault();
};


FirstPersonMovement.prototype._createCamera = function () {
    // If user hasn't assigned a camera, create a new one
    this.camera = new pc.Entity();
    this.camera.setName("First Person Camera");
    this.camera.addComponent("camera");
    this.entity.addChild(this.camera);
    this.camera.translateLocal(0, 0.5, 0);
};

FirstPersonMovement.prototype.setMobileMovementVector = function(x, y)
{
  mobileX = x;
  mobileY = y;  
};

Hi @Abneto,

I am not on my desk to take a look at your project, but in case it’s of any help here take a look at the engine first person camera script here it includes touch support:

And the playable example:

https://playcanvas.github.io/#camera/first-person.html