• 用cocos实现的立方体宣传查看页面


    cocos进入3.x时代,我也努力跟进,实现了一个将页面映射到立方体上进行旋转查看的效果。

    效果如下:

    要点

    为了这个效果,我主要实现了3个要点:

    • 将页面准确映射到立方体上面,适配不同尺寸的手机屏幕。
    • 对页面内容进行正常滑动、点击操作。
    • 不同页面数量能够在立方体上滑动屏幕循环查看。

    页面映射到立方体

    创建立方体和页面

    因为需要在4个面上显示不同的页面内容,引擎自带的立方体并不能简单地满足这个功能。这里我使用了4个quad拼凑成一个四面体,放在一个节点下,方便后面进行整体旋转。

    四个quad节点的坐标分别是:前(0,0,0.5),右(0.5,0,0),后(0,0,-0.5),左(-0.5,0,0)

    (图一:立方体拼凑示意)

    页面就是普通的渲染节点。不用scrollview做页面滚动,之后统一用屏幕操作来处理。

    页面没有铺满屏幕,在上方空出了导航栏的空间。

    (图二:页面布局示意)

    canvas的宽度尺寸是750,写有内容的页面的宽度是600(后面用pageWidth表示),居中。

    屏幕宽适配,页面距离canvas顶部始终是250(后面用top表示) ,距离底部始终是60(后面用bottom表示) 。

    页面节点加了mask组件所以只显示了一部分内容。

    透视摄像机参数调整

    做3d项目,首当其冲的是透视摄像机的引入。需要先了解它的各项参数的意思,才好完成一些3d功能。

    我想实现的是:调整主摄像机的参数,可以让立方体的前quad模型在页面上呈现和“图二”一样的尺寸效果。

    主要是调整主摄像机的位置坐标和fov值。(我想保持立方体不动)

    x坐标影响quad左右显示,因为页面左右对称,所以x坐标为0 。

    y坐标影响quad上下显示,值越大,页面越靠下。

    y的值可以直接由:(top-bottom)/(2*pageWidth)得到。这里是:(250-60)/(2*600)=0.1583333

    z坐标影响quad两边的空间,值越大,离quad越远,页面显示越小。

    摄像机距离立方体距离和fov的大概关系如下:

    (因为用了宽适配,不想fov影响宽度,所以fov axis使用了horizontal)

    (图三:透视摄像机距离和fov关系,俯视图)

    红色实线代表了前quad模型。宽度是1

    绿色实线是近裁剪面。

    BC比上AD的大小是:600/750 . 这里用ratio表示

    AD尺寸:2*near*tan(fov/2)

    可以算出DisZ和Fov的关系是:

    DisZ=1/(2*tan(Fov/2)*ratio)

    当Fov取60度时,DisZ是:1.0825 。那么主摄像机距离原点就是:1.5825(前quad距离立方体节点0.5)

    调整z坐标后的效果如下。

    (图四:调整主摄像机z坐标后的效果)

    可以看到调整主摄像机的z值后,透视摄像机的前quad模型和正交摄像机的页面宽度保持了一致。

    因为设备屏幕宽高比(aspect)不一致,所以quad的高度需要在启动后调节。

    适配需求:页面距离canvas顶部始终是250 ,距离底部始终是60 。

    quad高度和透视摄像机的关系如下图:

    (图五:透视摄像机和立方体前quad的关系,左视图)

    其中绿色实线为近裁剪面,红色实线为前quad模型

    黄色虚线是xz平面,经过立方体中心。

    蓝色虚线是摄像机xz平面,经过近裁剪面中心。

    近裁剪面的宽度除以高度等于aspect,这个可以通过屏幕尺寸得到。

    在cocos里可以通过camera.aspect得到

    通过之前的公式,计算得到红色实线即前quad的高度为:

    1/(ratio*aspect)-(top+bottom)/pageWidth

    如果基于iphoneSE的屏幕,该值为:1.706666

    调整好x,y,z和fov值后的效果:

    (图六:调整完摄像机参数后的效果)

    可以看到,2d页面和quad已经完全重合了!✌

    立方体旋转

    我直接监听了input的touchmove事件,然后根据delta来旋转立方体。

    旋转的时候,使用到了四元数,四元数又是一个新词,不好理解,

    但是cocos做了很好的封装,使用起来很方便。

    旋转api用到了Quat.fromAxisAngle函数,代表了基于旋转轴和旋转角度的旋转。

    旋转轴很容易确定,因为只是水平方向的旋转,那么就是绕着y轴旋转。即Vec3.UP

    旋转角度做了一个限制,就是希望玩家即使从屏幕一侧滑到另一侧,也不会看到背面。

    所以,最大旋转角度为90度。每次滑动屏幕距离是deltaX,那么每次的旋转角度是:90*deltaX/屏幕宽度

    核心旋转代码是:node.rotate(quat)

    设置后效果如下:

    (图七:立方体旋转示意)

    动图中,文字页面还没有映射到立方体上,所以为各个面加了基础颜色。

    图七中的效果,仅是旋转跟随滑动。当松手后,应该需要像pageview那样,根据一个阈值,要么还原,要么旋转到下一个页面。

    我使用了tween的方式,在松手后,直接旋转到原角度或者下一个页面的角度。

    为了方便,在旋转的时候加了锁,只有tween完成后,才可以进行下一次滑动旋转。

    这里还根据需要旋转的角度,算了一下时间,最长0.5秒,最短0.1秒。做一个线性插值。

    最后的旋转效果如下:

    (图八:立方体旋转示意,带自动旋转)

    页面映射

    我使用了rendertexture渲染贴图,为立方体的前,右,后,左各面片分别创建了对应的渲染贴图、材质、摄像机。材质绑定对应的渲染贴图。将材质赋值给各quad面片。为每个面设置一个layer。然后让对应的摄像机只拍摄这个layer。将渲染贴图赋值给对应的摄像机。

    总之创建了:

    4个渲染贴图,4个材质,4个摄像机。

    都配置好后的效果如下:

    (图九:配置渲染贴图后的效果)

    现在会有2个问题:不同屏幕尺寸,内容会变形;内容没有铺满。

    只要将各面的摄像机尺寸和主正交摄像机的orthoHeight设置成一样,

    将各面的渲染贴图的尺寸设置成(aspect*orthoHeight,orthoHeight)

    就能保证屏幕完整映射到立方体各面上。如下:

    (图十:页面完整映射到立方体各面)

    现在内容还是处于拉伸或者压缩的状态,因为页面的宽高比和屏幕的不一致。

    只需要将渲染贴图的材质的tiling offset参数进行调整,

    去掉黑色区域就可以还原内容的尺寸比例。

    tilingOffset的x,y对应uv的xy缩放,tilingOffset的zw对应uv的xy偏移。

    宽度是600/750=0.8;

    所以tilingOffset的x设置为0.8 .

    x方向应该采样0.1~0.9这个范围。所以将tilingOffset的z设置为0.1。

    tilingOffset的y需要用公式:y=(screenHeight-top-bottom)/screenHeight来运算。

    w需要用公式:z=top/screenHeight来运算。

    最后用

    material.setProperty('tilingOffset',new Vec4(0.8,(screenHeight-top-bottom)/screenHeight,0.1,top/screenHeight))  

    来设置最终的渲染效果,如下动图所示:

    (图十一:对材质进行tilingoffset后的效果)

    然后再加上一个背景图

    调整一下各个摄像机的参数

    主透视摄像机用depth only,priority 设置成最高

    页面的正交摄像机用solid color,clear color用半透明白色, priority 设置成中等

    主正交摄像机priority设置成最低,用于背景渲染。

    将各页面材质的technique设置成1-transparent。

    就实现了相对高级一点的效果:

    (图十二:添加背景和透明度的效果)

    对立方体各面内的页面进行长图滑动和点击

    我的做法概要:

    在滑动的第一帧,判断是想上下滑,还是想左右滑。

    如果判断deltaY大于deltaX,则上下滑动,对当前页面进行content坐标处理。

    如果无移动,则对当前页面进行点击处理。

    页面点击是利用世界坐标进行的判断。

    最后的效果如下:

    (图十三:添加滑动和点击的效果)

    因为不是3d内容范畴,所以写的比较简单。嘿嘿。

    对不同数量的页面进行映射

    我用过切换页面的layer,但是这样只能适用至少3个页面的情况。如果只有1、2个就不行了。

    所以最后使用了切换摄像机的visibility来实现。

    每次旋转结束,判断前,右,后,左四个页面,应该是哪个一。

    然后设置摄像机的visibility来对应。

    并且因为只有4个面片,所以layer也只需要用到1,2,4,8即可。

    去掉一个页面后的效果如下(也是开头的效果)

    (图十四:只有3个页面的效果)

    END...

  • 相关阅读:
    windos 下 nginx 安装
    prometheus的rules配置
    EMC原理 传导(共模 差模) 辐射(近场 远场) 详解
    从零开始学习Dubbo4——让模块独立运行
    大三第十二周学习笔记
    Java Double byteValue()方法具有什么功能呢?
    Leetcode 440. 字典序的第K小数字
    owl文件导入Neo4j
    PHP对接钉钉群机器人
    设计模式中的七大原则
  • 原文地址:https://blog.csdn.net/q314235965/article/details/127954959