记录–实现金币飞入钱包的动画

  • 记录–实现金币飞入钱包的动画已关闭评论
  • 76 次浏览
  • A+
所属分类:Web前端
摘要

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


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

记录--实现金币飞入钱包的动画

 

效果

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

记录--实现金币飞入钱包的动画

思路

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

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

计算金币的初始散开位置

生成圆周上的等分点

金币散开的位置看似随机,但实际上是围绕起点形成一个圆。对于圆上的等分点,我们可以利用基本的三角函数来计算。例如,若要将圆分成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;  @ccclass export 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 < count; i++) {       let coin = cc.instantiate(this.coinPrefab);       this.coinPool.put(coin);     }   }    /** 从节点池中取出节点 */   getCoinNode() {     let coin = null;     if (this.coinPool.size() > 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 < 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();     });   }    /**    * 以某点为圆心,生成圆周上等分点的坐标    * @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

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 记录--实现金币飞入钱包的动画