Problem with implementing multitouch for virtual joystick

Hi,
I am trying to make a First Person Shooter in Cocos Creator. But I have an issue with multitouch. I’ve made 2 virtual joysticks one for rotation other one for movement.
screen
For RotCanv and PosCanv I’ve made separate scripts to handle user inputs.

My script for movement (attached to PosCanv):

import {
  _decorator, Component, Node, systemEvent, SystemEventType, Touch, Vec2, Vec3
} from "cc";
const { ccclass, property } = _decorator;
const TOUCH_RADIUS = 400;
const ROLE_MOVE_FRAME = 0.1;
const ROLE_ROT_FRAME = 0.5;
const _tempPos = new Vec3();
const _tempDelta = new Vec2();
const Horizontal = new Vec2(1, 0);
const x_axis = new Vec3(1, 0, 0);
const y_axis = new Vec3(0, 1, 0);
const MOVE_DELTA = 0.05;
@ccclass("MovementController")

export class MovementController extends Component {

  @property(Node)
  ctrlSprite: Node = null!;
  @property(Node)
  role: Node = null!;
  @property(Vec3)
  originPos = new Vec3();
  @property(Node)
  boneNode: Node = null!;

  private _isTouch = false;
  private _touchPos = new Vec2();
  private _startPos = new Vec2();
  private _movePos = new Vec2();
  private _objectPosition = new Vec3();
  private _moveForward = true;
  private _tempRad = 0;

  start() {
    this.ctrlSprite.setPosition(this.originPos);
    _tempPos.set(0, 90, 0);
    this._objectPosition = this.role.position;
    systemEvent.on(SystemEventType.TOUCH_START, this.touchStart, this);
    systemEvent.on(SystemEventType.TOUCH_MOVE, this.touchMove, this);
    systemEvent.on(SystemEventType.TOUCH_END, this.touchEnd, this);
  }

  onDestroy() {
    systemEvent.off(SystemEventType.TOUCH_START, this.touchStart, this);
    systemEvent.off(SystemEventType.TOUCH_MOVE, this.touchMove, this);
    systemEvent.off(SystemEventType.TOUCH_END, this.touchEnd, this);
  }

  touchStart(touch: Touch) {
    touch.getUILocation(this._startPos);
    const distance = this._startPos.length();
    if (distance < TOUCH_RADIUS) {
      this._touchPos.set(this._startPos);
      this._movePos.set(this._startPos);
      _tempPos.set(this.ctrlSprite.position);
      this.ctrlSprite.setWorldPosition(
        this._startPos.x,
        this._startPos.y,
        _tempPos.z
      );
      this._isTouch = true;
    }
  }

  touchMove(touch: Touch) {
    if (!this._isTouch) {
      return;
    }
    touch.getUILocation(this._movePos);
    Vec2.subtract(_tempDelta, this._movePos, this._touchPos);
    this._tempRad = _tempDelta.angle(Horizontal);
    _tempDelta.multiply2f(MOVE_DELTA, MOVE_DELTA);
    if (this._movePos.y > this._startPos.y) {
      this._moveForward = true;
    } else {
      this._moveForward = false;
    }

    Vec2.add(this._movePos, this._startPos, _tempDelta);
    const distance = this._movePos.length();

    if (distance > TOUCH_RADIUS) {
      const radian = this._movePos.angle(Horizontal);
      const x = Math.cos(radian) * TOUCH_RADIUS;
      const y = Math.sin(radian) * TOUCH_RADIUS;
      this._movePos.set(x, y);
    }
    this.ctrlSprite.setWorldPosition(this._movePos.x, this._movePos.y, 0);
    this._touchPos.set(this._movePos);
  }

  touchEnd(touch: Touch) {
    this._isTouch = false;
    this.ctrlSprite.setPosition(this.originPos);
  }

  update(deltaTime: number) {
    if (!this._isTouch) {
      return;
    }

    let locPos = new Vec3(this.role.position.x,this.role.position.y,this.role.position.z);

    if (this._moveForward) {
      _tempPos.set(
        -ROLE_MOVE_FRAME * Math.cos(this._tempRad),
        0,ROLE_MOVE_FRAME * Math.sin(this._tempRad)
      );
    } else {
      _tempPos.set(
        -ROLE_MOVE_FRAME * Math.cos(this._tempRad),
        0,ROLE_MOVE_FRAME * -Math.sin(this._tempRad)
      );

    }
    this.role.translate(_tempPos, Node.NodeSpace.LOCAL);
  }
}

And for rotation (attached to RotCanv):

import {
  _decorator, Component, Node, systemEvent, SystemEventType, Touch, Vec2, Vec3, Quat, math,
} from "cc";
const { ccclass, property } = _decorator;

const TOUCH_RADIUS = 400;
const ROLE_MOVE_FRAME = 0.1;
const ROLE_ROT_FRAME = 0.5;
const _tempPos = new Vec3();
const _tempDelta = new Vec2();
const Horizontal = new Vec2(1, 0);
const x_axis = new Vec3(1, 0, 0);
const y_axis = new Vec3(0, 1, 0);
const z_axis = new Vec3(0, 0, 1);
const MOVE_DELTA = 0.05;
const MAX_X_ROT = 80;

@ccclass("RotationController")
export class RotationController extends Component {
  @property(Node)
  role: Node = null!;

  @property(Node)
  rot_ctrlSprite: Node = null!;
  @property(Vec3)
  rot_originPos = new Vec3();

  // Private variables
  private _isTouch = false;
  private _touchPos = new Vec2();
  private _startPos = new Vec2();
  private _movePos = new Vec2();
  private _tempRotVec = new math.Quat();
  private _tempNewRotVec = new math.Quat();
  private _newObjRot = new Vec3();
  private angle = new Vec3();
  private _isRot = false;
  private _isRotUp = true;
  private _tempRad = 0;

  // Clamp number between two values with the following line:
  // const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
  clamp(num: number, min: number, max: number) {
    return Math.min(Math.max(num, min), max);
  }

  start() {
    this.rot_ctrlSprite.setPosition(this.rot_originPos);
    _tempPos.set(0, 90, 0);
    this.role.eulerAngles = _tempPos;
    systemEvent.on(SystemEventType.TOUCH_START, this.touchStart, this);
    systemEvent.on(SystemEventType.TOUCH_MOVE, this.touchMove, this);
    systemEvent.on(SystemEventType.TOUCH_END, this.touchEnd, this);
  }
  onDestroy() {
    systemEvent.off(SystemEventType.TOUCH_START, this.touchStart, this);
    systemEvent.off(SystemEventType.TOUCH_MOVE, this.touchMove, this);
    systemEvent.off(SystemEventType.TOUCH_END, this.touchEnd, this);
  }
  touchStart(touch: Touch) {
    touch.getUILocation(this._startPos);
    const distance = this._startPos.length();
    if (distance > 1.5 * TOUCH_RADIUS) {
      this._touchPos.set(this._startPos);
      this._movePos.set(this._startPos);
      _tempPos.set(this.rot_ctrlSprite.position);
      this.rot_ctrlSprite.setWorldPosition(
        this._startPos.x,
        this._startPos.y,
        _tempPos.z
      );
      this._isTouch = true;
    }
  }
  touchMove(touch: Touch) {
    if (!this._isTouch) {
      return;
    }
    touch.getUILocation(this._movePos);
    Vec2.subtract(_tempDelta, this._movePos, this._touchPos);
    this._tempRad = _tempDelta.angle(Horizontal);
    _tempDelta.multiply2f(MOVE_DELTA, MOVE_DELTA);

    if (this._movePos.y > this._startPos.y) {
      this._isRotUp = true;
    } else {
      this._isRotUp = false; }

    Vec2.add(this._movePos, this._startPos, _tempDelta);
    const distance = this._movePos.length();

    if (distance < 1.5 * TOUCH_RADIUS) {
      const radian = this._movePos.angle(Horizontal);
      const x = Math.cos(radian) * TOUCH_RADIUS;
      const y = Math.sin(radian) * TOUCH_RADIUS;
      this._movePos.set(x, y);
    }

    this.rot_ctrlSprite.setWorldPosition(this._movePos.x, this._movePos.y, 0);

    this._touchPos.set(this._movePos); }

  touchEnd(touch: Touch) {
    this._isTouch = false;
    this._isRot = false;
    this.rot_ctrlSprite.setPosition(this.rot_originPos);
  }
  update(deltaTime: number) {
    if (!this._isTouch) {
      return;
    }
    this._tempRotVec = this.role.rotation;
    this._tempRotVec.getEulerAngles(this.angle);
    if (this._isRotUp) {
      this.angle.x -= 1.5 * Math.sin(this._tempRad);
    } else {
      this.angle.x += 1.5 * Math.sin(this._tempRad);
    }

    this.angle.y += 1.5 * -Math.cos(this._tempRad);

    console.log(this.angle.x, this.angle.y, this.angle.z);

    this.angle.x =
      this.angle.x >= 0
        ? this.clamp(this.angle.x, 0, 50)
        : this.clamp(this.angle.x, -60, 0);

    Quat.fromEuler(
      this._tempNewRotVec,
      this.angle.x,
      this.angle.y,
      this.angle.z
    );

    this.role.rotation = this._tempNewRotVec; } }

While testing when there is a single input it is working fine but when trying to move and rotate there is an issue. It looks like touches interfere and rotation virtual joystick is in wrong screen area. But it should not happed because touches are detected for different canvas.
Issue:

DoubleTouchIssue

Where rotation joystick should be:

Rotation

Where movement joystick should be:

movement

Can anyone help me with this?

1 Like

Hello try to use

node.on
Instead
systemEvent.on

because system event will broadcast for every listeners but node events will broadcast for every node separate