You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Cocos Creator中飞机沿指定直径圆周转向新角度而非立即转向?

问题描述

我想要模仿《Missiles》游戏的操作效果,当前在Cocos Creator中实现了触摸控制飞机旋转与直线移动,但希望调整为:改变角度时,飞机沿特定半径的圆周轨迹移动,而非直接直线转向移动。现有Main类代码如下:

const { ccclass, property } = cc._decorator;

export const event_instance = new cc.EventTarget();

@ccclass
export default class Main extends cc.Component {
    @property(cc.Node)
    cameraNode: cc.Node = null;

    @property(cc.Node)
    Game: cc.Node = null;

    private touchStartPos: cc.Vec2 = cc.Vec2.ZERO;

    private isRotating: boolean = false;

    private newAngle: number = 0;
    private moveDir: cc.Vec2 = cc.Vec2.ZERO;


    protected onLoad(): void {
        this.init();
    }

    init(): void {
        this.cameraNode.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
        this.cameraNode.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
        this.cameraNode.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
    }

    onTouchStart(event: cc.Event.EventTouch): void {
        this.touchStartPos = event.getLocation();
    }

    onTouchMove(event: cc.Event.EventTouch): void {
        this.isRotating = true;

        const touchPos = event.getLocation();

        const moveVec = touchPos.subtract(this.touchStartPos).normalize();

        this.moveDir = moveVec;

        this.newAngle = cc.misc.radiansToDegrees(Math.atan2(moveVec.y, moveVec.x)) - 90;
    }

    onTouchEnd(event: cc.Event.EventTouch): void {
        this.isRotating = false;
        this.touchStartPos = cc.Vec2.ZERO;
    }

    protected update(dt: number): void {
        if (this.isRotating) {
            this.Game.getChildByName('Plane').angle = this.newAngle;

            this.Game.getChildByName('Plane').setPosition(this.Game.getChildByName('Plane').getPosition().add(
                this.moveDir.clone().multiplyScalar(15)
            ));

            this.cameraNode.setPosition(this.Game.getChildByName('Plane').position);
        }
    }

}
解决方案

要实现飞机沿圆周轨迹移动,核心是基于圆周的圆心和半径,根据触摸输入的角度计算飞机在圆周上的位置,同时保持飞机朝向与圆周切线方向一致。具体修改步骤如下:

1. 添加圆周参数

在类中定义圆周轨迹的核心参数,可在编辑器中可视化调整半径:

// 圆周轨迹的半径(可在Cocos编辑器中调整)
@property
private circleRadius: number = 100;
// 圆周轨迹的圆心位置
private circleCenter: cc.Vec2 = cc.Vec2.ZERO;
// 记录飞机当前在圆周上的弧度角度
private currentRadian: number = 0;

2. 初始化圆心与初始弧度

在onLoad方法中,根据飞机初始位置确定圆周圆心(示例中默认飞机初始在圆心正上方):

protected onLoad(): void {
    this.init();
    const plane = this.Game.getChildByName('Plane');
    // 设置圆心为飞机初始位置下方radius距离处
    this.circleCenter = plane.position.subtract(cc.v2(0, this.circleRadius));
    // 初始弧度对应正上方位置
    this.currentRadian = Math.PI / 2;
}

3. 修改触摸移动逻辑

触摸移动时,根据横向偏移量调整飞机在圆周上的弧度位置,替代原有的直线方向计算:

onTouchMove(event: cc.Event.EventTouch): void {
    this.isRotating = true;

    const touchPos = event.getLocation();
    const moveVec = touchPos.subtract(this.touchStartPos);
    
    // 调整旋转灵敏度,数值越大旋转越快
    const rotateSpeed = 0.02;
    this.currentRadian += moveVec.x * rotateSpeed;
    
    // 保持弧度在0~2π范围内循环
    if (this.currentRadian < 0) this.currentRadian += Math.PI * 2;
    if (this.currentRadian > Math.PI * 2) this.currentRadian -= Math.PI * 2;

    // 更新触摸起始点,避免偏移量累积过大
    this.touchStartPos = touchPos;
}

4. 更新飞机位置与朝向

在update方法中,根据当前弧度计算飞机在圆周上的坐标,并设置朝向为圆周切线方向:

protected update(dt: number): void {
    if (this.isRotating) {
        const plane = this.Game.getChildByName('Plane');
        // 计算圆周上的目标位置:x=圆心x+半径*cos(弧度),y=圆心y+半径*sin(弧度)
        const targetPos = cc.v2(
            this.circleCenter.x + this.circleRadius * Math.cos(this.currentRadian),
            this.circleCenter.y + this.circleRadius * Math.sin(this.currentRadian)
        );
        plane.setPosition(targetPos);

        // 设置飞机朝向为切线方向(根据你的飞机精灵初始朝向调整角度偏移)
        plane.angle = cc.misc.radiansToDegrees(this.currentRadian) + 90;

        // 相机跟随飞机位置
        this.cameraNode.setPosition(targetPos);
    }
}
完整修改后的代码
const { ccclass, property } = cc._decorator;

export const event_instance = new cc.EventTarget();

@ccclass
export default class Main extends cc.Component {
    @property(cc.Node)
    cameraNode: cc.Node = null;

    @property(cc.Node)
    Game: cc.Node = null;

    // 圆周轨迹参数
    @property
    private circleRadius: number = 100;
    private circleCenter: cc.Vec2 = cc.Vec2.ZERO;
    private currentRadian: number = 0;

    private touchStartPos: cc.Vec2 = cc.Vec2.ZERO;
    private isRotating: boolean = false;

    protected onLoad(): void {
        this.init();
        const plane = this.Game.getChildByName('Plane');
        // 初始圆心设置为飞机初始位置下方radius距离处
        this.circleCenter = plane.position.subtract(cc.v2(0, this.circleRadius));
        this.currentRadian = Math.PI / 2;
    }

    init(): void {
        this.cameraNode.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
        this.cameraNode.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
        this.cameraNode.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
    }

    onTouchStart(event: cc.Event.EventTouch): void {
        this.touchStartPos = event.getLocation();
    }

    onTouchMove(event: cc.Event.EventTouch): void {
        this.isRotating = true;

        const touchPos = event.getLocation();
        const moveVec = touchPos.subtract(this.touchStartPos);
        
        // 调整旋转灵敏度
        const rotateSpeed = 0.02;
        this.currentRadian += moveVec.x * rotateSpeed;
        // 保持弧度在0~2π区间循环
        if (this.currentRadian < 0) this.currentRadian += Math.PI * 2;
        if (this.currentRadian > Math.PI * 2) this.currentRadian -= Math.PI * 2;

        this.touchStartPos = touchPos;
    }

    onTouchEnd(event: cc.Event.EventTouch): void {
        this.isRotating = false;
        this.touchStartPos = cc.Vec2.ZERO;
    }

    protected update(dt: number): void {
        if (this.isRotating) {
            const plane = this.Game.getChildByName('Plane');
            // 计算圆周上的目标位置
            const targetPos = cc.v2(
                this.circleCenter.x + this.circleRadius * Math.cos(this.currentRadian),
                this.circleCenter.y + this.circleRadius * Math.sin(this.currentRadian)
            );
            plane.setPosition(targetPos);

            // 设置飞机朝向为切线方向(根据你的飞机精灵初始朝向调整角度偏移)
            plane.angle = cc.misc.radiansToDegrees(this.currentRadian) + 90;

            this.cameraNode.setPosition(targetPos);
        }
    }
}

内容的提问来源于stack exchange,提问作者Khang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 14:00:56