爆弾ゲーム
爆弾の前にスイッチが何個か並んでいます。
そのうち1個だけ、押すと爆弾が爆発するスイッチがあります。
- 複数人で遊ぶ場合
- まだ押されていないスイッチを順番に1個ずつ押していきます。
- 爆弾を爆発させた人が負けです。
- 1人で遊ぶ場合
- スイッチを押していきます。
- 爆弾を爆発させたらゲームオーバーです。
- 爆弾を爆発させずに押していないスイッチを1個だけにできたらクリアです。
ここで遊べます。(なでしこ3貯蔵庫)
爆弾ゲーム
爆弾ゲームで用いる主ななでしこさんの機能
画像の表示
画像作成を用いることで、URLを指定して画像を表示できます。
URLには、Data URL も使用できました。
例えば、CyberChef を用いることで、データをBase64エンコードでき、Data URL を作成する助けになります。
例:爆弾の画像を表示する
爆弾URLは「data:image/png;base64,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」
爆弾は爆弾URLの画像作成。
DOMの操作
主に以下のコマンドを利用し、DOMを操作できます。
- DOM親部品設定 : 「DOM部品作成」などのコマンドで要素を追加する先を設定します。
- DOM親要素 : 「DOM部品作成」などのコマンドで要素を追加する先を取得します。
-
DOM部品作成 : DOM要素を作成し、設定された親要素に追加します。(≒
document.createElement()
+appendChild()
) - DOMスタイル設定 : DOM要素のスタイルのうち1項目を設定します。
- DOMスタイル一括設定 : DOM要素のスタイルを設定します。
-
DOM属性設定 : DOM要素の属性を設定します。(≒
setAttribute()
) -
DOM属性取得 : DOM要素の属性を取得します。(≒
getAttribute()
) -
クリックした時には(イベント) : DOM要素をクリックした時に実行する処理を無名関数で指定して追加します。(≒
addEventListener("click", function(event) { ... })
)
音声の再生
オーディオ開でURLを指定して音声ファイルを開き、それをオーディオ再生に渡すことで、音声を再生できます。
(「それ」を使わなくても大丈夫です)
ここでも Data URL が使用可能です。
実装の方針
今回は、DOM要素を用いて爆弾とスイッチを配置し、スイッチにクリックのイベントハンドラを設定して処理を行います。
スイッチには、爆破スイッチかどうかの情報を属性として持たせます。
今回は、以下の素材を用います。
- 画像
- 爆弾
- 爆発前
- 爆発後
- スイッチ
- 押されていない状態
- 押された状態
- 爆弾
- 音声
- ボタンを押した時
- 押したボタンが爆破ボタンでなかった時
- 爆発
制作中に発見した罠
簡易エディタと貯蔵庫の存在しない属性を取得したときの挙動の違い
例えば、以下のプログラムを簡易エディタと貯蔵庫でそれぞれ実行します。
画像Aは「https://placehold.jp/8080ff/ffffff/150x150.png?text=with%20attr.」の画像作成。
画像Bは「https://placehold.jp/ff80ff/ffffff/150x150.png?text=no%20attr.」の画像作成。
画像Aの「data-sample」に「yes」をDOM属性設定。
●(イベントの)サンプルハンドラとは
画像はイベント["target"]。
サンプルは画像の「data-sample」をDOM属性取得。
もし、サンプルが「yes」と等しいならば
「サンプル!」と表示。
違えば
「サンプルではない」と表示。
ここまで。
ここまで。
画像Aをクリックした時には(イベント)
イベントのサンプルハンドラ。
ここまで。
画像Bをクリックした時には(イベント)
イベントのサンプルハンドラ。
ここまで。
簡易エディタでは、属性 data-sample
を設定した画像も設定していない画像も、クリック時の処理を期待通り行うことができました。
一方、貯蔵庫では、属性 data-sample
を設定していない画像ではエラーになってしまいました。
Firefox 107.0.1
Google Chrome 108.0.5359.99
爆破ボタンだけにそれを表す属性を設定していましたが、それ以外のボタンにも同じ属性を(違う値に)設定することで解決しました。
いらすとやの素材
いらすとやで、今回のゲームに役立ちそうな以下の素材を発見しました。
しかし、よくあるご質問 | いらすとやにおいて、
またScratchのような素材の再配布機能のあるシステム内でのご利用もお断りしています。
となっています。
なでしこ3貯蔵庫には素材を誰でも利用できる形でアップロードする機能があるので、残念ながら貯蔵庫に投稿するプログラムでいらすとやの素材は使えないようです。
(いらすとやのお断り条件に「素材の再配布機能を使用する」ことは入っていないことに注意しましょう)
他の人が著作権を持つ素材
音声の素材の利用を検討しました。
例えば、
では、素材が CC BY 4.0 ライセンスで提供されており、再配布も問題無さそうです。
しかし、「なでしこ3貯蔵庫」のご利用規約に、
当サービスに書き込んだプログラムや素材データの著作権は投稿者にあります。
という記述があります。
したがって、前述のサービスで提供されているような素材データを貯蔵庫に書き込んでしまうと、事実に反して投稿者に素材データの著作権があることになり、利用規約違反や権利侵害につながるおそれがあります。
よって、このような素材も貯蔵庫に投稿するプログラムでは利用しづらいでしょう。
(素材データを貯蔵庫には直接書き込まず、GitHub Gistなどに置いてプログラムから参照する、などの選択肢はあるかもしれません)
なお、CC0の場合は、著作権は放棄するとあります。
したがって、素材そのものをアップロードすると本当は著作権が無いのに「著作権は投稿者にあります」となってしまい、利用規約違反となると考えられます。
素材を投稿者の著作物であるプログラムに埋め込む形で書き込むのは…CC0には「自作発言禁止」のような条項は無さそうだし…このくらいセーフであってくれ…
論理式の挙動が非直感的
なでしこさんは論理が苦手? - Qiita
で紹介した内容です。
爆破済はいいえ。
演出中はいいえ。
もし、爆破済または演出中ならば
「ボタン操作を無視」と表示。
ここまで。
のように記述すると、爆破済でも演出中でもないのに「ボタン操作を無視」が表示されてしまいます。
爆破済はいいえ。
演出中はいいえ。
もし、(爆破済)または(演出中)ならば
「ボタン操作を無視」と表示。
ここまで。
のようにカッコを加えることで改善できました。
でもカッコ悪いですね。(個人の感想です)
設定した変数が「秒後」の処理に引き継がれないことがある
秒後を用いると、指定した時間の経過後に処理を実行できます。
しかし、この「秒後」で実行する処理には、なぜかこの処理の前に定義した変数が引き継がれないことがあることがわかりました。
例えば、以下のような「イベントハンドラ内でイベントの発生元の要素の属性を参照し、時間経過後の処理に用いる」プログラム (JavaScript) を考えます。
const img = document.createElement("img");
img.setAttribute("src", "https://placehold.jp/3d4070/ffffff/150x150.png?text=test");
document.body.appendChild(img);
img.setAttribute("data-text", "テスト");
img.addEventListener("click", function(event) {
const contents = event.target.getAttribute("data-text");
setTimeout(function() {
document.body.appendChild(document.createElement("br"));
document.body.appendChild(document.createTextNode(contents));
}, 1000);
});
このプログラムでは、画像をクリックすると、意図通りに属性の値「テスト」が出力されます。
See the Pen use variable in nested function test by MikeCAT (@mike_cat) on CodePen.
これをなでしこさんに移植しました。
画像は「https://placehold.jp/3d4070/ffffff/150x150.png?text=test」の画像作成。
画像の「data-text」に「テスト」をDOM属性設定。
画像をクリックした時には(イベント)
発言内容はイベント["target"]の「data-text」をDOM属性取得。
1秒後には
発言内容を表示。
ここまで。
ここまで。
すると、画像をクリックした時、簡易エディタでは「テスト」ではなく「undefined」が表示されてしまいました。
貯蔵庫では、まず実行時に
[警告]main.nako3(6行目): 変数『発言内容』は定義されていません。
と表示され、さらに画像をクリックすると、Firefoxでは
[実行時エラー]main.nako3(6行目): TypeError: t is undefined
Google Chromeでは
[実行時エラー]main.nako3(6行目): TypeError: Cannot read properties of undefined (reading 'msg')
と表示されました。
しかし、開発中の爆弾ゲームでは、なぜか変数「押されたボタン」は「秒後」を超えて参照できていました。
このとき、イベントハンドラの前の処理で変数「押しボタン」を使っていました。
そこで、サンプルのイベントハンドラの前に、変数「発言内容」への適当な代入を追加すると、「テスト」と表示されるようになり、貯蔵庫でも警告が出なくなりました。
画像は「https://placehold.jp/3d4070/ffffff/150x150.png?text=test」の画像作成。
画像の「data-text」に「テスト」をDOM属性設定。
発言内容は「にゃーん」。
画像をクリックした時には(イベント)
発言内容はイベント["target"]の「data-text」をDOM属性取得。
1秒後には
発言内容を表示。
ここまで。
ここまで。
でもカッコ悪いですね。(個人の感想です)
デフォルトのDOM親要素の幅が簡易エディタと貯蔵庫で違う
簡易エディタでは、デフォルトのDOM親要素の幅が狭く設定されています。
一方、貯蔵庫では、デフォルトのDOM親要素の幅が画面いっぱいになっています。
そのため、簡易エディタでデフォルトのDOM親要素の幅を基準に開発していたプログラムを貯蔵庫で実行すると、表示が大きくなりすぎてしまいました。
デフォルトのDOM親要素にmax-width
スタイルを設定することで解決しました。
使用した素材
今回は、以下の素材を利用しました。
- 爆弾の画像
- スイッチの画像
- 自作 (Blender)
- 音声
- 重音テトの音源を用いて自作
画像は容量を抑えるため、縮小のほかに Online Image Сompressor で減色しました。
自作素材の元ファイルを置いておきます。
なでしこで爆弾ゲーム 素材の元ファイル