nana music マーケティングチームの @usangit です。
最近話題になった Pyxel を使って遊んでみようと思います。
Pyxel とは?
Python 向けのレトロゲームエンジンです。
https://github.com/kitao/pyxel
ファミコン世代には懐かしいあのレトロゲームを Python で作れるということで、本記事では Pyxel の作曲機能に注目してみます。
付属の Pyxel Editor を使用することで GUI を使用して画像やサウンドを作成することが出来ますが、今回は Python コードのみでレッツチャレンジ!
まずは出来上がったサウンドを聴いてみる
懐かしのファミコン曲を Pyxel で打ち込んでみました。1
https://nana-music.com/sounds/046ddc00/
<a href="https://nana-music.com/sounds/046ddc00/"
コードはこんな感じ
サウンドの一部のみ抜粋、引用2
import pyxel
class App:
def __init__(self):
pyxel.init(200, 150, caption="Sound API")
# ch0 前半
pyxel.sound(0).set(
"c2re2rg2rc3c3 c3c3b2a2g2f2e2d2",
"p",
"6",
"n",
20,
)
# ch0 後半
pyxel.sound(1).set(
"c2re-2rg2rc3c3 c3c3b2c3d3c3b2d3",
"p",
"6",
"nnnnnnnn nnnnnnnn",
20,
)
# ch1 前半
pyxel.sound(2).set(
"c1c1rrrrrr g1g1g1g1g0g0g0g0",
"t",
"6",
"n",
20,
)
# ch1 後半
pyxel.sound(3).set(
"rrrrrrrrrrrrrrrr",
"t",
"6",
"n",
20,
)
# ch2 前半
pyxel.sound(4).set(
"e1e1rrrrrr rrd2c2b1a1g1b1",
"s",
"3",
"n",
20
)
# ch2 後半
pyxel.sound(5).set(
"rrrrrrrrrrrrrrrr",
"p",
"5",
"nnnnnnnn nnnnnnnn",
20
)
self.is_playing = [True] * 3
self.play_music(True, True, True)
pyxel.run(self.update, self.draw)
def play_music(self, ch0, ch1, ch2):
self.is_playing = (ch0, ch1, ch2)
if ch0:
pyxel.play(0, [0, 1], loop=True)
else:
pyxel.stop(0)
if ch1:
pyxel.play(1, [2, 3], loop=True)
else:
pyxel.stop(1)
if ch2:
pyxel.play(2, [4, 5], loop=True)
else:
pyxel.stop(2)
def update(self):
if pyxel.btnp(pyxel.KEY_Q):
pyxel.quit()
if pyxel.btnp(pyxel.KEY_SPACE):
if True in self.is_playing:
self.play_music(False, False, False)
else:
self.play_music(True, True, True)
def draw(self):
pyxel.cls(0)
App()
- 作曲にはオーディオ API のサウンドクラスを使用
- Pyxel で使用できるチャンネルは ch0 〜 ch3 の計4チャンネルでチャンネルそれぞれに対して各値を設定することが可能
- 各チャンネルごとに再生/停止することが可能(今回は使用していません)
- キーイベントとして
Qキー
で終了、SPACEキー
で再生/停止を定義
今回のメイン、音符の打ち込みはset(note, tone, volume, effect, speed)
を使用します。
speed
1音の長さ (120 = 1音1秒)
今回は 1ノート = 16分音符 になるように 20 を設定
note
'CDEFGAB'+'#-'+'0123'または'R'の文字列で音程を設定する。大文字と小文字を区別せず、空白は無視される
例えば、C2
は 16 分音符の ド
の音。
G2G2G2G2
のように連続で打ち込むと 4 分音符の ソ
の音になり、
スタッカートで打ち込みたい場合は G2RG2R
と間に休符の R
を入れます。
tone
音色 (0:Triangle / 1:Square / 2:Pulse / 3:Noise) のリスト
頭文字を設定。p は Pulse
音色は以下パートで使われることが多いハズ
音色 | 主なパート |
---|---|
Triangle(三角波) | ベース音 |
Square(矩形波) | 主旋律、副旋律 |
Pulse(パルス波) | 主旋律、副旋律 |
Noise(ノイズ) | ドラム、パーカッション |
effect
エフェクト (0:None / 1:Slide / 2:Vibrato / 3:FadeOut) のリスト
'NSVF'の文字列でエフェクトを設定
1文字が1音の長さに対応
サウンド では以下のポイントでエフェクトを入れてみました
-
0:36
Vivrato -
0:48
FadeOut
まとめ
仕様もシンプルで用意されているクラスを使うことですぐにゲームが作れるので、Python を使ったことがあり、MIDI や DTM に馴染みのある人ならすんなり始めることが出来そうでした。
いつか Pyxel を使ったゲーム公開できればと思います。
nana music Advent Calendar 2018 はまだまだ続きます!
明日は @yamanemur さんです。どうぞお見逃しなく💁
番外編
試しにイメージクラスを追加して画面描画してみました。
import math
import pyxel
class App:
def __init__(self):
pyxel.init(200, 150, caption="Image API")
pyxel.image(0).set(
0,
0,
[
"11011100000011101100",
"11111110000111111100",
"11100111001110011100",
"11000011001100001100",
"11000011001100001100",
"11000011001110011100",
"11000011000111111100",
"11000011000011110100",
],
)
def draw(self):
pyxel.cls(0)
pyxel.text(58, 60, "nana music", 7)
pyxel.text(58, 70, "Advent Calendar 2018", 7)
pyxel.rectb(6, 97, 193, 143, 7)
pyxel.text(12, 102, "Everyone is a Co-Creator", 7)
pyxel.text(12, 110, "", 7)
for i, v in enumerate([0, 1]):
pyxel.pal(1, 14)
pyxel.blt(
156 + i * 21,
7 + math.sin(pyxel.frame_count * 0.2 + i * 1.1) * 2,
0,
0,
0,
20,
8,
0,
)
pyxel.pal()
App()
-
pyxel.image
でイメージのデータ (NumPy配列)をセット(よーくみると 01 のビットマップで na の文字が見えますね) - draw 関数で画面描画
- for文内で 正弦*フレームカウント でイメージに動きを付ける
実行してみると、、、
それっぽい!