Done with this? Go look at Particle Life! https://scratch.mit.edu/projects/1062499535/ - RUN IN TURBO MODE! The speed increase may seem small but it helps nonetheless. - DO NOT CHANGE WAVECOUNT WITHOUT RESTARTING THE PROJECT! It breaks things :( - Change waveCount to be lower if it's too slow (higher is better, though) - Hold space to show generation progress - If you're going to read all of the explanation, paste it into a text editor - there's pseudocode and it'll wrap if it's in such a tiny box! - There's also a ton of text: I would've put this in the notes and credits along with everything else but I ran out of space :(
This account is run by @AHypnoman –– See the end for credits and some other info. ––– How this works: TL;DR, this works by adding points along (a really large number of) sine waves that have been randomly (ish) oriented (ish). Every pixel's individual height is calculated by taking a position on and scaling an input line, then putting that through a sine function, then amplifying it. I initially used a number of fixed lines and changed them manually, but that produced some unnatural results and I couldn't feasibly create enough lines myself to fix this. I changed the code to use this equation, which I can control the direction of: (x and y are the coordinates of a given pixel) z = cos(θ)*y - sin(θ)*x There's also 3 lists of numbers, (currently randomly generated but could be created from a seed) that are used for determining the angle of the input line (above), and its wavelength and amplitude in the next step (below). List 'rotations' can have any value from 0 to 359, list 'scale' can have any value from -4 to 4, and list 'amplitude' can have any value from -2 to 2. getHeightOnWave( i ): return amplitude[i] * sin(scale[i] * (cos(rotations[i]) * y - sin(rotations[i]) * x)) end Now a loop iterates over getHeightOnWave, adding to a total as it goes. n = iteration count height = 0 for (i = 0; i < n; i++): height += getHeightOnWave(i) end Though this will produce very varied results depending on parameters and the three lists' contents. To limit this to between -1 and 1, the loop keeps track of the total amplitude and divides height by that at the end of the calculation. totalAmplitude = 0 height = 0 for (i = 0; i < n; i++): height += getHeightOnWave(i) totalAmplitude += amplitude[i] end height /= totalAmplitude This code is equivalent to the 'getHeight' custom block in the project (This isn't perfect, though. It limits height to between -1 and 1, but in practice it will always be far lower since the sine waves from getHeightOnWave sort-of average out when they're added in large numbers, although for convincing terrain this needs a ton of them) To improve this, modifications can be made to the random number generation function to distribute the rotations of lines better (sometimes they bunch up randomly and produce bad results). This project does that by dividing 360 by the number of waves, then multiplying that by the current length of the rotations list, then adding a random number between -360/waveNumber and 720/waveNumber. ––– Benefits: This is fast. Not as fast as some perlin implementations when it's running with a higher wave count, but that's rarely necessary. I've been running it on 40, which usually produces convincing terrain in about 10s (this will vary by device). I'm sure it could be optimised more, but it isn't right now. It's also really easy to implement. There's no complex maths stuff involved and there's only a single step that gets repeated. Like most other noise functions, a given point is not dependent on knowing anything about the surrounding points. This could be modified to produce any dimension of noise. ––– Issues: As mentioned earlier, there's a lack of control over the output as more waves will usually average out. A point is always between -1 and 1, but it can take some effort to manipulate it. The terrain will repeat eventually (on a really, really large scale). This usually won't be a problem, but if it is increasing the number of waves used is an easy solution. I'm sure there's more that could be done by modifying the generator function itself, though. ––– Why I'm making this: This is for another project which needs cave generation (hence why I haven't taken steps to scale this well, other than keeping it in -1 to 1). I was already using sine waves to generate 1d terrain, so I thought rather than implementing Perlin I could extend that. –– Credits: I've not used anyone else's code. Much of how adding waves works comes from ideas presented here, though I haven't implemented it: https://web.archive.org/web/20200919181541/https://grelf.net/forest_terrain.html Sine waves as terrain generation has been done before, but I can't find anything that does it without using the Fourier transform which looked like it would have been two slow for Scratch (and wasn't something I really wanted to learn the workings of right now). The only two results I could find in searching for stuff similar to this were from grelf as mentioned before, and this article: https://www.redblobgames.com/articles/noise/2d/ (I was apparently too inattentive to notice that they also had a simplified article linked at the top which I could've implemented, but I guess not haha) I'm going to finish my current project, then probably do some more terrain gen stuff (and hopefully improve this). I might also share a better explanation of this in project form on . Thanks for reading all that :D