这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助效果

金币从初始位置散开后逐个飞向指定位置,这是游戏中很常用的一个动画,效果如下:

思路

这个效果中,分成两个阶段:

  • 一定数量的金币从一个起点散开
  • 这些金币逐一飞向终点

计算金币的初始散开位置生成圆周上的等分点

金币散开的位置看似随机,但实际上是围绕起点形成一个圆。对于圆上的等分点,我们可以利用基本的三角函数来计算。例如,若要将圆分成8等分,每个点之间的夹角就是45度(360度/8)。已知圆心坐标和半径,就可以计算出每个等分点的坐标,如下图

随机偏移

为了让金币的位置看起来更自然,我们对每个点的位置进行随机偏移。这可以通过在计算出的坐标上加上一个随机向量来实现,从而让金币围绕起点呈现随机分布的效果。

位置代码如下:

/**   * 以某点为圆心,生成圆周上等分点的坐标   * @param {number} radius 半径   * @param {cc.Vec2} pos 圆心坐标   * @param {number} count 等分点数量   * @param {number} randomScope 等分点的随机波动范围   * @returns {cc.Vec2[]} 返回等分点坐标   */  getCirclePosition(radius: number, pos: cc.Vec2, count: number, randomScope: number = 60): cc.Vec2[] {    let positions = [];    let radians = (Math.PI / 180) * Math.round(360 / count);    for (let i = 0; i < count; i++) {      let x = pos.x + radius * Math.sin(radians * i);      let y = pos.y + radius * Math.cos(radians * i);      positions.unshift(cc.v3(x + Math.random() * randomScope, y + Math.random() * randomScope, 0));    }    return positions;  }

金币是一直在旋转,还需要在Cocos Creator编辑器中为预制体节点添加旋转动画

金币飞向目标位置计算金币到目标位置的距离

在金币飞向钱包的过程中,我们希望金币按照距离钱包的远近顺序进入。因此,需要先计算每个金币到钱包的距离。这可以通过计算每个金币位置和钱包位置之间的向量距离来实现。

let points = this.getCirclePosition(r, stPos, count);let coinNodeList = points.map(pos => {  let coin = this.getCoinNode();  coin.setPosition(stPos);  this.node.addChild(coin);  return {    node: coin,    stPos: stPos,    mdPos: pos,    edPos: edPos,    dis: (pos as any).sub(edPos).mag()  };});

排序和动画执行

根据计算出的距离对金币进行排序,使距离近的金币先飞入钱包。

coinNodeList = coinNodeList.sort((a, b) => {  if (a.dis - b.dis > 0) return 1;  if (a.dis - b.dis < 0) return -1;  return 0;});

通过缓动动画系统播放金币飞向目标位置的动画

// 执行金币落袋的动画coinNodeList.forEach((item, idx) => {  cc.tween(item.node)      .to(0.3, {position: item.mdPos})      .delay(idx * 0.01)      .to(0.5, {position: item.edPos})      .call(() => {        // 金币落袋后,将金币节点放入节点池中,并更新金币数值        this.coinNum += 20;        this.coinNumLabel.string = this.coinNum.toString();        this.coinPool.put(item.node);      })      .start();});

这里使用节点池来重复利用金币节点,以防性能紧张

完整代码如下:

const { ccclass, property } = cc._decorator;@ccclassexport default class CoinRewardEffect extends cc.Component {  /** 金币动画启动 */  @property(cc.Node)  startNode: cc.Node = null;  /** 金币动画终点 */  @property(cc.Node)  endNode: cc.Node = null;  /** 金币数值Label */  @property(cc.Label)  coinNumLabel: cc.Label = null;  /** 金币预制节点 */  @property(cc.Prefab)  coinPrefab: cc.Prefab = null;  /** 金币节点池 */  coinPool: cc.NodePool = null;    /** 金币数 */  coinNum: number = 1000;  onLoad() {    this.coinPool = new cc.NodePool();    this.coinNumLabel.string = this.coinNum.toString();    this.initCoinPool();  }  /** 先预先创建几个节点放入节点池中 */  initCoinPool(count: number = 20) {    for (let i = 0; i  0) {      coin = this.coinPool.get();    } else {      coin = cc.instantiate(this.coinPrefab);    }    return coin;  }  playAnim() {    let randomCount = 20;//Math.random() * 10 + 10;    let stPos = this.startNode.getPosition();    let edPos = this.endNode.getPosition();    this.playCoinRewardAnim(randomCount, stPos, edPos);  }  /**   * 金币飞向钱包的动画   *   * @param {number} count 金币数量   * @param {cc.Vec2} stPos 金币起始位置   * @param {cc.Vec2} edPos 金币终点位置   * @param {number} [r=130] 金币飞行的半径   */  playCoinRewardAnim(count: number, stPos: cc.Vec2, edPos: cc.Vec2, r: number = 130) {    // 生成圆,并且对圆上的点进行排序    let points = this.getCirclePosition(r, stPos, count);    let coinNodeList = points.map(pos => {      let coin = this.getCoinNode();      coin.setPosition(stPos);      this.node.addChild(coin);      return {        node: coin,        stPos: stPos,        mdPos: pos,        edPos: edPos,        dis: (pos as any).sub(edPos).mag()      };    });    coinNodeList = coinNodeList.sort((a, b) => {      if (a.dis - b.dis > 0) return 1;      if (a.dis - b.dis  {      cc.tween(item.node)          .to(0.3, {position: item.mdPos})          .delay(idx * 0.01)          .to(0.5, {position: item.edPos})          .call(() => {            // 金币落袋后,将金币节点放入节点池中,并更新金币数值            this.coinNum += 20;            this.coinNumLabel.string = this.coinNum.toString();            this.coinPool.put(item.node);          })          .start();    });  }  /**   * 以某点为圆心,生成圆周上等分点的坐标   * @param {number} radius 半径   * @param {cc.Vec2} pos 圆心坐标   * @param {number} count 等分点数量   * @param {number} randomScope 等分点的随机波动范围   * @returns {cc.Vec2[]} 返回等分点坐标   */  getCirclePosition(radius: number, pos: cc.Vec2, count: number, randomScope: number = 60): cc.Vec2[] {    let positions = [];    let radians = (Math.PI / 180) * Math.round(360 / count);    for (let i = 0; i < count; i++) {      let x = pos.x + radius * Math.sin(radians * i);      let y = pos.y + radius * Math.cos(radians * i);      positions.unshift(cc.v3(x + Math.random() * randomScope, y + Math.random() * randomScope, 0));    }    return positions;  }}

到此就实现了开头的效果。

本文转载于:https://juejin.cn/post/7302618003886194714如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。