首页 > 其他分享 >记录--实现金币飞入钱包的动画

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

时间:2023-11-20 18:44:23浏览次数:35  
标签:count 动画 -- number pos 金币 cc let 飞入

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

 

效果

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

思路

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

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

计算金币的初始散开位置

生成圆周上的等分点

金币散开的位置看似随机,但实际上是围绕起点形成一个圆。对于圆上的等分点,我们可以利用基本的三角函数来计算。例如,若要将圆分成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;

  onl oad() {
    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

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

 

标签:count,动画,--,number,pos,金币,cc,let,飞入
From: https://www.cnblogs.com/smileZAZ/p/17844597.html

相关文章

  • drop_caches 的简单学习
    drop_caches的简单学习背景最近一段时间一直在学习内存相关的知识Linux系统里面的内存管理还是非常复杂的.我这边理解Linux从宏观层次的段页式内存管理到细节的buddy和slab以及大页内存分配以及pagecache和buffers的缓存等设置.最近因为遇到了CentOS6和CentOS7......
  • 软件测试/人工智能|思维导图很难画,ChatGPT来帮你
    简介在我们日常的软件测试工作中,测试用例是非常重要的一环,但是很多时候我们并不是第一时间就设计测试用例的,我们会先根据需求先制定一个思维导图,后面再去设计我们的测试用例,有的公司更是会通过思维导图来设计测试用例的。本文就来给大家介绍一下如何使用ChatGPT来生成思维导图。......
  • pandas索引切片
    行 列 ......
  • Transferability of the Chinese Eco-environmental Protection Measure
    LondonConventionandLondonProtocolThe LondonConvention and LondonProtocol establishtheglobalrulesandstandardsforpreventing,reducing,andcontrollingpollutionofthemarineenvironmentbydumping.The 1982UnitedNationsConventionontheLa......
  • RTaW-Pegase构建可预测QoS的TSN网络架构
    ​1.设计下一代面向服务的E/E架构 1.设计下一代面向服务的E/E架构当今汽车上多达数以百计的ECU(电子控制单元),MCU(微控制处理器单元)及其上面运行着的大量的嵌入式软件代码,以及复杂的CAN、LIN、FlexRay等整车通讯网络决定了汽车不同于其他的IOT设备或智能手机。汽车上的电子电气架......
  • 软件测试/人工智能|如何利用ChatGPT帮助我们编写测试用例
    简介在我们通过思维导图设计了我们的测试用例,下面就需要我们实际去编写测试用例了,一个测试用例通常包含以下要素:测试模块测试标题前置条件测试步骤预期结果实际结果本文就来给大家介绍一下如何使用ChatGPT来帮助我们编写测试用例。生成实战我们如何让ChatGPT帮我们编......
  • 软件测试/人工智能|测试数据很头疼,ChatGPT帮你造
    简介在我们完成了测试用例的编写之后,我们就需要造不同的测试数据去进行实际的测试工作了,我们要让我们的测试尽可能地覆盖所有的情况,就需要我们根据测试用例的设计方法,去设计更多的测试数据,执行更多地测试用例,使得测试更加完善。在造测试数据的时候,ChatGPT当然也可以帮我们很多。......
  • 【杂谈】文件格式
    ......
  • flask 文件上传与接收
    在做flask后端,有个需求是将视频从前端上传至后端,然后再页面播放上传的视频,记录下知识点。 文件流接收1、前端传来的对象是二进制文件流,有两种方法保存本地。(1)通过open()方法将文件流写入保存(2)直接用调用file.save()方法保存传来的文件流:fromflaskimportFlask,request......
  • SpringBoot集成LDAP认证登录
    Maven依赖<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.......