見出し画像

実施報告02:Pygame RPG

Pygame RPG Fighter – Game Tutorial

まえにPlatformerのサンプルをひとつ作りました。こんかいはRPGゲームを取り扱います。ここでは、完全に機能するRPGゲームを作ることではなく、RPGゲームを作るに必要な概念、アイデア、テクニックのコレクションを意図しています。できるだけ説明は省略して、コード主体にします。説明は原文を参照してください。

残念ながら最後まではできませんでした。なぜなのか、よくわかりませんでしたが、ここではアニメーションが目新しいとこかと思います。後半でもいろいろと面白い機能が紹介されたりもしていますが、とりあえずはplayer
の動き、アニメーションなどが勉強になるかと思います。

前提条件

これは高度な pygame チュートリアル シリーズであり、初心者にはあまり適していません。今までの二つのチュートリアルの少なくとも一つは実行することをお勧めします。

フォーマット

このようなゲームに必要なコードのサイズとそれに付随する説明のために、このチュートリアルを 12 個の小さなチュートリアルに分割し、それぞれが 1 つまたは 2 つの異なるゲーム トピックをカバーしています。

Pygame RPG チュートリアルのリスト

コアゲーム (1.0)

追加の RPG 要素 (2.0)

ゲームの拡張 (3.0)

Pygame RPG チュートリアル – ベースの構築

序章

このチュートリアルでは、Pygame RPG の全体的な「ベース」または「フレーム」の構築に焦点を当てます。実際にどんなタイプのRPGを作ろうとしているのかを説明するところから始めます。

ゲーム解説

完全な免責事項です。何らかの完全なオープン ワールド 3D RPG や、これに類するものを期待している場合は、今すぐ戻って、Unity または Unreal Game Engines を検索してください。
私たちが作成しているゲームは、基本的な RPG 要素とゲーム コンセプトを備えた単純な 2-D RPG ゲームです。

ライブラリのインポート

import pygame
from pygame.locals import *
import sys
import random
from tkinter import filedialog
from tkinter import *

変数と設定の初期化

ygame.init()  # Begin pygame
 
# Declaring variables to be used through the program
vec = pygame.math.Vector2
HEIGHT = 350
WIDTH = 700
ACC = 0.3
FRIC = -0.10
FPS = 60
FPS_CLOCK = pygame.time.Clock()
COUNT = 0
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Game")

クラスの初期化

class Background(pygame.sprite.Sprite):
      def __init__(self):
            super().__init__()      
 
 
class Ground(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
           
 
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__() 
     
 
class Enemy(pygame.sprite.Sprite):
      def __init__(self):
        super().__init__()

ゲームとイベント ループの作成

while True:
       
    for event in pygame.event.get():
        # Will run when the close window button is clicked    
        if event.type == QUIT:
            pygame.quit()
            sys.exit() 
             
        # For events that occur upon clicking the mouse (left click) 
        if event.type == pygame.MOUSEBUTTONDOWN:
              pass
 
        # Event handling for a range of different key presses    
        if event.type == pygame.KEYDOWN:
              pass

Pygame RPG チュートリアル – 世界の構築

背景の作成

class Background(pygame.sprite.Sprite):
      def __init__(self):
            super().__init__()
            self.bgimage = pygame.image.load("Background.png")        
            self.bgY = 0
            self.bgX = 0
 
      def render(self):
            displaysurface.blit(self.bgimage, (self.bgX, self.bgY))

土づくり

class Ground(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("Ground.png")
        self.rect = self.image.get_rect(center = (350, 350))
 
    def render(self):
        displaysurface.blit(self.image, (self.rect.x, self.rect.y))  

ビジュアルのレンダリング

以下に示すように、最初に両方のクラスのオブジェクトを作成する必要があります。

background = Background()
ground = Ground()

このコードをゲーム ループ内に含めないでください。

while True:
    for event in pygame.event.get():
        # Will run when the close window button is clicked    
        if event.type == QUIT:
            pygame.quit()
            sys.exit() 
             
        # For events that occur upon clicking the mouse (left click) 
        if event.type == pygame.MOUSEBUTTONDOWN:
              pass
 
        # Event handling for a range of different key presses    
        if event.type == pygame.KEYDOWN:
              pass
 
    # Render Functions ------
    background.render() 
    ground.render()
 
    pygame.display.update() 
    FPS_CLOCK.tick(FPS)

いままでのコードをまとめます。

import pygame
from pygame.locals import *
import sys
import random
import time
from tkinter import filedialog
from tkinter import *


pygame.init()  # Begin pygame

# Declaring variables to be used through the program
vec = pygame.math.Vector2
HEIGHT = 350
WIDTH = 700
ACC = 0.3
FRIC = -0.10
FPS = 60
FPS_CLOCK = pygame.time.Clock()
COUNT = 0

# Create the display
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Game")


# light shade of the button 
color_light = (170,170,170)
color_dark = (100,100,100)
color_white = (255,255,255) 
  
# defining a font
headingfont = pygame.font.SysFont("Verdana", 40)
regularfont = pygame.font.SysFont('Corbel',25)
smallerfont = pygame.font.SysFont('Corbel',16) 
text = regularfont.render('LOAD' , True , color_light)
 



class Background(pygame.sprite.Sprite):
      def __init__(self):
            super().__init__()
            self.bgimage = pygame.image.load("Background.png")
            self.rectBGimg = self.bgimage.get_rect()        
            self.bgY = 0
            self.bgX = 0

      def render(self):
            displaysurface.blit(self.bgimage, (self.bgX, self.bgY))


class Ground(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("Ground.png")
        self.rect = self.image.get_rect(center = (350, 350))

    def render(self):
        displaysurface.blit(self.image, (self.rect.x, self.rect.y))     
          

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__() 
    

class Enemy(pygame.sprite.Sprite):
      def __init__(self):
        super().__init__()
 
      
    
player = Player()
background = Background()
ground = Ground()



while True:
      
    for event in pygame.event.get():
        # Will run when the close window button is clicked    
        if event.type == QUIT:
            pygame.quit()
            sys.exit() 
            
        # For events that occur upon clicking the mouse (left click) 
        if event.type == pygame.MOUSEBUTTONDOWN:
              pass


        # Event handling for a range of different key presses    
        if event.type == pygame.KEYDOWN:
              pass
            
    background.render()
    ground.render()


    pygame.display.update()      
    FPS_CLOCK.tick(FPS)

現在のとこの画面を表示します。

Pygame RPG チュートリアル – Player クラス

この RPG チュートリアルで最も重要なクラスは Player クラスです。このクラスは、動き、攻撃、衝突検出、レンダリング、状態追跡など、プレイヤーに関連するほぼすべての処理を担当します。

プレーヤー クラスの作成

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("Player_Sprite_R.png")
        self.rect = self.image.get_rect()
 
        # Position and direction
        self.vx = 0
        self.pos = vec((340, 240))
        self.vel = vec(0,0)
        self.acc = vec(0,0)
        self.direction = "RIGHT"
def move(self):
      pass
 
def update(self):
      pass
 
def attack(self):
      pass
 
def jump(self):
      pass

プレイヤー オブジェクトの作成

player = Player()

これは、ゲーム ループ内ではなく、ゲーム ループの前に宣言することを忘れないでください。

background.render()
ground.render()
displaysurface.blit(player.image, player.rect)

最後に、プレーヤーをレンダリングする必要があります。地面と背景の両方のレンダリングの「後」に、次のコード行を追加します。もちろん、これらのレンダリング関数はすべて、フレームごとに再描画する必要があるため、ゲーム ループ内にあります。

Playerが表示されました

Pygame RPG チュートリアル – プレイヤーの移動

プレーヤーの実行

以下のコードは、プレーヤーの速度が (通常の走行速度と比較して) 非常に低い状態にあるときに、プレーヤーが「走っている」と見なされないようにします。

def move(self):
 
      # Will set running to False if the player has slowed down to a certain extent
      if abs(self.vel.x) > 0.3:
            self.running = True
      else:
            self.running = False

キー・プレス

# Returns the current key presses
pressed_keys = pygame.key.get_pressed()
 
# Accelerates the player in the direction of the key press
if pressed_keys[K_LEFT]:
      self.acc.x = -ACC
if pressed_keys[K_RIGHT]:
      self.acc.x = ACC 
# Formulas to calculate velocity while accounting for friction
self.acc.x += self.vel.x * FRIC
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc  # Updates Position with new values

プレイヤーのワープ

# This causes character warping from one point of the screen to the other
if self.pos.x > WIDTH:
      self.pos.x = 0
if self.pos.x < 0:
      self.pos.x = WIDTH
 
self.rect.midbottom = self.pos  # Update rect with new pos   

完全なmove関数を次にまとめます。

def move(self):
      # Keep a constant acceleration of 0.5 in the downwards direction (gravity)
      #self.acc = vec(0,0.5)
 
      # Will set running to False if the player has slowed down to a certain extent
      if abs(self.vel.x) > 0.3:
            self.running = True
      else:
            self.running = False
 
      # Returns the current key presses
      pressed_keys = pygame.key.get_pressed()
 
      # Accelerates the player in the direction of the key press
      if pressed_keys[K_LEFT]:
            self.acc.x = -ACC
      if pressed_keys[K_RIGHT]:
            self.acc.x = ACC 
 
      # Formulas to calculate velocity while accounting for friction
      self.acc.x += self.vel.x * FRIC
      self.vel += self.acc
      self.pos += self.vel + 0.5 * self.acc  # Updates Position with new values
 
      # This causes character warping from one point of the screen to the other
      if self.pos.x > WIDTH:
            self.pos.x = 0
      if self.pos.x < 0:
            self.pos.x = WIDTH
     
      self.rect.midbottom = self.pos  # Update rect with new pos 

ゲームループに次を加えます。

while True:      
      ...
      ...
      player.move()
      ...
      displaysurface.blit(player.image, player.rect)
import pygame
from pygame.locals import *
import sys
import random
import time
from tkinter import filedialog
from tkinter import *


pygame.init()  # Begin pygame

# Declaring variables to be used through the program
vec = pygame.math.Vector2
HEIGHT = 350
WIDTH = 700
ACC = 0.3
FRIC = -0.10
FPS = 60
FPS_CLOCK = pygame.time.Clock()
COUNT = 0

# Create the display
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Game")


# light shade of the button 
color_light = (170,170,170)
color_dark = (100,100,100)
color_white = (255,255,255) 
  
# defining a font
headingfont = pygame.font.SysFont("Verdana", 40)
regularfont = pygame.font.SysFont('Corbel',25)
smallerfont = pygame.font.SysFont('Corbel',16) 
text = regularfont.render('LOAD' , True , color_light)
 



class Background(pygame.sprite.Sprite):
      def __init__(self):
            super().__init__()
            self.bgimage = pygame.image.load("Background.png")
            self.rectBGimg = self.bgimage.get_rect()        
            self.bgY = 0
            self.bgX = 0

      def render(self):
            displaysurface.blit(self.bgimage, (self.bgX, self.bgY))


class Ground(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("Ground.png")
        self.rect = self.image.get_rect(center = (350, 350))

    def render(self):
        displaysurface.blit(self.image, (self.rect.x, self.rect.y))     
          

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("Player_Sprite_R.png")
        self.rect = self.image.get_rect()
 
        # Position and direction
        self.vx = 0
        self.pos = vec((340, 240))
        self.vel = vec(0,0)
        self.acc = vec(0,0)
        self.direction = "RIGHT"
    	
	
    def move(self):
      # Keep a constant acceleration of 0.5 in the downwards direction (gravity)
      #self.acc = vec(0,0.5)
 
      # Will set running to False if the player has slowed down to a certain extent
      if abs(self.vel.x) > 0.3:
            self.running = True
      else:
            self.running = False
 
      # Returns the current key presses
      pressed_keys = pygame.key.get_pressed()
 
      # Accelerates the player in the direction of the key press
      if pressed_keys[K_LEFT]:
            self.acc.x = -ACC
      if pressed_keys[K_RIGHT]:
            self.acc.x = ACC 
 
      # Formulas to calculate velocity while accounting for friction
      self.acc.x += self.vel.x * FRIC
      self.vel += self.acc
      self.pos += self.vel + 0.5 * self.acc  # Updates Position with new values
 
      # This causes character warping from one point of the screen to the other
      if self.pos.x > WIDTH:
            self.pos.x = 0
      if self.pos.x < 0:
            self.pos.x = WIDTH
     
      self.rect.midbottom = self.pos  # Update rect with new pos 
        
    def update(self):
        pass
    
    def attack(self):
        pass
    
    def jump(self):
        pass
    

class Enemy(pygame.sprite.Sprite):
      def __init__(self):
        super().__init__()
 
      
    
player = Player()
background = Background()
ground = Ground()



while True:
      
    for event in pygame.event.get():
        # Will run when the close window button is clicked    
        if event.type == QUIT:
            pygame.quit()
            sys.exit() 
            
        # For events that occur upon clicking the mouse (left click) 
        if event.type == pygame.MOUSEBUTTONDOWN:
              pass


        # Event handling for a range of different key presses    
        if event.type == pygame.KEYDOWN:
            pass
        
     
    player.move()
                        
    background.render()
    ground.render()
    displaysurface.blit(player.image, player.rect)


    pygame.display.update()      
    FPS_CLOCK.tick(FPS)

この結果を次に示します。

Pygame RPG チュートリアル – 重力と跳躍

重力メカニック

まず、つぎのCodeをmoveメソッドの一番上に追加します。

def move(self):
      # Keep a constant acceleration of 0.5 in the downwards direction (gravity)
      self.acc = vec(0,0.5)

もう一つ、つぎの変数を加えます。

self.jumping = False

地面との衝突

ground = Ground()
ground_group = pygame.sprite.Group()
ground_group.add(ground)
def gravity_check(self):
      hits = pygame.sprite.spritecollide(player ,ground_group, False)
      if self.vel.y > 0:
          if hits:
              lowest = hits[0]
              if self.pos.y < lowest.rect.bottom:
                  self.pos.y = lowest.rect.top + 1
                  self.vel.y = 0
                  self.jumping = False
def jump(self):
    self.rect.x += 1
 
    # Check to see if payer is in contact with the ground
    hits = pygame.sprite.spritecollide(self, ground_group, False)
     
    self.rect.x -= 1
 
    # If touching the ground, and not currently jumping, cause the player to jump.
    if hits and not self.jumping:
       self.jumping = True
       self.vel.y = -12
while True:
    player.gravity_check() 
    ...
    ...
    ...
    # Event handling for a range of different key presses    
    if event.type == pygame.KEYDOWN:
           if event.key == pygame.K_SPACE:
                  player.jump()

整理するとつぎのようになります。

import pygame
from pygame.locals import *
import sys
import random
import time
from tkinter import filedialog
from tkinter import *


pygame.init()  # Begin pygame

# Declaring variables to be used through the program
vec = pygame.math.Vector2
HEIGHT = 350
WIDTH = 700
ACC = 0.3
FRIC = -0.10
FPS = 60
FPS_CLOCK = pygame.time.Clock()
COUNT = 0

# Create the display
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Game")


# light shade of the button 
color_light = (170,170,170)
color_dark = (100,100,100)
color_white = (255,255,255) 
  
# defining a font
headingfont = pygame.font.SysFont("Verdana", 40)
regularfont = pygame.font.SysFont('Corbel',25)
smallerfont = pygame.font.SysFont('Corbel',16) 
text = regularfont.render('LOAD' , True , color_light)
 



class Background(pygame.sprite.Sprite):
      def __init__(self):
            super().__init__()
            self.bgimage = pygame.image.load("Background.png")
            self.rectBGimg = self.bgimage.get_rect()        
            self.bgY = 0
            self.bgX = 0

      def render(self):
            displaysurface.blit(self.bgimage, (self.bgX, self.bgY))


class Ground(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("Ground.png")
        self.rect = self.image.get_rect(center = (350, 350))

    def render(self):
        displaysurface.blit(self.image, (self.rect.x, self.rect.y))     
          

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("Player_Sprite_R.png")
        self.rect = self.image.get_rect()
 
        # Position and direction
        self.vx = 0
        self.pos = vec((340, 240))
        self.vel = vec(0,0)
        self.acc = vec(0,0)
        self.direction = "RIGHT"
    	
	
    def move(self):
      # Keep a constant acceleration of 0.5 in the downwards direction (gravity)
        self.acc = vec(0,0.5)
        self.jumping = False
 
      # Will set running to False if the player has slowed down to a certain extent
        if abs(self.vel.x) > 0.3:
            self.running = True
        else:
            self.running = False
 
      # Returns the current key presses
        pressed_keys = pygame.key.get_pressed()
    
        # Accelerates the player in the direction of the key press
        if pressed_keys[K_LEFT]:
                self.acc.x = -ACC
        if pressed_keys[K_RIGHT]:
                self.acc.x = ACC 
    
      # Formulas to calculate velocity while accounting for friction
        self.acc.x += self.vel.x * FRIC
        self.vel += self.acc
        self.pos += self.vel + 0.5 * self.acc  # Updates Position with new values
    
      # This causes character warping from one point of the screen to the other
        if self.pos.x > WIDTH:
                self.pos.x = 0
        if self.pos.x < 0:
                self.pos.x = WIDTH
        
        self.rect.midbottom = self.pos  # Update rect with new pos 
        
    def update(self):
        pass
    
    def attack(self):
        pass
    
    def jump(self):
        self.rect.x += 1
    
        # Check to see if payer is in contact with the ground
        hits = pygame.sprite.spritecollide(self, ground_group, False)
        
        self.rect.x -= 1
    
        # If touching the ground, and not currently jumping, cause the player to jump.
        if hits and not self.jumping:
            self.jumping = True
            self.vel.y = -12
        
    def gravity_check(self):
      hits = pygame.sprite.spritecollide(player ,ground_group, False)
      if self.vel.y > 0:
          if hits:
              lowest = hits[0]
              if self.pos.y < lowest.rect.bottom:
                  self.pos.y = lowest.rect.top + 1
                  self.vel.y = 0
                  self.jumping = False

class Enemy(pygame.sprite.Sprite):
      def __init__(self):
        super().__init__()
 
      
    
player = Player()
background = Background()
ground = Ground()
ground_group = pygame.sprite.Group()
ground_group.add(ground)



while True:
    player.gravity_check()
      
    for event in pygame.event.get():
        # Will run when the close window button is clicked    
        if event.type == QUIT:
            pygame.quit()
            sys.exit() 
            
        # For events that occur upon clicking the mouse (left click) 
        if event.type == pygame.MOUSEBUTTONDOWN:
              pass


        # Event handling for a range of different key presses    
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                  player.jump()
        
     
    player.move()
                        
    background.render()
    ground.render()
    displaysurface.blit(player.image, player.rect)


    pygame.display.update()      
    FPS_CLOCK.tick(FPS)

ここまでの結果はつぎのようになります。ここまではPlatformerの場合と動きはあまり変わらないようですが、renderというのが目新しい感じがしますね。

Pygame RPG チュートリアル – 動きのアニメーション

プレイヤークラス

# Movement 
self.jumping = False
self.running = False
self.move_frame = 0

アニメーションの読み込み

# Run animation for the RIGHT
run_ani_R = [pygame.image.load("Player_Sprite_R.png"), pygame.image.load("Player_Sprite2_R.png"),
             pygame.image.load("Player_Sprite3_R.png"),pygame.image.load("Player_Sprite4_R.png"),
             pygame.image.load("Player_Sprite5_R.png"),pygame.image.load("Player_Sprite6_R.png"),
             pygame.image.load("Player_Sprite_R.png")]
 
# Run animation for the LEFT
run_ani_L = [pygame.image.load("Player_Sprite_L.png"), pygame.image.load("Player_Sprite2_L.png"),
             pygame.image.load("Player_Sprite3_L.png"),pygame.image.load("Player_Sprite4_L.png"),
             pygame.image.load("Player_Sprite5_L.png"),pygame.image.load("Player_Sprite6_L.png"),
             pygame.image.load("Player_Sprite_L.png")]

アニメーション更新機能

def update(self):
      # Return to base frame if at end of movement sequence 
      if self.move_frame > 6:
            self.move_frame = 0
            return

フレームチェンジ

# Move the character to the next frame if conditions are met 
if self.jumping == False and self.running == True:  
      if self.vel.x > 0:
            self.image = run_ani_R[self.move_frame]
            self.direction = "RIGHT"
      else:
            self.image = run_ani_L[self.move_frame]
            self.direction = "LEFT"
      self.move_frame += 1

フレームチェック

更新メソッドのこの最後のセクションの目的は、プレイヤーが (ほぼ) 静止しているが、正しくない動きが表示されている場合に、プレイヤーを元の立ち位置に戻すことです。

# Returns to base frame if standing still and incorrect frame is showing
 if abs(self.vel.x) < 0.2 and self.move_frame != 0:
       self.move_frame = 0
       if self.direction == "RIGHT":
             self.image = run_ani_R[self.move_frame]
       elif self.direction == "LEFT":
             self.image = run_ani_L[self.move_frame]

Codeをまとめるとつぎのようになります。

import pygame
from pygame.locals import *
import sys
import random
import time
from tkinter import filedialog
from tkinter import *
 
pygame.init()  # Begin pygame
 
# Declaring variables to be used through the program
vec = pygame.math.Vector2
HEIGHT = 350
WIDTH = 700
ACC = 0.3
FRIC = -0.10
FPS = 60
FPS_CLOCK = pygame.time.Clock()
COUNT = 0
 
# Create the display
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Game")
 
 
# Run animation for the RIGHT
run_ani_R = [pygame.image.load("Player_Sprite_R.png"), pygame.image.load("Player_Sprite2_R.png"),
             pygame.image.load("Player_Sprite3_R.png"),pygame.image.load("Player_Sprite4_R.png"),
             pygame.image.load("Player_Sprite5_R.png"),pygame.image.load("Player_Sprite6_R.png"),
             pygame.image.load("Player_Sprite_R.png")]
 
# Run animation for the LEFT
run_ani_L = [pygame.image.load("Player_Sprite_L.png"), pygame.image.load("Player_Sprite2_L.png"),
             pygame.image.load("Player_Sprite3_L.png"),pygame.image.load("Player_Sprite4_L.png"),
             pygame.image.load("Player_Sprite5_L.png"),pygame.image.load("Player_Sprite6_L.png"),
             pygame.image.load("Player_Sprite_L.png")]
 
 
  
 
class Background(pygame.sprite.Sprite):
      def __init__(self):
            super().__init__()
            self.bgimage = pygame.image.load("Background.png")
            self.rectBGimg = self.bgimage.get_rect()        
            self.bgY = 0
            self.bgX = 0
 
      def render(self):
            displaysurface.blit(self.bgimage, (self.bgX, self.bgY))      
 
 
class Ground(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("Ground.png")
        self.rect = self.image.get_rect(center = (350, 350))
        self.bgX1 = 0
        self.bgY1 = 285
 
    def render(self):
        displaysurface.blit(self.image, (self.bgX1, self.bgY1)) 
 
 
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("Player_Sprite_R.png")
        self.rect = self.image.get_rect()
 
        # Position and direction
        self.vx = 0
        self.pos = vec((340, 240))
        self.vel = vec(0,0)
        self.acc = vec(0,0)
        self.direction = "RIGHT"
 
        # Movement 
        self.jumping = False
        self.running = False
        self.move_frame = 0
 
 
    def move(self):
          # Keep a constant acceleration of 0.5 in the downwards direction (gravity)
          self.acc = vec(0,0.5)
 
          # Will set running to False if the player has slowed down to a certain extent
          if abs(self.vel.x) > 0.3:
                self.running = True
          else:
                self.running = False
 
          # Returns the current key presses
          pressed_keys = pygame.key.get_pressed()
 
          # Accelerates the player in the direction of the key press
          if pressed_keys[K_LEFT]:
                self.acc.x = -ACC
          if pressed_keys[K_RIGHT]:
                self.acc.x = ACC 
 
          # Formulas to calculate velocity while accounting for friction
          self.acc.x += self.vel.x * FRIC
          self.vel += self.acc
          self.pos += self.vel + 0.5 * self.acc  # Updates Position with new values
 
          # This causes character warping from one point of the screen to the other
          if self.pos.x > WIDTH:
                self.pos.x = 0
          if self.pos.x < 0:
                self.pos.x = WIDTH
         
          self.rect.midbottom = self.pos  # Update rect with new pos            
 
    def gravity_check(self):
          hits = pygame.sprite.spritecollide(player ,ground_group, False)
          if self.vel.y > 0:
              if hits:
                  lowest = hits[0]
                  if self.pos.y < lowest.rect.bottom:
                      self.pos.y = lowest.rect.top + 1
                      self.vel.y = 0
                      self.jumping = False
 
 
    def update(self):
          # Return to base frame if at end of movement sequence 
          if self.move_frame > 6:
                self.move_frame = 0
                return
 
          # Move the character to the next frame if conditions are met 
          if self.jumping == False and self.running == True:  
                if self.vel.x > 0:
                      self.image = run_ani_R[self.move_frame]
                      self.direction = "RIGHT"
                else:
                      self.image = run_ani_L[self.move_frame]
                      self.direction = "LEFT"
                self.move_frame += 1
 
          # Returns to base frame if standing still and incorrect frame is showing
          if abs(self.vel.x) < 0.2 and self.move_frame != 0:
                self.move_frame = 0
                if self.direction == "RIGHT":
                      self.image = run_ani_R[self.move_frame]
                elif self.direction == "LEFT":
                      self.image = run_ani_L[self.move_frame]
 
    def attack(self):
          pass
 
    def jump(self):
        self.rect.x += 1
 
        # Check to see if payer is in contact with the ground
        hits = pygame.sprite.spritecollide(self, ground_group, False)
         
        self.rect.x -= 1
 
        # If touching the ground, and not currently jumping, cause the player to jump.
        if hits and not self.jumping:
           self.jumping = True
           self.vel.y = -12
       
 
class Enemy(pygame.sprite.Sprite):
      def __init__(self):
        super().__init__()
  
       
     
player = Player()
Playergroup = pygame.sprite.Group()
 
background = Background()
 
ground = Ground()
ground_group = pygame.sprite.Group()
ground_group.add(ground)
 
 
while True:
    player.gravity_check() 
       
    for event in pygame.event.get():
        # Will run when the close window button is clicked    
        if event.type == QUIT:
            pygame.quit()
            sys.exit() 
             
        # For events that occur upon clicking the mouse (left click) 
        if event.type == pygame.MOUSEBUTTONDOWN:
              pass
 
 
        # Event handling for a range of different key presses    
        if event.type == pygame.KEYDOWN:
              if event.key == pygame.K_SPACE:
                    player.jump()
         
    # Player related functions        
    player.update()         
    player.move()
     
    # Display and Background related functions 
    background.render()
    ground.render()
     
    # Rendering Player
    displaysurface.blit(player.image, player.rect)
 
    pygame.display.update()      
    FPS_CLOCK.tick(FPS)

結果を下記に示します。

Pygame RPG チュートリアル – 攻撃アニメーション

攻撃アニメーション

# Attack animation for the RIGHT
attack_ani_R = [pygame.image.load("Player_Sprite_R.png"), pygame.image.load("Player_Attack_R.png"),
                pygame.image.load("Player_Attack2_R.png"),pygame.image.load("Player_Attack2_R.png"),
                pygame.image.load("Player_Attack3_R.png"),pygame.image.load("Player_Attack3_R.png"),
                pygame.image.load("Player_Attack4_R.png"),pygame.image.load("Player_Attack4_R.png"),
                pygame.image.load("Player_Attack5_R.png"),pygame.image.load("Player_Attack5_R.png"),
                pygame.image.load("Player_Sprite_R.png")]
 
# Attack animation for the LEFT
attack_ani_L = [pygame.image.load("Player_Sprite_L.png"), pygame.image.load("Player_Attack_L.png"),
                pygame.image.load("Player_Attack2_L.png"),pygame.image.load("Player_Attack2_L.png"),
                pygame.image.load("Player_Attack3_L.png"),pygame.image.load("Player_Attack3_L.png"),
                pygame.image.load("Player_Attack4_L.png"),pygame.image.load("Player_Attack4_L.png"),
                pygame.image.load("Player_Attack5_L.png"),pygame.image.load("Player_Attack5_L.png"),
                pygame.image.load("Player_Sprite_L.png")]

プレイヤークラス

# Combat
self.attacking = False
self.attack_frame = 0
def attack(self):        
      # If attack frame has reached end of sequence, return to base frame      
      if self.attack_frame > 10:
            self.attack_frame = 0
            self.attacking = False
 
      # Check direction for correct animation to display  
      if self.direction == "RIGHT":
             self.image = attack_ani_R[self.attack_frame]
      elif self.direction == "LEFT":
             self.correction()
             self.image = attack_ani_L[self.attack_frame] 
 
      # Update the current attack frame  
      self.attack_frame += 1
def correction(self):
      # Function is used to correct an error
      # with character position on left attack frames
      if self.attack_frame == 1:
            self.pos.x -= 20
      if self.attack_frame == 10:
            self.pos.x += 20

上記の修正が必要なようです。

ゲームループの更新

if event.type == pygame.KEYDOWN:
      if event.key == pygame.K_SPACE:
            player.jump()
      if event.key == pygame.K_RETURN:
          if player.attacking == False:
              player.attack()
              player.attacking = True 

つぎを忘れないように(^^♪

# Player related functions
player.update()
if player.attacking == True:
      player.attack() 
player.move() 

Codeを整理します

import pygame
from pygame.locals import *
import sys
import random
import time
from tkinter import filedialog
from tkinter import *
 
pygame.init()  # Begin pygame
 
# Declaring variables to be used through the program
vec = pygame.math.Vector2
HEIGHT = 350
WIDTH = 700
ACC = 0.3
FRIC = -0.10
FPS = 60
FPS_CLOCK = pygame.time.Clock()
COUNT = 0
 
# Create the display
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Game")
 
 
# Run animation for the RIGHT
run_ani_R = [pygame.image.load("Player_Sprite_R.png"), pygame.image.load("Player_Sprite2_R.png"),
             pygame.image.load("Player_Sprite3_R.png"),pygame.image.load("Player_Sprite4_R.png"),
             pygame.image.load("Player_Sprite5_R.png"),pygame.image.load("Player_Sprite6_R.png"),
             pygame.image.load("Player_Sprite_R.png")]
 
# Run animation for the LEFT
run_ani_L = [pygame.image.load("Player_Sprite_L.png"), pygame.image.load("Player_Sprite2_L.png"),
             pygame.image.load("Player_Sprite3_L.png"),pygame.image.load("Player_Sprite4_L.png"),
             pygame.image.load("Player_Sprite5_L.png"),pygame.image.load("Player_Sprite6_L.png"),
             pygame.image.load("Player_Sprite_L.png")]
 
# Attack animation for the RIGHT
attack_ani_R = [pygame.image.load("Player_Sprite_R.png"), pygame.image.load("Player_Attack_R.png"),
                pygame.image.load("Player_Attack2_R.png"),pygame.image.load("Player_Attack2_R.png"),
                pygame.image.load("Player_Attack3_R.png"),pygame.image.load("Player_Attack3_R.png"),
                pygame.image.load("Player_Attack4_R.png"),pygame.image.load("Player_Attack4_R.png"),
                pygame.image.load("Player_Attack5_R.png"),pygame.image.load("Player_Attack5_R.png"),
                pygame.image.load("Player_Sprite_R.png")]
 
# Attack animation for the LEFT
attack_ani_L = [pygame.image.load("Player_Sprite_L.png"), pygame.image.load("Player_Attack_L.png"),
                pygame.image.load("Player_Attack2_L.png"),pygame.image.load("Player_Attack2_L.png"),
                pygame.image.load("Player_Attack3_L.png"),pygame.image.load("Player_Attack3_L.png"),
                pygame.image.load("Player_Attack4_L.png"),pygame.image.load("Player_Attack4_L.png"),
                pygame.image.load("Player_Attack5_L.png"),pygame.image.load("Player_Attack5_L.png"),
                pygame.image.load("Player_Sprite_L.png")]
  
 
class Background(pygame.sprite.Sprite):
      def __init__(self):
            super().__init__()
            self.bgimage = pygame.image.load("Background.png")
            self.rectBGimg = self.bgimage.get_rect()        
            self.bgY = 0
            self.bgX = 0
 
      def render(self):
            displaysurface.blit(self.bgimage, (self.bgX, self.bgY))      
 
 
class Ground(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("Ground.png")
        self.rect = self.image.get_rect(center = (350, 350))
        self.bgX1 = 0
        self.bgY1 = 285
 
    def render(self):
        displaysurface.blit(self.image, (self.bgX1, self.bgY1)) 
 
 
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("Player_Sprite_R.png")
        self.rect = self.image.get_rect()
 
        # Position and direction
        self.vx = 0
        self.pos = vec((340, 240))
        self.vel = vec(0,0)
        self.acc = vec(0,0)
        self.direction = "RIGHT"
 
        # Movement 
        self.jumping = False
        self.running = False
        self.move_frame = 0

        # Combat
        self.attacking = False
        self.attack_frame = 0
        
 
    def move(self):
          # Keep a constant acceleration of 0.5 in the downwards direction (gravity)
          self.acc = vec(0,0.5)
 
          # Will set running to False if the player has slowed down to a certain extent
          if abs(self.vel.x) > 0.3:
                self.running = True
          else:
                self.running = False
 
          # Returns the current key presses
          pressed_keys = pygame.key.get_pressed()
 
          # Accelerates the player in the direction of the key press
          if pressed_keys[K_LEFT]:
                self.acc.x = -ACC
          if pressed_keys[K_RIGHT]:
                self.acc.x = ACC 
 
          # Formulas to calculate velocity while accounting for friction
          self.acc.x += self.vel.x * FRIC
          self.vel += self.acc
          self.pos += self.vel + 0.5 * self.acc  # Updates Position with new values
 
          # This causes character warping from one point of the screen to the other
          if self.pos.x > WIDTH:
                self.pos.x = 0
          if self.pos.x < 0:
                self.pos.x = WIDTH
         
          self.rect.midbottom = self.pos  # Update rect with new pos            
 
    def gravity_check(self):
          hits = pygame.sprite.spritecollide(player ,ground_group, False)
          if self.vel.y > 0:
              if hits:
                  lowest = hits[0]
                  if self.pos.y < lowest.rect.bottom:
                      self.pos.y = lowest.rect.top + 1
                      self.vel.y = 0
                      self.jumping = False
 
 
    def update(self):
          # Return to base frame if at end of movement sequence 
          if self.move_frame > 6:
                self.move_frame = 0
                return
 
          # Move the character to the next frame if conditions are met 
          if self.jumping == False and self.running == True:  
                if self.vel.x > 0:
                      self.image = run_ani_R[self.move_frame]
                      self.direction = "RIGHT"
                else:
                      self.image = run_ani_L[self.move_frame]
                      self.direction = "LEFT"
                self.move_frame += 1
 
          # Returns to base frame if standing still and incorrect frame is showing
          if abs(self.vel.x) < 0.2 and self.move_frame != 0:
                self.move_frame = 0
                if self.direction == "RIGHT":
                      self.image = run_ani_R[self.move_frame]
                elif self.direction == "LEFT":
                      self.image = run_ani_L[self.move_frame]
 
    def attack(self):        
        # If attack frame has reached end of sequence, return to base frame      
        if self.attack_frame > 10:
                self.attack_frame = 0
                self.attacking = False
    
        # Check direction for correct animation to display  
        if self.direction == "RIGHT":
                self.image = attack_ani_R[self.attack_frame]
        elif self.direction == "LEFT":
                self.correction()
                self.image = attack_ani_L[self.attack_frame] 
    
        # Update the current attack frame  
        self.attack_frame += 1
 
    def correction(self):
        # Function is used to correct an error
        # with character position on left attack frames
        if self.attack_frame == 1:
                self.pos.x -= 20
        if self.attack_frame == 10:
                self.pos.x += 20


    def jump(self):
        self.rect.x += 1
 
        # Check to see if payer is in contact with the ground
        hits = pygame.sprite.spritecollide(self, ground_group, False)
         
        self.rect.x -= 1
 
        # If touching the ground, and not currently jumping, cause the player to jump.
        if hits and not self.jumping:
           self.jumping = True
           self.vel.y = -12
       
 
class Enemy(pygame.sprite.Sprite):
      def __init__(self):
        super().__init__()
  
       
     
player = Player()
Playergroup = pygame.sprite.Group()
 
background = Background()
 
ground = Ground()
ground_group = pygame.sprite.Group()
ground_group.add(ground)
 
 
while True:
    player.gravity_check() 
       
    for event in pygame.event.get():
        # Will run when the close window button is clicked    
        if event.type == QUIT:
            pygame.quit()
            sys.exit() 
             
        # For events that occur upon clicking the mouse (left click) 
        if event.type == pygame.MOUSEBUTTONDOWN:
              pass
 
 
        # Event handling for a range of different key presses    
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                    player.jump()
            if event.key == pygame.K_RETURN:
                if player.attacking == False:
                    player.attack()
                    player.attacking = True  
                
    # Player related functions        
    player.update()
    if player.attacking == True:
      player.attack()       
    player.move() 
     
    # Display and Background related functions 
    background.render()
    ground.render()
     
    # Rendering Player
    displaysurface.blit(player.image, player.rect)
 
    pygame.display.update()      
    FPS_CLOCK.tick(FPS)

結果を示します。


以下、下記のように続きますが、敵をうまく作ることができませんでした。
今回はここでおしまいとします。また、情報をみつけたら、再度、チャレンジしたいと思います。

                       20230501 作成


Pygame RPG チュートリアル – 敵クラス


移動機能

レンダリング

Pygame RPG チュートリアル – 衝突検出

プレイヤーのクールダウン

敵衝突チェック

プレイヤーの衝突処理

ゲームループ

Pygame RPG チュートリアル – ステージ生成

ダンジョンの入り口を作る

Tkinter でウィンドウを作成する

ワールドの作成

ゲームループの変更


この記事が気に入ったらサポートをしてみませんか?