【摸鱼研究所】让你的屏幕下个雪呗 (番外篇)

509 阅读2分钟

当然雪落地其实并不难,我们来点好玩的,如果地上有个障碍物怎么办,这个场景其实就像我们的 app 页面中底下有一排按钮,比较明显的是 floating button , 我们想把雪下到他们头上,该怎么办呢?


1. 怎样的雪花会撞在障碍物上呢

这其实很简单,假设雪花都是球,啊不,都是圆的, 只要:

雪花中心到障碍物中心的距离 <= 障碍物半径,

就认为他撞墙了:

假设障碍物属性:

  Offset circlePosition = Offset(screenSize.width/2,  screenSize.height);
  double circleRadius = 50;

那么我们搞个方法来判断:

   bool _checkDangerous(SnowBasicInfo snow) {
    // 我们通过snow的x,y距离底下障碍物的中心距离来判断snow会不会撞到障碍物
    double xToBarrier = (circlePosition.dx - (snow.x)).abs();
    double yToBarrier = (circlePosition.dy - (snow.y)).abs();
    
    // 勾股定理求雪球中心到障碍物中心距离
    double distance = pow((xToBarrier * xToBarrier + yToBarrier* yToBarrier), 0.5);
    return distance < circleRadius;
  }
  

通过上面方法来判断雪花是不是危险,但是如果你说雪花比障碍物大怎么办?那你说我怎么办~当然随便他怎么办,我反正不办。

2. 那撞墙了要怎么办呢

雪花撞墙后,我们假设和它落地一个下场,会变成一根条条,嗯,只是他是一段弧,而不是线段,那么问题来了,弧要怎么算出来呢

来,捡起你的数学课本看下面这个图吧:

图中蓝色部分其实就是我们想要的弧,小圆即为雪花,大圆是障碍物,小圆在障碍物左侧和右侧分开计算。

在左图中,我们只需要求 S 点的位置所对应的 角θ ,就可以知道圆弧的起始角度,弧度对应 2倍的角1,半径即障碍物半径。

  • 小 r 为雪花半径,由之前定的雪花变化系数 snow.r 和雪花图宽共同决定: snow.r * snowImage.width.toDouble() / 2
  • 大 R 为障碍物半径:circleRadius
  • centerX, centerY 即为障碍物坐标
  • x, y. 即为当时雪花中心坐标:(snow.x, snow.y)

而右边的图就只要求角1就行,相对简单,

公式我都写图里了接下来只要变成代码就行了鸭:

和雪落地一样,我们给雪添加属性:

class SnowBasicInfo {
  ...
  Rect touchRect;
  
  // 起始角度
  double startAngle;
  
  // 滑过的角度
  double swipeAngle;
  
  //弧的粗细
  double stroke;
  
  // 是否撞墙
  bool touchBarrier = false;
  ...
}

与之前的雪落地一样,我们希望随着时间变长,积雪厚度增加,然鹅我们只能用和之前一样的蠢方法,即通过一个增长系数:

var multip = pow((snows.where((element) => element.touchRect != null).length / 10 ), 0.5);

我们在雪融化前添加这段逻辑:

  if(snow.r != 0 && (_checkDangerous(snow) || snow.touchBarrier)) {
    snow.touchBarrier = true;
    if(snow.touchRect == null) {
      var multip = pow((snows.where((element) => element.touchRect != null).length / 10 ), 0.5);
      var angleSnow = atan((snow.r * snowImage.width.toDouble() / 2) / circleRadius);
      snow.touchRect = Rect.fromCircle(center: circlePosition, radius: circleRadius);
      // 雪厚度就在这变化啦
      snow.stroke = multip * snow.r * 10 ;
      snow.startAngle = snow.x < circlePosition.dx ? pi + asin((circlePosition.dy - snow.y) / circleRadius) - angleSnow : 2* pi -(asin((circlePosition.dy - snow.y) / circleRadius));
      snow.swipeAngle = snow.x < circlePosition.dx ? angleSnow * 2 * snow.r / 2: -angleSnow * 2 * snow.r / 2;
    } else {

    }

    // canvas.drawArc 中的stroke用法和之前地上积雪一样哦
    canvas.drawArc(snow.touchRect, snow.startAngle, snow.swipeAngle, false, snowBarrierPainter..strokeWidth = snow.stroke);
    continue;
  }

好了,其实这段就是把上面图里的草稿翻译出来而已,这样就能看到最顶上gif里的效果啦,是不是只要细心想,一切都会很简单?

3. 如果雪能从障碍上滑下来呢...

我们可以选择加一个特效,当雪落到障碍上时,障碍上的积雪会随着时间而慢慢贴着障碍物往地上滑,同时越来越薄

毕竟没怎么见过积雪,只能xjbyy...

其实效果并不好,所以在顶上也没这样做,效果很简单,只要在雪撞墙后,让雪的旋转初始角度往边上累加就行啦:

if(snow.touchRect == null) {
      ...
} else {
  snow.startAngle = snow.x <= circlePosition.dx ? snow.startAngle - pi / 1000 : snow.startAngle + pi / 1000;
  if(snow.startAngle < pi || snow.startAngle > 2 * pi) {
   snow.invalid = true;
   snow.melted = true;
   continue;
  }
 snow.stroke = snow.stroke - 0.001;
}


至此,番外篇也结束了,如果你有更好玩的想法,快来一起摸鱼鸭~

摸更多鱼?

如果你对相关内容感兴趣呢,欢迎来一起摸鱼鸭: github.com/SpiciedCrab…