Jasmine Teaでビーム!

この記事は、 [Jasmine Tea アドベントカレンダー 2023](https://qiita.com/advent-calendar/2023/jasminetea) の第16日目です。

# はじめに

[Jasmine Tea](https://jasminetea.app) (ジャスミンティー) は、プログラミングの楽しさを多くの人に知ってもらうために作られたプログラミング言語です。初心者が学びやすい仕組みで、実際に社会で使われている多くのプログラミング言語に応用できる基礎が身に付きます。

ここでは、Jasmine Teaを実際に開いて、ゲームで使えるちょっと実践的なプログラミングをしたいと思います。

まず、Studio 画面を開いてください。もし Studio 画面をまだ開いたことがなければ、 [Studio画面を表示する - Jasmine TeaでHello, world!](https://qiita.com/yoichiro6642/items/3dc478726f4f73941b46#studio%E7%94%BB%E9%9D%A2%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B) を参考にして Jasmine Tea の試用を開始してください。

# どんなものを作るの？

いろいろなゲームが楽しまれていますが、そのジャンルの中に「シューティングゲーム」があります。自機を操作して、ビームを放ち、敵をやっつけていく、そんなゲームです。ちゃんと遊べるゲームをプログラミングするのは比較的大変ですが、基本的なテクニックが身についていれば、その組み合わせで様々なゲームが作れるようになります。

ここでは、自機からビームを放つことができる、それだけができるプログラムを作ってみたいと思います。仕様（どんなものなのか）は、以下です。

* 自機から発射できるビームは、5発まで。
* PCであれば実行画面をマウスでクリック、iPadであれば実行画面を指でタップしたときに、ビームが発射される。
* 左に自機のロケットがあり、その先端からビームが発射され、実行画面の右端まで移動したらビームは消える。

![スクリーンショット 2023-12-16 7.08.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632303/e18e676e-c10c-f7ca-ec33-008cd7a35369.png)

自機の横幅が 32 なので、ビームが発射される位置の横の座標も 32 です。そこから実行画面の右端まで移動するので、移動距離は `実行画面の横幅 - 自機の横幅 - ビームの横幅` となり、具体的には `640 - 32 - 16 = 592` がビームの移動距離です。

# スプライトを準備しよう

まずやることは、自機のロケットとビーム5発分のキャラクターを、スプライトに割り当てる作業です。Studio 画面のエディターの下にある「スプライト」ボタンを押して、スプライトダイアログを表示します。

最初に、スプライトダイアログの右に並んでいるキャラクターから、下の方にある右向きのロケット（67番）を探します。そして、そのロケットのキャラクターをドラッグして、左にある「スプライト 0」と書かれた部分の左にドロップします。

![スクリーンショット 2023-12-16 7.12.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632303/ea63ec9c-8f8a-13e6-52bc-809c1582aa72.png)

次に、ビーム（72番）のキャラクターを、スプライト 1 からスプライト 5 までの左の部分に、同じくドラッグアンドドロップします。

![スクリーンショット 2023-12-16 7.15.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632303/cc2e7f48-0283-8308-f0b9-807226d4eae5.png)

これでスプライトの準備は完了です。「閉じる」ボタンを押して、スプライトダイアログを閉じてください。

# 自機を表示してビームの移動設定をしよう

ここからはプログラムを書いていきましょう。[`show` 命令](https://jasminetea.app/docs/references/command-show) を使って、自機のロケットを実行画面に表示することから始めます。エディターに以下のプログラムを入力してください。

```vb
show 0,(0,150)
```

このプログラムを実行すると、画面の中央左にロケットが表示されます。

![スクリーンショット 2023-12-16 7.20.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632303/d60f08b2-05f4-f960-fd9b-0058dccf8ccd.png)

次に、ビーム5発分のスプライトの移動設定をします。5発全て、以下のように設定します。

* 移動距離: 592
* 移動速度: 最速（20）
* 移動方向: 右（3）

[`direction` 命令](https://jasminetea.app/docs/references/command-direction)、[`speed` 命令](https://jasminetea.app/docs/references/command-speed)、[`distance` 命令](https://jasminetea.app/docs/references/command-distance)をそれぞれ5回プログラムしても良いのですが、[`for` 命令](https://jasminetea.app/docs/references/command-for) を使って繰り返しのテクニックを使うことで、同じようなプログラムを何度も書く必要がなくなりますので、ここでは楽をしましょう。

以下のプログラムをエディターの2行目から入力してください。

```vb
for beam=1 to 5
  distance beam,592
  speed beam,20
  direction beam,3
next
```

`for` 命令のパラメーターで指定しているループ変数 `beam` には、 `1` から `5` まで順に数が代入されます。その数を `distance` 命令、`speed` 命令、`direction` 命令の一つ目のパラメーターに利用することで、ビームの各スプライトの移動設定をするプログラムを簡潔に書くことができました。

そして、メインループも必要になりますので、7行目から以下のプログラムを入力してください。

```vb
do
loop
```

[`do` 命令](https://jasminetea.app/docs/references/command-do) と [`loop` 命令](https://jasminetea.app/docs/references/command-loop) を使って、無限ループを作っています。

# スプライトの状態遷移を把握しよう

水は、常温だと液体、冷やすと氷になり、熱すると水蒸気になります。同じ水でも、その状態が変化します。あるものの状態が変化することを「状態遷移」と呼びます。実は、Jasmine Tea におけるスプライトも、状態遷移が起きます。

今回のビームでは、以下の状態遷移となります。

![スクリーンショット 2023-12-16 7.45.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632303/3c295dfd-935b-04b9-18ce-d63c3f5a6fff.png)

Jasmine Tea では、スプライトを使ったプログラムを作るときには、

* スプライトの状態を変化させる
* スプライトの状態を確認して何かする

という2つのことを、メインループの中で行っていきます。

今回のビームについて、もう少し具体的に説明すると、以下のようになります。

1. ビームの発射前は、ビームのスプライトが画面に表示されていない状態。
1. 実行画面がクリックまたはタップされたときは、ビームのスプライトを画面に表示して、移動を開始する。
1. ビームが表示されていて、しかも停止している状態のときは、ビームが移動を終了したと見なして、ビームのスプライトを画面から消去する。

一つずつプログラムで書いていきましょう。

# ビームの発射処理をプログラムしよう

ビームの発射処理は、実行画面上をクリックまたはタップされたかどうかを確認することから始めます。実行画面上をクリックまたはタップされたかどうかを知るには、[`tap` 命令](https://jasminetea.app/docs/references/command-tap) を使います。

エディター内の `do` 命令が書かれた行の次の行から、以下のプログラムを入力してください。

```vb
  tap x,y
  if x<>-1 then
  end if
```

`tap` 命令では、

* もし実行画面上がクリックまたはタップされていたときは、パラメーターで書かれた変数にクリックまたはタップされた座標がそれぞれ代入される。
* もし実行画面上がクリックまたはタップされていなかったときは、パラメーターで書かれた変数に`-1` が代入される。

という動作になります。つまり、[`if` 命令](https://jasminetea.app/docs/references/command-if) を使って、条件式で「クリックまたはタップされた位置が `-1` ではなかったとき」、つまり `x<>-1` が成り立つときは、実行画面がクリックまたはタップされたと判断できます。

クリックまたはタップされていたときは、「まだ発射されていないビームのスプライトを表示して移動を開始する」ということを行います。まずは先にそのためのプログラムを紹介しましょう。エディター内で `if x<>-1 then` と書かれた行の次の行から、以下のプログラムを入力してください。

```vb
    for beam=1 to 5
      if not visible(beam) then
        show beam,(32,158)
        move beam
        exit for
      end if
    next
```

最初にスプライトの移動設定を行ったときと同じように、`for` 命令を使って、ループ変数 `beam` に `1` から `5` までを代入させています。そして、`if` 命令を使って、ビームの各スプライトが画面にまだ表示されていないかどうかを確認しています。[`visible` 関数](https://jasminetea.app/docs/references/function-visible) の引数にスプライト番号を指定してあげることで、そのスプライトが実行画面に表示されているかどうかを確認することができます。今回の場合は「表示されていなければ、条件式を成立させる」という確認をしたいので、`not` を使います。結果として、`not visible(beam)` という条件式によって、まだ表示されていないスプライトの番号を探すことができます。

もし表示されていないビームのスプライトがあったときは、[`show` 命令](https://jasminetea.app/docs/references/command-show) のパラメーターに変数 `beam` に代入されている値と、表示位置の座標を指定して、ビームのスプライトを表示します。そして、[`move` 命令](https://jasminetea.app/docs/references/command-move) 命令に同じく変数 `beam` に代入されている数を指定して、そのビームのスプライトの移動を開始します。

エディターには、以下のようにプログラムが入力されているかと思います。

![スクリーンショット 2023-12-16 8.15.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632303/e85852ce-68a4-d6b4-b4f4-e979b84a6358.png)

では、実行してみましょう。エディターの下にある青色の「実行」ボタンを押して、プログラムを実行してください。

![beam-1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632303/ee3e1d72-76a6-82dc-a8e4-049fcb8b32d7.gif)

実行画面をクリックまたはタップすると、ちゃんとビームが発射されるようになりました。しかし、発射されたビームは全て実行画面の右端で止まっていて、表示されっぱなしです。また、5回クリックやタップしてしまうと、あとはクリックやタップしても何も起きません。

# ビームの消去処理をプログラムしよう

ビームが発射された後に、ビームのスプライトは移動距離分だけ移動して、移動を停止します。ビームのスプライトの状態遷移では、最後は以下のように書かれていました。

* ビームが表示されていて、しかも停止している状態のときは、ビームが移動を終了したと見なして、ビームのスプライトを画面から消去する。

上記を満たすために、メインループの中で、「表示されていて、しかも停止している状態のビームのスプライトがあるかどうか」をチェックするプログラムを追加しましょう。

エディター内の17行目の次の行から、以下のプログラムを入力してください。

```vb
  for beam=1 to 5
    if visible(beam) and not moving(beam) then
      hide beam
    end if
  next
```

エディターには以下のように入力されているはずです。19行目は、見た目上は2行に渡って書かれていますが、実際には1行です。

![スクリーンショット 2023-12-16 9.30.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632303/737488b0-dce4-ab32-c041-6a47fb8e94c4.png)

`for` 命令にて、再び変数 `beam` に `1` から `5` まで代入して繰り返しています。そして、`if` 命令を使って、以下の条件式が成り立つかどうかを確認しています。

* `visible(beam) and not moving(beam)`

`visible` 関数によって、パラメーターで指定された番号のスプライトが表示されているかどうかを確認しています。そして、`and` によって、さらに `not moving(beam)` によって、パラメーターで指定された番号のスプライトが停止しているかどうかを確認しています。[`moving` 関数](https://jasminetea.app/docs/references/function-moving) は、スプライトが移動中のときに成り立つ関数です。それに対して `not` を指定しているので、「移動していないかどうか = 停止しているかどうか」という確認になります。

上記の条件式が成立していれば、そのスプライトは「実行画面の右端まで移動し終えた」となりますので、[`hide` 命令](https://jasminetea.app/docs/references/command-hide) を使って、スプライトを実行画面から消去します。

これで、ビームのスプライトの状態遷移がプログラムで書けました。プログラムを実行してみましょう。「実行」ボタンを押して、プログラムを実行してください。

![beam-2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632303/7e74464c-8cf1-89a0-a7da-ee4f7af20ecf.gif)

実行画面をクリックまたはタップし続けることで、次々とビームを発射できるようになりました。

# 明日はどんなお話？

今日は、スプライトの状態遷移とそれに応じた処理をプログラムで扱う方法を紹介しました。今回はビームを飛ばしましたが、基本的には「非表示、表示、移動中、停止中」という各状態になったらどうするかを考え、そして次の状態に変化させていくことで、スプライトを自由自在に扱うプログラムを書くことができます。

ちなみに、ビームが「表示されていて、移動中」のときは、敵のスプライトとの当たり判定をしてあげることで、シューティングゲームが完成にほぼ近づきます。「敵を表示して動かす」ことと「ビームと敵の当たり判定」を追加して、ぜひシューティングゲームを完成させてみてください。

明日は、 @shio3oi さんによる記事が投稿される予定です。どんなお話になるのか、楽しみですね！