LoginSignup
6
3

バレンタインなんであめを降らせた

Posted at

はじめに

本日2/14はバレンタインデー。
「もしかすると机の中や靴箱にチョコが入っているかも」とかいうドキドキした気持ちなんて十数年前から忘れてしまった自分ですが、こんな日くらいは甘い気持ちに包まれたいと思い、あめを降らせてみることにしました。

完成品

その名の通り「あめちゃん」を降らせています。

あめのほかにもチョコや
image.png

リンゴ飴
image.png

任意の画像をアップロードして降らせることもできます。
image.png

少し解説

完全に出オチネタなので、少しコードの説明。
オープンソースのGISエンジン、Cesiumを使っています。
Cesiumの基本的なはじめかたはこちらを参照。

CesiumのSandcastleから、Particle System Weatherを参考にしました。
image.png

これは、Cesium上でサポートしているパーティクルで表現しています。
パーティクルについての詳しい説明は公式サイトをご覧いただければ幸いですが、こんな表現や、

こんな表現

ができるようです。

あめの表現としては以下のようにパーティクルを発生しています。


const viewer = new Cesium.Viewer("cesiumContainer", {
          shouldAnimate: true,
          terrain: Cesium.Terrain.fromWorldTerrain(),
        });
        
const scene = viewer.scene;

const snowParticleSize = 12.0;
const snowRadius = 100000.0;
const minimumSnowImageSize = new Cesium.Cartesian2(
    snowParticleSize,
    snowParticleSize
    );

const maximumSnowImageSize = new Cesium.Cartesian2(
    snowParticleSize * 2.0,
    snowParticleSize * 2.0
    );

let snowGravityScratch = new Cesium.Cartesian3();

//callback
const snowUpdate = function (particle, dt) {
    snowGravityScratch = Cesium.Cartesian3.normalize(
        particle.position,
        snowGravityScratch
        );
    Cesium.Cartesian3.multiplyByScalar( //重力の強さ
        snowGravityScratch,
        Cesium.Math.randomBetween(-10.0, -50.0),
        snowGravityScratch
        );
    particle.velocity = Cesium.Cartesian3.add(
        particle.velocity,
        snowGravityScratch,
        particle.velocity
        );    
    
    };


function startCandy(image_file) {

        //カラーをランダムに決定
          let rand_red= Math.floor( Math.random()  *100)/100;
          let rand_green = Math.floor( Math.random()  *100)/100;
          let rand_blue= Math.floor( Math.random()  *100)/100;
          let col = new Cesium.Color(rand_red, rand_green, rand_blue, 1);


          scene.primitives.removeAll();
          scene.primitives.add(
            new Cesium.ParticleSystem({
              modelMatrix: new Cesium.Matrix4.fromTranslation(
                scene.camera.position
              ),
              minimumSpeed: 1.0,  //particleのランダムスピードの最小値
              maximumSpeed: 2.0,  //particleのランダムスピードの最大値
              lifetime: 15.0,
              emitter: new Cesium.SphereEmitter(1000),  //particleの種類
              startScale: 2,  //particleのスタート時のサイズ
              endScale: 1.0,  //particleの終了時のサイズ
              image: image_file,  //画像ファイル
              emissionRate: 700.0, //particleの量/秒
              startColor: col,  //particleのスタート時の色(今回はランダムの色)
              endColor: Cesium.Color.WHITE.withAlpha(1.0),  //particleの終了時の色
              minimumImageSize: minimumSnowImageSize,
              maximumImageSize: maximumSnowImageSize,
              updateCallback: rainUpdate,
            })
          );
        }

scene.primitives.add()でパーティクルをシーンに追加して、new Cesium.ParticleSystem()で発生させるパーティクルのプロパティを設定するようです。

html部分はメニューを追加しただけで、特に何も説明するものはありません。


<div id="cesiumContainer" class="cesium"></div>

<div id = "menubar">
  <div>
    <div>降飴量</div>
    <input id ="range_slide" type="range" min="0.0" max="1000.0" step="1" data-bind="value: height, valueUpdate: 'input'">
  </div>
  <div>
    <select name="menu" id="menu">
      <option value="img/candy.png">あめ</option>
      <option value="img/heart_love.png">チョコ</option>
      <option value="img/ringoame.png">りんご飴</option>
      <option value="other">アップロード</option>
    </select>
    <input type="file" id="upload_img" name="upload_img" accept="image/png, image/jpeg" />
  </div>
  <div>
    <button id="button_reset">リセット</button>
  </div>
</div>

「あめ」を降らすのに手を加えたParticleSystemの主なプロパティを紹介します。

emitter

発生させるパーティクルの形状を指定します。

emitter: new Cesium.BoxEmitter(new Cesium.Cartesian3(0.5, 0.5, 0.5))

とすればボックス状にパーティクルを発生

emitter: new Cesium.CircleEmitter(0.5)

とすれば円形にパーティクルを発生

emitter: new Cesium.ConeEmitter(Cesium.Math.toRadians(30.0))

円錐形にパーティクルを発生

emitter: new Cesium.SphereEmitter(Cesium.Math.toRadians(30.0))

とすれば球形にパーティクルを発生させることができます。

同じくSandCastleに上がっているFireworksもSphereでパーティクルを発生させているようです。

image.png

image

パーティクルで表示させる画像のURLを指定します。
今回は、「あめ」のほか、チョコレートやリンゴ飴、あとアップロードした画像も降らせることができるようにしています。
image.png

それぞれの選択に合わせて、このイメージURLを変更してパーティクル発生を実行しています。

document.getElementById("menu").addEventListener("change", function () {
          if(document.getElementById("menu").value=="other"){
            document.getElementById("upload_img").style.display = "block";
          }else{
            document.getElementById("upload_img").style.display = "none";
            let value = document.getElementById("menu").value;
            startCandy(value);
          }
        });

emissionRate

1秒間におけるパーティクルを発生させる量を指定します。
降雨量ならぬ降飴量として、スライドバーを設けてこのemissionRateの数値を変化できるようにしています。
image.png

document.getElementById("range_slide").addEventListener("input", function () {
  let rangeValue = document.getElementById("range_slide").value;
  scene.primitives._primitives[0].emissionRate = rangeValue;
});

emissionRateが小さい時
image.png

emissionRateが大きい時
image.png

updateCallback

パーティクルの更新として、毎フレーム呼び出されるコールバック関数を指定しています。コールバック関数の内容は以下のようになっていますが、ここでそれぞれのパーティクルの重力やスピードなどをランダムに変化させているようです。

//callback
const snowUpdate = function (particle, dt) {
    snowGravityScratch = Cesium.Cartesian3.normalize(
        particle.position,
        snowGravityScratch
    );
    Cesium.Cartesian3.multiplyByScalar( //重力の強さ
        snowGravityScratch,
        Cesium.Math.randomBetween(-10.0, -50.0),
        snowGravityScratch
    );
    particle.velocity = Cesium.Cartesian3.add(
        particle.velocity,
        snowGravityScratch,
        particle.velocity
    );    
    
};

そのほかのプロパティはこちらのドキュメントをご覧ください。

リセットボタン

今回のパーティクルはカメラの表示位置に対してパーティクルが発生しているので、地図を移動すると降水域(降飴域)の外に出る。その際はリセットを押してパーティクルを発生し直せるようになっています。

image.png

document.getElementById("button_reset").addEventListener("click", function () {
  let value = scene.primitives._primitives[0].image;
  startCandy(value);
});

思ってたんとちゃう

このコールバック関数内に、パーティクルの色をランダムで設定されるようにすれば、いろんな色のあめが降ってきて、さらに幸せな気持ちになるんちゃう、と思い以下を追記したところ

particle.startColor.red= Math.floor( Math.random()  *100)/100;
particle.startColor.green = Math.floor( Math.random()  *100)/100;
particle.startColor.blue= Math.floor( Math.random()  *100)/100;

ゲーミングキャンディーが降ってくるようになった・・・。

思ってたんとちゃうけど、これはこれで楽しいからいっか。

パーティクル、ちょっと難しいイメージやったけど、ちょっと仲良くできてよかった。
皆さんも少しでも甘い気持ちになってくれれば幸いです。

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3