夏だけじゃなく、最近は冬にもクリスマスやカウントダウンのイベントで花火が上がること、多いですよね?
そして、お祝いと言えば、やはり花火です!
なでしこ20周年を締めくくるのにふさわしいネタと言えるでしょう。
というわけで、過去作を雑に使いまわしつつ、一日目の電光掲示板で得た知見をもとに改良したりなんだりします。
かこのいぶつ
花火はずっと以前に一度作りました。
元ネタは、これでした。
クジラ飛行机氏の記事ですが、なでしこさんじゃなくてJavaScriptの記事でしたので、うっ、チョットなに言ってるかわかんにゃい💧️とか、アルファベットのコードぐあいわるい😵️とかなりつつ作ったものでした。
通常花火
ちゃんとした移植ではありません。
なんか、イメージで!
あんまよくわかんないけど、ようするに前に紙吹雪作った時みたいに、花火の火花いっこいっこに辞書型変数で設定を作って、画面更新時実行で全部描画するのを繰り返せばいいんでしょ!? とゆう😅️
夏の花火大会は、その後色数を増やしたり花火の種類を増やしたりしたことでゴチャッとしているので、当初の状態がコチラ。
# キャンバスの準備
変数 [キャンバス幅,キャンバス高さ,背景色]=[400,400,黒色]。
描画中キャンバスの「幅」にキャンバス幅をDOM属性設定。
描画中キャンバスの「高さ」にキャンバス高さをDOM属性設定。
背景描画。
●背景描画
背景色に塗り色設定。
[0,0,キャンバス幅,キャンバス高さ]へ四角描画。
ここまで。
# 宣言
花火データ=空配列。
粒子データ={"x":100,"y":100,"半径":2,"色":赤色,"透明度":1,"重力":0.05}。
粒子数=100。
前時間=時間ミリ秒取得。
# 花火粒子の位置や透明度を更新
●(粒子を経過時間で)花火更新
粒子["x"]を粒子["vx"]*経過時間だけ増やす。
粒子["y"]を粒子["vy"]*経過時間だけ増やす。
粒子["vy"]を粒子["重力"]*経過時間だけ増やす。
粒子["透明度"]を0.01*経過時間だけ減らす。
粒子["半径"]を0.05*経過時間だけ減らす。
粒子["時間"]を経過時間だけ増やす。
もし、粒子["半径"]<0ならば、粒子["半径"]=0。
もし、粒子["透明度"]<0ならば、粒子["透明度"]=0。
ここまで。
●花火描画
# 経過時間を計算
今時間=時間ミリ秒取得。
経過時間=(今時間-前時間)/100。
前時間=今時間。
# キャンバスをクリア
背景描画。
# 各粒子を更新して描画
総粒子数=花火データの要素数。
花火データの要素数回
粒子=花火データ[総粒子数-回数]。# 消失した粒子を削除するため逆順で回す
粒子を経過時間で花火更新。
色=粒子["透明度"]と(粒子["色"]をRGB分解)のカラーコードRGBA。
色に塗り色設定。空に線色設定。
[粒子["x"],粒子["y"]]へ粒子["半径"]の円描画。
# 半径が0か透明度が0になった粒子は削除
もし、(粒子["透明度"]≦0)または(粒子["半径"]≦0)ならば、花火データの総粒子数-回数を配列削除。
ここまで。
「花火描画」を画面更新時実行。
ここまで。
# 花火色候補の色定数。
定数 赤色=「#FF0000」
定数 橙色=「#ffa500」
定数 金色=「#ffd700」
定数 黄色=「#ffff00」
定数 空色=「#87ceeb」
定数 薄桃色=「#ffb6c1」
定数 薄緑色=「#90ee90」
定数 矢車菊色=「#6495ed」
●(xyへ)花火打ち上げ
変数 花火色配列=[[赤色,金色,黄色,橙色],[空色,薄桃色,薄緑色,矢車菊色]]。
変数 [cx,cy]=xy。
粒子色配列=花火色配列[(花火色配列の要素数)の乱数]。
粒子数回
粒子=粒子データを配列複製。
粒子["x"]=cx。粒子["y"]=cy。
粒子["色"]=粒子色配列[(粒子色配列の要素数)の乱数]。
粒子["半径"]=3の乱数+1。
粒子["速度"]=30の乱数/10+1。
粒子["角度"]=(PI*2を度変換)の乱数。
粒子["vx"]=COS(粒子["角度"])*粒子["速度"]。
粒子["vy"]=SIN(粒子["角度"])*粒子["速度"]。
花火データに粒子を配列追加。
ここまで。
ここまで。
#-------------------------------------------------
「花火描画」を画面更新時実行。
0.3秒毎には、
cx=キャンバス幅の乱数。
cy=キャンバス高さの乱数。
[cx,cy]へ花火打ち上げ。
ここまで。
#-------------------------------------------------
//「#RRGGBB」→[R,G,B]
●(色を|色の)RGB分解
分色とは変数。分色=空配列。
色=色の「#」を「0x」に置換。
色=色を整数変換。
数を2から0まで繰り返す
分色[数]=色%256。
色=(色-分色[数])/256
ここまで。
分色で戻る。
ここまで。
// (0~1)と[R,G,B]→「RGBA(R,G,B,A)」
●(透明度とrgbの)カラーコードRGBA
「RGBA({rgb[0]},{rgb[1]},{rgb[2]},{透明度})」で戻る。
ここまで。
#-------------------------------------------------
乱数
なでしこさんの乱数とJavaScriptのMath.random()は、違います。
なでしこさんの乱数は、0からA-1までのいずれかの整数を適当に返すものです。なので、6の乱数+1とかすれば、簡単にサイコロが作れます👍️
ところがMath.random()は、0以上1未満の範囲で浮動小数点の擬似乱数を返すんだって。知ってたけどわかってなかった。
3の乱数+1じゃ、粒子のサイズは1,2,3の三種類しかないわけですよ🤣️
ピクセルに小数はないでしょって思ってたんだけど、サブピクセルってものがあるラシイ。
とはいえ、そんなに細かくったってしょうがないだろうから、100の乱数/100*2+1とかでどうだろう・・・?🤔️
「Math.random()」をJS実行とかすれば、まったく同様にすることもできますが、ま、いいでしょ💧️
JavaScriptはもちろん、乱数と言えば0~1という言語は結構多いと思うので、なでしこさんにもあっていいと思うんだけど。
って話、どっかで聞いたような気がすると思ったら、過去のアドベントカレンダーにこんなのありました!
小数乱数ね👍️
透明度
描画中コンテキストのglobalAlphaとゆうものを知りませんでしたんで、花火に使う色は全て、なでしこさんの色定数を使わずにカラーコードで定義して、透明度を加えてrgba形式にしています。
型物花火
これは、花火の粒子を作っていません。
丸点線で星やハートやスマイルマーク🙂を描いているだけです。
●(粒子の)型物花火描画
r=粒子["サイズ"]
粒子["半径"]*2に線太さ設定。「round」に線端形状設定。
[1,r/粒子["元半径"]]に破線パターン設定。
キャンバス状態保存。
[粒子["x"],粒子["y"]]に描画起点設定。
粒子["傾き"]に描画回転。
粒子["型物"]で条件分岐
「星」ならば、5で[0,0]にrの星描画。ここまで。
「ハート」ならば、[0-r,0-r,r*2,r*2]のハート描画。ここまで。
「スマイル」ならば、
[0,0]にrの円描画。
[0,0]に(20をラジアン変換)から(170をラジアン変換)までr/2の円弧描画。
[r/3*-1,r/3*-1]にr/20の円描画。
[r/3,r/3*-1]にr/20の円描画。
ここまで。
ここまで。
キャンバス状態復元。
ここまで。
なでしこの描画命令に、線端の形状(lineCap)や破線パターンの設定(setLineDash())を変更するものはありませんが、ワタクシどうしても点線が描きたい! とか、扇形描きたい! とか、v1ではできる角丸四角をv3でもぜったいに描きたい! とかとかいろいろあって、プラグイン(ライブラリ)を作っています。
花火のためだけに、星とハートもしゅっと描けるようにしました🤣️
昇竜
打ち上げ時にシュルシュルと尾を引くやつです。
今回の改良点にある、背景を薄くして残像を残すということを知らなかったので、さっきまで自分がいたとこに尻尾の粒子を置いてくる、ということをしています。
また、ただ残像を残しただけではシュルシュル感が出ないので、ランダムに左右に少し振っています。
さいんこさいんのじゅもんで、らせんを描くようにしてみたらどうじゃろうとも思うたが、ワタクシの能力とセンスが不足で、変な感じにしかならなかったような記憶が~😥️
今回の改良でもリベンジはせず💧️
改良します
オブジェクトプロパティ構文など使って、今風な感じで1から作り直そうかとも思いましたが、時間とやる気が不足なため、過去の遺物にそのままいろいろ付け足しマス😅️
- 祝 なでしこ 20周年! の文字花火
- 花火の粒子が尾を引くようにする
- 花火の粒子のキラキラ感を出す
- 打ち上げ時の昇龍に角度を付ける
こんなところです。
文字花火
これはもう完全に一日目の電光掲示板と同様に、描画した文字からドットを作成しています。
それを花火の粒子にしているだけ!
テキスト=「祝 なでしこ 20周年!」。
文字幅=画面幅*1.2を切り捨て。
フォントサイズ=80と文字幅/(テキストの文字数)の最小値。
フォント=「bold {フォントサイズ}px sans-serif」
文字用=[0,0]のキャンバス作成。//領域を確保しない
文字用.可視=オフ。
文字用.幅属性=文字幅。
文字用.高さ属性=画面高さ/2。
フォントに描画フォント設定。
黒色に塗り色設定。
[0,0,文字幅,画面高さ]へ四角描画。
白色に塗り色設定。
描画中コンテキスト.textAlign=「center」
[文字幅/2,画面高さ/4]へテキストを文字描画。
色データ=[0,0,文字幅,画面高さ/2]の色データ取得。
文字花火データ=空配列。
粒子間隔=フォントサイズ/12を切り捨て。
yを0から画面高さまで粒子間隔ずつ増やし繰り返す。
xを0から文字幅まで粒子間隔ずつ増やし繰り返す。
ID=(y*文字幅+x)*4。
もし、色データ[ID]>128ならば、文字花火データに{"種別":"文字","tx":x-(文字幅-画面幅)/2,"ty":y,"透明度":1,"重力":0.09,"時間":0}を配列追加。
ここまで。
ここまで。
描画中コンテキストのtextAlignをcenterに設定すると、文字幅取得して計算する必要もなく描画位置に指定したxを中心にして中央寄せで文字描画出来るんですね❗️
描画中コンテキスト奥が深い・・・
花火の粒子が尾を引く(残像)
検索すると、背景描画時に描画中コンテキストにglobalAlphaを設定するか、背景色自体を透明度のついたものにするかといったところのようです。
これらも、電光掲示板の消灯LEDを描画するときに学びました。
ようするに背景のほうを半透明にして、前の描画が透けて見えるようにすることで残像を残すということですね。
ただこの方法だと、しだれ柳のように長く尾を引く花火と、牡丹のように点状の粒子が丸く広がる花火を共存させることは出来ず不満が残りますが、今回はとりあえず折角なのでglobalAlphaを採用してみます。
また、これを機に前述した花火の粒子のほうの透明度も、こちらに変えました。
それにより、花火の色に、なでしこの色定数がフツーに使えるようになり、追加の色定数もカラーネームにしてみました。
●背景描画
夜空色に塗り色設定。空に線色設定。
描画中コンテキスト.globalAlpha=0.15。
[0,0,画面幅,画面高]へ四角描画。
ここまで。
あまり残像が残りすぎると、文字花火や型物が変な感じになるのでほどほどの設定に。
実際、型物のスマイル🙂は、尾を引くととてもそうは見えなくなってしまうのでやめました。
ワタクシ本当は、しだれ柳が一番好きなんですけどね~😅
キラキラ感(明滅)
周期的に粒子の透明度を変化させることでキラキラ✨💍✨明滅して見えるようにします。
こんなかんじ。
# 花火打ち上げ時
粒子["明滅速度"]=100の乱数/100*0.2+0.05。
# 花火更新時
粒子["透明度"]=SIN(時間ミリ秒取得*粒子["明滅速度"])の絶対値。
ただ、型物花火はただの点線なので、すべての点が同じタイミングでチカチカしてしまい不都合なので明滅はさせず、色が変わる感じにしました。
また、花火描画時に、
# 花火描画
描画中コンテキスト.globalCompositeOperation=「lighter」
とゆうじゅも~ん。
globalCompositeOperationは、画像の合成演算や色の混合演算の方法を設定できるプロパティで、lighterは、色値加算で重なった部分は明るくなります。キラキラ感が増します✨️
背景を塗るときには、元のsource-overに戻します。
昇龍の角度
前は単純にyの値だけを変化させていたので、全部打ち上げ位置の真下からまっすぐ真上に上がっていましたが、イマイチかっこよくないので角度を付けました。
二点間の途中座標を求めるじゅもーん!
xt = x + (x2 - x) * t
yt = y + (y2 - y) * t
tは、(0=出発点)〜(1=到着点)で進行度を指定します。
移動元=[100,300]。
移動先=[200,50]。
黒色へ塗り色設定。黒色へ線色設定。
移動元へ3の円描画。移動先へ3の円描画。
赤色へ塗り色設定。赤色へ線色設定。
20回
進行度=回数/20。
移動元から移動先まで進行度で途中座標計算。
それへ2の円描画
ここまで。
●(xyからxy2までtで|xy2までのtを)途中座標計算
変数[x,y]=xy。
変数[x2,y2]=xy2。
xt=x+(x2-x)*t。
yt=y+(y2-y)*t。
[xt,yt]を戻す。
ここまで。
できました🎆️
おわります
皆様のご尽力により、なでしこ20周年のアドベントカレンダー、無事に全て埋めることが出来ました!
参加してくださった方はもちろん、読んでくださった方々もありがとうございました!!

