2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

 アドベントカレンダー2024 開幕!
 ・・・にもかかわらず特にやることが思いつかないので、12月だし、クリスマスだし、雪でも降らせてみようかなぁ、という。
 アドベントカレンダーのページでも雪が降っていますよね。
 今回は、なでしこさんの貯蔵庫エディタでキャンバスに雪を降らせてみます。

 あるいは、新しく導入されたオブジェクトプロパティ構文をお試し。

なでしことは

 日本語プログラミング言語「なでしこ」は、その名の通り日本語でプログラミングできる、素敵プログラミング言語です❣️
 みんなが義務教育でプログラミングを勉強するというこんにち、「誰でも簡単プログラマー」と謳われているとおり、ちょっとプログラムを作って遊んだり生活が便利になったりしたいという時に、日本語なら取っつきやすいですよね✨
 教育用途としてもなでしこは、中学校の教科書に採用されたり、大学入試のセンター試験で利用される疑似言語、DNCLDNCL2の互換モードが実装されたりしています。

 詳しくはホームページで👇

雪の結晶を描いてみるよ!

 なでしこではHTML5のCanvasを使うことが出来、日本語で色々な描画が行えます。

👉なでしこ3 マニュアル > plugin_browser/描画

 カンタンに「円描画」で丸とかでもいいのですが、少し華やかにしてみたいと思います。
 現実には雪の結晶の形が降ってるの見られるわけじゃないですけれどね~。

 雪の結晶は正六角形を元にした対象形なのでプログラムで描きやすいアレだと思いますが、ワタクシ能力もなければセンスも不足なので、とにかく「多角形描画」したり「線描画」したりして頑張ります。

👉なでしこ3 マニュアル > plugin_browser/多角形描画

 「多角形描画」は座標配列vを指定して多角形を描画する命令で、正n角形を描きたいと思ったら、その座標を自分で計算して指定してあげなくちゃいけないんですよね~。

 ユキノは、さいんこさいんのじゅもんをとなえた!

 x = r cos θ
 y = r sin θ

 半径rの円の中心(0,0)からx軸に対し角度θの直線が円周と交わる点(x,y)の座標を求める公式。
 これを使って正六角形の頂点の配列を作成し、六角形を描いたり線を引っ張って枝を付けたり・・・

// x = r cos θ y = r sin θ の呪文
●(中点から半径でnの)多角形頂点取得
    変数 v空配列
    n
        変数 角度(360/n)*(回数-1)
        変数 ラジアン度(角度-90)ラジアン変換
        変数 x半径*COS(ラジアン度)中点[0]
        変数 y半径*SIN(ラジアン度)中点[1]
        v[x,y]配列追加
    ここまで
    v戻る
ここまで

//中点は[x,y]の配列。雪径は雪の「直径」
●(中点へ雪径の)雪描画
    塗り色設定
    変数 m=雪径/10m/4*3線太設定
    中点からm*26多角形頂点取得
    それ多角形描画
    変数 頂点中点からm*56多角形頂点取得
    3:
      
頂点[回数-1]から頂点[回数+2]まで線描画
    変数 分岐点中点からm*36多角形頂点取得
    分岐点反復:
        
変数 対象からm*26多角形頂点取得
        変数 A(対象キー-1+6)%6
        変数 B(対象キー+1+6)%6
        [A]から対象まで線描画
        [B]から対象まで線描画
ここまで
[50,50]100雪描画

雪の結晶.png

 こんなかんじ? ちょっとそれらしくなったんじゃないでしょうか?
 雪の結晶の形は無数にあるので、是非もっとかっこいい結晶を描いてみて下さい。
 色々な結晶が降れば、なお華やかな感じになりますよね。

①とりあえず降らせます

 今度は、この雪を降らせてみます。

雪のデータを作ります

 とりあえず雪のサイズは決め打ちで、縦位置も降り始めなので一番上に決め打ちで良いかと思いますので、横位置だけ乱数で決めます。
 設定項目はこれから増える予定なので、辞書型変数形式で雪一覧配列に追加します。
 一片一片降ってくる感じを出したいので、とりあえず0.5秒毎に雪データが追加されるようにしておきます。

#設定
雪径30//雪のサイズ

#データ作成
雪一覧空配列
0.5秒毎には雪追加。。。

●雪追加
    # 雪位置
    変数 横位置画面幅乱数
    変数 縦位置雪径/2*-1//雪が見えない所から降らせる
    変数 雪データ{
        "位置":[横位置,縦位置]
    }
    雪一覧雪データ配列追加
ここまで

アニメーションします

 「画面更新時実行」は、画面更新のタイミングで画面を指定した関数を実行する命令で、画面を一旦全て塗り潰し、そこへ雪の縦位置を変化させながら雪一覧の雪を次々を描画していく関数で自分を呼び出し続けることでアニメーションになります。
 縦位置が画面の下に見えなくなったら、その雪は雪一覧から消すの忘れず。

👉なでしこ3 マニュアル > plugin_browser/画面更新時実行

 本当は雪追加もタイムスタンプを取ってこの中で行った方がイイのかなぁ? と思いましたが、面倒なので別のタイマーを使っています。
 画面を塗り潰す空描画はとりあえず四角を描いているだけです。
 なでしこだと「空」は「から」と読みたくなっちゃうけど、「そら」描画です~。

●降雪アニメーション
    [画面幅,画面高さ]空描画
    もし雪一覧要素数ならば雪追加
    雪一覧反復
        No対象キー整数変換
        雪位置対象「位置」
        横位置,縦位置雪位置
        # 描画
        「snow」線色設定
        雪位置雪径雪描画
        # 次の位置を変更
        縦位置1増やす
        もし縦位置-雪径画面高さならば
            雪一覧No配列削除
        違えば
            対象「位置」[横位置,縦位置]
        ここまで
    ここまで
    「降雪アニメーション」画面更新時実行
ここまで
「降雪アニメーション」画面更新時実行

●(whへ)空描画
    「midnightblue 」塗色設定1線太さ設定
    [0,0,画面幅,画面高さ]四角描画
ここまで

とりあえず降らす.png

 できました。
 と言っても、上から下へ全部同じ速さでスーっと移動しているだけなので、雪が降ってる感はないですね。

②設定を追加していくよ!

 雪の一片ごとに大きさや落ちる速さ、風で左右に流れる感じなどを付与して、自然な感じにしていきたいと思います。
 変化できる範囲は後から変更しやすいよう辞書型変数で先に設定しておきます。

👉なでしこ3 マニュアル > 文法/辞書型変数

変数 雪設定{
    "最大径":50,
    "最小径":10,
    "最大落下速度":20,
    "最小落下速度":10,
    "最大風速":5,
    "最小風速":1,
}
 このようにJSON記法でまるっと設定することが出来、設定などに利用すると個別に変数を用意するより管理しやすく大変便利です。

オブジェクトプロパティ構文を使ってみるよ!

 先ほどはあえて対象@「位置」という書き方をしていました。
 元々辞書型変数の参照はA["キー"]もしくはA@「キー」でした。
 ここで!
 今回新たに導入された「オブジェクトプロパティ構文」です!
 もともとはDOM部品の属性値へ効率的にアクセスするために導入されましたが、DOMもオブジェクトなら辞書型変数もオブジェクトです。
 英語イヤンな人からすると、オブジェクトもプロパティもまあまあな呪文ですが、辞書型変数のキーと思えば分かりやすいですね(?)

👉なでしこ3 マニュアル > 文法/プロパティ構文「$」

 この「オブジェクトプロパティ構文」ではA$キーまたはA.キーで参照することが出来ます。
 「」がなくなっただけじゃんって? いやもうそれ大事!
 めちゃくちゃ書きやすくなりました。
 ここが書きにくかったり見づらかったりすると、いっそもう全部バラの変数にしちゃいたくなるし!💧
 そして本当はもっと、DOMの読み書きに真価を発揮できる予定の便利構文なのですが、それはまた今度頑張りましょう。
 今回はお試しとゆうことで、とりあえず使ってみます。

●雪追加
    変数 雪設定
    変数 方向[-1,1]
    # 雪の降り方
    変数 風力($最小風速から$最大風速までの乱数範囲)/10
    変数 風向方向[2乱数]
    変数 速度($最小落下速度から$最大落下速度までの乱数範囲)/10
    # 雪のサイズ
    変数 雪径$最小径から$最大径までの乱数範囲
    # 雪位置
    変数 横位置画面幅乱数
    変数 縦位置雪径/2*-1//雪が見えない所から降らせる
    # 雪色
    変数 R0xCCから0xFFまでの乱数範囲
    変数 G0xDDから0xFFまでの乱数範囲
    変数 B0xEEから0xFFまでの乱数範囲
    変数 A(5から8までの乱数範囲)*0.1
    変数 雪データ{
        "位置":[横位置,縦位置],
        "径":雪径,
        "色":「RGBA({R},{G},{B},{A})」,
        "左右方向":風力*風向き,
        "落下速度":速度
    }
    雪一覧雪データ配列追加
ここまで

#雪を降らせる
●降雪アニメーション
    [画面幅,画面高さ]空描画
    もし雪一覧要素数ならば雪追加
    雪一覧反復
        変数 No対象キー整数変換
        変数 雪位置対象$位置
        変数 [横位置,縦位置]雪位置
        変数 雪径対象$
        変数 雪色対象$
        # 描画
        雪色線色設定
        雪位置雪径雪描画
        # 次の位置を変更
        横位置対象$左右方向だけ増やす
        縦位置対象$落下速度だけ増やす
        もし縦位置-雪径画面高さならば
            雪一覧No配列削除
        違えば
            対象$位置[横位置,縦位置]
        ここまで
    ここまで
    「降雪アニメーション」画面更新時実行
ここまで
「降雪アニメーション」画面更新時実行

とりあえず降らす2.png

 動きました! いい感じ🎶
 1に決め打ちだった縦方向への移動を、最小落下速度から最大落下速度の間の乱数で決めるようにして、同様に風の影響による左右方向への移動も追加。
 ちょっとは動きが自然になり、雪が降ってる感も出てきたんじゃないでしょうか?
 ついでに色もうっすら幅を持たせ、透明度も設定して、自然っぽさとキラキラ感(?)がアップしました✨

③くるくるひらひら舞わせたい!

 「描画変換マトリクス設定」を使って、雪の結晶をくるくる舞わせたいと思います。

👉なでしこ3 マニュアル > plugin_browser/描画変換マトリクス設定

 「描画変換マトリクス設定」は、描画を変化させる命令です。
 既に描かれているものではなく、これから描画するものが変化します。
 使い方については2021年のアドベントカレンダーで一度やりました。

 引数 [a,b,c,d,e,f] に[伸縮x, 傾斜y, 傾斜x, 伸縮y, 移動x, 移動y]を指定することで、拡大、傾斜、回転、起点設定をまとめて設定出来る、[伸縮x, 傾斜y, 傾斜x, 伸縮y]を[cos,sin,-sin,cos]とすることで回転するということでした。

 でも、ただフツーに回転させたら、雪っていうより歯車に見えてきちゃいますからねー💧
 伸縮の片一方を0にしてやったらどうかな?

角度0
●雪回転とは
    全描画クリア
    角度角度1
    sinSIN((角度ラジアン変換))
    cosCOS((角度ラジアン変換))

    キャンバス状態保存
    #---
  ※[伸縮x,傾斜y,傾斜x,伸縮y,起点x,起点y]
    [0,sin,-sin,cos,150,150]だけ描画変換マトリクス設定
    [0,0]100雪描画
    #---
    キャンバス状態復元
    「雪回転」画面更新時実行
ここまで
「雪回転」画面更新時実行

 ちょっとくるくるひらひらしてるっぽい?
 伸縮を0にして一旦見えなくなると、そこを軸にして回転してるように見える説😅
 変換マトリクスの行列式の意味が分かる賢い人は、もっとかっこよくひらめかすことが出来るのかも知れませんが、とりあえずこんなところで。

完成させます

 回転の向きと伸縮の向きはランダムにして動作のバリエーションを増やします。
 画面いっぱいに降らせるようにして、背景の空をムダにグラデーションにしてみました。
 が! ムダに動作が重くなってしまったので、グラデーションの描画は一回で良いよう背景用のキャンバスを用意する始末💧

 先ほどはオブジェクトプロパティ構文の「$」を使いましたが、今回は「.」を使ってみましたよ。入力のしやすさや見やすさで、どっちでも好きな方を使えばOKです。
 ワタシは個人的にv1やじゃばすくりぷとさんと一緒な「.」のほうが気に入っています✨

#画面
画面幅クライアント幅/10*9.5
画面高クライアント高/10*9.5

メイン画面描画中キャンバス
メイン画面.width画面幅
メイン画面.height画面高さ

背景[画面幅,画面高さ]キャンバス作成
背景.非表示オン
[画面幅,画面高さ]空グラデ描画

メイン画面描画開始

#設定
変数 雪設定{
    "最大径":50,
    "最小径":10,
    "最大落下速度":20,
    "最小落下速度":10,
    "最大風速":5,
    "最小風速":1,
    "最大数":(画面幅+画面高)/50
}

#データ作成
雪一覧空配列
雪作成間隔基準値0.5
雪作成間隔(雪作成間隔基準値/(画面幅/400))
(雪作成間隔)秒毎にはもし(雪一覧要素数)(雪設定.最大数)ならば雪追加。。。

●雪追加
    変数 雪設定
    変数 方向[-1,1]
    変数 伸縮[[0,1],[1,0]]
    # 雪の降り方
    変数 風力(.最小風速から.最大風速までの乱数範囲)/10
    変数 風向方向[2乱数]
    変数 速度(.最小落下速度から.最大落下速度までの乱数範囲)/10
    # 雪のサイズ
    変数 雪径.最小径から.最大径までの乱数範囲
    # 雪位置
    変数 横位置画面幅乱数
    変数 縦位置雪径/2*-1//雪が見えない所から降らせる
    # 雪色
    変数 R0xCCから0xFFまでの乱数範囲
    変数 G0xDDから0xFFまでの乱数範囲
    変数 B0xEEから0xFFまでの乱数範囲
    変数 A(5から8までの乱数範囲)*0.1
    変数 雪データ{
        "位置":[横位置,縦位置],
        "径":雪径,
        "色":「RGBA({R},{G},{B},{A})」,
        "左右方向":風力*風向き,
        "落下速度":速度,
        "回転方向":方向[2乱数],
        "伸縮":伸縮[2乱数],
        "角度":0
    }
    雪一覧雪データ配列追加
ここまで

#雪を降らせる
降雪アニメーション
●降雪アニメーション
    [0,0]背景画像描画

    もし雪一覧要素数ならば雪追加
    雪一覧反復
        変数 No対象キー整数変換
        変数 雪位置対象.位置
        変数 [横位置,縦位置]雪位置
        変数 雪径対象.
        変数 雪色対象.
        雪色線色設定

        変数 伸縮対象.伸縮
        変数 角度対象.角度対象.回転方向
        変数 sinSIN((角度ラジアン変換))
        変数 cosCOS((角度ラジアン変換))
        変数 [伸縮x,傾斜y,傾斜x,伸縮y][cos*伸縮[0],sin,-sin,cos*伸縮[1]]
        変数 [起点x,起点y][横位置,縦位置]

        キャンバス状態保存
        #---
        [伸縮x,傾斜y,傾斜x,伸縮y,起点x,起点y]だけ描画変換マトリクス設定
        [0,0]雪径雪描画
        #---
        キャンバス状態復元

        横位置対象.左右方向だけ増やす
        縦位置対象.落下速度だけ増やす
        もし縦位置-雪径画面高さならば
            雪一覧No配列削除
        違えば
            対象.位置[横位置,縦位置]
            対象.角度角度
        ここまで
    ここまで
    「降雪アニメーション」画面更新時実行
ここまで

#描画
//中点は[x,y]の配列。雪径は雪の「直径」
●(中点へ雪径の)雪描画
    塗り色設定
    変数 m=雪径/10m/4*3線太設定
    中点からm*26多角形頂点取得
    それ多角形描画
    変数 頂点中点からm*56多角形頂点取得
    3:
      
頂点[回数-1]から頂点[回数+2]まで線描画
    変数 分岐点中点からm*36多角形頂点取得
    分岐点反復:
        
変数 対象からm*26多角形頂点取得
        変数 A(対象キー-1+6)%6
        変数 B(対象キー+1+6)%6
        [A]から対象まで線描画
        [B]から対象まで線描画
ここまで

// x = r cos θ y = r sin θ の呪文
●(中点から半径でnの)多角形頂点取得
    変数 v空配列
    n
        変数 角度(360/n)*(回数-1)
        変数 ラジアン度(角度-90)ラジアン変換
        変数 x半径*COS(ラジアン度)中点[0]
        変数 y半径*SIN(ラジアン度)中点[1]
        v[x,y]配列追加
    ここまで
    v戻る
ここまで

「https://n3s.nadesi.com/plain/ImageProcessing.nako3」取り込む
●(whへ)空グラデ描画
    変数 上空色「#000066」
    変数 地上色「#CCCCFF」
    変数 [w,h]=wh
    1線太さ設定上空色塗り色設定上空色線色設定
    [0,0,w,h/3]四角描画
    h/3*2
        C回数-1
        空色[C,h/3*2]上空色地上色色等比計算色混ぜ
        空色線色設定
        [0,C+h/3]から[w,C+h/3]線描画
    ここまで
ここまで

●クライアント幅
  「document.documentElement.clientWidth」JS実行
ここまで

●クライアント高さ
  「document.documentElement.clientHeight」JS実行
ここまで

 降っています! ひらひらと舞っていますよ~🎶

おわります

 キャンバスにお絵かきするだけじゃなく、それが動かせると楽しいですね!

 アドベントカレンダー始まりました。
 皆さんどうかご参加下さい~🙇‍♀️
 

2
1
4

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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?