実施報告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)
最後に、プレーヤーをレンダリングする必要があります。地面と背景の両方のレンダリングの「後」に、次のコード行を追加します。もちろん、これらのレンダリング関数はすべて、フレームごとに再描画する必要があるため、ゲーム ループ内にあります。
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 でウィンドウを作成する
ワールドの作成
ゲームループの変更
この記事が気に入ったらサポートをしてみませんか?