見出し画像

【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つ以上になったらランダムで方向転換」というルールも付け加えました。

▲こういった分岐に来たら方向転換します。

次にメインのクラスで敵のオブジェクトを操作する部分です。

          #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に近くにいる敵の数を格納しています。この表示部分は後ほど。

画面表示部分に移ります。

▲だいぶ情報量が増えました。

赤四角がミニマップ、緑は特殊アクションの残回数(予定)、紫が敵の位置ヒント、オレンジがメッセージコンソールです。

順々に書いていきます。

▲外の丸と内側の丸の間にはみ出たマップは、pyxel1.6.8からの新機能である塗りつぶしを使って黒くしています。

ミニマップは丸く縁取りして深海感を出してみました。潜水艦っぽい感じで。
また、特殊コマンド(今は床への印付けしかありませんが…)は無限に使えると簡単すぎるので、回数制限をします。とりあえず印付けはスプレー缶のアイコンで5回に設定。

▲なんか赤くなった時には手遅れじゃないか? これ…。

ミニマップの下に、敵のヒントを表示します。近くにいる敵の数を表示。そしてさらに近い場合は表示色を赤にして警告します。あんまり遠くから敵の位置が分かっても面白くないし、どの距離から検知するかは要検討です。

▲スタート地点に進むとメッセージが表示されます。
末尾に追加されて、一番上のメッセージが消えます。

最後にメッセージコンソールについて。ここはヒントや情報を表示させる予定です。メッセージは配列として持たせて、新しく追加されると先頭のメッセージが消えるようにしています。

コード部分です。

#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の方に置いてあります。

今回はここまで。
読んでいただきありがとうございました。

この記事が参加している募集

ここまで読んでいただきありがとうございます!