倒水小游戏

这是我学习shader的一个练手项目


倒水.gif

实现倒水动作,核心问题有三个。

  • 杯子倾斜过程中,水面要保持水平
  • 水要根据颜色分层
  • 倒水的时候第一层水面要荡漾起来

对于问题一,旋转的过程中要保证水的体积不变,我是假定瓶子是个矩形(不考虑圆底),根据瓶子水量和瓶子倾斜角度,计算出水面的中点,然后将纹理坐标绕这个中点进行旋转,然后丢弃y>0的像素。

计算中点需要区分高度和倾斜角的几种情况,要判断水面有没有触及瓶口以及瓶底,再根据三角关系算出体积不变的情况下,对某一倾斜角度水面的中点

    float ratio = iResolution.y/iResolution.x;
    bool toLeft = sin(angle)>=0.0;
    vec2 center = vec2(0.5,1.0-_height);//水面倾斜时,以哪个店为中心点

    float _t = abs(tan(angle));
    if(_height<0.5){//水的体积小于杯子的一半,先碰到下瓶底
      bool is_bottom = _t/ratio>2.0*_height;//倾斜角度达到瓶底
      if(is_bottom){
        center.x = sqrt(2.0*_height/_t*ratio)/2.0;
        center.y = 1.0 - sqrt(2.0*_height*_t/ratio)/2.0;

        bool is_top = _t>(ratio)/(_height*2.0);//倾斜角度达到瓶口
        if(is_top){
          center.y = 0.5;
          center.x = _height;
        }
      }
      if(!toLeft){
        center.x = 1.0-center.x;
      }
    }else{//水比较多,先碰到上瓶底
      bool is_top = _t>2.0*ratio*(1.0-_height);
      if(is_top){
        center.x = sqrt(2.0*ratio*(1.0-_height)/_t)/2.0;
        center.y = sqrt(2.0*ratio*(1.0-_height)*_t)/2.0/ratio;

        bool is_bottom = _t>ratio/(2.0*(1.0-_height));
        if(is_bottom){
          center.y = 0.5;
          center.x = 1.0-_height;
        }
      }

      if(toLeft){
        center.x = 1.0-center.x;
      }
    }
    
    uv.y = uv.y*ratio;
    uv -= vec2(center.x,center.y*ratio);
    
    vec2 uv1 = tranPt(uv,angle,vec2(0.0));

对于问题2,是传入颜色、高度数组,从最下面颜色开始遍历,使用不同的高度进行绘制,如果绘制成功就跳出循环。

    for(int i=0;i<MAX_ARR_LEN;i++){
      if(heights[i].x<0.001){
        continue;
      }
      _height+=heights[i].x;
      a += drawWater(uv,angle,_height,size,i);
      if(a>0.0){//绘制过的,跳过
          ret *= a*colors[i]; 
          break;
      }
    }

水面荡漾,我使用的是最简单的三角函数,还不是很自然,怎样模拟自然的水面荡漾效果,还有待学习。

float y = 0.0;

    bool hasWave = curIdx==arrSize-1;//只有最上面一层有波浪
    hasWave = hasWave;
    if(hasWave){
      // 代入正弦曲线公式计算模拟水面波浪 y = Asin(ωx ± φ)
      float amplitude = 0.0;// 振幅(控制波浪顶端和底端的高度)
      float angularVelocity = 0.0;// 角速度(控制波浪的周期)
      float frequency = 0.0;// 频率(控制波浪移动的速度)
      if(abs(waveType-1.0)<0.01){//往里倒水
        amplitude = 0.06;
        angularVelocity = 10.0;
        frequency = 10.0;
      }else if(abs(waveType-2.0)<0.01){//往外倒水
        amplitude = 0.03;
        angularVelocity = 5.0;
        frequency = 6.0;
      }
      
      y = amplitude * sin((angularVelocity * uv1.x) + (frequency * cc_time.x)*(toLeft ? 1. : -1.));
    }

倾倒的水流是用的cc.Graphics组件。

倒水的过程其实就是不停的减少水面高度、修改杯子倾斜度。

倒水小游戏完整源码,已经发布到 cocosstore

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容