![見出し画像](https://assets.st-note.com/production/uploads/images/71661090/rectangle_large_type_2_88c047da14df152ec28e5a131ae46ecd.png?width=1200)
【pyxel 1.5】pyxelで3D迷路を作ってみる(6)
前回は敵キャラクターのグラフィックを試作するところまで進めました。今回は実際に敵のオブジェクトを作成し、迷路の中で動かします。また、画面表示を改修して、情報量を増やしてみます。
前回までの進行具合は以下のnoteをご参照ください。
まずは敵のオブジェクト作りからです。
新規にEnemyクラスを作っていきます。
class Enemy:
def __init__(self, x, y, m, p):
self.ene_x = x
self.ene_y = y
self.ene_m = 1
self.maze = m
self.move_permit = p
self.move_c = 0
def update(self):
if self.maze[self.ene_y - 1][self.ene_x] in self.move_permit:
self.move_c += 1
if self.maze[self.ene_y + 1][self.ene_x] in self.move_permit:
self.move_c += 1
if self.maze[self.ene_y][self.ene_x + 1] in self.move_permit:
self.move_c += 1
if self.maze[self.ene_y][self.ene_x - 1] in self.move_permit:
self.move_c += 1
if self.move_c > 2:
self.ene_m = pyxel.rndi(1, 4)
#ene_m = Angle E=Enemy
# ^
# 1
#<4 E 2>
# 3
# v
#
if self.ene_m == 1:
if self.maze[self.ene_y - 1][self.ene_x] in self.move_permit:
self.ene_y = self.ene_y - 1
self.ene_m = 1
else:
self.ene_m = pyxel.rndi(1, 4)
elif self.ene_m == 3:
if self.maze[self.ene_y + 1][self.ene_x] in self.move_permit:
self.ene_y = self.ene_y + 1
self.ene_m = 3
else:
self.ene_m = pyxel.rndi(1, 4)
if self.ene_m == 2:
if self.maze[self.ene_y][self.ene_x + 1] in self.move_permit:
self.ene_x = self.ene_x + 1
self.ene_m = 2
else:
self.ene_m = pyxel.rndi(1, 4)
if self.ene_m == 4:
if self.maze[self.ene_y][self.ene_x - 1] in self.move_permit:
self.ene_x = self.ene_x - 1
self.ene_m = 4
else:
self.ene_m = pyxel.rndi(1, 4)
self.move_c = 0
▲新規作成したEnemyクラスです。
インスタンス作成時にいろいろとパラメーターを設定する「__init__」関数(コンストラクタ)と敵の動きを制御する「update」関数があります。
Enemyクラスにはプレイヤーと同じく向いている方向を示すパラメーターがあります(self.ene_m)。
敵の動きのルールとしては、「向いている方向に進み続ける」、「行き止まりになったらランダムで方向転換」の2つで最初は作っていたのですが、動きにバリエーションがないので、「進行可能方向が3つ以上になったらランダムで方向転換」というルールも付け加えました。
![](https://assets.st-note.com/img/1644201193868-qHMnvAUkxf.png)
次にメインのクラスで敵のオブジェクトを操作する部分です。
#Enemy action
self.e_msg_c = 0
for e in self.enemys:
if e.ene_x == self.pos[0] and e.ene_y == self.pos[1]:
self.enemy_pos = 0
self.game_over = True
if self.enemy_pos > 0:
if self.move_flag == True:
e.update()
x = abs(self.pos[0] - e.ene_x)
y = abs(self.pos[1] - e.ene_y)
if x < 5 and y < 5:
self.e_msg_c += 1
if x < 3 and y < 3:
self.e_msg_F = True
self.e_msg = "Enemies nearby: " + str(self.e_msg_c)
if e.ene_x == self.pos[0] and e.ene_y == self.pos[1]:
self.enemy_pos = 0
self.bubble_cnt = 0
self.game_over = True
▲このコードはメインクラスのupdate関数で実行されます。敵のオブジェクトが詰まっている「self.enemys」という配列からfor文で敵を1体ずつ取り出して動かします。ここでやっている処理としては、「敵とプレイヤーの座標が重なったらゲームオーバーにする」と「敵位置のヒントとなるself.e_msgを編集」の2つです。プレイヤーへのヒントとしてself.e_msgに近くにいる敵の数を格納しています。この表示部分は後ほど。
画面表示部分に移ります。
![](https://assets.st-note.com/img/1644194694086-miGTPTuDnP.png?width=1200)
赤四角がミニマップ、緑は特殊アクションの残回数(予定)、紫が敵の位置ヒント、オレンジがメッセージコンソールです。
順々に書いていきます。
![](https://assets.st-note.com/production/uploads/images/71661762/picture_pc_08cc0019d006e9c364e61ab2545069d0.gif)
ミニマップは丸く縁取りして深海感を出してみました。潜水艦っぽい感じで。
また、特殊コマンド(今は床への印付けしかありませんが…)は無限に使えると簡単すぎるので、回数制限をします。とりあえず印付けはスプレー缶のアイコンで5回に設定。
![](https://assets.st-note.com/production/uploads/images/71661927/picture_pc_b032c4847b827ba04fd1902d9f13f667.gif)
ミニマップの下に、敵のヒントを表示します。近くにいる敵の数を表示。そしてさらに近い場合は表示色を赤にして警告します。あんまり遠くから敵の位置が分かっても面白くないし、どの距離から検知するかは要検討です。
![](https://assets.st-note.com/production/uploads/images/71661441/picture_pc_a48bf69fb91d9fbc3a10adacd534406d.gif)
末尾に追加されて、一番上のメッセージが消えます。
最後にメッセージコンソールについて。ここはヒントや情報を表示させる予定です。メッセージは配列として持たせて、新しく追加されると先頭のメッセージが消えるようにしています。
コード部分です。
#Draw Compass-----------------------------------------------------------------
pyxel.ellib(-50, -10, 360, 170, 5)
pyxel.fill(0, 1, 0)
pyxel.rect(3, 2, 15, 7, 0)
pyxel.fill(0, 140, 0)
pyxel.rect(0, 141, 17, 15, 0)
pyxel.fill(255, 9, 0)
pyxel.rect(242, 0, 15, 9, 0)
pyxel.fill(255, 141, 0)
pyxel.rect(242, 141, 15, 9, 0)
pyxel.rectb(0, 0, 256, 151, 1)
pyxel.rect(0, 150, 256, 106, 0)
pyxel.rectb(0, 150, 256, 106, 1)
pyxel.rectb(110, 150, 146, 106, 1)
pyxel.rectb(114, 154, 137, 97, 5)
#Wall
for d0 in range(5):
if (self.pos[1]-1 < 0 or
self.pos[0]-2+d0 < 0 or self.pos[0]-2+d0 > 31):
pyxel.rect(17+10*d0, 167, 9, 9, 7)
elif self.maze[self.pos[1]-1][self.pos[0]-2+d0] in self.wall_list:
pyxel.rect(17+10*d0, 167, 9, 9, 7)
elif self.maze[self.pos[1]-1][self.pos[0]-2+d0] == (2, 0):
pyxel.rect(17+10*d0, 167, 9, 9, 3)
elif self.maze[self.pos[1]-1][self.pos[0]-2+d0] == (0, 1):
pyxel.rectb(17+10*d0, 167, 9, 9, 8)
for d1 in range(5):
if self.pos[0]-2+d1 < 0 or self.pos[0]-2+d1 > 31:
pyxel.rect(17+10*d1, 177, 9, 9, 7)
elif self.maze[self.pos[1]][self.pos[0]-2+d1] in self.wall_list:
pyxel.rect(17+10*d1, 177, 9, 9, 7)
elif self.maze[self.pos[1]][self.pos[0]-2+d1] == (2, 0):
pyxel.rect(17+10*d1, 177, 9, 9, 3)
elif self.maze[self.pos[1]][self.pos[0]-2+d1] == (0, 1):
pyxel.rectb(17+10*d1, 177, 9, 9, 8)
for d2 in range(5):
if (self.pos[1]+1 > 31 or
self.pos[0]-2+d2 < 0 or self.pos[0]-2+d2 > 31):
pyxel.rect(17+10*d2, 187, 9, 9, 7)
elif self.maze[self.pos[1]+1][self.pos[0]-2+d2] in self.wall_list:
pyxel.rect(17+10*d2, 187, 9, 9, 7)
elif self.maze[self.pos[1]+1][self.pos[0]-2+d2] == (2, 0):
pyxel.rect(17+10*d2, 187, 9, 9, 3)
elif self.maze[self.pos[1]+1][self.pos[0]-2+d2] == (0, 1):
pyxel.rectb(17+10*d2, 187, 9, 9, 8)
for d3 in range(5):
if (self.pos[1]+2 > 31 or
self.pos[0]-2+d3 < 0 or self.pos[0]-2+d3 > 31):
pyxel.rect(17+10*d3, 197, 9, 9, 7)
elif self.maze[self.pos[1]+2][self.pos[0]-2+d3] in self.wall_list:
pyxel.rect(17+10*d3, 197, 9, 9, 7)
elif self.maze[self.pos[1]+2][self.pos[0]-2+d3] == (2, 0):
pyxel.rect(17+10*d3, 197, 9, 9, 3)
elif self.maze[self.pos[1]+2][self.pos[0]-2+d3] == (0, 1):
pyxel.rectb(17+10*d3, 197, 9, 9, 8)
#Prayer
if self.pos_angle == 1:
pyxel.tri(41, 178, 37, 185, 45, 185, 1)
elif self.pos_angle == 2:
pyxel.tri(45, 181, 37, 178, 37, 185, 1)
elif self.pos_angle == 3:
pyxel.tri(41, 185, 37, 178, 45, 178, 1)
elif self.pos_angle == 4:
pyxel.tri(37, 183, 45, 178, 45, 186, 1)
pyxel.circb(40, 185, 27, 5)
pyxel.circb(40, 185, 32, 1)
pyxel.fill(18, 168, 0)
pyxel.fill(64, 168, 0)
pyxel.fill(63, 202, 0)
pyxel.fill(18, 204, 0)
#Enemy message
pyxel.ellib(12, 220, 90, 20, 5)
pyxel.rectb(12, 220, 90, 20, 1)
#pyxel.fill(42, 228, 3)
if self.e_msg_F == False:
c = 3
else:
c = 8
pyxel.text(23, 228, self.e_msg, c)
#Paint count
pyxel.blt(74, 160, 1, 0, 0, 16, 16, 15)
pyxel.text(90, 170, "x " + str(self.paint_cnt), 7)
#Message
for m in range(len(self.game_msg.msg)):
pyxel.text(120, 160 + m * 10, self.game_msg.msg[m][0],
self.game_msg.msg[m][1])
#-----------------------------------------------------------------------------
情報表示部分の描画コードです。冗長になってしまいましたが、やっていることは単純で、pyxelの描画命令で丸や四角を描いているだけです。所々で色コードを変数にして、状況に応じて色分けできるようにしていますね。
class GameMsg:
def __init__(self):
self.msg = [
["test1",3],
["test2",5],
["test3",6],
["test4",7],
["test5",8],
["test6",9],
["test7",10],
["test8",11],
["test9",12],
]
def update(self, msg, c):
self.msg.pop(0)
self.msg.append([msg, c])
メッセージを制御するGameMsgクラスです。今回初めて配列のpopメソッドを使いました。「popで削除してappendで追加」このまとまりをupdate関数としています。
self.game_msg.update("Start position", 3)
self.game_msg.update("Find out the secret information!", 10)
こんな感じで引数にメッセージとカラーコードを入れると、その色でメッセージを出してくれます。
結構長くなっちゃいましたけど、ゲームっぽくなってきました。見た目も今まで作ったゲームよりまとまりがある感じがする。
コードやpyxresファイル、モジュールファイルはGitHubの方に置いてあります。
今回はここまで。
読んでいただきありがとうございました。
この記事が参加している募集
ここまで読んでいただきありがとうございます!