CocosCreator实战篇 | 制作一个物体围绕另一个物体旋转

  • 📢博客主页:https://blog.csdn.net/dxt19980308

  • 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!

  • 📢本文由肩匣与橘编写,首发于CSDN🙉

  • 📢生活依旧是美好而又温柔的,你也是✨


🏳️‍🌈一个物体围绕另一个物体旋转

📢前言

🏳️‍🌈整体思路

🏳️‍🌈代码实现


📢前言

        在游戏开发过程中,可能会有让一个物体围绕另一个物体旋转的需求,就比如月球围绕着地球旋转,同时地球也在围绕着太阳旋转。本文给大家分享一个实现一个物体围绕另一个物体旋转的方案。


🏳️‍🌈整体思路

对于这类持续运动的实现,我们都可以在 update 中每帧进行操作。

首先假定有两个物体 A 与 B ,且二者处于同一层级或者将 A 作为 B 的父节点。我们每帧都根据 A 的位置、两者的角度以及他们之间的距离来计算 B 的位置,这样即使 A 的位置发生变化 B 也能在正确的轨道上运动。

并且我们可以根据二者之间的角度来让 B 的某一面始终指向 A 。


🏳️‍🌈代码实现

首先定义一个枚举 Axis 来作为旋转时的指向选项,同时导出给外部调用:

export enum Axis {
    PositiveX, // 正 X 轴
    PositiveY, // 正 Y 轴
    NegativeX, // 负 X 轴
    NegativeY, // 负 Y 轴
}

定义我们需要用到的属性和参数(只有 faceToTarget 为 true 时才会显示 faceAxis 选项):

@property({ type: cc.Node, tooltip: '围绕旋转的目标' })
public target: cc.Node = null;

@property({ tooltip: '顺时针旋转' })
public clockwise: boolean = true;

@property({ tooltip: '旋转一圈花费的时间' })
public timePerRound: number = 10;

@property({ tooltip: '是否始终面向目标节点' })
public faceToTarget: boolean = false;

@property({
    type: cc.Enum(Axis),
    tooltip: '面向目标节点的轴:\n- PositiveX:正 X 轴\n- PositiveY:正 Y 轴\n- NegativeX:负 X 轴\n- NegativeY:负 Y 轴',
    visible() { return this.faceToTarget }
    })
public faceAxis: Axis = Axis.NegativeY;

@property({ tooltip: '自动开始旋转' })
public autoStart: boolean = false;

public angle: number = 0; // 角度

public radius: number = 0; // 半径

private isRotating: boolean = false; // 标志位,是否正在旋转

然后我们需要一个 public 的 run 函数来作为启动函数,这样就可以在其他脚本主动调用该函数了。并且在启动函数里先获取初始的角度和半径:

/**
 * 开始围绕目标节点旋转
 * @param target 目标节点
 * @param clockwise 是否顺时针旋转
 * @param timePerRound 旋转一圈的时间
 * @param faceToTarget 是否始终面向目标节点
 * @param faceAxis 面向目标节点的轴
 */
public run(target?: cc.Node, clockwise?: boolean, timePerRound?: number, faceToTarget?: boolean, faceAxis?: Axis) {
    if (target) this.target = target;
    if (clockwise) this.clockwise = clockwise;
    if (timePerRound) this.timePerRound = timePerRound;
    if (faceToTarget) this.faceToTarget = faceToTarget;
    if (faceAxis) this.faceAxis = faceAxis;
    if (!this.target) {
        cc.log('No target!');
        return;
    }
    // 计算初始角度和半径
    this.angle = this.getAngle(this.target.getPosition(), this.node.getPosition());
    this.radius = this.getDistance(this.target.getPosition(), this.node.getPosition());
    // 开始
    this.isRotating = true;
}

其中的 getAngle 函数用来计算两个点之间的角度:

/**
 * 获取两点间的角度
 * @param p1 点1
 * @param p2 点2
 */
private getAngle(p1: cc.Vec2, p2: cc.Vec2): number {
    return Math.atan(p2.y - p1.y / p2.x - p1.x);
}

而 getDistance 函数用来计算两个点之间的距离:

/**
 * 获取两点间的距离
 * @param p1 点1
 * @param p2 点2
 */
private getDistance(p1: cc.Vec2, p2: cc.Vec2): number {
    return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
}

接下来在 update 中实现主要逻辑:

update(dt: number) {
    if (!this.isRotating || !this.target) return;
    // 将角度转换为弧度
    let radian = Math.PI / 180 * this.angle;
    // 更新节点的位置
    this.node.x = this.target.x + this.radius * Math.cos(radian);
    this.node.y = this.target.y + this.radius * Math.sin(radian);
    // 更新节点的角度
    if (this.faceToTarget) {
        switch (this.faceAxis) {
            case Axis.PositiveX:
                this.node.angle = this.angle + 180;
                break;
            case Axis.PositiveY:
                this.node.angle = this.angle + 90;
                break;
            case Axis.NegativeX:
                this.node.angle = this.angle;
                break;
            case Axis.NegativeY:
                this.node.angle = this.angle - 90;
                break;
        }
    }
    // 计算下一帧的角度
    let anglePerFrame = dt * (360 / this.timePerRound);
    if (this.clockwise) this.angle -= anglePerFrame;
    else this.angle += anglePerFrame;
    // 重置角度,避免数值过大
    if (this.angle >= 360) this.angle %= 360;
    else if (this.angle <= -360) this.angle %= -360;
}

在 start 中执行自动启动的逻辑:

start() {
    if (this.autoStart) this.run();
}

最后还需要一个 public 的 stop 函数来停止当前的旋转:

/**
 * 停止旋转
 */
public stop() {
    this.isRotating = false;
}
  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肩匣与橘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值