【なでしこさん】爆弾ゲーム

# 爆弾ゲーム

爆弾の前にスイッチが何個か並んでいます。
そのうち1個だけ、押すと爆弾が爆発するスイッチがあります。

* 複数人で遊ぶ場合
  * まだ押されていないスイッチを順番に1個ずつ押していきます。
  * 爆弾を爆発させた人が負けです。
* 1人で遊ぶ場合
  * スイッチを押していきます。
  * 爆弾を爆発させたらゲームオーバーです。
  * 爆弾を爆発させずに押していないスイッチを1個だけにできたらクリアです。

ここで遊べます。(なでしこ3貯蔵庫)
[爆弾ゲーム](https://n3s.nadesi.com/id.php?1929)

# 爆弾ゲームで用いる主ななでしこさんの機能

## 画像の表示

[画像作成](https://nadesi.com/v3/doc/index.php?plugin_browser%2F%E7%94%BB%E5%83%8F%E4%BD%9C%E6%88%90&show)を用いることで、URLを指定して画像を表示できます。
URLには、[Data URL](https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/Data_URLs) も使用できました。
例えば、[CyberChef](https://gchq.github.io/CyberChef/) を用いることで、データをBase64エンコードでき、Data URL を作成する助けになります。

例：爆弾の画像を表示する

```
爆弾URLは「data:image/png;base64,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」
爆弾は爆弾URLの画像作成。
```

## DOMの操作

主に以下のコマンドを利用し、DOMを操作できます。

* [DOM親部品設定](https://nadesi.com/v3/doc/index.php?plugin_browser%2FDOM%E8%A6%AA%E9%83%A8%E5%93%81%E8%A8%AD%E5%AE%9A&show) : 「DOM部品作成」などのコマンドで要素を追加する先を設定します。
* [DOM親要素](https://nadesi.com/v3/doc/index.php?plugin_browser%2FDOM%E8%A6%AA%E8%A6%81%E7%B4%A0&show) : 「DOM部品作成」などのコマンドで要素を追加する先を取得します。
* [DOM部品作成](https://nadesi.com/v3/doc/index.php?plugin_browser%2FDOM%E9%83%A8%E5%93%81%E4%BD%9C%E6%88%90&show) : DOM要素を作成し、設定された親要素に追加します。(≒[`document.createElement()`](https://developer.mozilla.org/ja/docs/Web/API/Document/createElement) + [`appendChild()`](https://developer.mozilla.org/ja/docs/Web/API/Node/appendChild))
* [DOMスタイル設定](https://nadesi.com/v3/doc/index.php?plugin_browser%2FDOM%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB%E8%A8%AD%E5%AE%9A&show) : DOM要素のスタイルのうち1項目を設定します。
* [DOMスタイル一括設定](https://nadesi.com/v3/doc/index.php?plugin_browser%2FDOM%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB%E4%B8%80%E6%8B%AC%E8%A8%AD%E5%AE%9A&show) : DOM要素のスタイルを設定します。
* [DOM属性設定](https://nadesi.com/v3/doc/index.php?plugin_browser%2FDOM%E5%B1%9E%E6%80%A7%E8%A8%AD%E5%AE%9A&show) : DOM要素の属性を設定します。(≒[`setAttribute()`](https://developer.mozilla.org/ja/docs/Web/API/Element/setAttribute))
* [DOM属性取得](https://nadesi.com/v3/doc/index.php?plugin_browser%2FDOM%E5%B1%9E%E6%80%A7%E5%8F%96%E5%BE%97&show) : DOM要素の属性を取得します。(≒[`getAttribute()`](https://developer.mozilla.org/ja/docs/Web/API/Element/getAttribute))
* [クリックした時には(イベント)](https://nadesi.com/v3/doc/index.php?plugin_browser%2F%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E6%99%82&show) : DOM要素をクリックした時に実行する処理を[無名関数](https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2F%E7%84%A1%E5%90%8D%E9%96%A2%E6%95%B0&show)で指定して追加します。(≒ `addEventListener("click", function(event) { ... })`)

## 音声の再生

[オーディオ開](https://nadesi.com/v3/doc/index.php?plugin_browser%2F%E3%82%AA%E3%83%BC%E3%83%87%E3%82%A3%E3%82%AA%E9%96%8B&show)でURLを指定して音声ファイルを開き、それを[オーディオ再生](https://nadesi.com/v3/doc/index.php?plugin_browser%2F%E3%82%AA%E3%83%BC%E3%83%87%E3%82%A3%E3%82%AA%E5%86%8D%E7%94%9F&show)に渡すことで、音声を再生できます。
(「[それ](https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2F%E3%81%9D%E3%82%8C&show)」を使わなくても大丈夫です)
ここでも [Data URL](https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/Data_URLs) が使用可能です。

# 実装の方針

今回は、DOM要素を用いて爆弾とスイッチを配置し、スイッチにクリックのイベントハンドラを設定して処理を行います。
スイッチには、爆破スイッチかどうかの情報を属性として持たせます。

今回は、以下の素材を用います。

* 画像
  * 爆弾
    * 爆発前
    * 爆発後
  * スイッチ
    * 押されていない状態
    * 押された状態
* 音声
  * ボタンを押した時
  * 押したボタンが爆破ボタンでなかった時
  * 爆発

# 制作中に発見した罠

## 簡易エディタと貯蔵庫の存在しない属性を取得したときの挙動の違い

例えば、以下のプログラムを[簡易エディタ](https://nadesi.com/doc3/index.php?%E3%81%AA%E3%81%A7%E3%81%97%E3%81%933%E7%B0%A1%E6%98%93%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF)と[貯蔵庫](https://n3s.nadesi.com/)でそれぞれ実行します。

```
画像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` を設定した画像も設定していない画像も、クリック時の処理を期待通り行うことができました。

![簡易エディタで存在しない属性を取得するテスト](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/bc022316-16a9-4e4a-de72-8c15fa584898.png)

一方、貯蔵庫では、属性 `data-sample` を設定していない画像ではエラーになってしまいました。

Firefox 107.0.1
![貯蔵庫で存在しない属性を取得するテスト Firefox](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/884c6a4a-3bce-bded-77e1-1afac754a1ef.png)
Google Chrome 108.0.5359.99
![貯蔵庫で存在しない属性を取得するテスト Google Chrome](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/24462a90-a268-1b62-165f-ae6c383ad209.png)

爆破ボタンだけにそれを表す属性を設定していましたが、それ以外のボタンにも同じ属性を(違う値に)設定することで解決しました。

## いらすとやの素材

[いらすとや](https://www.irasutoya.com/)で、今回のゲームに役立ちそうな以下の素材を発見しました。

* [爆弾のイラスト | かわいいフリー素材集 いらすとや](https://www.irasutoya.com/2014/04/blog-post_4.html)
* [爆発のイラスト | かわいいフリー素材集 いらすとや](https://www.irasutoya.com/2014/03/blog-post_2687.html)
* [オンオフスイッチのイラスト（電子部品） | かわいいフリー素材集 いらすとや](https://www.irasutoya.com/2016/04/blog-post_63.html)

しかし、[よくあるご質問 | いらすとや](https://www.irasutoya.com/p/faq.html)において、

> またScratchのような素材の再配布機能のあるシステム内でのご利用もお断りしています。

となっています。
[なでしこ3貯蔵庫](https://n3s.nadesi.com/index.php)には素材を誰でも利用できる形でアップロードする機能があるので、残念ながら貯蔵庫に投稿するプログラムでいらすとやの素材は使えないようです。
(いらすとやのお断り条件に「素材の再配布機能を**使用する**」ことは入っていないことに注意しましょう)

## 他の人が著作権を持つ素材

音声の素材の利用を検討しました。
例えば、

* [魔王魂 | 無料で使える森田交一の音楽](https://maou.audio/)
* [BGM・ジングル・効果音のフリー素材｜OtoLogic](https://otologic.jp/)

では、素材が CC BY 4.0 ライセンスで提供されており、再配布も問題無さそうです。

しかし、[「なでしこ3貯蔵庫」のご利用規約](https://n3s.nadesi.com/index.php?action=kiyaku)に、

> 当サービスに書き込んだプログラムや素材データの著作権は投稿者にあります。

という記述があります。
したがって、前述のサービスで提供されているような素材データを貯蔵庫に書き込んでしまうと、事実に反して投稿者に素材データの著作権があることになり、利用規約違反や権利侵害につながるおそれがあります。
よって、このような素材も貯蔵庫に投稿するプログラムでは利用しづらいでしょう。
(素材データを貯蔵庫には直接書き込まず、GitHub Gistなどに置いてプログラムから参照する、などの選択肢はあるかもしれません)

なお、[CC0](https://creativecommons.org/publicdomain/zero/1.0/legalcode.ja)の場合は、著作権は放棄するとあります。
したがって、素材そのものをアップロードすると本当は著作権が無いのに「著作権は投稿者にあります」となってしまい、利用規約違反となると考えられます。
素材を投稿者の著作物であるプログラムに埋め込む形で書き込むのは…CC0には「自作発言禁止」のような条項は無さそうだし…このくらいセーフであってくれ…:pray:

## 論理式の挙動が非直感的

[なでしこさんは論理が苦手？ - Qiita](https://qiita.com/mikecat_mixc/items/635beaff17443221be72)
で紹介した内容です。

```
爆破済はいいえ。
演出中はいいえ。

もし、爆破済または演出中ならば
　「ボタン操作を無視」と表示。
ここまで。
```

のように記述すると、爆破済でも演出中でもないのに「ボタン操作を無視」が表示されてしまいます。

```
爆破済はいいえ。
演出中はいいえ。

もし、(爆破済)または(演出中)ならば
　「ボタン操作を無視」と表示。
ここまで。
```

のようにカッコを加えることで改善できました。
でもカッコ悪いですね。(個人の感想です)

## 設定した変数が「秒後」の処理に引き継がれないことがある

[秒後](https://nadesi.com/v3/doc/index.php?plugin_system%2F%E7%A7%92%E5%BE%8C&show)を用いると、指定した時間の経過後に処理を実行できます。
しかし、この「秒後」で実行する処理には、なぜかこの処理の前に定義した変数が引き継がれないことがあることがわかりました。

例えば、以下のような「イベントハンドラ内でイベントの発生元の要素の属性を参照し、時間経過後の処理に用いる」プログラム (JavaScript) を考えます。

```js
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);
});
```

このプログラムでは、画像をクリックすると、意図通りに属性の値「テスト」が出力されます。

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="ExRJObm" data-user="mike_cat" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/mike_cat/pen/ExRJObm">
  use variable in nested function test</a> by MikeCAT (<a href="https://codepen.io/mike_cat">@mike_cat</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

これをなでしこさんに移植しました。

```
画像は「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`](https://developer.mozilla.org/ja/docs/Web/CSS/max-width)スタイルを設定することで解決しました。

# 使用した素材

今回は、以下の素材を利用しました。

* 爆弾の画像
  * [[無料イラスト] 点火した爆弾 - パブリックドメインQ：著作権フリー画像素材集](https://publicdomainq.net/bomb-explosive-weapon-0001415/)
  * [[フリーイラスト] 爆発 - パブリックドメインQ：著作権フリー画像素材集](https://publicdomainq.net/nuclear-explosion-0041190/)
* スイッチの画像
  * 自作 (Blender)
* 音声
  * [重音テト](https://kasaneteto.jp/)の音源を用いて自作

画像は容量を抑えるため、縮小のほかに [Online Image Сompressor](https://imagecompressor.com/) で減色しました。

自作素材の元ファイルを置いておきます。
[なでしこで爆弾ゲーム 素材の元ファイル](https://gist.github.com/mikecat/da771e0e662b16358aac6a9b0f1e4a5a)