PICO-8 の公式サンプルで遊んでみよう (1) HELLO


# PICO-8 とは

[PICO-8](https://www.lexaloffle.com/pico-8.php) は「ファンタシー・コンソール」で、楽しく遊べる小さな仮想マシン環境です。まずは [PICO-8 公式ページ](https://www.lexaloffle.com/pico-8.php) を開き、トップページにあるデモで遊んでみると、だいたい感覚がつかめるかとおもいます。

有料($14.99)ですが、作成されたゲームやソフトウェアを実行するのは無料です。とてもシンプルな環境で、開発環境も実行環境も統合されているため、プログラミング入門には最適かとおもいます。

以下は関連リソースです。良質な入門記事が多い印象。

* [PICO-8 - Wikipedia (英語)](https://en.wikipedia.org/wiki/PICO-8)
* [PICO-8 日本語版マニュアル](https://github.com/kitao/pico8-jp/blob/master/README.md)
* [PICO-8初心者ガイド](https://pico-8-guide.com/)
* [PICO-8で遊ぼう！【PICO-8入門】](https://inwans.com/lets-try-pico-8/)
* [PICO–8って何？ – PICO–8ゲーム開発入門(1)](https://automaton-media.com/devlog/technical/pico-8-for-beginners-vol1/)


ちなみに似た方向性をもつ環境は幾つかあります。以前にご紹介した記事は以下なのですが、だいたいの雰囲気はつかんでいただけるかとおもいます。

* [無料でオープンソースな架空のレトロゲーム機「TIC-80」に関するまとめ](https://qiita.com/yamachan360/items/3544a96f0dbea31224be)
* [プチコン3号経験者によるプチコン4まとめ (4) スプライト機能を生かしたシューティングゲーム【前編】](https://qiita.com/yamachan360/items/77dd7bcf851a8f5f6b12)

# デモを試してみる

ネット上に数多くのPICO-8ソフト(カートリッジ)があるため忘れられがちですが、PICO-8 自体に幾つかのデモ(サンプルカートリッジ)が用意されています。

マニュアルから抜粋しますが、PICO-8 を起動し、コンソールから以下のコマンドを入力すると利用できます。

```
INSTALL_DEMOS
CD DEMOS
```

`DIR` すると一覧が表示されます。バージョンによって異なるかもしれませんが、以下のようなデモがあるはず。

|プログラム名|説明|
| --- | --- |
|HELLO|PICO-8からの挨拶|
|API|PICO-8の主要機能のデモ|
|JELPI|2プレイヤー対応のプラットフォーム型ゲームデモ|
|CAST|2.5Dレイキャスト描画デモ|
|MANDEL|マンデルブロー描画|
|COLLIDE|壁とキャラクターの衝突判定サンプル|
|BUTTERFLY|シェルピンスキーのフラクタル三角形描画|
|DRIPPY|液体のように滴る曲線の描画|
|STOMPY|音楽カートリッジ|

最初の `HELLO` デモを実行してみましょう。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/2c265cbc-34df-6e15-e701-f3162b913aaa.png)

実行すると以下のような表示になります。ESC キーで終了し、コンソールに戻ります。

![hello_0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/795e0978-cf98-1736-9cb1-5693a1c2dd30.gif)

衝突判定サンプルの `COLLIDE` なども楽しいですね。

![collide_0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/c5a3f2bb-5c48-fa9d-48d8-523cd50ac8be.gif)

# HELLO サンプル

いちばん簡単そうな `HELLO` サンプルの中身を見て、理解していきましょう。今回利用するのは PICO-8 v0.2.5g です。

## 元のコード

まずは HELLO サンプルの Lua ソースコードを見てみましょう。
```lua:HELLO
-- hello world
-- by zep

music(0)

function _draw()
	cls()
	
	-- for each color
	-- (from pink -> white)
	
	for col = 14,7,-1 do
		
		-- for each letter
		for i=1,11 do
		
			-- t() is the same as time()
			t1 = t()*30 + i*4 - col*2
			
			-- position
			x = 8+i*8     +cos(t1/90)*3
			y = 38+(col-7)+cos(t1/50)*5
			pal(7,col)
			spr(16+i, x, y)
		end
 end
	
	print("this is pico-8",
	 37, 70, 14) 
	print("nice to meet you",
	 34, 80, 12) 
	spr(1, 64-4, 90) -- ♥
end
```
そしてスプライト定義はこちらです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/21fbba3e-b8d4-b8ea-0206-40a73a631ecf.png)

## 短縮版のコード

上記コードからコメントと改行を削除してみました。結果、ギリで1画面に収まっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/7738680b-b4c2-61d3-3955-6bf57b4e9be2.png)

## 基本的な表示を理解してみる

`HELLO WORLD` 文字列のアニメーションが面白いわけですが、まずはこの動きを止めて、基本的な表示部分を確認しましょう。以下の赤枠の部分をコメントアウトします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/465fb13b-890f-c5ab-cd0c-ebe15dc6d709.png)
※ -- 以降がコメント文となり処理されません

`RUN` で実行すると、以下のようにアニメーションが止まった画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/a2e5fd55-91c1-c945-d18d-643cf7267dfe.png)

`HELLO WORLD` 以外の部分から見ていきましょう。左が短縮したコートで、右がその実行画面です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/9faafeba-5f92-70d8-80dc-d57e28b490e8.png)

① の `_DRAW()` 関数は PICO-8 での標準のお作法で、画面更新に関する表示ロジックはこの関数内で記述します。

>【補足】同様に定期的に呼ばれる関数に `_UPDATE()` がありますが「_draw関数は通常30fpsで呼ばれますが、間に合わない場合、PICO-8は15fpsで画面更新を行い、代わりに1回の表示ごとに_update関数を2回呼びます。」と説明があり、こちらは内部処理に適しているとおもわれます。

②と③はテキストメッセージの表示ですね。最後の引数が色の指定で、以下 [PICO-8 API 早見表](https://kitao.github.io/pico8-api-jp/) から転載します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/679a4373-ef45-3450-9442-1cdb0235ec19.png)

④ はスプライト表示ですね。最初の引数 `1` がハートのスプライト定義を指定しています。

## HELLO WORLD 表示部を理解してみる

さて、ちょっと複雑な `HELLO WORLD` 表示部をみていきましょう。ここですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/a3d27820-e7ae-58f9-a278-65843f8077a9.png)
※ 見やすくするためコメントアウトしたコードを削除しています

この表示部ですが、2段階の `FOR` ループで構成されていますね。最初(外側)のループは制御変数が `COL` で、値を「14から7まで」変化させています。これは表示色を制御しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/6951fa2a-6cb5-dd8a-b81e-4ad3f96c6658.png)

試しに、このループにおける値の変化を「14から7まで」でなく、「14から9まで」に変更してみましょう。上の表示色の表から、結果が予想できますか？実行してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/04e2bf40-2a24-184f-078f-b88e30a455cc.png)

予想通りでしたでしょうか？修正の結果、表示色7である白色と、表示色8である赤色の `HELLO WORLD` 表示が消えたことを確認できます。

> 【補足】このループでは -1 を指定して、「7から14まで」ではなく「14から7まで」の逆順でループを回しています。これは後で表示するほうが優先されるため、下から順に表示していき、白の `HELLO WORLD` 文字列が最後に表示される、つまり他の色に上書きされるようにしています。

さて、2番目(内側)のループは制御変数が `I` で、値を「1から11まで」変化させています。これは `HELLO WORLD` 各文字の表示を制御しています。間のスペースまで数えると、11文字ありますね。

試しに、このループにおける値の変化を「1から11まで」でなく、「1から8まで」に変更してみましょう。3文字ぶん減るわけですが、結果が予想できますか？実行してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/b56c6692-bf3f-3d2c-3eff-f683c2a6875a.png)

予想通りでしたでしょうか？修正の結果、`HELLO WORLD` の最後の `RLD` 部分が表示されなくなったことを確認できます。

もう少し見ていきましょう。各文字の横の表示位置 `X` を計算している部分に注目します。ここの `I*8` を `I*10` に変更したらどうなるでしょう？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/8e12aca4-3d9c-a57a-c9ea-4c1614dd0892.png)

結果として、`HELLO WORLD` の文字間隔が広がりましたね。右の実行画面のほうに注目して欲しいのですが、`H` 文字から次の `E` 文字までの距離を計算しているのが `I*8` の部分です。この幅が8ピクセルありました。ここを `I*10` と変更したので、この幅が10ピクセルに増え、結果として文字間隔が広くなったのです。

なお PICO-8 において、標準のスプライトの大きさは 8x8 ピクセルです。なので `I*8` で8ピクセルごとに横にズラして、画面上でちょうど良い感じで並ぶわけです。最初のスプライト定義部分をみるとわかるのですが、今回のサンプルにおいて、文字データは右の1ピクセルを使わないでデザインされています。なので `I*7` として7ピクセルごとに横に並べると、隙間ない感じで表示できたりします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/52a30c9b-6441-34bf-93ef-ce06021460d9.png)

この章はやたら長くなってしまいましたが、最後に各文字の縦の表示位置 `Y` を計算している部分もみておきましょう。各色の縦位置が1ピクセルしかズレておらず、重なっていますので、3ピクセルごとの表示にしてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/12f5b030-13a4-d442-2686-6e4fa2d3d117.png)

結果は想像通りでしたでしょうか？縦の差がかなり広がって、各色がわかりやすくなりましたね。

以上で、基本的な表示部分の説明は終わりです。

## HELLO WORLD を動かそう

さて、もう一度、完全な状態の `HELLO` デモを見てみましょう。`HELLO WORLD` の動きが面白いですよね。

![hello_0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/795e0978-cf98-1736-9cb1-5693a1c2dd30.gif)

さて、この動きは以下の計算部分で実現されています。`t()` もしくは `time()` は「最後に起動してからの経過秒を返す」関数です。

```lua
t1 = t()*30 + i*4 - col*2
x = 8+i*8     +cos(t1/90)*3
y = 38+(col-7)+cos(t1/50)*5
```

まず、この 't()' 関数の影響を無くしてみましょう。`T()*30` とある部分を `T()*0` と書き換えます。0を掛けることで値は常に0となり、プログラム実行直後の状態から時間が進まない感じになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/837c70ec-3a39-07ed-b6d6-5cc3381f9cbb.png)

さて、ここからは実際のロジックの説明になるのですが、三角関数(`sin()`, `cos()`) の説明からはじめると更に長くなってしまいます。ここでは三角関数を「-1 から 1 の範囲で移動する値を返す関数」としてとらえましょう。

具体的には、ある座標 `X` を求めるときに、以下のようなロジックが良い感じで働く、ぐらいの理解でいきましょう。

```lua
-- T: 時間変化
-- DT: 変化速度(大きいほど遅い)
-- C: 中心点となる座標
-- W: 移動できる範囲
X = C + COS(T / DT) * W
```

例えば今回のサンプルで `DT` に相当する部分を倍にしてみると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/a17156ac-8165-3273-41d3-57c5a4d32295.png)

動作が1/2にスローダウンするのがわかるとおもいます。

![hello_0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/d0ace4ca-6dc7-bca3-3582-b4bf275cc64e.gif)

> 【補足】この例はちょっと手抜きで、本来はその前にある部分もあわせて `T1 = T()*30 + I*8 - COL*4` と修正しないと動きの幅が減ります

さて、せっかく三角関数を使っているので、最後は、その特徴を生かした修正をして遊んでみましょう。`X` のほうを `sin()` 関数にして、変化量も `Y` とあわせてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/7e273281-b896-2847-faa6-57830a7f0109.png)

`SIN()` と `COS()` の組み合わせ、三角関数といえば円運動ですよね！

![hello_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/6fe4f8aa-b668-ee32-620d-06a2b18fbbb3.gif)

皆さんも、この `HELLO` サンプルの `X` `Y` 座業の計算式など変更して、いろいろ遊んでみてください。

# というわけで

PICO-8 楽しいですよね。

`HELLO` サンプルの説明だけで今回の投稿が終わってしまいました。プログラミング初心者の方を想定して書いてみたので、経験者の方には冗長、かつあまり情報量はなかったと思います…

PICO-8 ぐらいの限定された環境のほうが、プログラミング入門には適している気がしています。ですので興味があれば、ぜひ購入して、いろいろ試していただければとおもいます。

私も学習を兼ねて、引き続き PICO-8 公式サンプルを読み解いていく予定です。余裕があれば、今回のように簡単な解説を書くかもしれません。あまり期待せずお待ちいただければ幸いです。

それではまた！