0
0

pygameでRPGを作ってみる1

Last updated at Posted at 2024-09-22

目次

どんな人が書いてる?
環境
参考にしたサイト
pyrpg27の修正個所
画面の表示
テキスト表示
効果音を鳴らす
BGMを鳴らす
選択肢を作る

どんな人が書いてる?

ここをクリックしてください

1.おじさん
2.python初心者

環境

ここをクリックしてください

python3.11
IDLE

参考にしたサイト

ここをクリックしてください

人工知能に関する断創録
PythonとPygameで作る レトロ風RPG 全コード
Pygameド素人がRPGを作る1
Pygameド素人がRPGを作る2
PygameでRPG制作 ーーそれっぽい画面を作ってみよう
PygameでRPG制作 ーーそれっぽい画面を作ってみよう② バトル画面のメニューを作ろう
PygameでRPG制作 ーーそれっぽい画面を作ってみよう③効果音を使ってみよう
PygameでRPG制作 ーーそれっぽい画面を作ってみよう④ 各アクションのでの動作とHP機能の実装
PygameでRPG制作 ーーそれっぽい画面を作ってみよう⑤ HP表示とスライムリッキーの反撃、終了処理

 人工知能に関する断創録は特に役に立ちます。ぼくのコードは、ほとんどaidiaryさんのコードをコピペしたものです。
 すぐ動かせるようにpyrpg27のエラーを修正したものを書いておきます。
pyrpg27 サイトへ移動

pyrpg27の修正個所

ここをクリックしてください

Ln23(23行目) 行数はdef()の行数です。修正個所ではありません。

 def load_image(dir, file, colorkey=None):
    file = os.path.join(dir, file)
    try:
        image = pygame.image.load(file)
    except (pygame.error, message):#ここ
        print ("Cannot load image:"), file#ここ
        raise (SystemExit, message)#ここ
    image = image.convert()
    if colorkey is not None:
        if colorkey == -1:#ここ
        colorkey = image.get_at((0,0))
        image.set_colorkey(colorkey, RLEACCEL)
    return image

Ln365(365行目)

    def draw(self, screen, offset):
        """マップを描画する"""
        offsetx, offsety = offset
        # マップの描画範囲を計算
        startx = int(offsetx / GS)#ここ
        endx = int(startx + SCR_RECT.width/GS + 1)#ここ
        starty = int(offsety / GS) - 1#ここ
        endy = int(starty + SCR_RECT.height/GS) + 1 #ここ

Ln556(556行目)

    def update(self, map):
        """キャラクター状態を更新する。
        mapは移動可能かの判定に必要。"""
        # プレイヤーの移動処理
        if self.moving == True:
            # ピクセル移動中ならマスにきっちり収まるまで移動を続ける
            self.rect.move_ip(self.vx, self.vy)
            if self.rect.left % GS == 0 and self.rect.top % GS == 0:  # マスにおさまったら移動完了
                self.moving = False
                self.x = self.rect.left // GS#ここ
                self.y = self.rect.top // GS#ここ
        # キャラクターアニメーション(frameに応じて描画イメージを切り替える)
        self.frame += 1
        self.image = self.images[self.name][self.direction*4+self.frame//self.animcycle%4]#ここ

Ln603(行目)

    def update(self, map, battle):
        """プレイヤー状態を更新する。
        mapは移動可能かの判定に必要。
        battleはエンカウントに必要。"""
        global game_state
        # プレイヤーの移動処理
        if self.moving == True:
            # ピクセル移動中ならマスにきっちり収まるまで移動を続ける
            self.rect.move_ip(self.vx, self.vy)
            if self.rect.left % GS == 0 and self.rect.top % GS == 0:  # マスにおさまったら移動完了
                self.moving = False
                self.x = self.rect.left // GS#ここ
                self.y = self.rect.top // GS#ここ
        # キャラクターアニメーション(frameに応じて描画イメージを切り替える)
        self.frame += 1
        self.image = self.images[self.name][self.direction*4+self.frame//self.animcycle%4]#ここ

Ln764(行目)

    def draw_character(self, screen, pos, ch):
        """1文字だけ描画する"""
        x, y = pos
        try:
            rect = self.kana2rect[ch]
            screen.blit(self.image, (x,y), (rect.x+self.color,rect.y,rect.width,rect.height))
        except KeyError:
            print ("描画できない文字があります:%s" % ch)#ここ
            return

Ln843(843行目)

    def set(self, message):
        """メッセージをセットしてウィンドウを画面に表示する"""
        self.cur_pos = 0
        self.cur_page = 0
        self.next_flag = False
        self.hide_flag = False
        # 全角スペースで初期化
        self.text = [u' '] * (self.MAX_LINES*self.MAX_CHARS_PER_LINE)
        # メッセージをセット
        p = 0
        for i in range(len(message)):
            ch = message[i]
            if ch == "/":  # /は改行文字
                self.text[p] = "/"
                p += self.MAX_CHARS_PER_LINE
                p = (p//self.MAX_CHARS_PER_LINE)*self.MAX_CHARS_PER_LINE#ここ
            elif ch == "%":  # \fは改ページ文字
                self.text[p] = "%"
                p += self.MAX_CHARS_PER_PAGE
                p = (p//self.MAX_CHARS_PER_PAGE)*self.MAX_CHARS_PER_PAGE#ここ
            else:
                self.text[p] = ch
                p += 1
        self.text[p] = "$"  # 終端文字
        self.show()

Ln868(868行目)

    def update(self):
        """メッセージウィンドウを更新する
        メッセージが流れるように表示する"""
        if self.is_visible:
            if self.next_flag == False:
                self.cur_pos += 1  # 1文字流す
                # テキスト全体から見た現在位置
                p = self.cur_page * self.MAX_CHARS_PER_PAGE + self.cur_pos
                if self.text[p] == "/":  # 改行文字
                    self.cur_pos += self.MAX_CHARS_PER_LINE
                    self.cur_pos = (self.cur_pos//self.MAX_CHARS_PER_LINE) * self.MAX_CHARS_PER_LINE#ここ
                elif self.text[p] == "%":  # 改ページ文字
                    self.cur_pos += self.MAX_CHARS_PER_PAGE
                    self.cur_pos = (self.cur_pos//self.MAX_CHARS_PER_PAGE) * self.MAX_CHARS_PER_PAGE#ここ
                elif self.text[p] == "$":  # 終端文字
                    self.hide_flag = True
                # 1ページの文字数に達したら▼を表示
                if self.cur_pos % self.MAX_CHARS_PER_PAGE == 0:
                    self.next_flag = True
        self.frame += 1

Ln888(888行目)

    def draw(self, screen):
        """メッセージを描画する
        メッセージウィンドウが表示されていないときは何もしない"""
        Window.draw(self, screen)
        if self.is_visible == False: return
        # 現在表示しているページのcur_posまでの文字を描画
        for i in range(self.cur_pos):
            ch = self.text[self.cur_page*self.MAX_CHARS_PER_PAGE+i]
            if ch == "/" or ch == "%" or ch == "$": continue  # 制御文字は表示しない
            dx = self.text_rect[0] + MessageEngine.FONT_WIDTH * (i % self.MAX_CHARS_PER_LINE)
            dy = self.text_rect[1] + (self.LINE_HEIGHT+MessageEngine.FONT_HEIGHT) * (i // self.MAX_CHARS_PER_LINE)
            self.msg_engine.draw_character(screen, (dx,dy), ch)#ここ
        # 最後のページでない場合は▼を表示
        if (not self.hide_flag) and self.next_flag:
            if self.frame / self.animcycle % 2 == 0:
                dx = self.text_rect[0] + (self.MAX_CHARS_PER_LINE/2) * MessageEngine.FONT_WIDTH - MessageEngine.FONT_WIDTH/2
                dy = self.text_rect[1] + (self.LINE_HEIGHT + MessageEngine.FONT_HEIGHT) * 3
                screen.blit(self.cursor, (dx,dy))

Ln919(919行目)

    def draw(self, screen):
        Window.draw(self, screen)
        if self.is_visible == False: return
        # はなす、つよさ、そうび、とびらを描画
        for i in range(0, 4):
            dx = self.text_rect[0] + MessageEngine.FONT_WIDTH
            dy = self.text_rect[1] + (self.LINE_HEIGHT+MessageEngine.FONT_HEIGHT) * (i % 4)
            self.msg_engine.draw_string(screen, (dx,dy), self.COMMAND[i])
        # じゅもん、どうぐ、さくせん、しらべるを描画
        for i in range(4, 8):
            dx = self.text_rect[0] + MessageEngine.FONT_WIDTH * 6
            dy = self.text_rect[1] + (self.LINE_HEIGHT+MessageEngine.FONT_HEIGHT) * (i % 4)
            self.msg_engine.draw_string(screen, (dx,dy), self.COMMAND[i])
        # 選択中のコマンドの左側に▶を描画
        dx = self.text_rect[0] + MessageEngine.FONT_WIDTH * 5 * (self.command // 4)#ここ 
        dy = self.text_rect[1] + (self.LINE_HEIGHT+MessageEngine.FONT_HEIGHT) * (self.command % 4)
        screen.blit(self.cursor, (dx,dy))

画面の表示

ここをクリックしてください
画面

import pygame
import sys

pygame.init()
screen = pygame.display.set_mode((320, 320))
pygame.display.set_caption('screen only')
while (1):
    screen.fill((0, 0, 0))
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

import pygame
ライブラリのインポート
事前にインストールしておきましょう。
コマンドで py -m pip install pygame

import sys
画面を閉じるのに必要です。

pygame.init()
初期化します。

screen = pygame.display.set_mode((320, 320))
数値は画面の大きさです。(よこ、たて)

pygame.display.set_caption('screen only')
ウィンドウの名前です。

while (1):
screen.fill((0, 0, 0))
画面の色を指定します。黒(0, 0, 0)白(255, 255, 255)灰色(128, 128, 128)

pygame.display.update()
画面の更新をします。

for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
ウィンドウの閉じるボタンを押すと動かなくなります。まだ閉じません。

sys.exit()
ウィンドウを閉じます。

テキスト表示

ここをクリックしてください
テキスト

import pygame
import sys

pygame.init()
screen = pygame.display.set_mode((160, 160))
pygame.display.set_caption('text only')
font = pygame.font.Font(r"date\Mplus1-Regular.ttf", 30)        

while (1):
    screen.fill((0, 0, 0))
    message_text = font.render("こんにちは", True, (255, 255, 255))
    screen.blit(message_text, (5, 65))
    
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

font = pygame.font.Font(r"date\Mplus1-Regular.ttf", 30)
フォントファイルをロードします。数値はフォントの大きさです。

message_text = font.render("こんにちは", True, (255, 255, 255))
Falseにすると、文字がギザギザします。数値は文字の色です。

screen.blit(message_text, (5, 65))
文字を画面に描画します。数値は座標です。(よこ、たて)
*座標は左上が(0,0)です。ヨコは右に行くほど増えます。タテは下に行くほど増えます。
スクリーンショット (617).png

.ttfファイルを探しましょう。.pngファイルだと自分でコードを書かないとダメです。

GitHub(ここからダウンロード)

Windowsでパスをそのまま貼り付けるとエラーが出るので、""の前にrをつけましょう。(r"")

効果音を鳴らす

ここをクリックしてください
効果音

import pygame
import sys
from pygame.locals import *

pygame.init()
screen = pygame.display.set_mode((160, 160))
pygame.display.set_caption('SE only')
sound = pygame.mixer.Sound("date\ボタン音45.mp3")

while (1):
    screen.fill((0, 0, 0))
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == KEYDOWN:
            sound.play()

from pygame.locals import *

from pygame.locals import * は特定の定数のみをインポートし、from pygame import * はPygame全体をインポートします。
これがあると KEYDOWNやQUITの前にpygame. を書かなくてよいです。すべてのpygame.がいらなくなるわけではないです。

sound = pygame.mixer.Sound("date\ボタン音45.mp3")
効果音をロードします。

elif event.type == KEYDOWN:
何かキーが押されたときに処理を実行します。
for event in pygame.event.get():の下に書きます。

sound.play()
効果音を鳴らします。(ここではsoundは変数名です。)

On-Jin ~音人~ サイトへ移動

BGMを鳴らす

ここをクリックしてください
BGM

import pygame
import sys 
from pygame.locals import *

pygame.init()
screen = pygame.display.set_mode((160,160))
pygame.display.set_caption("BGM_only")
pygame.mixer.music.load("date\MusMus-BGM-104.mp3")
pygame.mixer.music.play(-1)

while True:
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
            

pygame.mixer.music.load("date\MusMus-BGM-104.mp3")
BGMをロードします。

pygame.mixer.music.play(-1)
BGMをループします。1だと1回、鳴らします。何も書かない場合でも1回、鳴らします。
start = 1.0 でスタート地点を指定できます。ただし2回目は最初から再生されます。数値は秒単位です。pygame.mixer.music.play(1,1.0)と書くこともできます。

かまタマゴ サイトへ移動

選択肢を作る

ここをクリックしてください
選択肢

import pygame
from pygame.locals import *
import sys
    
pygame.init()
screen = pygame.display.set_mode((160,160))
font = pygame.font.Font("date\ipaexg.ttf", 30)

cmd = 0
arrow1 = ''
arrow2 = '   '


while (1):
    screen.fill((0, 0, 0))    
    text1 = font.render(arrow1 + "はじめる", True, (255, 255, 255))
    text2 = font.render(arrow2 + "やめる", True, (255, 255, 255))
    screen.blit(text1, (10 , 50))
    screen.blit(text2, (10 , 80))
    pygame.display.update()    

    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

        elif event.type == KEYDOWN:
            if event.key == K_DOWN:
                if cmd == 0:
                    cmd = 1
                    arrow1 = '   '
                    arrow2 = ''
                else:
                    arrow1 = ''
                    arrow2 = '   '
                    cmd = 0
            elif event.key == K_UP:
                if cmd == 1:
                    arrow1 = ''
                    arrow2 = '   '
                    cmd = 0
                else:
                    arrow1 = '   '
                    arrow2 = ''
                    cmd = 1               
    

スクリーンショット (618).png

半透明

ここをクリックしてください
半透明

import pygame
import sys
from pygame.locals import *

x,y = 0,0
# Pygameの初期化
pygame.init()

# 画面サイズの設定
screen = pygame.display.set_mode((640, 480))#FULLSCREEN
font = pygame.font.Font("date\ipaexg.ttf", 20)        

# 角の丸みの半径
border_radius = 5  

# 半透明のSurfaceを作成
#文字の背景(小)
transparent_surface = pygame.Surface((160, 26), pygame.SRCALPHA)
transparent_surface.fill((0, 0, 0, 128))  # RGBA (黒, 透明度128)
#文字の背景(大)
transparent_surface1 = pygame.Surface((172, 98), pygame.SRCALPHA)
transparent_surface1.fill((0, 0, 0, 64))  # RGBA (黒, 透明度128)
#選択の背景(小)
transparent_surface2 = pygame.Surface((156, 22), pygame.SRCALPHA)
transparent_surface2.fill((51, 153, 255, 80))  # RGBA (黒, 透明度128)


# メインループ
running = True
while running:
    screen.fill((255,255,255))
    
    #文字の背景(小)を描画
    screen.blit(transparent_surface, (240, 290))
    screen.blit(transparent_surface, (240, 320))
    screen.blit(transparent_surface, (240, 350))

    #文字の背景(大)を描画
    screen.blit(transparent_surface1, (234, 284))
    
    # 選択の背景(小)を描画
    if x >= 242 and x <= 398:
        if y >=292  and y <= 314:
            screen.blit(transparent_surface2, (242, 292))
        elif y >=292  and y <= 344:
            screen.blit(transparent_surface2, (242, 322))
        elif y >=292  and y <= 374:
            screen.blit(transparent_surface2, (242, 352))
            
    #枠を描画
    pygame.draw.rect(screen,(123, 123, 255),Rect(232,282,176,102), 3,border_radius=border_radius)
    
    #テキスト表示
    message_text = font.render("ニューゲーム", True, (255, 255, 255))
    screen.blit(message_text, (260, 293))
    message_text = font.render("コンティニュー", True, (255, 255, 255))
    screen.blit(message_text, (250, 323))
    message_text = font.render("フィニッシュ", True, (255, 255, 255))
    screen.blit(message_text, (260, 353))

    # 画面を更新
    pygame.display.flip()
    for event in pygame.event.get():
        
        #マウスの位置を取得
        mouse_pos = pygame.mouse.get_pos()
        x = mouse_pos[0]
        y = mouse_pos[1]
        
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        

スクリーンショット (619).png

背景動画

ここをクリックしてください
背景動画

import pygame
import cv2
import numpy as np
import sys
import time

# 初期化
pygame.init()

# 画面サイズの設定
screen_width, screen_height = 800, 600
screen = pygame.display.set_mode((screen_width, screen_height))

# 動画の読み込み
cap = cv2.VideoCapture("date\sakuraschool01.mp4")

# フレームレートの取得
fps = cap.get(cv2.CAP_PROP_FPS)
frame_duration = 1 / fps

# メインループ
running = True
while running:
    start_time = time.time()
    
    ret, frame = cap.read()
    if not ret:
        break

    # OpenCVの画像をpygame用に変換
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    
    # フレームのリサイズ
    frame = cv2.resize(frame, (screen_width, screen_height))
    frame = cv2.flip(frame, 1)  # 水平方向に反転
    frame = np.rot90(frame)

    frame = pygame.surfarray.make_surface(frame)

    # 画面に描画
    screen.blit(frame, (0, 0))
    pygame.display.update()

    # イベント処理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # フレームレートに合わせて待機
    elapsed_time = time.time() - start_time
    if elapsed_time < frame_duration:
        time.sleep(frame_duration - elapsed_time)

# 終了処理
cap.release()
pygame.quit()
sys.exit()

背景動画 サイトへ移動

Copilotに書いてもらったので説明できません。

frame = cv2.flip(frame, 1) # 水平方向に反転、ここではなぜか垂直方向に反転している
frame = np.rot90(frame)
この2つがないと、元画像から左右反転して、左に90度回転した状態。
それを、frame = cv2.flip(frame, 1)で垂直方向に反転し、
frame = np.rot90(frame)で右に90度回転して修正してあります。
本来、frame = cv2.flip(frame, 1)は水平方向に反転するようですから、python初心者のぼくには意味不明です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0