【Pyxel】16色を超える色を使ってのゲーム作り

「Pyxel (ピクセル) は、 Python 向けのレトロゲームエンジンです。使える色は 16 色のみ、」と書かれているのはPixel冒頭の文言ですが、実は16色を超える色を使うことができます。

[Pyxelのユーザー作品集](https://github.com/kitao/pyxel/wiki/Pyxel-User-Examples)に以下の作品を公開しています。
![pyxelblk20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927925/63742161-bc5a-4382-a541-078932c07738.png)
ブロック崩しタイプのゲームです。
Pyxelで16色を超える色を使って作りました。

ここではその作り方について書いていこうと思います。

# はじめに

Pyxelの仕様は
[README.ja.md](https://github.com/kitao/pyxel/blob/main/docs/README.ja.md)
に書かれている通り、
> ・Windows、Mac、Linux、Web で動作
> ・Python によるプログラミング
> ・16 色パレット
> ・256x256 サイズ、3 イメージバンク
> ・256x256 サイズ、8 タイルマップ
> ・4 音同時再生、定義可能な 64 サウンド
> ・任意のサウンドを組み合わせ可能な 8 ミュージック
> ・キーボード、マウス、ゲームパッド入力
> ・画像・サウンド編集ツール
> ・パレット、発音数、各種バンクのユーザー拡張

![PyxelColorPalet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927925/3c042528-b038-873a-4434-954cb9539ada.png)

仕様上「16 色パレット」だけど「パレット、発音数、各種バンクのユーザー拡張」と書かれています。パレットはユーザー拡張できるようですが具体的な方法は書かれていません。

Pyxel開発者がQiitaに書いた記事[【公式】レトロゲームエンジンPyxelを使わない理由が見つからない](https://qiita.com/kitao/items/eae53dd47c663b497352)では、
> 「違う色が使いたいし、16 色じゃ全然足りない」
> ファミコンですら52色使えますものね。
> Pyxelはパレットの色、数共に変更可能です。プログラムから直接変更もできますし、.pyxpal形式の別ファイルを用意して、そこに色を列挙することでまとめて変更することもできます。
> 一番簡単な方法は、画像を読み込むload関数にincl_colorsオプションを指定することでしょうか。

とありますが、画像を読み込まずに16色を超える色を使うにはどうすれば良いかということは書かれていません。16色でもそれなりに表現はできますが、それ以上の色が使えればゲームが映える事間違い無しです。

以下から実際にPyxel開発する上での16色を超える色を使う方法を書いていきます。注意点ですが、ここではPyxelの開発環境がすでに手元にあるという前提で書いています。

#  .pyxpal形式のファイル

リソースを作成するエディタ（以後、Pyxel Editorと記述）では16色しか使えません。また、Pyxel Editor上で色データを編集することはできません。既存の色を変更するには、.pyxpal形式のファイルを使う必要があります。

.pyxpal形式のファイルというのは、前述のPyxelの仕様にも記述がありますが
> リソース
> load(filename, [excl_images], [excl_tilemaps], [excl_sounds], [excl_musics])
> リソースファイル (.pyxres) を読み込みます。オプションにTrueを指定すると、そのリソースは読み込まれません。また、同名のパレットファイル (.pyxpal) がリソースファイルと同じ場所に存在する場合は、パレットの表示色も変更されます。パレットファイルは、表示色を改行区切りの 16 進数 (例：1100FF) で入力します。パレットファイルを使うことで Pyxel Editor の表示色も変更可能です。

この説明だけではわかりにくいのでその方法を書いていきます。

「pyxel edit」と入力してPyxel Editorを起動、何も編集せずに「保存」だけすると
my_resource.pyxres
というファイルができますが、.pyxpal形式のファイルは作成されません。

まずはデフォルトカラーの.pyxpal形式のファイルを作成してみます。

前述のPyxelの仕様に書かれたパレットの情報をもとにテキストエディタ等で以下の内容を記述。（１６進数の数値ですが、「0x」は不要です）
```
000000
2b335f
7e2072
19959c
8b4852
395c98
a9c1ff
eeeeee
d4186c
d38441
e9c35b
70c6a9
7696de
a3a3a3
ff9798
edc7b0
```
これを先ほどのPyxel Editorで作成したリソースファイル名と同じ名前の.pyxpal形式のファイルとして保存します。

先ほど作成したリソースファイルのファイル名は「my_resource.pyxres」でしたので、ファイル名を「my_resource.pyxpal」とします。保存形式は、仮に「UTF-8、改行LF、BOM無し」とします。（Shift-JISでも大丈夫かもしれません）

この後、「pyxel edit」と入力してPyxel Editorを起動してみると、先ほど起動したPyxel Editorとパレットの色に変化は無いのがわかります。今ここで作成したmy_resource.pyxpalの色の値を編集（変更）してPyxel Editorを起動しなおすとパレットの色が変わることが確認できます。

ここまでがPyxel Editorの色を変更する方法となります。

# 16色を超える色の設定と使い方

Pyxel Editor上では上記で記述した通りの16色しか使えません。

pyxpalファイル内の17行目以降を記述してもPyxel Editorは起動できますが、Pyxel Editor上では最初の16行しか使えません。リソースのイメージバンクを使った出力方法（bltコマンド）では色指定はできないので、この最初の16色しか使えないことになります。

上で記述した通り、.pyxpal形式のファイルの内容は17行目以降にも記述することが可能です。イメージバンクを使った出力方法では反映されないだけで使うことはできます。

ではどうするか。

前述のPyxelの仕様のグラフィックスには色指定できるAPIがいくつか見られます。ここでは、「16色を超える色を使ってのゲーム作り」をしたいのでドット絵を描画するためにpsetコマンドを使用します。

psetコマンドとは、
> pset(x, y, col)
> (x, y) に色colのピクセルを描画します。

つまり１ドットの点を描画するAPIです。

ここで指定する色colは、0～254まで指定可能です。（255は指定できません、指定して実行するとエラーになります。Pyxelのソースを覗くと「MAX_COLORS=255」となっているので0～254までの255色が最大値のようです）

つまり、.pyxpal形式のファイルには最大255行まで使用できることになります。（256行以上指定するとエラーになります）

ここからはPyxel Editorだけを使って「16色を超える色を使ってのゲーム作り」の方法を記述します。
※256色使える描画ツールを使えばもっと効率的に色を自由に使えるはずですが、かなり面倒になるのでここでは割愛します。

最初に紹介したブロック崩しのブロックを例にして説明します。このゲーム内では設定にてデフォルトカラーのみの場合と16色を超える色を使う場合を切り替えられるようにしています。

![pyxelblk_pair.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927925/57e1c258-8e0c-9349-29ff-31e01050c640.png)
左側が16色を超える色を使用、右側がデフォルトカラーのみの場合

※ブロック崩しでは背景の模様にも16色を超える色を使用しています。今回紹介する記事を応用するとこういうこともできます。

まず、ブロックをPyxel Editorのデフォルト色で作ってみます。
![PyxelEditor_Def.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927925/e7c6b45c-a949-b044-2659-679b4eaa41c6.png)

これをもう少し立体的にきれいに描きたいと思った時、グラデーションの色が必要になります。グラデーションの色を設定すると16色だけでは２、３種類しか描けないことになってしまいます。また、ゲームの中にはブロックだけでなくいろいろな描画物があり、ブロック以外も色を使います。

そんな時、ブロック専用の色データを追加してあげればいいことになります。

デフォルト色を使ってブロック以外を描画すれば、ブロック以外の表示物はイメージバンクを使用した従来の描画方法が使えます。
もちろん背景データもデフォルト色以外を使う方法もありますが、ここではブロックのみについてで記述します。

最大255色、デフォルトカラーで16色使っているので、239色が自由に設定できます。
今回はグラデーション１色に対して16色を割り当てようとしているので最大１４種類の色とすると都合が良いと思います。ブロック崩しでは、赤、緑、青、黄、水、紫、橙、桃、灰、白の10種類を使っています。

まず１色の作り方を説明します。他の色も同様に作成できます。
赤色をベースに６段階のグラデーション色を準備します。
※段階数は最大15段階指定可能なのでグラデーションの段階数を細かくすればもっとリアルに描くこともできます。
```
000000
FFE0E0
FF0000
AA0000
880000
220000
660000
000000
000000
000000
000000
000000
000000
000000
000000
000000
```
これをmy_resource.pyxpalに保存してPyxel Editorを起動、空いたスペースで立体的できれいなブロックを描きます。

![PyxelEditor_Multi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927925/6486af96-cc4d-a7b2-88bf-f5679920887f.png)

描けたらリソースを保存・・・ではなくて、ドット絵からテキストを作成します。（バックアップとして保存するのは構いません）

前述の通り、「16色を超える色」を使うにはイメージバンクからは対応できないのでPyxelAPIのpsetコマンドを使う必要があります。なので、立体的できれいなブロックはプログラムを使って描画します。

ブロックの大きさは、横16ドット、縦9ドット、色データを0～15で配列データ（リスト）を作成します。
```
#MULTI BLOCK
#	0 1 2 3 4 5 6 7   8 9 a b c d e f
#0	□■■■■■■■　■■■■■■■□
#1	■■■■■■■■　■■■■■■■■
#2	■■■■■■■■　■■■■■■■■
#3	■■■■■■■■　■■■■■■■■
#4	■■■■■■■■　■■■■■■■■
#5	■■■■■■■■　■■■■■■■■
#6	■■■■■■■■　■■■■■■■■
#7	■■■■■■■■　■■■■■■■■
#8	□■■■■■■■　■■■■■■■□

#データ２つで１ドットを示す
#１データ目：上位４ビットがX方向のドット位置
#          ：下位４ビットがY方向のドット位置
#２データ目：色データ（0x00から0x0f、ここでは６段階なので0x01から0x06を指定）
#ドットの無いところは記述しない（描画しない）
#終端は0xff

mblk_tbl = [
	0x10, 0x1, 0x20, 0x2, 0x30, 0x2, 0x40, 0x2, 0x50, 0x2, 0x60, 0x2, 0x70, 0x2, 0x80, 0x2, #0
	0x90, 0x2, 0xa0, 0x2, 0xb0, 0x2, 0xc0, 0x2, 0xd0, 0x2, 0xe0, 0x3, 0x01, 0x1, 0x11, 0x1, 
	0x21, 0x1, 0x31, 0x2, 0x41, 0x2, 0x51, 0x2, 0x61, 0x2, 0x71, 0x2, 0x81, 0x2, 0x91, 0x2, #1
	0xa1, 0x2, 0xb1, 0x2, 0xc1, 0x2, 0xd1, 0x3, 0xe1, 0x3, 0xf1, 0x3, 0x02, 0x2, 0x12, 0x2, 
	0x22, 0x3, 0x32, 0x3, 0x42, 0x3, 0x52, 0x3, 0x62, 0x3, 0x72, 0x3, 0x82, 0x3, 0x92, 0x3, #2
	0xa2, 0x3, 0xb2, 0x3, 0xc2, 0x3, 0xd2, 0x3, 0xe2, 0x6, 0xf2, 0x6, 0x03, 0x2, 0x13, 0x2, 
	0x23, 0x3, 0x33, 0x3, 0x43, 0x3, 0x53, 0x3, 0x63, 0x3, 0x73, 0x3, 0x83, 0x3, 0x93, 0x3, #3
	0xa3, 0x3, 0xb3, 0x3, 0xc3, 0x3, 0xd3, 0x3, 0xe3, 0x6, 0xf3, 0x6, 0x04, 0x2, 0x14, 0x2, 
	0x24, 0x3, 0x34, 0x3, 0x44, 0x3, 0x54, 0x3, 0x64, 0x3, 0x74, 0x3, 0x84, 0x3, 0x94, 0x3, #4
	0xa4, 0x3, 0xb4, 0x3, 0xc4, 0x3, 0xd4, 0x3, 0xe4, 0x6, 0xf4, 0x6, 0x05, 0x2, 0x15, 0x2, 
	0x25, 0x3, 0x35, 0x3, 0x45, 0x3, 0x55, 0x3, 0x65, 0x3, 0x75, 0x3, 0x85, 0x3, 0x95, 0x3, #5
	0xa5, 0x3, 0xb5, 0x3, 0xc5, 0x3, 0xd5, 0x3, 0xe5, 0x6, 0xf5, 0x6, 0x06, 0x2, 0x16, 0x2, 
	0x26, 0x3, 0x36, 0x3, 0x46, 0x3, 0x56, 0x3, 0x66, 0x3, 0x76, 0x3, 0x86, 0x3, 0x96, 0x3, #6
	0xa6, 0x3, 0xb6, 0x3, 0xc6, 0x3, 0xd6, 0x3, 0xe6, 0x6, 0xf6, 0x6, 0x07, 0x3, 0x17, 0x3, 
	0x27, 0x3, 0x37, 0x4, 0x47, 0x4, 0x57, 0x4, 0x67, 0x4, 0x77, 0x4, 0x87, 0x4, 0x97, 0x4, #7
	0xa7, 0x4, 0xb7, 0x4, 0xc7, 0x4, 0xd7, 0x5, 0xe7, 0x5, 0xf7, 0x5, 0x18, 0x3, 0x28, 0x4, 
	0x38, 0x4, 0x48, 0x4, 0x58, 0x4, 0x68, 0x4, 0x78, 0x4, 0x88, 0x4, 0x98, 0x4, 0xa8, 0x4, #8
	0xb8, 0x4, 0xc8, 0x4, 0xd8, 0x4, 0xe8, 0x5, 0xff]
```

CODEフォーマットで書くとわかりにくいので以下に図示しました。

![ドット説明.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927925/08d1bc98-1303-6eea-d839-c12557532aa9.png)

これを下記の関数を使ってブロックを適宜描画します。

```
#ドットパターン描画関数
#_dx : X座標
#_dy : Y座標
#_tp : カラータイプ（1～）
#_adr: ドットパターンテーブル（ここでは上で作成したmblk_tblを指定）
def dot_pattern( _dx, _dy, _tp, _adr ):
	_cnt = 0
	while( _adr[ _cnt * 2 + 0 ] != 0xff ):
		_xp = pyxel.floor( _adr[ _cnt * 2 + 0 ] / 0x10 )
		_yp = pyxel.floor( _adr[ _cnt * 2 + 0 ] & 0x0f )
		_col = _adr[ _cnt * 2 + 1 ] + _tp * 0x10
		pyxel.pset( _dx + _xp, _dy + _yp, _col )
		_cnt += 1
```
カラータイプの指定に関して、下記コードで色データを作成しています。
```
_col = _adr[ _cnt * 2 + 1 ] + _tp * 0x10
```
としている通り、色データに0x10を乗算しています（_tp値は1以上で）
0x10で乗算するのはそれぞれの色を16色で作成して、16色ごとに区切って使用するためです。

my_resource.pyxpalは、デフォルトカラーをそのままに17行目以降に色データを追加します。デフォルトカラーは背景やフォント等に使用したいので最初の16色は変えないようにします。（変更することも可能ですがここでは考えません）

デフォルトカラーを.pyxpalファイルの１行目から１６行目（色番号は、0x00から0x0f）
グラデーションカラーを.pyxpalファイルの１７行目から３２行目（色番号は、0x10から0x1f）
以後同様に色データを0x10(16行)単位で追加作成します。

なので、先ほど作成したグラデーションカラーとデフォルトカラーを合わせます。
デフォルトカラーの後ろにグラデーションカラーを追加します。
```
000000
2b335f
7e2072
19959c
8b4852
395c98
a9c1ff
eeeeee
d4186c
d38441
e9c35b
70c6a9
7696de
a3a3a3
ff9798
edc7b0
000000
FFE0E0
FF0000
AA0000
880000
220000
660000
000000
000000
000000
000000
000000
000000
000000
000000
000000
```
途中に空行を入れてはいけません。空行があるとエラーになります。

同様に他の色のグラデーションも赤のグラデーションに合わせて作成してmy_resource.pyxpalの「赤のグラデーション色」の後に追記します。そうすると上記プログラムコードのカラータイプ（_tp）を指定すれば複数色のグラデーションカラーのブロックが表示できることになります。

# 最後に

ブロックなど簡単な形状のものを使って色変化だけで作るならこの方法で良いと思います。256色画像エディタがあればもっといろんなことができるとは思いますが、テキスト化や描画プログラムの変更等かなりたいへんな作業になるのではないかと思います。コンバーターを作ればいいのかもしれませんが、psetコマンドで１ドットずつの描画なのである程度の処理負荷がかかります。なので作成するゲームも限られるとは思いますが、使用することで見栄えは確実に良くなります。

説明が長くなってしまいましたが、ご理解できたでしょうか？
16色を超える色を使ってのゲーム作りはアイデア次第かなと思います。
作品作りの参考になれば幸いです。