pygameを使用してPythonでプラットフォーマーを作成する

画像

ここに書かれていることを最も小さな初心者のためにすぐに言わなければなりません。



私は長い間ゲームメーカーとして自分自身を試してみたかったのですが、最近Pythonを学び、古い夢を実現する機会がありました。



プラットフォーマーとは何ですか?



プラットホーマー(platformer)-ゲームプレイの主な機能がプラットフォームでジャンプし、階段を登り、通常レベルを完了するために必要なアイテムを収集するコンピューターゲームのジャンル。

Wiki



このジャンルの私のお気に入りのゲームの1つは、スーパーマリオブラザーズとスーパーミートボーイです。 間に何かを作成してみましょう。





ほとんどが始まりです。



注意! Pythonブランチ2.xを使用します。3.xでは、以下で説明するスクリプトの実行で問題が発生しました。



おそらく、ゲームだけでなく、pygameを使用するすべてのアプリケーションは次のように起動します。



#!/usr/bin/env python # -*- coding: utf-8 -*- #   pygame import pygame from pygame import * #  WIN_WIDTH = 800 #   WIN_HEIGHT = 640 #  DISPLAY = (WIN_WIDTH, WIN_HEIGHT) #        BACKGROUND_COLOR = "#004400" def main(): pygame.init() #  PyGame,   screen = pygame.display.set_mode(DISPLAY) #   pygame.display.set_caption("Super Mario Boy") #    bg = Surface((WIN_WIDTH,WIN_HEIGHT)) #    #     bg.fill(Color(BACKGROUND_COLOR)) #     while 1: #    for e in pygame.event.get(): #   if e.type == QUIT: raise SystemExit, "QUIT" screen.blit(bg, (0,0)) #      pygame.display.update() #        if __name__ == "__main__": main()
      
      







ゲームはループで「スピン」し(1の場合)、各反復はすべて(バックグラウンド、プラットフォーム、モンスター、デジタルメッセージなど)をすべて再描画する必要があります。 描画は連続的であることに注意することが重要です。 最初にヒーローを描いてから背景を塗りつぶすと、ヒーローは見えなくなります。これは将来のために覚えておいてください。



このコードを実行すると、緑色で塗りつぶされたウィンドウが表示されます。





(画像はクリック可能)



さあ、始まりました。先に進みましょう。



レベル。





そして、どうして彼なしで? 「レベル」という言葉は、あらゆるもので満たされた仮想の2次元空間の限られた領域を意味し、それに沿ってキャラクターが移動します。



レベルを構築するには、m x nの2次元配列を作成します。 各セル(m、n)は長方形になります。 長方形はそれ自体に何かを含む場合もあれば、空の場合もあります。 プラットフォームを長方形で描画します。



定数を追加します



 PLATFORM_WIDTH = 32 PLATFORM_HEIGHT = 32 PLATFORM_COLOR = "#FF6262"
      
      







次に、 メイン関数にレベル宣言を追加します



 level = [ "-------------------------", "- -", "- -", "- -", "- -- -", "- -", "-- -", "- -", "- --- -", "- -", "- -", "- --- -", "- -", "- ----------- -", "- -", "- - -", "- -- -", "- -", "- -", "-------------------------"]
      
      





メインループで、次を追加します。

 x=y=0 #  for row in level: #   for col in row: #   if col == "-": # ,       pf = Surface((PLATFORM_WIDTH,PLATFORM_HEIGHT)) pf.fill(Color(PLATFORM_COLOR)) screen.blit(pf,(x,y)) x += PLATFORM_WIDTH #      y += PLATFORM_HEIGHT #      x = 0 #      
      
      







つまり 2次元配列レベルを反復処理し、シンボル「-」が見つかった場合、座標(x * PLATFORM_WIDTH、y * PLATFORM_HEIGHT)で、x、yはレベル配列のインデックスです



実行すると、次のように表示されます。







キャラクター





背景の立方体だけが非常に退屈です。 プラットフォームで走ってジャンプするキャラクターが必要です。



ヒーローのクラスを作成します。



便宜上、キャラクターを別個のplayer.pyファイルに保存します



 #!/usr/bin/env python # -*- coding: utf-8 -*- from pygame import * MOVE_SPEED = 7 WIDTH = 22 HEIGHT = 32 COLOR = "#888888" class Player(sprite.Sprite): def __init__(self, x, y): sprite.Sprite.__init__(self) self.xvel = 0 # . 0 -    self.startX = x #   ,      self.startY = y self.image = Surface((WIDTH,HEIGHT)) self.image.fill(Color(COLOR)) self.rect = Rect(x, y, WIDTH, HEIGHT) #   def update(self, left, right): if left: self.xvel = -MOVE_SPEED #  = x- n if right: self.xvel = MOVE_SPEED #  = x + n if not(left or right): # ,     self.xvel = 0 self.rect.x += self.xvel #     xvel def draw(self, screen): #     screen.blit(self.image, (self.rect.x,self.rect.y))
      
      









何がそんなに面白いの?

まず、 pygame.sprite.Spriteクラスを継承して、スプライトのすべての特性を継承する新しいクラスを作成します。

スプライトは動くビットマップです。 多数の便利なメソッドとプロパティがあります。



self.rect = Rect(x、y、WIDTH、HEIGHT)この行では、キャラクターの実際の境界線を作成します。これに沿って、ヒーローを移動させるだけでなく、衝突をチェックします。 しかし、それについては以下をご覧ください。



update(self、left、right))メソッドは、オブジェクトの動作を記述するために使用されます。 親更新をオーバーライドします(* args)-> None 。 スプライトグループで呼び出すことができます。



draw(self、screen)メソッドは、画面にキャラクターを表示するために使用されます。 次に、このメソッドを削除し、より興味深い方法を使用してヒーローを表示します。



ヒーローをプログラムのメイン部分に追加します。



レベルを決定する前に、ヒーローの定義と動きの変数を追加します。



 hero = Player(55,55) #    (x,y)  left = right = False #   — 
      
      







イベントチェックで、次を追加します。



 if e.type == KEYDOWN and e.key == K_LEFT: left = True if e.type == KEYDOWN and e.key == K_RIGHT: right = True if e.type == KEYUP and e.key == K_RIGHT: right = False if e.type == KEYUP and e.key == K_LEFT: left = False
      
      







つまり 左キーを押した場合は、左に進みます。 リリースされた場合、停止します。 また、右ボタンで



ムーブメント自体は次のように呼ばれます:(背景とプラットフォームを再描画した後に追加します)



 hero.update(left, right) #  hero.draw(screen) # 
      
      







画像



しかし、ご覧のとおり、灰色のブロックの動きが速すぎるため、1秒あたりのフレーム数に制限を追加しています。 これを行うには、レベルを決定した後、タイマーを追加します



 timer = pygame.time.Clock()
      
      







メインループの開始時に、次を追加します。



 timer.tick(60)
      
      







空中にぶら下がっている





はい、私たちのヒーローは絶望的な状況にあり、空中に浮かんでいました。

重力とジャンプ機能を追加します。



そして、 player.pyファイルで作業します



定数を追加します



 JUMP_POWER = 10 GRAVITY = 0.35 # ,     
      
      







_init_メソッドに行を追加します。



  self.yvel = 0 #    self.onGround = False #    ?
      
      







入力引数を更新メソッドに追加します

def update(self、left、right、up):

そして、メソッドの最初に以下を追加します:

 if up: if self.onGround: # ,       self.yvel = -JUMP_POWER
      
      







そして、行self.rect.x + = self.xvelの前

追加する



 if not self.onGround: self.yvel += GRAVITY self.onGround = False; #   ,    (( self.rect.y += self.yvel
      
      







そして、プログラムの主要部分に追加します。

行の左=右=偽

変数を追加します

 up = false
      
      







イベントチェックで追加



 if e.type == KEYDOWN and e.key == K_UP: up = True if e.type == KEYUP and e.key == K_UP: up = False
      
      







そして、新しいup引数を追加して、 updateメソッドの呼び出しを変更ます。

hero.update(左、右



 hero.update(left, right, up)
      
      







ここで重力を作成しました。これは、地面に立っていないと飛行中にジャンプすることができない場合に、絶えず速度を上げて引き下げます。 そして、私たちはまだ何かにしっかりと立ち向かうことができないので、次のアニメーションでは、ヒーローは可視性の境界をはるかに超えています。

画像



両足を地面につけて立ちます。





私たちが地面または他の固体表面にいることを知る方法は? 答えは明らかです-交差のチェックを使用しますが、このためにプラットフォームの作成を変更します。



別のblocks.pyファイルを作成し、プラットフォームの説明をそこに転送します。



 PLATFORM_WIDTH = 32 PLATFORM_HEIGHT = 32 PLATFORM_COLOR = "#FF6262"
      
      







次に、 pygame.sprite.Spriteを継承するクラスを作成します



 class Platform(sprite.Sprite): def __init__(self, x, y): sprite.Sprite.__init__(self) self.image = Surface((PLATFORM_WIDTH, PLATFORM_HEIGHT)) self.image.fill(Color(PLATFORM_COLOR)) self.rect = Rect(x, y, PLATFORM_WIDTH, PLATFORM_HEIGHT)
      
      







ここでよく知らないことは何もありません。



メインファイルで、 レベル配列の説明の前に変更を加え、追加します



 entities = pygame.sprite.Group() #   platforms = [] # ,        entities.add(hero)
      
      







エンティティスプライトグループは、このグループのすべての要素を表示するために使用されます。

プラットフォーム配列は、プラットフォームとの交差を確認するために使用されます。



次のブロック

 if col == "-": # ,       pf = Surface((PLATFORM_WIDTH,PLATFORM_HEIGHT)) pf.fill(Color(PLATFORM_COLOR)) screen.blit(pf,(x,y))
      
      







で置き換える

 if col == "-": pf = Platform(x,y) entities.add(pf) platforms.append(pf)
      
      







つまり Platformクラスのインスタンスを作成し、 エンティティスプライトグループとプラットフォーム配列に追加しますエンティティで 、各ブロックに表示ロジックを書き込まないようにします。 プラットフォームに追加して、後でプレーヤーとの交差についてブロックの配列をチェックします



さらに、ループからレベル生成コード全体を削除します。



また、行

hero.draw(スクリーン)#mapping

で置き換える

 entities.draw(screen) #  
      
      







実行すると、何も変わっていないことがわかります。 そうだね。 結局のところ、衝突についてヒーローをチェックしません。 修正を始めましょう。



player.pyファイルで作業します



drawメソッドを削除しました。もう必要ありません。 そして、新しい衝突メソッドを追加します



 def collide(self, xvel, yvel, platforms): for p in platforms: if sprite.collide_rect(self, p): #       if xvel > 0: #    self.rect.right = p.rect.left #     if xvel < 0: #    self.rect.left = p.rect.right #     if yvel > 0: #    self.rect.bottom = p.rect.top #     self.onGround = True #    -  self.yvel = 0 #     if yvel < 0: #    self.rect.top = p.rect.bottom #     self.yvel = 0 #    
      
      





この方法では、ヒーローとプラットフォームの座標の交差についてチェックが行われ、座標がある場合は、上記のアクションが実行されます。



さて、これを実現するには、このメソッドを呼び出す必要があります。

updateメソッドの引数の数を変更すると、次のようになります。



 update(self, left, right, up, platforms)
      
      







また、メインファイルで呼び出しを変更することを忘れないでください。



そしてライン

 self.rect.y += self.yvel self.rect.x += self.xvel #     xvel
      
      







置換:

 self.rect.y += self.yvel self.collide(0, self.yvel, platforms) self.rect.x += self.xvel #     xvel self.collide(self.xvel, 0, platforms)
      
      







つまり ヒーローを垂直に移動し、垂直交差をチェックし、水平に移動し、水平交差を再度チェックしました。



それは私たちがそれを実行すると起こることです。



画像



ふ[y]! 動く長方形は美しくありません!





マリオボーイを少し飾りましょう。



プラットフォームから始めましょう。 これを行うには、 blocks.pyファイルに小さな変更を加えます。



この行の画像の塗りつぶしを置き換えます

self.image.fill(色(PLATFORM_COLOR))

で置き換える

 self.image = image.load("blocks/platform.png")
      
      







単色ではなく写真をアップロードします。 もちろん、 platform.pngファイルはブロックフォルダーに配置する必要があります。 ブロックフォルダーはソースディレクトリに配置する必要があります。



それが起こったことです







今、私たちはヒーローに渡します。 動くオブジェクトの場合、静止画像ではなくアニメーションが必要です。 このタスクを容易にするために、素晴らしいpyganimライブラリを使用します。 始めましょう。



まず、定数のブロックに追加します。



 ANIMATION_DELAY = 0.1 #    ANIMATION_RIGHT = [('mario/r1.png'), ('mario/r2.png'), ('mario/r3.png'), ('mario/r4.png'), ('mario/r5.png')] ANIMATION_LEFT = [('mario/l1.png'), ('mario/l2.png'), ('mario/l3.png'), ('mario/l4.png'), ('mario/l5.png')] ANIMATION_JUMP_LEFT = [('mario/jl.png', 0.1)] ANIMATION_JUMP_RIGHT = [('mario/jr.png', 0.1)] ANIMATION_JUMP = [('mario/j.png', 0.1)] ANIMATION_STAY = [('mario/0.png', 0.1)]
      
      







ここでは、主人公のさまざまな行動のアニメーションが理解できると思います。



次に、以下を__init__メソッドに追加します

 self.image.set_colorkey(Color(COLOR)) #    #    boltAnim = [] for anim in ANIMATION_RIGHT: boltAnim.append((anim, ANIMATION_DELAY)) self.boltAnimRight = pyganim.PygAnimation(boltAnim) self.boltAnimRight.play() #    boltAnim = [] for anim in ANIMATION_LEFT: boltAnim.append((anim, ANIMATION_DELAY)) self.boltAnimLeft = pyganim.PygAnimation(boltAnim) self.boltAnimLeft.play() self.boltAnimStay = pyganim.PygAnimation(ANIMATION_STAY) self.boltAnimStay.play() self.boltAnimStay.blit(self.image, (0, 0)) # -,  self.boltAnimJumpLeft= pyganim.PygAnimation(ANIMATION_JUMP_LEFT) self.boltAnimJumpLeft.play() self.boltAnimJumpRight= pyganim.PygAnimation(ANIMATION_JUMP_RIGHT) self.boltAnimJumpRight.play() self.boltAnimJump= pyganim.PygAnimation(ANIMATION_JUMP) self.boltAnimJump.play()
      
      







ここでは、各アクションに対して、一連のアニメーションを作成し、それらをオンにします(つまり、フレーム変更をオンにします)。

 for anim in ANIMATION_LEFT: boltAnim.append((anim, ANIMATION_DELAY
      
      



)))

各フレームには画像と表示時間があります。



目的のアニメーションを表示するのに適切なタイミングのままです。




アニメーションの変更をupdateメソッドに追加します



 if up: if self.onGround: # ,       self.yvel = -JUMP_POWER self.image.fill(Color(COLOR)) self.boltAnimJump.blit(self.image, (0, 0)) if left: self.xvel = -MOVE_SPEED #  = x- n self.image.fill(Color(COLOR)) if up: #       self.boltAnimJumpLeft.blit(self.image, (0, 0)) else: self.boltAnimLeft.blit(self.image, (0, 0)) if right: self.xvel = MOVE_SPEED #  = x + n self.image.fill(Color(COLOR)) if up: self.boltAnimJumpRight.blit(self.image, (0, 0)) else: self.boltAnimRight.blit(self.image, (0, 0)) if not(left or right): # ,     self.xvel = 0 if not up: self.image.fill(Color(COLOR)) self.boltAnimStay.blit(self.image, (0, 0))
      
      





出来上がり!

画像



より多くのスペースが必要



ダイナミックカメラを作成して、ウィンドウサイズの制限を克服します。



これを行うには、 Cameraクラスを作成します



 class Camera(object): def __init__(self, camera_func, width, height): self.camera_func = camera_func self.state = Rect(0, 0, width, height) def apply(self, target): return target.rect.move(self.state.topleft) def update(self, target): self.state = self.camera_func(self.state, target.rect)
      
      







次に、カメラの初期構成を追加します。



 def camera_configure(camera, target_rect): l, t, _, _ = target_rect _, _, w, h = camera l, t = -l+WIN_WIDTH / 2, -t+WIN_HEIGHT / 2 l = min(0, l) #      l = max(-(camera.width-WIN_WIDTH), l) #      t = max(-(camera.height-WIN_HEIGHT), t) #      t = min(0, t) #      return Rect(l, t, w, h)
      
      







カメラインスタンスを作成して、メインループの前に追加します。



 total_level_width = len(level[0])*PLATFORM_WIDTH #     total_level_height = len(level)*PLATFORM_HEIGHT #  camera = Camera(camera_configure, total_level_width, total_level_height)
      
      







私たちは何をしましたか?



次のように寸法が計算される大きな長方形の内部に作成しました。



 total_level_width = len(level[0])*PLATFORM_WIDTH #     total_level_height = len(level)*PLATFORM_HEIGHT # 
      
      







ウィンドウと同じサイズの小さな長方形。



小さな長方形はメインキャラクターに対して相対的に中央に配置され( 更新方法)、すべてのオブジェクトは小さな長方形に描画され( 適用方法)、カメラの動きの印象を作成します。



上記の場合、オブジェクトの描画を変更する必要があります。



行を置き換える

entities.draw(画面)#mapping



 for e in entities: screen.blit(e.image, camera.apply(e))
      
      







そしてそれを追加する前に

 camera.update(hero) #    
      
      







これでレベルを変更できます。



 level = [ "----------------------------------", "- -", "- -- -", "- -", "- -- -", "- -", "-- -", "- -", "- ---- --- -", "- -", "-- -", "- -", "- --- -", "- -", "- -", "- --- -", "- -", "- ------- ---- -", "- -", "- - -", "- -- -", "- -", "- -", "----------------------------------"]
      
      





実際、ここに結果があります

画像



結果をダウンロードできます、 GitHubへリンク



次の部分では、コミュニティから要求された場合、さまざまな種類のプラットフォーム、モンスター、テレポーター、そしてもちろんプリンセスを持つブラックジャックと売春婦で独自のレベルジェネレータ作成します。



upd pygameはここからダウンロードできます 、ありがとう、 Chris_Griffinのコメント

upd1 第2部



All Articles