Amazon Q CLI でFlappy Bird風ゲームをVibe coding

# この記事の内容

https://community.aws/content/2xIoduO0xhkhUApQpVUIqBFGmAc/build-games-with-amazon-q-cli-and-score-a-t-shirt

Build Games with Amazon Q CLI and score a T shirt というキャンペーンがあったのでAmazon Q CLIに簡単なゲームを作らせてみた記録です。
特に何か新しい手法を取り入れたというものではないので、制作のポエムだと思っていただけると良いかなと思います。
キャンペーンの要項に以下を含めるように、と記載があったのでそこは意識して記載しています。

- 選んだゲームとその理由
- 効果的なプロンプトテクニック
- AIが古典的なプログラミング課題をどのように処理したか
- 時間を節約した開発自動化の例
- AIが生成した興味深いソリューションのコード例
- 最終作品のスクリーンショットまたはゲームプレイ映像

Amazon Q CLIとやりとりをしてみた感想としては、なんかちょっとかわいいなと思いました。

# 選んだゲームとその理由
私が音楽制作をするときにマスコットキャラクター的なものとして作ってもらった「フーカ」というキャラクターがいます。（アイコンのやつです）
この子を使ったゲームを作ってみたいなと思っていました。

![fu_ca.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1126770/2575079f-39a6-445f-958c-78ecd78a1e3c.png)

鳥っぽい見た目をしているのでそれが活かせて、仕組みの難しくないゲームということで、Flappy Bird風ゲームを作ってみることにしました。

# できたもの

簡単にデプロイできるものだったので、一応デプロイしておきました。
（音が出ますのでご注意ください）
https://d3g3dwbwsppgk0.cloudfront.net/

iPhoneのタップ操作で遊ぼうとするとちょっと難しいなと感じましたが、PCのスペースキーで遊ぶとちょうど良いぐらいの難易度かなと思います。（このあたりは一切調整していないので、すごい）

コードは1行も書いておらず、エラーで起動しなくなったりした際も自然言語の指示とエラーメッセージを見せるだけで修正してくれたので、制作の過程ではコードを読むこともありませんでした。

# 出した指示とやり取りの流れ

最初の指示
```
FlappyBird風のゲームをReactで作ってください
```
なんとも大雑把な指示ですが、この指示だけで動作としてはほぼ完成したものができてしまいました。
スクリーンショットは撮り忘れましたが、丸いオブジェクトがスペースキーを押すとぴょこぴょこと跳ねて、
障害物を避けていくゲームとして十分遊ぶことができました。

```
鳥の画像は、fu_ca.pngを用意したのでそれを使ってください
```

次にプロジェクト内に「フーカ」の画像を配置し、それを鳥の画像として利用するように依頼しました。
これもあっさりとなんの問題もなく実装してくれましたが、用意した画像の背景が透過されていなかったので、うまいこと透過処理をしてくれないか依頼しました。

```
fu_ca.pngの背景を透過させてください
```

返答

```
了解しました。fu_ca.pngの背景を透過させるには、画像自体を編集する必要があります。ただ、すでに画像ファイルが存在している場合、その画像自体を直接編
集することはできませんが、CSSを使って背景を透過させたように見せることができます。

・・・中略

1. Bird.cssの更新:
   • 画像の表示方法を最適化し、透過効果を高めるためのCSSプロパティを追加
   • image-rendering: -webkit-optimize-contrast を追加して画像の表示品質を向上
   • filter: drop-shadow(0 0 0 transparent) を追加して透過効果を強化

2. Bird.jsの更新:
   • 画像要素に直接 backgroundColor: 'transparent' スタイルを追加
   • これにより、画像の背景が透明になるよう指定

3. index.htmlの更新:
   • グローバルなスタイルとして、すべての画像に対して最適な表示設定を追加

これらの変更により、fu_ca.pngの背景が透過して表示されるようになります。ただし、完全な透過効果を得るためには、以下のいずれかの方法が最も効果的です
```

何やらできる風な返答で、cssもいじってくれていたのですが、実装後も背景は透過されていませんでした。
ここはまあ画像自体を透過するように修正。
以下のような感じですでに十分遊べるものになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1126770/559b4b5e-912f-4568-abb6-b3b3fa9beb6b.png)

ということでここからはアレンジを加えていく感じの依頼です。

```
背景が少し寂しい気がします。
夜の高層ビルを駆け抜けるような感じにしたいので、そういう背景にしてください。
```

なかなか無茶な依頼な感じがしますが、やってくれます。

```
このゲームの背景を夜の高層ビルの景色に変更したいとのことですね。
背景を夜の都市の高層ビルに変更して、より臨場感のあるゲーム体験にしましょう。

・・・中略
 
背景を夜の高層ビルの景色に変更しました！以下の変更を行いました：

1. ゲームの背景：
   • 青空から夜空のグラデーションに変更（濃い紺色から青紫色）
   • 高層ビルのシルエットを追加
   • ビルの窓を表現する光の点を追加し、きらめく効果をつけました

2. パイプのデザイン：
   • 緑色のパイプから夜景に合う暗いグレーのパイプに変更
   • パイプに窓のような模様を追加して、ビルの一部のように見せる効果を追加
   • シャドウ効果を追加して立体感を出しました

3. 鳥のキャラクター：
   • 夜空で目立つように光の効果を追加

これで、ゲームは夜の高層ビル街を飛び回るような雰囲気になりました。ビルのシルエットが下部に表示され、窓の光がきらめいています。パイプもビルの一部の
ようなデザインに変更し、全体的に夜の都市を飛ぶ臨場感が出ています。

ゲームを確認するには、以下のコマンドで開発サーバーを起動してください：

変更が気に入らない場合や、さらに調整したい部分があれば、お知らせください。
例えば、星空を追加したり、月を表示したりすることもできます。
```

> 夜空で目立つように光の効果を追加

曖昧な指示に対してこういう謎のこだわりを健気に入れてくるところがかわいいなと思いました。ただこれの結果はよく分からない背景が追加されていたので、修正を依頼しました。
（どう考えても指示が悪いのですが）

```
画面の下部にある黒い塊はちょっとなんなのかよく分からないから、消してほしいかな。
星空や月を追加するのは良いと思うから試してみてほしい。
```

```
背景を改善しました！
下部の黒い塊（高層ビルのシルエット）を削除し、
代わりに星空と月、そして流れ星のエフェクトを追加しました。以下が変更点です：

1. 星空の背景：
   • 夜空に複数の星を配置し、きらめく効果を追加
   • 星の大きさや位置をランダムに配置して自然な星空を表現

2. 月の追加：
   • 画面右上に大きな月を追加
   • 柔らかい光の効果を持つ月で、夜空の雰囲気を強調

3. 流れ星のエフェクト：
   • ゲームプレイ中にランダムに流れ星が現れるアニメーション効果を追加
   • 流れ星は画面上部からランダムな位置に出現し、斜め下に流れていきます
   • 約10%の確率で2秒ごとに出現するようにしています

これらの変更により、下部の黒い塊（高層ビル）がなくなり、
代わりに美しい星空と月の景色になりました。流れ星のエフェクトも加わり、
より幻想的な夜空の雰囲気になっています。
```

しかしこの修正によってbuildが通らなくなってしまったので、修正を依頼。

```
コンパイルに失敗してしまって起動できなくなってしまったので修正してください。
エラーは以下。

<出力されたエラー内容を貼り付け>
```

修正してもらい以下の画面になりました。
動画でないと分からないのですが、流れ星と月がいい感じになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1126770/e0807709-e5cf-4a18-bcff-d2b609531baa.png)

```
ゲームのBGMが流れるようにしてください。
ファイルはこれ。amega-furu-offvocal.mp3
ゲームが開いたら流し始めて、ずっと流しっぱなしで良いよ。終わったらリピートしてね。
```

次にBGMを追加してもらいました。
こちらもサクッと実装してくれます。
BGMには私が作った夜っぽい曲の歌を抜いたトラックを使っています。

[原曲はこれです。（宣伝）](https://www.youtube.com/watch?v=MnMMqnWiWUA
)

あとは細かな修正をいくつか実装してもらいました。
このあたりも基本的に指示一発でほぼ意図通りの実装をしてくれました。
すごい。

- もう少し流れ星が流れる頻度を増やしてほしい
- ゲームをスタートした後一度羽ばたくまでは真っ直ぐに飛んで障害物も発生しない状態にしてほしい
- BGMをループする時に2秒ほど空白を挟みたい
- ハイスコアをローカルリポジトリに残してほしい
- ゲームオーバーから間違って再開してしまうことがあるので、再開ボタンを押したときだけ再開するようにしてほしい

# 効果的なプロンプトテクニック
サンプルがたくさんありそうなものは◯◯風のゲームを作ってと指示するとかなりの精度で完成品が出てくるのではないかと思います。そこから細かな修正を指示してオリジナリティーを出して行くのが良いのかなと思いました。

その際抽象的な指示を出すと、よくわからないものが出てくることも多いのですが、うまくやってくれることもあるので、どんな出力をしてくれるのかを楽しみながら改善していくのが良いのかなと思いました。

# 時間を節約した開発自動化の例
今回はあまり工夫して取り組んだことはないかなと思っていますが、感じたことを書きます。

当然の話なのですが、コードを一切読まないと相当短い時間で開発ができる、ということを実感しました。またゲームの内容の改善にも集中できたと思います。

実際にリリースするようなゲームやアプリケーションを作る際は脆弱性に関わる部分や保守性など、色々な理由からコードを読む必要が出てくると思いますが、そういった点を自動検知するなどしていかに減らせるかがポイントなのかなということを感じました。

# コードの中身の話
さてここまで一切コードを読まずに作業してきましたが、中身についても触れるようにとのことだったので一応さらっと読んでみようと思います。
コードは以下リポジトリにも置いています。

https://github.com/gengen0719/flappy-fuca-prototype

# AIが古典的なプログラミング課題をどのように処理したか
メインになる処理は鳥の挙動の部分になると思いますが、以下のようなコードを生成してくれていました。

```React
  // Game loop
  useEffect(() => {
    if (gameStarted && !gameOver) {
      const gameLoop = () => {
        // Update bird position
        setBirdPosition((position) => {
          // 最初のジャンプ前は鳥の位置を固定
          if (!firstJump) {
            return GAME_HEIGHT / 2;
          }
          
          const newPosition = position + birdVelocity;
          
          // Check if bird hits the ground or ceiling
          if (newPosition >= GAME_HEIGHT - BIRD_HEIGHT || newPosition <= 0) {
            setGameOver(true);
            return position;
          }
          
          return newPosition;
        });
        
        // 最初のジャンプ後のみ重力を適用
        if (firstJump) {
          // Update bird velocity (gravity)
          setBirdVelocity((velocity) => velocity + GRAVITY);
        }
        
        // Update pipes position
        setPipes((currentPipes) => {
          return currentPipes
            .map((pipe) => {
              // Check collision
              const birdRect = {
                left: 50,
                right: 50 + BIRD_WIDTH,
                top: birdPosition,
                bottom: birdPosition + BIRD_HEIGHT,
              };
              
              const topPipeRect = {
                left: pipe.x,
                right: pipe.x + PIPE_WIDTH,
                top: 0,
                bottom: pipe.topHeight,
              };
              
              const bottomPipeRect = {
                left: pipe.x,
                right: pipe.x + PIPE_WIDTH,
                top: pipe.bottomY,
                bottom: GAME_HEIGHT,
              };
              
              // Check collision with pipes
              if (
                (birdRect.right > topPipeRect.left &&
                  birdRect.left < topPipeRect.right &&
                  birdRect.top < topPipeRect.bottom) ||
                (birdRect.right > bottomPipeRect.left &&
                  birdRect.left < bottomPipeRect.right &&
                  birdRect.bottom > bottomPipeRect.top)
              ) {
                setGameOver(true);
              }
              
              // Update score when bird passes a pipe
              if (!pipe.passed && pipe.x + PIPE_WIDTH < 50) {
                setScore((s) => s + 1);
                return { ...pipe, passed: true, x: pipe.x - PIPE_SPEED };
              }
              
              return { ...pipe, x: pipe.x - PIPE_SPEED };
            })
            .filter((pipe) => pipe.x + PIPE_WIDTH > 0);
        });
        
        frameRef.current = requestAnimationFrame(gameLoop);
      };
      
      frameRef.current = requestAnimationFrame(gameLoop);
      
      return () => {
        cancelAnimationFrame(frameRef.current);
      };
    }
  }, [gameStarted, gameOver, birdPosition, birdVelocity, firstJump]);
```
鳥に落下の加速度がかかる計算やランダムにパイプを出現させる処理を行いながらループする処理が実装されています。ゲーム制作の経験がないので自分で一から作成すると相当時間がかかったのではないかと思います。
ここでもAmazon Qと会話しながら説明してもらうことでコードの内容をスムーズに理解することができました。
Game.jsはコード量が多くなっているので分割はもう少しやりようがありそうだとは思いました。

# AIが生成した興味深いソリューションのコード例
背景の夜空や月は素材がない中でも表現できるように健気に頑張ってくれたなーと感じました。
Game.cssの一部
```css
/* 星空の効果 */
.game-container::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-image: 
    radial-gradient(1px 1px at 25px 5px, white, rgba(255,255,255,0)),
    radial-gradient(1px 1px at 50px 25px, white, rgba(255,255,255,0)),
    radial-gradient(1px 1px at 125px 20px, white, rgba(255,255,255,0)),
    radial-gradient(1.5px 1.5px at 50px 75px, white, rgba(255,255,255,0)),
    radial-gradient(2px 2px at 175px 15px, white, rgba(255,255,255,0)),
    radial-gradient(2px 2px at 20px 125px, white, rgba(255,255,255,0)),
    radial-gradient(2.5px 2.5px at 100px 150px, white, rgba(255,255,255,0)),
    radial-gradient(2px 2px at 300px 100px, white, rgba(255,255,255,0)),
    radial-gradient(1px 1px at 280px 30px, white, rgba(255,255,255,0)),
    radial-gradient(1.5px 1.5px at 300px 160px, white, rgba(255,255,255,0)),
    radial-gradient(1px 1px at 200px 220px, white, rgba(255,255,255,0)),
    radial-gradient(1px 1px at 380px 210px, white, rgba(255,255,255,0)),
    radial-gradient(1.5px 1.5px at 350px 95px, white, rgba(255,255,255,0));
  background-repeat: repeat;
  opacity: 0.8;
  z-index: 1;
  animation: twinkle 5s infinite alternate;
}

/* 月の効果 */
.game-container::after {
  content: '';
  position: absolute;
  top: 40px;
  right: 40px;
  width: 60px;
  height: 60px;
  background: radial-gradient(circle at 40% 40%, 
    rgba(255, 255, 230, 0.9) 0%, 
    rgba(255, 255, 230, 0.7) 50%, 
    rgba(255, 255, 230, 0) 100%);
  border-radius: 50%;
  box-shadow: 0 0 20px 10px rgba(255, 255, 230, 0.4);
  z-index: 2;
}
```

# おわりに
指示を出すとどんどん改善されていく体験が楽しかったです◎
素材もしっかり用意して、ゲームを作り込むというこも一度やってみたいなーと思いました。

おわり。