A few more attempts at wave simulation
Following up from my last post, I wanted to show a few more attempts in this space.
Image height map
The idea here is to create an image that contains the height information for my background water graphic tile, and sample the number of black pixels at the front & back of the jetski to get its tilt. This image is will not be seen, it's only used as a height map.
Recapping from the prior post, the approach is to translate the world coordinates of my jetski back onto the tile (un-rotating and modulo to stay in the tile) and sample a 3x3 grid to get a "height".
For the first approach, I created a gradient version of my water tile, to try to give a height map that matches the actual visible pattern.
Here is this approach, visualized:
I am showing the gradient tile I created, and showing the mapping of the current position of my jetski onto that tile.
In theory - this "works", but had some issues:
- The pattern is "tight", especially at speed, which meant really rapid/choppy animation, it doesn't feel smooth at all.
- I am sampling a 3x3 grid at the front & back of the jetski to get a height, 0-9. This often works, but, dithering isn't perfectly uniform, so there are pockets where there are unexpected tilts, depending on just how the pixels happen to line up. I tried moving to a 5x5 grid to have a larger capture, and it helped, but still wasn't great.
For the second approach, I tried using a tile with perlin noise (generated in GIMP).
Same idea as the other approach, but instead using perlin noise to generate the height map.
This was smoother, but, a couple issues/concerns:
- Same pixel sampling/dithering issues as the gradient image - there are pockets of unexpected results just due to sampling.
- The waves are now decoupled, visually, from the game. I am not sure how I feel about this. On one hand, it's a fairly small game, having this be approximate might be fine, and trying to marry this to visuals might be an uphill battle. I think treating my graphics as a "texture" is fair game, but I sort of want a visual indication of the wave pattern.
Compound Sine Waves
Following the lead from Matt here: Wave Racer | Dev Log - #9 by matt
I started using compound sine waves to represent wave information.
Initial results with this felt smoother right off the bat, and in theory the "height map" is purely computational, I don't need to create tiles or 2d arrays.
I still need to map coordinates back to world view, so that I don't need to worry about rotating the sine wave function (I think not directly possible).
Anyways, finally pulled together a visualization so that I could "see" the waves in the game, and help me tune (ie tough to tell if I have a really choppy function or too smooth of a function).
Here's a first version of of getting a visualization:
There is an element of time included in the function, so that there is some natural "wave" movement. Seeing this live, I could see it was a little too tight and too much on a grid.
After some tuning, I arrived at a smoother & more organic looking approach. I also took a moment to re-introduce more water graphics:
This is likely the coolest GIF I'll post It does not run on the device (< 1 FPS) but, to me, really starts to feel like water. I'd love to run this type of viz for the full game, but rotating this does not work (the waves are all fixed on the world view) and generating the graphic is very slow.
I am basically creating a series of 5px squares and filling the entire screen (so 400 / 5 x 240 / 5) = 4,000 tiles. For each tile's z/y, I am running the compound sine function in order to get the height. I then normalize the height between 0-1, and use that as the dither pattern. I then draw a 5x5 square via fillRect
. With a time element, the animation just runs as you see here (the visualization is not tied to any movement or rotation).
Showing the ghost of the jetski getting mapped back into the sine waves. This is sort of trippy - the 3 circles are showing the location of the jetski if the camera were fixed and never rotated:
While visualizing this is prohibitively expensive (4000 compound sine function innovations, 4000 rectangles drawn...)j, I only need to compute the height at ~4 coordinates to get my "tilt", which is pretty efficient.
The result is smooth - no more sampling dither patterns, instead relying on sin functions. Keeping this visualization debugging in my back pocket - it's sort of like magic in terms of tuning, but at least I can "see" it now.
Here's a GIF back to game mode, where I am applying some directional impulse to the jetsk, but using the sine function from the above GIF for height:
Can see a little bobbing at the beginning, harder to quickly show off the impulse at speed but I push the jetski around little bit, which can, somewhat, impact the ability to navigate.
Bonus: playing with perlin noise in the SDK
I spent a long while trying to create a grid of perlin noise via the SDK.
I found this post & source code to be very helpful after struggling for a while: Perlin Noise explorer
However, I never got to the point where I got a smooth, water like pattern, showing one of the variations:
(the diagonal movement is, I believe, just me getting one of the x/y tick values wrong)
At some point I just figured the compound sine wave was going to be better for a water effect that scales, just at looking at examples/tutorials/images of both on the internet.
Next thoughts
At this point, the effect is okay (ish) - I like how smooth it is, tho I need to work on my actual pixel animations, and I like the impact it has back on the jetski...if only I could "see" it while playing.
So...the big downside here is that the height is still "invisible". If you are trying to make a buoy and get pushed by an invisible force and miss or collide etc - is that frustrating? Maybe if the waves are sort of ambient and don't cause much impact to the player, they won't be frustrating. But at that point, why bother with all this math and simulation?
I have lots of spiraling thoughts on next steps on still using sine waves, but I don't think any of them will come under my CPU budget if I have to rotate and/or visualize.
Definitely open to feedback, if anyone is reading this: do you think the waves need to be visualized? Is there another approach?