Jacob Bell's Blog

Wind Effects in Oasis in Curses

24 December 2021

Oasis in Curses is a relatively non-interactive game. If you've programmed your characters well, you shouldn't even have to touch the controller once you're in a battle. This extends to the openly explorable natural environments consisting of non-interactive static objects. To bring more life to the plants that make up the walls of the game world, I wanted to add gusts of wind blowing through them.

Research

While information on applications of wind is readily available, there's surprisingly few people who care about the movement of wind itself. My college physics textbook only mentions wind to talk about wind resistance on a moving object, most measurements of wind I could find online were taken for meteorological records and have hour- or day-resolution, and there's no end to the websites that want to explain how turbines work. I would have loved a table of second-by-second measurements of two anemometers 100 meters apart in the direction of wind, but I wasn't able to find anything like that on google searches. I even found out that no one had developed a Newtonian model of why sailboats work until 2020.

I decided to model my wind gusts based only on visual observations. I went over to nearby foothills on a somewhat windy day and spent a couple hours recording video of wind moving through grasses, bushes, and trees. I also found a few good youtube videos to reference gusts in large fields.

I was researching a little bit at a time for around a month while thinking about how I could implement wind in my game. The most important thing to me was that gusts move from one side of the screen to the other over time, rather than all the plants suddenly moving at once. I could do this with a function parameterized by time and the object's position in the world, and evaluate it for each object. The function would need to be randomized while being easy to artistically tweak.

The last one was the simple and flexible solution I was looking for, and I started on implementing wind as soon as I thought of it.

Animation

I came across a nice paper on physics-based effects of wind on game objects, but my engine has nothing close to physics as it is. Defining physics parameters for each part of the plant would also be time consuming. My engine does have skeleton animation, and the assets were already 3D models I made in Blender.

I created a skeleton joint for each section of the plant and made a looping animation of the plant blowing in the wind. This is blended with the rest pose depending on wind speed. In the game, each plant's wind animation starts from a random frame in the loop so they're not all moving in the same way at once.

Gust Function

Wikipedia was good enough to learn how to parameterize a Gaussian function, and I used Desmos to play around with which parameters I need to tie to which variables in-engine. A wind gust has an origin (past the furthest extent of the game level), speed, duration, and start time. These correspond to the Gaussian function like so:

x = object.position.x - gust.origin.x;
a = gust.speed;
b = gust.speed * (runTime - gust.startTime);
c = (x-b < 0)? gust.duration/5 : gust.duration/20;
effect = a*powf(eulerNumber, -(x-b)*(x-b)/(4*c*c));

Wind gusts come suddenly and leave slowly (or at least that's how it feels when you're in one). Skewing a Gaussian function is apparently complicated, so instead I broke it into two halves at the peek, giving it a short head and long tail. You can play with this model here: https://www.desmos.com/calculator/fdvxfnkntx

Subtle Details

Everything in this system looked pretty good except for the motion at low wind speeds. As a gust came in and left, you could see all the parts of the plant be forced into and out of the rest pose, looking very mechanical. Adding a small, constant wind force helped, but it still looked a bit uncanny. Fortunately, I had my close-up videos from the foothill trip to study.

In each clump of grass, some pieces are bobbing around when there's little wind, while others are completely still. In my wind animations, either all the pieces are moving, or none of them are. To fix this problem, I needed not just one weight for blending in the wind animation, but per-joint weights.

When building the animation, each joint gets its weight from this sine-wave function I concocted: https://www.desmos.com/calculator/a3gyuvmywc

x is based on the game world's clock and a random offset per joint. Because it always uses the same starting seed, and plants will not be added or removed during gameplay, the random series is evaluated the same way every frame, avoiding discontinuities. With this function, joints get more variance in blend weight at lower wind speeds, and they all get the maximum blend weight as wind reaches its normalized maximum speed for the game level.

Radial Wind

I had already animated the characters' hair and clothes to blow around when casting curses. Using the wind system, I extended this to plants.

These radial gusts are largely the same as linear gusts. Most importantly, they fall off with distance, and can blow plants to the left as well as to the right. To make them blow left, I just used a negative wind speed, which corresponds with negative joint weights. I may re-evaluate this hack when I make a level where the natural winds blow right to left.

Audio

In the video at the top, you can hear the wind sound effects I recorded in a handful of minutes with a $7 lapel mic. I really need to get a better mic and redo these, but it gets the point across. A low-pitch wind sound plays continuously and follows a y = (normalizedWindspeed^2)-0.01 volume curve, evaluated at the camera's position.

By itself, it's missing that high-pitch whistle you hear at high wind speeds. At first I tried adjusting the playback speed of the sound by the wind speed, but it sounded more like a vacuum cleaner turning on and off. I searched youtube for "ASMR wind" to get some reference videos of what nice-sounding wind sounds like, and really, it's more like the whistling is layered on top of the lower-pitch blowing. So, I added another high-pitch wind sound that kicks in at higher wind speeds with a y = (normalizedWindspeed^8)-0.01 volume curve, and playback speed adjusted by normalizedWindspeed+0.2.

Particles

One last touch is sand kicking up and leaves blowing off bushes when the wind speed crosses a threshold. These are created by hand-placed spawner objects. While I'm using engine features I'm calling a "particle system," it's in a very early state and they're really just sprite animations moving across the screen.