6

我正在寻找一种以画布设计的形状创建波浪的方法。经过大量研究,我发现了一些非常接近我想要的东西:

var c = document.getElementById('c'),
  ctx = c.getContext('2d'),
  cw = c.width = window.innerWidth,
  ch = c.height = window.innerHeight,
  points = [],
  tick = 0,
  opt = {
    count: 5,
    range: {
      x: 20,
      y: 80
    },
    duration: {
      min: 20,
      max: 40
    },
    thickness: 10,
    strokeColor: '#444',
    level: .35,
    curved: true
  },
  rand = function(min, max) {
    return Math.floor((Math.random() * (max - min + 1)) + min);
  },
  ease = function(t, b, c, d) {
    if ((t /= d / 2) < 1) return c / 2 * t * t + b;
    return -c / 2 * ((--t) * (t - 2) - 1) + b;
  };

ctx.lineJoin = 'round';
ctx.lineWidth = opt.thickness;
ctx.strokeStyle = opt.strokeColor;

var Point = function(config) {
  this.anchorX = config.x;
  this.anchorY = config.y;
  this.x = config.x;
  this.y = config.y;
  this.setTarget();
};

Point.prototype.setTarget = function() {
  this.initialX = this.x;
  this.initialY = this.y;
  this.targetX = this.anchorX + rand(0, opt.range.x * 2) - opt.range.x;
  this.targetY = this.anchorY + rand(0, opt.range.y * 2) - opt.range.y;
  this.tick = 0;
  this.duration = rand(opt.duration.min, opt.duration.max);
}

Point.prototype.update = function() {
  var dx = this.targetX - this.x;
  var dy = this.targetY - this.y;
  var dist = Math.sqrt(dx * dx + dy * dy);

  if (Math.abs(dist) <= 0) {
    this.setTarget();
  } else {
    var t = this.tick;
    var b = this.initialY;
    var c = this.targetY - this.initialY;
    var d = this.duration;
    this.y = ease(t, b, c, d);

    b = this.initialX;
    c = this.targetX - this.initialX;
    d = this.duration;
    this.x = ease(t, b, c, d);

    this.tick++;
  }
};

Point.prototype.render = function() {
  ctx.beginPath();
  ctx.arc(this.x, this.y, 3, 0, Math.PI * 2, false);
  ctx.fillStyle = '#000';
  ctx.fill();
};

var updatePoints = function() {
  var i = points.length;
  while (i--) {
    points[i].update();
  }
};

var renderPoints = function() {
  var i = points.length;
  while (i--) {
    points[i].render();
  }
};

var renderShape = function() {
  ctx.beginPath();
  var pointCount = points.length;
  ctx.moveTo(points[0].x, points[0].y);
  var i;
  for (i = 0; i < pointCount - 1; i++) {
    var c = (points[i].x + points[i + 1].x) / 2;
    var d = (points[i].y + points[i + 1].y) / 2;
    ctx.quadraticCurveTo(points[i].x, points[i].y, c, d);
  }
  ctx.lineTo(-opt.range.x - opt.thickness, ch + opt.thickness);
  ctx.lineTo(cw + opt.range.x + opt.thickness, ch + opt.thickness);
  ctx.closePath();
  ctx.fillStyle = 'hsl(' + (tick / 2) + ', 80%, 60%)';
  ctx.fill();
  ctx.stroke();
};

var clear = function() {
  ctx.clearRect(0, 0, cw, ch);
};

var loop = function() {
  window.requestAnimFrame(loop, c);
  tick++;
  clear();
  updatePoints();
  renderShape();
  //renderPoints();
};

var i = opt.count + 2;
var spacing = (cw + (opt.range.x * 2)) / (opt.count - 1);
while (i--) {
  points.push(new Point({
    x: (spacing * (i - 1)) - opt.range.x,
    y: ch - (ch * opt.level)
  }));
}

window.requestAnimFrame = function() {
  return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(a) {
    window.setTimeout(a, 1E3 / 60)
  }
}();

loop();
canvas {
  display: block;
}
<canvas id="c"></canvas>

http://codepen.io/jackrugile/pen/BvLHg

问题是波浪的运动看起来有点不真实。我想保留这种随机运动的概念,而不是通过从左到右移动来重复自己的形状,但如果我找到一种方法来创造一个“逼真的”水运​​动(良好的流体动力学,碰撞此波浪及其容器的边缘(自定义形状))。

我想我问了很多但是......一小部分研究可能会有所帮助:)

4

3 回答 3

20

干涉

您可以使用干涉来制作更逼真的波浪。

  • 让一大波(膨胀)以大动作缓慢运行
  • 运行另外一两个较小的正弦波(振荡器)
  • 全部具有随机幅度
  • 使用平均值水平混合波浪并计算各个点
  • 使用基数样条绘制结果(或者如果分辨率很高,您可以在点之间绘制简单的线)。

使用各种参数,以便您可以实时调整它以找到一个好的组合。

您还可以添加振荡器来表示 z 轴,以使其更加逼真,以防您想要将波浪分层以制作伪 3D 波浪。

例子

我不能给你波浪碰撞、流体动力学——这对于 SO 来说有点太宽泛了,但我可以给你一个流体式波浪的例子(因为你有每个片段的点,你可以用它来进行碰撞检测)。

示例是创建一个振荡器对象,您可以在其上设置各种设置,例如振幅、旋转速度(相位)等。

然后有一个混合器功能来混合您使用的这些振荡器的结果。

现场演示在这里全屏版本在这里

演示快照

此演示中的振荡器对象如下所示:

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;
}

这为我们完成了所有的设置和计算,我们需要做的就是调用getAmp()来为每一帧获取一个新值。

我们可以使用“混合器”来代替手动操作。这个混音器允许我们在混音中添加任意数量的振荡器:

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
}

将其放入一个带有点记录器的循环中,该点记录器将点沿一个方向移动,将产生一个看起来像流体的波。

上面的演示使用了三个振荡器。(在这方面的一个提示是保持转速低于大浪,否则你会得到小颠簸。)

注意:我创建一个新的随机最大值的方式并不是我使用断点的最佳方式(但对于演示目的来说很简单)。你可以用更好的东西代替它。

于 2013-11-06T03:11:52.383 回答
14

由于您正在寻找逼真的效果,最好的办法可能是模拟水。事实上,这并不难:水可以通过连接在一起的泉水网络很好地近似。

结果很好,你可以在这里找到它:http: //jsfiddle.net/gamealchemist/Z7fs5/

在此处输入图像描述

所以我假设它是 2D 效果,并为水面的每个点构建了一个数组,包含它的加速度、速度和位置。我将它们存储在一个数组中,分别为 3*i + 0、3*i + 1 和 3*i+2。
然后在每次更新时,我只是简单地应用具有弹性的牛顿定律,并通过一些摩擦来使运动停止。
我影响每个点,使其进入稳定状态+受到左右邻居的影响。
(如果你想要更平滑的动画,也可以使用 i-2 和 i+2 点,并降低 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;
}

绘制非常简单:只需迭代点并绘制。但是在绘制时很难获得平滑的效果,因为很难让 bezier 和 quadraticCurve 让它们的导数匹配。我推荐了几种绘图方法,你可以随意切换。

然后我加了雨,这样水就可以随机移动。这里只是非常简单的轨迹,然后我计算是否与水发生碰撞,如果是,我添加一些速度并移动点。

于 2013-11-07T00:16:01.173 回答
2

我想创建一个具有良好流体动力学的“现实”水运动,这种波浪与自定义容器边缘的碰撞..

哦,男孩..那真是一口。你可能应该在这里问你的问题:gamedev.stackexchange

无论如何,您是否尝试过编写任何类型的波,或者您只是在寻找WaveCreator.js

Go 和 Google 一些关于如何创建 2D 水的非语言特定指南。

如果您是初学者,请从一些简单的事情开始,以了解事物背后的想法。为“我的世界风格”水创建一堆盒子怎么样?

在此处输入图像描述

在这里,水的每一“线”都可以表示为数组中的一个位置。然后循环遍历它并根据之前的数组索引设置水的“高度”。(您可以通过使块变得非常薄(为您的程序做更多工作!)或通过平滑边缘并根据其他正方形给它们一个角度来平滑水。

我认为这可能是一个很好的解决方案。无论如何。希望能给你一些想法。

于 2013-11-05T11:58:46.770 回答