
トップ画像の元ネタがわからない人は、「遊戯王 バーサーカーソウル」で検索!
前回は静止画で、四角形などの図形を表示しただけで、動きもなくて、ただのお絵かきだったけど、今回から動きを加えていくよ!ここからどんどんゲームらしくなっていくよ!
アニメの絵やゲームのキャラクターがぬるぬる動いているのは、パラパラ漫画のように、少しずつ異なる絵を高速で切り替えて、動いているように見せているから。みんなが見ているアニメ1話分で、だいたい3000枚~4000枚の作画がされているらしい!アクションの多いアニメやクオリティの高いアニメだと、その作画枚数が多くて、アニメーターさんがめちゃくちゃ大変らしい
進撃の巨人:立体機動シーンコマ送り映像
これ、実際のアニメとかだと10秒もないシーンだけど、これだけの作画が入ってると思うとすごいよね。昔は全部手書きだったし、今ではCGもあるけどこだわってる部分は人間が手書きで書くみたい。
<参考>そもそもTVアニメは今でも手書きで作られているのか?



スラムダンクの名言、知ってる?
今回は、前回表示した図形を動かすプログラムを書いていくから、アニメーションの流れとしては
図形を表示 → 図形を移動 → 図形を表示 → 図形を移動 → 図形を表示。。。
という繰り返しになる。この「移動」の部分でx座標・y座標を操作するので、今回は座標を python でいかに制御するかがカギになってくるよ!
前回、図形を描画するときに、
import pyxel
pyxel.init(160, 120, title="pyxel training")
pyxel.show()
という書き方をしたけど、「この書き方は動かない絵を描く用だから、アニメーションの場合は違う書き方をするよ」といったの、覚えてる?
では、アニメーション用の pyxel の基本構文を書いてみよう。
import pyxel
# ゲームに必要な変数を宣言する
# 画面の大きさを決める辞書
screen = {
"yoko": 160,
"tate": 120
}
def update():
# フレームが変わるたびに変更する処理を書く
def draw():
# 画面上に描画する処理を書く
# スクリーンの大きさの設定
pyxel.init(screen["yoko"], screen["tate"])
# プログラムを実行
pyxel.run(update, draw)
update, drar, run という、3つの関数がでてきたね。
update=アップデート、更新という意味
この関数は、フレームが移り変わっていくときに変更する処理を書いておく関数。
今回はアニメーションを作るので、図形の座標を変更する処理をここに書くよ
draw=ドロー:描く、描画するという意味
前回やった、図形を描画する処理を書くところ
run=ラン:走る、実行するという意味
引数に、update 関数と draw()関数が入っているね。これは、run 関数が実行されると
・draw 関数で図形を描画
・update 関数で必要な情報を更新
・draw 関数で図形を描画
・update 関数で必要な情報を更新
・draw 関数で図形を描画
・update 関数で必要な情報を更新...
という処理が繰り返し行われるよ、という意味。この run 関数があるから、フレーム処理に繰り返し分(while 文や for 文)を書かなくていいわけだね。
図形を最初に描画する場所のデータなど、ゲームに必要な変数は、関数の中ではなく外に書いておくよ!
関数内で宣言した変数は、基本的に関数内でしか使えないけど、関数外で宣言した変数はリストや辞書などの 配列 にしておくと、いつでも参照・更新ができるよ!
今回描画する四角形の名前を jump_box として、jump_box という辞書を作成して、その中に jump_box 関連の情報をいれておこう。
・x軸:20
・y軸:100
・移動スピード:-1
移動スピードがマイナス??ってなると思うけど、これはy座標が「上に行くほど下がる」仕組みになっているから。前回のこの図を思い出してみよう。

今回 jup_box は上方向に動いてもらうので、y 軸方向に1ずつ座標が減っていくから-1となってるよ!これはゲームスタートに必要な変数だから、関数の外に書く必要がある!import
文と update 関数の間に書いておこう。
import pyxel
#ゲームに必要な変数を宣言する
# 画面の大きさを決める辞書
screen = {
"yoko": 160,
"tate": 120
}
# jump_boxの情報が入った辞書
jump_box = {
"x":20,
"y":100,
"speed":-1
}
def update():
# フレームが変わるたびに変更する処理を書く
def draw():
# 画面上に描画する処理を書く
# スクリーンの大きさの設定
pyxel.init(screen["yoko"], screen["tate"])
# プログラムを実行
pyxel.run(update, draw)
前回やった、rect 関数を使うよ。今回は x 軸y軸の情報を jump_box リストからとってこよう
import pyxel
#ゲームに必要な変数を宣言する
# 画面の大きさを決める辞書
screen = {
"yoko": 160,
"tate": 120
}
# jump_boxの情報が入った辞書
jump_box = {
"x":20,
"y":100,
"speed":-1
}
def update():
# フレームが変わるたびに変更する処理を書く
def draw():
# 画面上に描画する処理を書く
# rect関数:引数は:
# 左上のx座標
# 左上のy座標
# 横幅
# 縦幅
# 色番号(1は白色)
pyxel.rect(jump_box["x"], jump_box["y"], 10, 10, 1)
# スクリーンの大きさの設定
pyxel.init(screen["yoko"], screen["tate"])
# プログラムを実行
pyxel.run(update, draw)
今回は jump_box に上方向に移動してもらうので、図形のy軸の数字を-1 だけ、つまり、
jump_box["y"]の数字を jump_box["speed"]だけ、動かす必要があるわけ。じゃあ実装しよう。
import pyxel
#ゲームに必要な変数を宣言する
# 画面の大きさを決める辞書
screen = {
"yoko": 160,
"tate": 120
}
# jump_boxの情報が入った辞書
jump_box = {
"x":20,
"y":100,
"speed":-1
}
def update():
# フレームが変わるたびに変更する処理を書く
jump_box["y"] = jump_box["y"] + jump_box["speed"]
def draw():
# 画面上に描画する処理を書く
pyxel.rect(jump_box["x"], jump_box["y"], 10, 10, 1)
# スクリーンの大きさの設定
pyxel.init(screen["yoko"], screen["tate"])
# プログラムを実行
pyxel.run(update, draw)
できたら早速、実行してみよう。
なんか動いたっぽいけど、残像みたいに前の画像が。。。のびた??

実はさっき、run 関数の説明をしたときに、draw 関数、update 関数、draw 関数、update
関数と繰り返す、って説明したけど、実はちょっと説明不足だったことがあります。
draw 関数は、すでにある絵に絵を描き足していくイメージなので、draw→update を繰り返すと、さっきみたいに四角がびよーんとのびていくよ。
じゃあどうすればいいかというと、
pyxel.cls(0) # 黒色で画面を塗りつぶす
というコードを追加すると、アニメーションがちゃんと動きます。この cls()関数は、黒いペンキを画面上にぶちまけるイメージ。これを追加することで、
draw で描画 →update で更新 → 画面を黒くリセット→draw で描画 →update で更新 →
画面を黒くリセット→draw で描画 →update で更新 → 画面を黒くリセット...
という繰り返しになって、一回一回、座標が移動した四角形を書き直す感じになるので、上に移動していくように見える!
さて問題、この一行はコードのどこに追加したらいいでしょう?
正解は、draw 関数の中で四角形を描画する直前。ほかのところに cls()関数を入れて、どうしてそうなるかを考えてみると、勉強になるかも!
今の状態だと、四角が画面外にすっ飛んで行ってしまうから、こんな感じで、画面端に言ったら反対側に帰ってきて、反対側の端に言ったらまた逆に進むアニメーションを作ってみよう。
さて、プログラミングで何か実装しようというときに、いきなりコードを書き始めるのもいいけれど、最初に日本語でやることを書きながら、「あれがつかえるな!これが必要だな!」というのを整理していくのも結構いい作戦。
もし、画面の端についたら
四角の進行方向をかえる
こう書くと、結構シンプルだよね。これをもうちょっと詳しく書き換えてみよう。
もし、四角が、y座標が0の線に触れたとき、またはy座標が120の線に触れたとき、
四角のスピードにマイナス1をかける
もう一声!
もし、四角のy座標が、0になったとき、または120になったとき
四角のスピードをにマイナス1をかける
さて、ここで、四角のy座標、四角のスピードは、今まで書いてきた python コードでどうなっていたか、思い出してみよう。
四角の情報をリストにしたところを見てみたら、jump_box["y"]がy座標、jump_box["speed"]がスピードになってるのがわかるね。
もう一度書き直してみよう。
もし、jump_box["y"] == 0、またはjump_box["y"]==120になったとき、
jump_box["speed"] *= -1をする。
あとは、「もし」を使っているので、if 文にするだけ
if jump_box["y"] == 0 or jump_box["y"]==120:
jump_box["speed"] *= -1
さてじゃあこれをどこに書くか。
進む方向を変えてから、y 座標を更新する。つまり、update()関数の中に書いていこう。
import pyxel
#ゲームに必要な変数を宣言する
# 画面の大きさを決める辞書
screen = {
"yoko": 160,
"tate": 120
}
# jump_boxの情報が入った辞書
jump_box = {
"x":20,
"y":100,
"speed":-1
}
def update():
# フレームが変わるたびに変更する処理を書く
if jump_box["y"] == 0 or jump_box["y"]==120:
jump_box["speed"] *= -1
jump_box["y"] = jump_box["y"] + jump_box["speed"]
def draw():
# 画面上に描画する処理を書く
pyxel.cls(0)
pyxel.rect(jump_box["x"], jump_box["y"], 10, 10, 1)
# スクリーンの大きさの設定
pyxel.init(screen["yoko"], screen["tate"])
# プログラムを実行
pyxel.run(update, draw)
できたら実行してみよう
なんか、四角のバウンドする位置がズレてる??
原因を究明して、四角がはみ出ないように修正しよう!
jump_box["y"]って、四角形の左上のy座標だったよね。。。
みんな、ロブロックスはやったことある?数あるロブロックスのゲームの中の一つで、よく Youtuber が雑談しながらやってたりするやつ。今回は完成まではいけないけれど、その基本となるプログラムを組んでいくよ。

四角いキューブがぴょんぴょんはねて、障害物を避けていくゲーム。
ジャンプするとき、当然物体は下から上に移動するんだけど、そのスピードは一定じゃないよね。
ジャンプし始めはスピードがあるけど、だんだんなくなってきて、一番高いところでゼロになって、そのあと逆方向にスピードが上がっていき、着地する瞬間が一番早くなってるよね。
じゃあ、さっきの行ったり来たりするバージョンの python
コードを改良しながら、連続でジャンプし続けるプログラムを作っていくよ。(本来ならスペースキーなどを押したときにジャンプするようにしたいけど、それはまた今度。ゲームが開始したら無限にジャンプを繰り返すようにするよ。)
行ったり来たりバージョンでは、逆側の時にマイナスになるとはいえ、スピード自体は一定だったよね、ジャンプバージョンはそのスピードが変化する(=更新される)ので、update()関数のところで、jump_box["speed"]が変化するようにすればよさそうだね。
ここで、一つ注意しなくちゃいけないのが、上方向に進む=y軸のマイナス方向に進む、ということ。たとえば10ピクセル上に進むには、y 座標が-10
変化するということ。だから、ジャンプするとき最初のスピードはマイナスにするべきなんだよね。そして、上に進むごとに、だんだんスピードが0に近づいていく=スピードが1ふえて、一番上まで行くと、スピードが0になって、下に下がる時には、スピードがだんだん増えていくという感じ。
変化の条件を考えてみよう。
・もし(if)jump_box がスタート位置(y 座標= 100)にいるなら、スピード(jump_box["speed"])に-13 する。
・一回ジャンプするごとに、jump_box["speed"]を 1 あげる
import pyxel
screen = {
"yoko": 160,
"tate": 120
}
jump_box = {
"x":20,
"y":100,
"speed_x":0,
"speed_y":-13
}
def update():
if jump_box["y"] == 100:
jump_box["speed_y"] = -13
jump_box["y"] += jump_box["speed_y"]
jump_box["speed_y"] += 1
def draw():
pyxel.cls(0)
pyxel.rect(jump_box["x"], jump_box["y"], 10, 10, 7)
pyxel.init(screen["yoko"], screen["tate"])
pyxel.run(update, draw)
jump_boxの辞書に、[speed_x][speed_y]が追加されてるのに気づいたかな?実際に今jump_boxはy方向にしか動いてないけど、もしかしたら今後、「右左のキーを押したら四角が移動する」みたいな機能が実装される可能性があるかもしれないから、xのキーも追加してあるよ!
障害物の動きを考えよう。触れたらダメージをくらったり、ゲームオーバーになったりする予定なので、名前は enemy_box としよう。
画面右端からすーっと一直線にひだりに進んでくるから、さっきの行ったり来たりバージョンだとy軸だったけど、今回はx軸の数字が変わっていくね。
update に追加する処理:enemy_box のy座標を更新する
draw 関数に追加する処理:enemy_box を表示
今まで書いてきたコードを参考にしながら、実装してみよう!
import pyxel
import pyxel
screen = {
"yoko": 160,
"tate": 120
}
jump_box = {
"x":20,
"y":100,
"speed_x":0,
"speed_y":13
}
enemy_box = {
"x":160,
"y":100,
"speed_x":-1,
"speed_y":0
}
def update():
if jump_box["y"] == 100:
jump_box["speed_y"] = 13
jump_box["y"] -= jump_box["speed_y"]
jump_box["speed_y"] -= 1
if enemy_box["x"] == -10:
enemy_box["x"] = 160
enemy_box["x"] += enemy_box["speed_x"]
def draw():
pyxel.cls(0)
pyxel.rect(jump_box["x"], jump_box["y"], 10, 10, 7)
pyxel.rect(enemy_box["x"], enemy_box["y"], 10, 10, 8)
pyxel.init(screen["yoko"], screen["tate"])
pyxel.run(update, draw)

せっかく形になった来たので、完成までいきたいよね!あと必要なものが何か、整理しておこう。
これは、「スペースキーが押されたよ」ということを、python コードに教えてあげることができれば、実現できそうだね。そしてジャンプしている途中に何度もスペースキーを押して無限にジャンプさせるのはゲームが簡単になってしまうから、次に着地するまでスペースキーを反応しなくするなど、工夫が必要かもしれませんね
実は単純そうに見えて、あたり判定のシステムは奥が深い!例えばオンライン対戦ゲームとかだと、離れた場所がインターネットを通じてゲームをするわけだから、ある程度の「ラグ」が発生することを計算したうえで、わざとあたり判定をゆるくしたりすることがある。基本的には2つの物体の距離を計算して判定するんだけど、いろんなやり方があるので、一緒に勉強していこう
今は一定のタイミングで enemy_box が出てくるので、プレーヤーは簡単すぎて飽きちゃうよね。これも、例えばマリオのステージみたいに、あらかじめステージに障害物が出るタイミングを決めておくパターンや、完全にランダムなタイミングで出すパターンもある!
以前ちょっと紹介したように、pyxel では自分で音楽を制作できたり、好きな効果音を設定したりできる!
ほかにもいろんな機能を付けていくよ!完成形を楽しみにしながら、また次回も開発していこうね!