一 , 数学准备

    Ⅰ, 三角函数

        Cocos Creator之摇杆的实现_Cocos Creator

ox1/ox = oy1/oy (相似三角形)

二 , 关于弧度 Math.atan2 在-PI (3,4象限)到 PI(1,2象限) 之间 . 如果要在3,4象限得到正值, 则需要使用 2PI - X的方式. 角度也是一样, 代码如下:
/**
 * 获取方向弧度
 */
public getRadian(): number {
    if( this.dir.x == 0 && this.dir.y == 0){
        return 0;
    }
    let radian: number =  Math.atan2( this.dir.y ,this.dir.x );
    if( radian >= 0 ) return radian;
    return 2*Math.PI + radian;
}

/**
 * 获取方向角度
 */
public getAngle(): number{
    let angle: number = this.getRadian()/ Math.PI * 180;
    if( angle >= 0 ) return angle;
    return 360 + angle;
}
三, 层级管理器如下

Cocos Creator之摇杆的实现_joystick_02

场景设计如下图, 可以看到要把stick限制在bg内需要用到相似三角形的原理, 部分代码如下:

let screenPos: Vec2 = e.getLocation();//获取屏幕坐标(stick所在的)
let pos: Vec3 = (this.node.getComponent(UITransform) as UITransform).convertToNodeSpaceAR( v3( screenPos.x, screenPos.y, 0 ));//转joystick和bg一样
let len: number = Vec2.len( v2(pos.x,pos.y) );//求模
this.dir.x = pos.x / len;//cos x
this.dir.y = pos.y / len;//sin y
if( len > this.maxR ){
    pos.x = pos.x * this.maxR/len;
    pos.y = pos.y * this.maxR/len;
}else if( len < this.minR && this.minR > 0){
    pos.x = pos.x * this.minR/len;
    pos.y = pos.y * this.minR/len;
}
this.stick.setPosition( pos.x,pos.y);

 

Cocos Creator之摇杆的实现_joystick_03

四, 全部代码
import { _decorator, Component, Node, log, EventTouch, Vec2, UITransform, Vec3, v3, v2 } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('JoyStickCtr')
export class JoyStickCtr extends Component {
    @property( { type: Node }  )
    // @ts-ignore
    private stick: Node;
    @property
    private maxR: number = 128;//最大移动半径
    @property
    private minR: number = 20;//最小移动半径
    //@ts-ignore
    private dir: Vec2;

    onEnable(){
        this.listener( true );
    }
    onDisable(){
        this.listener( false );
    }


    private listener( isAdd: boolean ): void {
        if( isAdd ) {
            this.stick.on( Node.EventType.TOUCH_START, this.onTouchHandler, this);
            this.stick.on( Node.EventType.TOUCH_MOVE, this.onTouchHandler, this);
            this.stick.on( Node.EventType.TOUCH_END, this.onTouchHandler, this);
            this.stick.on( Node.EventType.TOUCH_CANCEL, this.onTouchHandler, this);
        }else{
            this.stick.off( Node.EventType.TOUCH_START, this.onTouchHandler, this);
            this.stick.off( Node.EventType.TOUCH_MOVE, this.onTouchHandler, this);
            this.stick.off( Node.EventType.TOUCH_END, this.onTouchHandler, this);
            this.stick.off( Node.EventType.TOUCH_CANCEL, this.onTouchHandler, this);
        }
    }
    private onTouchHandler( e : EventTouch): void{
        switch (e.type){
            case Node.EventType.TOUCH_START:
                break;
            case Node.EventType.TOUCH_MOVE:
                let screenPos: Vec2 = e.getLocation();//获取屏幕坐标(stick所在的)
                let pos: Vec3 = (this.node.getComponent(UITransform) as UITransform).convertToNodeSpaceAR( v3( screenPos.x, screenPos.y, 0 ));//转joystick和bg一样
                let len: number = Vec2.len( v2(pos.x,pos.y) );//求模
                this.dir.x = pos.x / len;//cos x
                this.dir.y = pos.y / len;//sin y
                if( len > this.maxR ){
                    pos.x = pos.x * this.maxR/len;
                    pos.y = pos.y * this.maxR/len;
                }else if( len < this.minR && this.minR > 0){
                    pos.x = pos.x * this.minR/len;
                    pos.y = pos.y * this.minR/len;
                }
                this.stick.setPosition( pos.x,pos.y);
                break;
            case Node.EventType.TOUCH_END:
            case Node.EventType.TOUCH_CANCEL:
                this.stick.setPosition(0,0);
                this.dir.x = this.dir.y = 0;
                break;
        }
    }
    /**
     * 获取方向向量
     */
    public getDir(): Vec2{
        return this.dir;
    }

    /**
     * 获取方向弧度(0~2PI)
     */
    public getRadian(): number {
        if( this.dir.x == 0 && this.dir.y == 0){
            return 0;
        }
        let radian: number =  Math.atan2( this.dir.y ,this.dir.x );
        if( radian >= 0 ) return radian;
        return 2*Math.PI + radian;
    }

    /**
     * 获取方向角度(0~360)
     */
    public getAngle(): number{
        let angle: number = this.getRadian()/ Math.PI * 180;
        if( angle >= 0 ) return angle;
        return 360 + angle;
    }

    start () {
        this.dir = v2(0,0);
        // this.listener( true );
    }

    // update (deltaTime: number) {
    //     // [4]
    // }
}



注: 使用Cocos Creator 3.0.1 引擎