0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pygameでRPG(キャラクター移動)

Last updated at Posted at 2024-10-03

お知らせ

記事全体の目次へ移動

GIF

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

walking_video.gif

キャラクターについて書いていこうと思いますが、aidiaryさんのコードを参考にした方が早いかもしれませんね。
人工知能に関する断創録

ぼくのコードはaidiaryさんのコードをちょっと短くしただけです。

キャラクターの描画

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

まずはキャラクターの画像をダウンロードしましょう。
キャラチップ.zipを選びましょう。
ぴぽや倉庫 ここからダウンロード

manto_12.png

FileNotFoundError
というエラーが出たら、rを"パス"の前につけましょう。。

キャラクター
import pygame
import sys
from pygame.locals import *

pygame.init()
screen = pygame.display.set_mode((160, 160))
pygame.display.set_caption('chara only')
# ここにキャラクター画像ののパスを貼り付ける
player = pygame.image.load(パス)
while (1):
    screen.fill((0, 0, 0))
    screen.blit(player,(64,64),(0,0,32,32))
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

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

screen.blit(player,(64,64),(0,0,32,32))
(64,64)は画像を中央に描画するための座標です。
画面サイズ / 2 - 画像サイズ / 2
160 / 2 - 32 / 2 = 64

無題.png

(0,0,32,32)
(キャラ画像の横座標、キャラ画像の縦座標、横の大きさ、縦の大きさ)

キャラクターの移動

ここをクリックしてください
キャラクターの移動
import pygame
from pygame.locals import *
import sys

pygame.init()
screen = pygame.display.set_mode((160,160))
pygame.display.set_caption('chara_move')
x,y = 2,2
GS = 32
#ここにキャラのパスを貼り付ける
player = pygame.image.load(パス)

while (1):
    screen.fill((0,0,0))
    screen.blit(player, (x*GS, y*GS), (0, 0, GS, GS))
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        elif event.type==KEYDOWN:
            if event.key==K_DOWN:
                y += 1      
            elif event.key==K_UP:
                y -= 1
            elif event.key==K_RIGHT:
                x += 1
            elif event.key==K_LEFT:
                x -= 1

x,y = 0,0
マス単位の座標です。
ここを変えることでキャラクターの初期位置を変えることができます。

GS = 32
マスの大きさです。

screen.blit(player, (x*GS, y*GS))
マス単位の座標にマスの大きさをかけています。
x,y = 0, 0
(0*32, 0*32) つまり (0, 0)

キャラクターの描画では(64, 64)でしたね。これをここと同じように書くと
x,y = 2, 2
(2*32, 2*32) つまり (64, 64)

図で位置を表してみました。赤が(0, 0) 青が(64, 64)

マス - コピー.png

if event.type==KEYDOWN:
何かキーが押された時。

elif event.key==K_DOWN:
ダウンキーが押されたとき。

y += 1
yを変更しています。

x,y = 0, 0 だと x,y = 0, 1 に変更。
screen.blit(player, (x*GS, y*GS))だから
(0*GS, 0*GS) だと (0*GS, 1*GS) に変更。
ピクセル単位の座標にすると (0, 0) が (0, 32) になります。
つまり、キャラクターが下に移動します。

マス - コピー (2).png

キャラアニメ

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

ぴぽや倉庫 サイトへ移動

ここからキャラクターチップをダウンロードできます。

↓画像を追加するコードです。
画像を追加

キャラが、その場足踏みしているように見えるコードです。
aidiaryさんのコードを短くしたコードです。

背景が透明な画像用に書き換えました。

キャラアニメ
import pygame
from pygame.locals import *
import sys


def split_img(img):
    split_list = []
    for i in range(0, 128, GS):
        surface = pygame.Surface((GS, GS), pygame.SRCALPHA)
        surface.blit(img, (0, 0), (i, 0, GS, GS))
        split_list.append(surface)
    return split_list


pygame.init()
screen = pygame.display.set_mode((160,160))
pygame.display.set_caption('chara anime')
x,y = 2,2
GS = 32
#ここにキャラのパスを貼り付ける
playerImgList = split_img(pygame.image.load(パス))
# アニメーション速度
animcycle = 24  
frame = 0
clock = pygame.time.Clock()
 
while True:
    screen.fill((255,255,255))
    clock.tick(60)    
    # 経過フレーム数に応じて表示する画像を変える
    frame += 1
    player = playerImgList[int(frame/animcycle%4)]
    screen.blit(player, (x*GS, y*GS),(0,0,GS,GS))
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

animcycle = 24
ずっと24のままです。

frame += 1
数字が一つずつ増えていきます。

player = playerImgList[frame//animcycle%4)]
キャラクター画像はリストに入っている。
計算式でインデックスを決めている。

playerImgList[0]
playerImgList[1]
playerImgList[2]
playerImgList[3]

[frame//animcycle%4]の計算(分かりやすくしてみました)

a = 0//2%2 0割る2で0     0%2余り0
b = 1//2%2 1割る2で0.5(整数除算で小数点以下が切り捨てられ0)
      0%2余り0

c = 2//2%2 2割る2で1     1%2余り1
d = 3//2%2 3割る2で1.5(整数除算で小数点以下が切り捨てられ1)
      1%2余り1

e = 4//2%2 4割る2で2     2%2余り0
f = 5//2%2 5割る2で2.5(整数除算で小数点以下が切り捨てられ2)
      2%2余り0

split_img 関数の説明

import pygame

def split_img(img, GS):
    split_list = []
    for i in range(0, 128, GS):
        surface = pygame.Surface((GS, GS), pygame.SRCALPHA)
        surface.blit(img, (0, 0), (i, 0, GS, GS))
        split_list.append(surface)
    return split_list

説明

### `split_img` 関数の説明

```python
def split_img(img, GS):

img にはロードした画像が入ります。GS はグリッドサイズ(ピクセル単位)を指定します。

split_list = []

空のリストです。ここに分割した画像を入れていきます。

for i in range(0, 128, GS):

繰り返し処理です。0~128の範囲で GS 間隔の数字を作ります(例: GS が32の場合、0, 32, 64, 96)。
ただし、128は含まれません。range()で作られるのは指定した数未満までです。

surface = pygame.Surface((GS, GS), pygame.SRCALPHA)

透明なサーフェイスの生成です。
(サーフェイスとは、画像やテキストのことです)

((GS,GS))

サーフェイスのサイズです。GS は32です。

pygame.SRCALPHA

サーフェイスを透明にします。

surface.blit(img, (0, 0), (i, 0, GS, GS))

サーフェイスに分割した画像を描画します。

  • img: キャラクター画像です。
  • (0,0): 描画する座標です。サーフェイスと分割した画像は同じサイズなので、ぴったり合わさります。
  • (i,0,GS,GS): i は横の座標です。range 関数で生成された 0, 32, 64, 96 が順に入ります。GS は32なので 32x32 の大きさです。
split_list.append(surface)

透明なサーフェイスに分割したキャラクター画像を貼り付けて、できた画像をリストに追加します。

return split_list

繰り返し処理が終わり、4つの画像が入っているリストを返します。

向きを変えながら歩く

ここをクリックしてください
向きを変える
import pygame
from pygame.locals import *
import sys


def split_image(image):
    """32x128のキャラクターイメージを32x32の4枚のイメージに分割
    分割したイメージを格納したリストを返す"""
    imageList = []
    for i in range(0, 128, GS):
        for j in range(0, 128, GS):
            surface = pygame.Surface((GS,GS))
            surface.blit(image, (0,0), (j, i,GS,GS))
            surface.set_colorkey(surface.get_at((0,0)), RLEACCEL)
            surface.convert()
            imageList.append(surface)
    return imageList


pygame.init()
screen = pygame.display.set_mode((160,160))
pygame.display.set_caption('walking')
x,y = 2,2
GS = 32
DOWN,LEFT,RIGHT,UP = 0,1,2,3
#ここキャラのパスを貼り付ける
playerImgList = split_image(pygame.image.load(パス))  # プレイヤーx,y = 1,1
direction = DOWN
animcycle = 24  # アニメーション速度
frame = 0
clock = pygame.time.Clock()
 
while True:
    clock.tick(60)
    screen.fill((255,255,255))
    # 経過フレーム数に応じて表示する画像を変える
    frame += 1
    player = playerImgList[int(direction*4+frame/animcycle%4)]
    screen.blit(player, (x*GS, y*GS))
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type==KEYDOWN:
            if event.key==K_DOWN:
                direction = DOWN
                y += 1      
            elif event.key==K_UP:
                direction = UP
                y -= 1
            elif event.key==K_RIGHT:
                direction = RIGHT
                x += 1
            elif event.key==K_LEFT:
                direction = LEFT
                x -= 1

def split_image(image):の説明
概要
キャラクター画像を16個に分割してリストに入れる。
補足
キャラクター画像の大きさは128x128
GSは32

def split_image(image):
imageはキャラクター画像。
player.png

imageList = []
空のリストを作る。

for i in range(0, 128, GS):
0から128の範囲でGS(32)間隔の数字を作る。
ただし、128は含まれません。range()で作られるのは指定した数未満までです。
0 32 64 96

for j in range(0, 128, GS):
0から128の範囲でGS(32)間隔の数字を作る。
ただし、128は含まれません。range()で作られるのは指定した数未満までです。
0 32 64 96

surface = pygame.Surface((GS,GS))
サーフェイスを作る。
たとえるなら、ペイントのキャンパスのようなもの。
色はデフォルトで、黒。
((GS,GS))は大きさ。
surface.png

surface.blit(image, (0,0), (j, i,GS,GS))
作ったサーフェイスに画像の一部を貼り付ける。
imageはキャラクター画像。
(0,0)はサーフェイスの座標。
j,iはキャラクター画像の座標。
GS,GSは切り取る大きさ。
surface.png 
split.png
surface - コピー.png

surface.set_colorkey(surface.get_at((0,0)), RLEACCEL)
.set_colorkeyは指定した色を透明にする。
.get_atは指定した座標の色を取得する。
ここでは(0,0)の色は黒なので黒がすべて透明になる。
ぼくは最初背景が白の画像を使っていたので、マントが透明になってました。
RLEACCELは描画を高速化する。

surface.convert()
サーフェイスを最適化する。

imageList.append(surface)
リストに追加する。

return imageList
結果を返します。

おまけ
set_alpha: サーフェス全体の透明度を設定します。透明度は0から255までの範囲で指定し、サーフェス全体が均一に透明になります。

set_colorkey: 特定の色を透明に設定します。サーフェス上の指定した色が透明になります。

スクロール

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

 キャラクターの移動処理にはスクロールというものがあるのですが、ぼくは理解してないので紹介だけしておきます。
(aidiaryさんの記事です)
タイルスクロール
変更箇所(すべて変更済みのコードです)
pygame.quit()をつけてます。

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

()をつけたり、isを==に変えたりしてます。

def load_image(filename, colorkey=None):
    filename = os.path.join("data", filename)
    try:
        image = pygame.image.load(filename)
    except (pygame.error, message):#ここ
        print ("Cannot load image:"), filename#ここ
        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

int()をつけてます。

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

/ を // にします。

    def update(self):
        # キャラクターアニメーション(frameに応じて描画イメージを切り替える)
        self.frame += 1
        self.image = self.images[self.direction*4+self.frame//self.animcycle%4]#ここ

(aidiaryさんの記事です)
ピクセルスクロール
変更箇所。(変更済みのコードです)
/ を // にしてます。

        # プレイヤーの移動処理
        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#ここ

これ以外はタイルベーススクロールと同じところを変更してください。

セーブとロード

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

Sキーを押すと、座標を保存します。別の場所に移動して、Lキーを押してみてください。座標を保存した場所に戻ります。

import pygame
from pygame.locals import *
import sys
import pickle

pygame.init()
screen = pygame.display.set_mode((160,160))
pygame.display.set_caption('save load')
x,y = 2,2
GS = 32
#ここにキャラクター画像のパスを貼り付ける
player = pygame.image.load(パス)

while (1):
    screen.fill((0,0,0))
    screen.blit(player, (x*GS, y*GS))
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        elif event.type==KEYDOWN:
            if event.key==K_DOWN:
                y += 1      
            elif event.key==K_UP:
                y -= 1
            elif event.key==K_RIGHT:
                x += 1
            elif event.key==K_LEFT:
                x -= 1

            elif event.key==K_s:
                # ゲームの状態を辞書として定義
                save_data = {
                    "x": x,"y": y           
                }

                # ゲームの状態をファイルに保存
                with open('save_data.pickle', 'wb') as file:
                    pickle.dump(save_data, file)
                    print("セーブしました")
            elif event.key==K_l:
                # セーブデータを読み込む
                with open('save_data.pickle', 'rb') as file:
                    save_data = pickle.load(file)
                    x = save_data["x"]
                    y = save_data["y"]
                    print("ロードしました")

pickleファイルの中身を表示

セーブやロードで、エラーが起きた時に本当に保存されてるか確かめる必要があります。

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

# データを読み込む関数
def load_pickle_data(file_path):
    with open(file_path, 'rb') as file:
        data = pickle.load(file)
    return data

# 読み込んだデータを表示する関数
def display_data(data):
    print("読み込んだデータ:")
    print(data)

# 使用例
file_path =   # 保存されたpickleファイルのパス
data = load_pickle_data(file_path)
display_data(data)


0
2
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
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?