专栏首页异名移动残影效果

移动残影效果

游戏中的人物移动带起残影,用来表达速度是很有视觉表现力的。异名的实现思路是从“白玉无冰”那里参照过来的,在具体的实现上面添加了一些异名自己的理解。

demo

实现思路

投影到多个画布

“白玉无冰”的这张图解析得很清晰,我们在实现的时候会在移动的角色中新建一个独立摄像机的子节点,专门拍摄需要移动的角色,然后投影到五个不同透明度的Sprite中。当角色移动的时候,我们也让作为残影的五个Sprite,分别有延迟地移动到角色当前的位置,这样子在视觉上就有五个残影在跟随了。

detail

在具体的实现有两个注意点,一个是因为摄像机仅仅只需拍摄移动的角色,所以要为角色新建一个分组,相机只拍摄这个分组;还有一个就是相机拍摄出来的画面投影在RenderTexture上是一个上下颠倒的镜像图像,所以要设置每个Sprite所在节点的scaleY = -1

这块的具体实现上我和白玉无冰的做法不一样,白玉无冰直接在编辑器上对残影所在的Sprite节点做上述提到的透明处理、层级管理、颠倒处理,异名会觉得把这块的设置放到代码层面处理比较好,一来是编辑器的功能最好还是专职于布局,比如像这个残影透明参数和层级管理确实可以通过编辑器来配置,但是透明度的细微变化和变量绑定的先后顺序这些微小区别,其实是不利于后面的维护和他人接手的,后面阅读代码逻辑的时候也无法看出整个实现的思路。二来就是在代码内做设置参数可以随时在代码上做调整,比如我的残影透明度的初始值,用代码赋值的方式就会比在编辑器中设置会更加一目了然。

代码如下?

const roleZindex = 10;
this.role.zIndex = roleZindex;

const texture = new cc.RenderTexture();
texture.initWithSize(this.node.width, this.node.height);
const spriteFrame = new cc.SpriteFrame();
spriteFrame.setTexture(texture);
this.roleCamera.targetTexture = texture;
this.ghostCanvasList.forEach((ghost, idx) => {
  ghost.node.scaleY = -1;
  ghost.node.zIndex = roleZindex - idx;
  ghost.node.opacity = 100 - idx * 15;
  ghost.spriteFrame = spriteFrame;
});

角色移动

因为我们的实现是把相机作为子节点绑定在角色节点下面,当角色移动的时候我们的相机也跟着移动了,我们就需要把相机投影所在的Sprite节点们分别做一个延时移动,带出”残影“效果。代码如下:

this.schedule(this.ghostFollow, 0.1, cc.macro.REPEAT_FOREVER);
this.node.on(cc.Node.EventType.TOUCH_MOVE, this.touchMoveEvent, this);

touchMoveEvent(evt: cc.Event.EventTouch) {
  this.role.x += evt.getDeltaX();
  this.role.y += evt.getDeltaY();
}

ghostFollow() {
  this.ghostCanvasList.forEach((ghost, i) => {
    const dis = (ghost.node.position as any).sub(this.role.position).mag();
    if (dis < 0.5) return;
    ghost.node.stopAllActions();
    ghost.node.runAction(cc.moveTo(i * 0.04 + 0.02, this.role.x, this.role.y));
  });
}

这里注意到,我们并不是在每次touchMoveEvent的时候去调用ghostFollow函数,而是启用了一个无限重复的定时器去同步,为什么呢?大家去试一试就知道了原因了,其实cc.Node.EventType.TOUCH_MOVE是帮我们做了节流的,大部分时候我们受益于这个节流,但是在这个功能里面,节流调用会导致我们的位置同步不及时而导致残影不流畅,所以我们需要单开一个定时器去实时同步,同时记得在destroy之前别忘了去销毁它。

ghostFollow函数主要是同步残影和角色的位置,白玉无冰在这里也有两个小的疏忽。一个是在计算dis的时候,正确的做法是拿残影的位置去和角色的位置做距离运算的,这个失误白玉无冰有在公众号留言中提到,但是还没有在代码仓库中修正过来,大家借鉴的时候要注意。还有一个就是判断是否静止,异名的判断条件是dis < 0.5就认为它们已经重合了,0.5的像素差别在画面上人眼是看不出来的,那为什么不能是if(dis > 0) { xxxxx }呢?在现代的语言中,浮点数计算是有误差的,dis的结果是通过向量计算得出的,经过了加减乘除,中间的计算过程肯定产生了浮点数,它产生出来的结果肯定也是有误差的,大家可以在控制台把dis变量打印出来,你会发现,每次静止的时候,理论上静止了,dis的结果应该为0,但是实际上有可能每次产生的dis值都是不一样的,可能是0也有可能是0.00012340.1222222等等,但是这个值我们已经可以认为它们已经静止了。因此大家要有意识,当涉及到精确判断的时候,要做容错处理,if(dis > 0) { xxxxx }这种写法其实没有考虑到容错。

本文分享自微信公众号 - 异名(async-code),作者:异名君

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-04-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 从Chrome小恐龙游戏学习2D游戏制作

    在chrome浏览器的断网页面,按空格键或者向上键会出现一个小恐龙跑酷小游戏,这个2D小游戏在设计上精致小巧,在代码上也只有三千多行,思路清晰严谨,很有学习价值

    异名
  • 遥控杆实现

    遥控杆的构造分为中间的控制点和外层的圆形,首先给遥控杆绑定个触控事件,然后在touch_move的时候让触控杆保持在圆形中,同时把鼠标的位置偏移信息传给需要移动...

    异名
  • 微信小游戏首包超出4M之后

    微信小游戏平台上对首包的的限制是4M,超出限制之后可以采取什么样的措施呢?异名做了一下盘算,大概可以有以下操作

    异名
  • STL 源码剖析之动态数组 vector

    vector 的数据安排以及操作方式,与 array 非常相似。两者的唯一差别在于空间的运用的灵活性,array 是静态的,一旦配置了就不能改变,而 vecto...

    公众号guangcity
  • 开发一个Node命令行小玩具全过程--高颜统计工具

    命令行工具对于我们来说非常的熟悉,一些命令行的操作也极大的简化了我们的日常工作。本文就基于我写的一个Node命令行代码计数器来进行展开。

    秋风的笔记
  • Vue 3 选项 API

    其实我们经常使用到组件里面的数据,而这些数据是定义在 data 对象函数里面的,为什么要实现定义在 data 对象函数里面呢?在 data 对象函数中的数据,V...

    公众号---志学Python
  • day86-ES6一般的语法和Vue的认识

    少年包青菜
  • 开发笔记:基于Electon的图片采集工具

    题图,由ACE Land 人工智能设计师赞助。 ? 人这一辈子没法做太多的事情, 所以每一件都要做得精彩绝伦。 你的时间有限, 所以不要为别人而活。 不要被教条...

    mixlab
  • 以‘不作恶’来作恶的谷歌和被略根性滋养的中国互联网

    互联网一个特点是,从来不缺乏热闹。这几天在网上又引发了一件让人经不住想去凑热闹的事情。《人们日报》在Twitter和facebook上发言,知道这两个网站的人可...

    望月从良
  • 静态测试技术之 Lint 冗余资源清理

    谈到冗余资源清理,我们不妨先来看看Android的资源组织方式和访问方式。本文通过冗余资源的清理 、冗余资源清理原理解析以及手管的冗余资源清理应用三方面来讲解分...

    腾讯移动品质中心TMQ

扫码关注云+社区

领取腾讯云代金券