こんにちは!
前回こんな記事を書きました↓
本記事は『Pythonでレトロゲームを作ろう!』シリーズの”Day4”です!
Contents
【Day 4】Pyxelでレトロゲームを作ろう!
”Day3”で作成したソースコードでは、常に猫が同じ方向を向いていました。
“Day2″でマウスの左クリックで方向転換するコードも書きましたが…
マウスの移動方向に応じて猫が向きを変える方が”可愛い”よね!!
この前よりテンション高めの”くるる”ちゃんが今日も可愛い(*・ω・)ノ♪
ベクトルとは
”ベクトル”の知識があるとソースコードを理解しやすいと思うので、少しだけ”ベクトル”の説明をします。
”ベクトル”とは「向きと大きさをもつ量」のことで、矢印で表現することが多いです。
例えば、A点からB点までの移動を矢印(ベクトル)で表現した図が↓です。
矢印なので、”向き”があるのは分かると思います。”大きさ”は矢印の長さで表現できます。
”Day1”で「画像と座標の関係」について説明しています。
(忘れてしまった or まだ読んでいないという人は先に読んでほしいなぁー)
A点とB点も画像上の点なので、座標で表現できます。
今回はA点(Xa, Ya), B点(Xb, Yb)と定義します。
A点からB点に移動するということは…
- x軸方向:A点から Xb – Xa だけ移動する
- y軸方向:A点から Yb – Ya だけ移動する
という意味です。
ベクトルの”大きさ”は絶対値を付けて|Xb – Xa|で表現しますが…本記事では、正負を意識してほしいので、あえて絶対値をつけません。
x軸方向で考えたとき、B点がA点より右側にある場合、Xb – Xa > 0となります。
間違えやすいのはy軸方向で、B点がA点より上側にある場合、Yb – Ya < 0となります。
↑の図で『A点とB点の位置関係と正負の関係』を確認してみて下さい。
もう”ピン”ときた人もいるかな?
- Xb – Xa > 0 (正)なら猫を右向き
- Xb – Xa < 0 (負)なら猫を左向き
にすれば良いよね(*・ω・)ノ♪
”ベクトル”について、もっと勉強したいという人のために、以下の記事を紹介しておきます。
また、機械学習を学ぶ上でも”ベクトル”などの数学の知識が必要です。
機械学習を意識して”ベクトル”を勉強したいという人には、以下の記事を紹介しておきます。
Pyxelとベクトル
ふわぁーー💤という大きな欠伸が聞こえてきた。
大きい”くるる”ちゃんの目が、更に大きく”カッ”と開いた!
”やる気スイッチ”入りました!笑
【復習】猫の座標とマウスの座標
Day3で作ったソースコードは以下の通りでした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
import pyxel WINDOW_H = 120 WINDOW_W = 160 CAT_H = 16 CAT_W = 16 class Vec2: def __init__(self, x, y): self.x = x self.y = y class cat: def __init__(self, img_id): self.pos = Vec2(0, 0) self.img_cat = img_id def update(self, x, y): self.pos.x = x self.pos.y = y class App: def __init__(self): self.IMG_ID0_X = 60 self.IMG_ID0_Y = 65 self.IMG_ID0 = 0 self.IMG_ID1 = 1 # self.IMG_ID2 = 2 pyxel.init(WINDOW_W, WINDOW_H, caption="Hello Pyxel") pyxel.image(self.IMG_ID0).load(0, 0, "assets/pyxel_logo_38x16.png") pyxel.image(self.IMG_ID1).load(0, 0, "assets/cat_16x16.png") self.mcat = cat(self.IMG_ID1) # pyxel.mouse(True) pyxel.run(self.update, self.draw) def update(self): if pyxel.btnp(pyxel.KEY_Q): pyxel.quit() # ====== ctrl cat ====== self.mcat.update(pyxel.mouse_x, pyxel.mouse_y) def draw(self): pyxel.cls(0) pyxel.text(55, 40, "Are you Kururu?", pyxel.frame_count % 16) pyxel.blt(self.IMG_ID0_X, self.IMG_ID0_Y, self.IMG_ID0, 0, 0, 38, 16) pyxel.blt(self.mcat.pos.x, self.mcat.pos.y, self.mcat.img_cat, 0, 0, -CAT_W, CAT_H, 5) App() |
猫の座標は以下の通り…
1 2 |
self.mcat.pos.x self.mcat.pos.y |
マウスの座標は以下の通りでしたね!
1 2 |
pyxel.mouse_x pyxel.mouse_y |
猫と座標をA点, マウスの座標をB点としたときの図が以下です。
update関数に書いた以下のコードで猫をマウスに追従させることができました。
1 |
self.mcat.update(pyxel.mouse_x, pyxel.mouse_y) |
より正確にいえば、猫の座標をマウスの座標に更新することで、猫をマウスの動きに連動させていました。
技術や知識の積み重ねが大事です!
猫の向きについて
次に猫が『どちらの”方向”に移動するか?』を考えてみます。
今回は猫が「右向きか?左向きか?」のみを考えれば良い…つまり、「x軸方向」のみ考えれば良いです。
このときの”dx”を算出し「正か?負か?」によって、猫の向きを決めることができます。
苦手な分野(今回の場合は数学)でも自分の好きな分野(ゲームなど)に落とし込めば、学ぶのが楽しくなるかも!
【実践】猫の向きとマウスの向きを連動
さてと…
【catクラス】
1 2 3 4 5 6 7 8 9 10 |
class cat: def __init__(self, img_id): self.pos = Vec2(0, 0) self.vec = 0 self.img_cat = img_id def update(self, x, y, dx): self.pos.x = x self.pos.y = y self.vec = dx |
catクラスの属性に”猫の向き(vec)”を追記したよ!
あとupdate関数で、座標だけじゃなくて”猫の向き(vec)”も更新できるようにした!
【update関数(Appクラス)】
1 2 3 4 5 6 7 8 9 10 |
def update(self): if pyxel.btnp(pyxel.KEY_Q): pyxel.quit() # ====== ctrl cat ====== dx = pyxel.mouse_x - self.mcat.pos.x # x軸方向の移動量(マウス座標 - cat座標) if dx != 0: self.mcat.update(pyxel.mouse_x, pyxel.mouse_y, dx) # cat座標をマウス座標に更新 else: self.mcat.update(self.mcat.pos.x, self.mcat.pos.y, self.mcat.vec) # 静止状態 |
コメントの”ctrl cat”以下を追記したよ!
- dxが0以外 ⇒ マウスが動いている ⇒ 猫の座標をマウスの座標に更新!
- dxが0 ⇒ マウスが動いていない ⇒ そのまま猫の座標を更新!
【draw関数(Appクラス)】
1 2 3 4 5 6 7 8 9 10 |
def draw(self): pyxel.cls(0) pyxel.text(55, 40, "Are you Kururu?", pyxel.frame_count % 16) pyxel.blt(self.IMG_ID0_X, self.IMG_ID0_Y, self.IMG_ID0, 0, 0, 38, 16) # ====== ctrl cat ====== if self.mcat.vec > 0: pyxel.blt(self.mcat.pos.x, self.mcat.pos.y, self.IMG_ID1, 0, 0, -CAT_W, CAT_H, 5) else: pyxel.blt(self.mcat.pos.x, self.mcat.pos.y, self.IMG_ID1, 0, 0, CAT_W, CAT_H, 5) |
これも”ctrl cat”以下を追記したよ!
……!!
ただ、少しだけ直すなら…
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def update(self): if pyxel.btnp(pyxel.KEY_Q): pyxel.quit() # ====== ctrl cat ====== dx = pyxel.mouse_x - self.mcat.pos.x # x軸方向の移動量(マウス座標 - cat座標) dy = pyxel.mouse_y - self.mcat.pos.y # y軸方向の移動量(マウス座標 - cat座標) if dx != 0: self.mcat.update(pyxel.mouse_x, pyxel.mouse_y, dx) # 座標と向きを更新 elif dy != 0: self.mcat.update(pyxel.mouse_x, pyxel.mouse_y, self.mcat.vec) # 座標のみ更新(真上or真下に移動) # else: # self.mcat.update(self.mcat.pos.x, self.mcat.pos.y, self.mcat.vec) # 静止状態(更新不要) |
あまり無いかもしれないけど、x軸方向を一切移動させずにy軸方向のみ移動させる場合を考慮して、こんな感じにした方が良かったね(*・ω・)ノ♪
- dxが0以外 ⇒ マウスが動いている ⇒ 猫の座標と向きを更新
- (dxが0)dyが0以外 ⇒ マウスが真上or真下に移動 ⇒ 猫の座標のみ更新
dxが0 ⇒ マウスが動いていない ⇒ そのまま猫の座標を更新!- dx,dxがともに0 ⇒ マウスが動いていない ⇒ 更新不要(何もしない)
調子にノリノリな”くるる”ちゃんが楽しそうで嬉しい!笑
”数学の知識”がゲームやロボットなど”目に見える形”になると楽しいよね!本記事をきっかけに『”数学”や”プログラミング”って面白い!』というのが少しでも伝わると嬉しいな(*・ω・)ノ♪
最終的に完成したソースコード
最終的に”くるる”ちゃんが完成させたコードはこちらです↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
import pyxel WINDOW_H = 120 WINDOW_W = 160 CAT_H = 16 CAT_W = 16 class Vec2: def __init__(self, x, y): self.x = x self.y = y class cat: def __init__(self, img_id): self.pos = Vec2(0, 0) self.vec = 0 self.img_cat = img_id def update(self, x, y, dx): self.pos.x = x self.pos.y = y self.vec = dx class App: def __init__(self): self.IMG_ID0_X = 60 self.IMG_ID0_Y = 65 self.IMG_ID0 = 0 self.IMG_ID1 = 1 # self.IMG_ID2 = 2 self.MOUSE_X_PRE = 80 self.MOUSE_Y_PRE = 0 pyxel.init(WINDOW_W, WINDOW_H, caption="Cat Game") pyxel.image(self.IMG_ID0).load(0, 0, "assets/pyxel_logo_38x16.png") pyxel.image(self.IMG_ID1).load(0, 0, "assets/cat_16x16.png") # pyxel.mouse(True) # make instance self.mcat = cat(self.IMG_ID1) pyxel.run(self.update, self.draw) def update(self): if pyxel.btnp(pyxel.KEY_Q): pyxel.quit() # ====== ctrl cat ====== dx = pyxel.mouse_x - self.mcat.pos.x # x軸方向の移動量(マウス座標 - cat座標) dy = pyxel.mouse_y - self.mcat.pos.y # y軸方向の移動量(マウス座標 - cat座標) if dx != 0: self.mcat.update(pyxel.mouse_x, pyxel.mouse_y, dx) # 座標と向きを更新 elif dy != 0: self.mcat.update(pyxel.mouse_x, pyxel.mouse_y, self.mcat.vec) # 座標のみ更新(真上or真下に移動) # else: # self.mcat.update(self.mcat.pos.x, self.mcat.pos.y, self.mcat.vec) # 静止状態(更新不要) def draw(self): pyxel.cls(0) pyxel.text(55, 40, "Are you Kururu?", pyxel.frame_count % 16) pyxel.blt(self.IMG_ID0_X, self.IMG_ID0_Y, self.IMG_ID0, 0, 0, 38, 16) # ====== ctrl cat ====== if self.mcat.vec > 0: pyxel.blt(self.mcat.pos.x, self.mcat.pos.y, self.IMG_ID1, 0, 0, -CAT_W, CAT_H, 5) else: pyxel.blt(self.mcat.pos.x, self.mcat.pos.y, self.IMG_ID1, 0, 0, CAT_W, CAT_H, 5) App() |
おわりに
『Pythonでレトロゲームを作ろう!Day 4-ベクトル-』について説明しました。
ゲーム開発(pyxelの使い方)を題材にして、楽しくプログラミングの勉強ができると良いなーとか考えながら、シリーズ記事を書いています。
今回はゲームを題材にして、数学の基礎”ベクトル”を勉強しました。
”くるる”ちゃんみたいに途中で眠くなった人もいるのかな?
はい!笑 ”くるる”ちゃんみたいに後半からでも覚醒し、最後まで読んで「楽しかったぁ~」と感じて頂ければ、嬉しいです!
さてと…次は何をしようかな?
連続攻撃!!#pyxel #pyxelでゲーム作り pic.twitter.com/PtGlvjvuGW
— はやぶさ (@Cpp_Learning) December 23, 2018
”くるる”ちゃん…実はDay1~Day4で得た知識だけで多くのことができるよ!
次回説明する”猫がボールを投げる”も今まで説明した知識でソースコードを書けると思うよ!
繰り返す!技術や知識の積み重ねが大事です!
本記事楽しかったかな?次回もお楽しみに~