Make and animate wave in canvas

后端 未结 3 683
夕颜
夕颜 2021-01-31 23:45

I’m looking for a way to create a wave in a shape designed in canvas. After much research I found something that is pretty close to what I want:

相关标签:
3条回答
  • 2021-02-01 00:14

    Since you are searching for a realistic effect, best idea might be to simulate the water. It is not that hard, in fact : water can be nicely enough approximated by a network of springs linked together.

    Result is quite good, you can find it here : http://jsfiddle.net/gamealchemist/Z7fs5/

    enter image description here

    So i assumed it was 2D effect and built an array holding, for each point of a water surface, its acceleration, speed, and position. I store them in a single array, at 3*i + 0, 3*i + 1, and 3*i+2.
    Then on each update, i simply apply newton's laws with elasticity, and with some friction to get the movement to stop.
    I influence each point so it goes to its stable state + get influenced by its right and left neighboor.
    (If you want smoother animation, use also i-2 and i+2 points, and lower kFactor.)

    var left = 0, y = -1;
    var right = water[2];
    for (pt = 0 ; pt < pointCount; pt++, i += 3) {
        y = right;
        right = (pt < pointCount - 1) ? water[i + 5] : 0;
        if (right === undefined) alert('nooo');
        // acceleration
        water[i] = (-0.3 * y + (left - y) + (right - y)) * kFactor - water[i + 1] * friction;
        // speed
        water[i + 1] += water[i] * dt;
        // height
        water[i + 2] += water[i + 1] * dt;
        left = y;
    }
    

    The draw is very simple : just iterate though the points and draw. But it's hard to get a smooth effect while drawing, since it's hard to have bezier and quadraticCurve to have their derivates match. I suggested a few drawing methods, you can switch if you want.

    Then i added rain, so that the water can move in a random way. Here it's just very simple trajectory, then i compute if there's collision with the water, and, if so, i add some velocity and shift the point.

    0 讨论(0)
  • 2021-02-01 00:19

    Interference

    You can make a more realistic wave using interference.

    • Have one big wave (swell) running slowly with a big motion
    • Have another one or two smaller sine waves running (oscillators)
    • All with random amplitudes
    • Mix the waves horizontally using average and calculate the various points
    • Draw the result using a cardinal spline (or if the resolution is high you can just draw simple lines between the points instead).

    Use various parameters so you can adjust it live to find a good combination.

    You can also add oscillators to represent the z axis to make it more realistic in case you want to layer the waves to make a pseudo-3D wave.

    Example

    I cannot give you wave collision, fluid dynamics - that would be a bit too broad for SO but I can give you a fluid-ish wave example (as you have the point of each segment you can use that for collision detection).

    And example would be to create an oscillator object which you could set the various settings on such as amplitude, rotation speed (phase) etc.

    Then have a mixer function which mixes the result of these oscillators that you use.

    Live demo here (full-screen version here)

    Demo snapshot

    The oscillator object in this demo look like this:

    function osc() {
    
        /// various settings        
        this.variation = 0.4;  /// how much variation between random and max
        this.max       = 100;  /// max amplitude (radius)
        this.speed     = 0.02; /// rotation speed (for radians)
    
        var me  = this,        /// keep reference to 'this' (getMax)
            a   = 0,           /// current angle
            max = getMax();    /// create a temp. current max
    
        /// this will be called by mixer
        this.getAmp = function() {
    
            a += this.speed;   /// add to rotation angle
    
            if (a >= 2.0) {    /// at break, reset (see note)
                a = 0;
                max = getMax();
            }
    
            /// calculate y position
            return max * Math.sin(a * Math.PI) + this.horizon;
        }
    
        function getMax() {
            return Math.random() * me.max * me.variation +
                   me.max * (1 - me.variation);
        }
    
        return this;
    }
    

    This do all the setup and calculations for us and all we need to do is to call the getAmp() to get a new value for each frame.

    Instead of doing it manually we can use a "mixer". This mixer allows us to add as many oscillators we want to the mix:

    function mixer() {
    
        var d = arguments.length,  /// number of arguments
            i = d,                 /// initialize counter
            sum = 0;               /// sum of y-points
    
        if (d < 1) return horizon; /// if none, return
    
        while(i--) sum += arguments[i].getAmp(); /// call getAmp and sum
    
        return sum / d + horizon;  /// get average and add horizon
    }
    

    Putting this in a loop with a point recorder which shifts the point in one direction will create a fluid looking wave.

    The demo above uses three oscillators. (A tip in that regard is to keep the rotation speed lower than the big swell or else you will get small bumps on it.)

    NOTE: The way I create a new random max is not the best way as I use a break point (but simple for demo purpose). You can instead replace this with something better.

    0 讨论(0)
  • 2021-02-01 00:19

    I'd like to create a ‘realistic’ water movement with good fluid dynamics, collisions of this wave with the edges of a custom container..

    Oh boy.. That is quite a mouthful. You should probably ask your Question here: gamedev.stackexchange

    Anyways, have you tried to program any sort of wave yet, or are you just looking for WaveCreator.js ?

    Go and Google some Non-language-specific Guides on how to create 2D water.

    If you are a beginner, then start with something simple to get the idea behind things. How about creating a bunch of Boxes for "minecraft-style" water ?

    enter image description here

    Here every "line" of water could be represented as a position in an Array. Then loop through it and set the "height" of the water based on the previous Array Index. (You could smooth the water out by either making the blocks very thin (More work for your program!) or by smoothing out the edges and giving them an angle based on the other Squares.

    I think this could be a neat solution. Anyhow. Hope that gave you some ideas.

    0 讨论(0)
提交回复
热议问题