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

干渉

干渉を利用してよりリアルな波を作ることができます。

  • ひとつの大きな波(うねり)を大きな動きでゆっくりと走らせる
  • 別の 1 つまたは 2 つの小さい正弦波を実行します (オシレーター)。
  • すべてランダムな振幅
  • 平均を使用して水平方向に波を混合し、さまざまなポイントを計算します
  • カーディナル スプラインを使用して結果を描画します (または、解像度が高い場合は、代わりにポイント間に単純な線を描画できます)。

さまざまなパラメーターを使用して、ライブで調整して適切な組み合わせを見つけることができます。

波を重ねて疑似 3D 波を作成する場合は、z 軸を表すオシレータを追加して、よりリアルにすることもできます。

波の衝突、流体力学を提供することはできません-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
}

これをポイントレコーダーでループに入れ、ポイントを一方向にシフトすると、滑らかに見える波が作成されます。

上記のデモでは、3 つのオシレーターを使用しています。(その点でのヒントは、回転速度を大きなうねりよりも低く保つことです。そうしないと、小さな隆起が発生します。)

注: 新しいランダム最大値を作成する方法は、ブレーク ポイントを使用するため、最善の方法ではありません (ただし、デモ目的では単純です)。代わりに、これをより良いものに置き換えることができます。

于 2013-11-06T03:11:52.383 に答える
14

リアルな効果を探しているので、水をシミュレートするのが最善のアイデアかもしれません。実際、それほど難しいことではありません。水は、互いにリンクされた泉のネットワークによって十分にうまく近似できます。

結果は非常に良好です。ここで見つけることができます: http://jsfiddle.net/gamealchemist/Z7fs5/

ここに画像の説明を入力

だから私はそれが2D効果であると仮定し、水面の各ポイント、その加速度、速度、および位置を保持する配列を構築しました。それらを 3*i + 0、3*i + 1、および 3*i+2 の 1 つの配列に格納します。
次に、更新のたびに、ニュートンの法則を弾力性と摩擦で適用して動きを止めるだけです。
私は各ポイントに影響を与えて、安定した状態になり、左右の隣人から影響を受けます。
(よりスムーズなアニメーションが必要な場合は、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

とにかく、まだ Wave をプログラミングしようとしたことがありますか、それとも WaveCreator.jsを探しているだけですか?

2D 水を作成する方法については、言語固有でないガイドを Google で検索してください。

初心者の場合は、物事の背後にあるアイデアを理解するために、簡単なことから始めてください。「Minecraft スタイル」の水用のボックスをたくさん作成してみませんか?

ここに画像の説明を入力

ここでは、水のすべての「線」を配列内の位置として表すことができます。次に、それをループして、前の配列インデックスに基づいて水の「高さ」を設定します。(ブロックを非常に薄くするか(プログラムの作業が増えます!)、エッジを滑らかにして他の正方形に基づいて角度を付けることで、水を滑らかにすることができます.

これはきちんとした解決策になると思います。とにかく。それがあなたにいくつかのアイデアを与えたことを願っています。

于 2013-11-05T11:58:46.770 に答える