はじめに
今年の8月につくば市主催のつくばメディアアートフェスティバルにて行なった、子ども向けワークショップの内容について紹介したいと思います。
ワークショップの概要
本ワークショップは筑波大学芸術系の授業、ADP(Art Design Produce)の1プロジェクトとして行いました。
私たちのチームは子どもたちにプログラミングとアートに親しんでもらうためのワークショップの企画、設計、運営を行いました。
「夏祭り」をテーマに4つのワークショップを行い、私はその中で「おえかき花火をうちあげよう!」というワークショップを企画しました。
メディアアートフェスティバルの会期中、一日をワークショップ開催日とし、残りの期間はワークショップで作っていただいた作品を、メディアアートフェスティバルに展示しました。
ワークショップの流れ
ワークショップは、以下の流れで体験してもらいました。
[1] 参加者が紙に絵を描く
↓
[2] スキャナで絵をスキャン
↓
[3] Enterキーで打ち上げ
技術構成
実装の主な構成は以下の4つです。
花火はUnityのParticleSystemで表現しました。
| 役割 | 技術 | ファイル |
|---|---|---|
| 画像の前処理 | Python | changePNG.py |
| ParticleSystemのSprite変更 | Unity C# | FireworkSpriteUpdater.cs |
| 花火の打ち上げ | Unity C# | PlayManager.cs |
| 花火の演出 | Unity C# | ShapeScaler.cs |
Python:絵を花火テクスチャ用PNGに変換する
描いてもらったイラストを、ParticleSystemで使えるように加工します。
画像を透過し、黒ペンで描いたイラストの透過部分以外を白色にします。
加工した画像をIndex管理できるようfirework_xxxの形でリネームし、UnityのStremingAssetsフォルダに移動するところまで行います。
def extract_transparent_white_lines(img, output_index):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, alpha = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY_INV)
white = np.full_like(gray, 255)
b, g, r = white, white, white
rgba = cv2.merge([b, g, r, alpha])
output_filename = f"firework_{output_index:03d}.png"
output_path = os.path.join(OUTPUT_DIR, output_filename)
unity_path = os.path.join(UNITY_DIR, output_filename)
cv2.imwrite(output_path, rgba)
shutil.copy(output_path, unity_path)
| スキャン画像 | 処理後画像 |
|---|---|
![]() |
![]() |
Unity
FireworkSpriteUpdater
このスクリプトでは花火画像のIndex操作と、指定したIndexの花火画像をParticleSystemのSpriteとTextureに適用する処理を行います。
void LoadSpriteAndTexture(int index)
{
string filename = spriteBaseName + index.ToString("D3") + spriteExtension;
string path = Path.Combine(Application.streamingAssetsPath, filename);
byte[] imageData = File.ReadAllBytes(path);
Texture2D texture = new Texture2D(2, 2, TextureFormat.RGBA32, false);
Sprite sprite = Sprite.Create(
texture,
new Rect(0, 0, texture.width, texture.height),
new Vector2(0.5f, 0.5f)
);
var shape = imageParticle.shape;
shape.shapeType = ParticleSystemShapeType.Sprite;
shape.sprite = sprite;
shape.texture = texture;
Debug.Log($"スプライトとテクスチャを更新: {filename}");
}
PlayManager
このスクリプトでは花火の打ち上げ処理を行います。
イラストを適用するパーティクル(illustParticle)の他に、打ち上がるような演出のパーティクル(trailParticle)を用意し、trailParticleの終了を待ってからillustParticleを再生します。
IEnumerator PlaySequence()
{
isFiring = true;
trailParticle.Play();
yield return new WaitWhile(() => trailParticle.IsAlive());
illustParticle.Play();
yield return new WaitWhile(() => illustParticle.IsAlive());
isFiring = false;
}
ShapeScaler
このスクリプトではillustParticleの演出を制御します。
パーティクルが拡大する時間やフェードアウトの時間を設定し、より花火らしい表現に近づけます。
完成形
展示の際は、花火大会を模した自作のジオラマ付きで展示しました。

実装時の工夫点
- 画像番号(Index)を変更するだけで花火を切り替えられること
- 裏での処理をSpaceキー、触れてもらう部分をEnterキーのみに単純化したこと
- 花火らしさを残しつつ、自分の描いたイラストだと認識できる演出になるよう調整したこと
改善点
- 黒ペンで描いたイラストをそのまま花火のテクスチャとして使えず白色に加工したため、カラーイラストに対応することができなかった
- 塗りつぶしの多いイラストと比較して、細い線で構成されたイラストは上手く描画することができなかった
自分の成長と学び
3時間ずっと動かし続けても問題なく、10日間の展示にも耐え、また子ども向けということもあり想定外のことが起きた時の対処法も考えて実装をするという経験はなかなか無く、良い経験になりました。
実際、Index管理していたことで、30分後にもう一度自分のイラストが見たい!と来た参加者に対応できたりと、自分の実装に助けられた場面もあり、様々な可能性を考慮した実装は大事だと改めて強く感じました。
終わりに
何日触ってもどんな触り方をしても大丈夫なものを作るというのは、ワークショップのみならずどんな開発にも必要な考え方だと改めて感じました。
今後も色々な場面でのものづくりを経験し、精進していきたいと思います。
最後まで読んでいただきありがとうございました。


