After playing along, your screen will look like this:

If you like this effect and have other ideas, welcome to join the fish research institute to submit your fish results ~

Next comes the prequel

1. Random drops start

At the end of the last post, we simulated throwing a handful of small balls into the scene to release ourselves, but our theme… It’s snowing, so we removed the effect of bouncing up after free fall, and added the concept of randomness:

  • It’s just turning all the constants that we set up before into random numbers.

This in the animation is very practical, (of course is XJB random, regular random only physics bricks can do, for me this kind of college entrance examination physics seems to have failed for even…)

The constants we use are:

  • The longitudinal acceleration (gravitational acceleration) ay
  • The transverse average velocity vx
  • The initial size of the ball is r
  • Ball size changes acceleration AR

So let’s put these constants together:

Class SnowInfoUtils {// Static double get snowOriginSize => math.random ().nextint (30).todouble (); static double get snowRadius => math.Random().nextInt(4).toDouble() ; Ay static double get ga => math.random ().nextint (100) * math.random ().nextint (1000).todouble () / 1000; Ar static double get meltA => - math.random ().nextint (100) / 1000000; // static double get meltA => - math.random ().nextint (100) / 1000000; Vx static double get wa => math.random ().nextbool ()? math.Random().nextInt(150) / 1000 : -math.Random().nextInt(150) / 1000; Static double get secondMultiply => 1000; static double get secondMultiply => 1000; }Copy the code

In order to keep the code from getting messy, we have made the independent changes of the vertical/horizontal/ball’s own volume into three methods, which are also easy to change:

Class SnowMoving {// Vertical SnowBasicInfo freeFall(SnowBasicInfo info) {info.ay = info.isup? SnowInfoUtils.bouncedGa(info.upTimes) / (SnowInfoUtils.secondMultiply) : SnowInfoUtils.ga / SnowInfoUtils.secondMultiply; info.vy = info.vy + info.ay; if(info.vy > 0) { info.isUp = false; } info.y = info.y + info.vy; return info; } // SnowBasicInfo windPush(SnowBasicInfo info) {info.ax = snowinFoutils.wa; info.vx = info.vx + info.ax; info.x = info.vx + info.x; return info; } melt(SnowBasicInfo info) {info.ar = snowinFoutils.melta;} melt(SnowBasicInfo info) {info.ar = snowinFoutils.melta; info.vr = info.vr + info.ar; info.r = math.max(0, info.r + info.vr); return info; }}Copy the code

Also, since we removed the effect that snowflakes bounce up when they hit bottom, we defined snowflakes as invalid = true when y >= screen height

Move this operation into snowflake’s model as well,

class SnowBasicInfo { double x, y, r; double vx, vy, vr; double ax, ay, ar; bool invalid = false; bool melted = false; firstUp() { vy = - vy; invalid = true; if(r <= 1) { melted = true; } } SnowBasicInfo({this.x = 0, this.y = 0, this.r = 10, this.vx = 0, this.vr = 0, this.vy = 0, this.ax = 0, this.ay = 0, this.ar}) { vx = SnowInfoUtils.wa; }}Copy the code

We also assume that when r <= 1, snowflakes will disappear, i.e. they really melt

In this way, the code for the whole animation scene will become a bit cleaner. Finally, let’s tidy up the part of Painter:

@override void paint(Canvas canvas, Size size) { for(var snow in snows) { if(snow.invalid) { continue; } final falled = SnowMoving().freeFall(snow); if(falled.y >= size.height - 20) { falled.firstUp(); } final pushed = SnowMoving().windPush(falled); if(pushed.x >= size.width || pushed.x < 0) { pushed.vx = - pushed.vx; } final melted = SnowMoving().melt(pushed); canvas.drawImageRect(snowImage, Rect.fromLTWH(0, 0, snowImage.width.toDouble(), snowImage.height.toDouble()), Rect.fromLTWH(melted.x, melted.y, snowImage.width.toDouble() * melted.r, snowImage.height.toDouble() * melted.r) , pointPainter); }}Copy the code

SnowImage is a kind of snowflake material you can find online. If you are in the mood, you can draw one by yourself

Here our order is to let the snowflake first process the fall, then move across, then shrink melt, and finally draw it. It is a simple action:

In fact, we have almost finished building the whole scene at this time, if you are interested you can show it to the girls around you, of course, after watching it, they will think you are too idle is another matter

2. Snow

Of course, like the snow in Shanghai, it is certain that it will fall, but it is certainly impossible to accumulate, but we still hope to have some appearance so that it can accumulate

Because the author was really anxious and couldn’t work out the formula of snow falling and then superimposed on the ground, so he changed his mind. If you are very interested in this aspect, your comments and PR are welcome

So let’s assume that a snowflake falling on the ground will turn into a rounded line segment of thickness h and length H over 2,

H = r of the snowflake * a fixed coefficient * the amount of snow that falls on the ground x

So we add two properties to snowflakes:

class SnowBasicInfo { ... Offset meltedStart; Offset meltedEnd; . }Copy the code

MeltedStart is used to record the initial/final position of a line segment on the ground, and since the floating snowflake must be null, we can also use it to determine if the snowflake has landed:

Let’s say the fixed coefficient is 10

h = r * 10 * snows.where((element) => element.meltedStart ! = null).length

This means that the more snow there is on the ground, the thicker the lines will become when the snow falls on the ground, which seems to simulate the effect of snow accumulation, but this is really only a visual effect, and it is still a lot of problems (but this is the best solution the author can come up with).

 snow.r = h;
 snow.meltedStart = Offset(snow.x + h / 2, size.height - h / 2);
 snow.meltedEnd = Offset(snow.x + h, size.height - h / 2);
 
Copy the code

But the results are particularly exaggerated:

The problem is simple: the size of a single piece of snow on the ground does not increase linearly with the amount of snow on the ground. The thicker the snow, the less the snow height changes after it snows.

So I imagine that the top half of the parabola that opens to the right seems to fit our needs:

Something like that, so by experimenting with some coefficients, we ended up changing the amount of snow falling on the snow as a function of the size h of the next piece of snow falling on the ground to:

h = r * 10 * pow((snows.where((element) => element.meltedStart ! = null).length / 20), 0.5)

So our final paint method is:

@override void paint(Canvas canvas, Size size) { for(var snow in snows) { if(snow.invalid) { if(snow.melted) { continue;  } else {// When invalid = true and the snow has not melted, Snow.meltedstart == null {var multip = pow((snows. Where (element) => eld.meltedstart! = null).length / 20), 0.5); var referredR = snow.r * 10 * multip; snow.r = referredR; snow.meltedStart = Offset(snow.x + referredR / 2, size.height - referredR / 2); snow.meltedEnd = Offset(snow.x + referredR, size.height - referredR / 2); } canvas.drawLine(snow.meltedStart, snow.meltedEnd, storedPainter.. strokeWidth = snow.r); continue; } } final falled = SnowMoving().freeFall(snow); if(falled.y >= size.height - 20) { falled.firstUp(); } final pushed = SnowMoving().windPush(falled); if(pushed.x >= size.width || pushed.x < 0) { pushed.vx = - pushed.vx; } final melted = SnowMoving().melt(pushed); canvas.drawImageRect(snowImage, Rect.fromLTWH(0, 0, snowImage.width.toDouble(), snowImage.height.toDouble()), Rect.fromLTWH(melted.x, melted.y, snowImage.width.toDouble() * melted.r, snowImage.height.toDouble() * melted.r) , pointPainter); }}Copy the code

This way, we can simulate a falling snow + snow scene

Touch more fish?

  • Make snow fall on your screen – Prequel
  • Let snow fall on your screen – Post ii
  • Make snow fall on your screen – Extras

If you are interested in related content, welcome to touch fish duck: github.com/SpiciedCrab…