見出し画像

pygameで始めるpythonプログラム 3(キャラクターを動かしてみる編)

はじめに

前回の記事でキャラクターを画面に出しました。今回はキャラクターを動かしてみたいと思います。

やること

左右キーでキャラクターを動かし、スペースでジャンプ。

コード

これが今回作成したコード。

import pygame
import sys

# 定数の設定
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

# Pygameの初期化
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("ゲームタイトル")

# ゲームのメインクラス
class Game:
    def __init__(self):
        self.character_pos = [50, SCREEN_HEIGHT - 100]
        self.goal_pos = [SCREEN_WIDTH - 70, SCREEN_HEIGHT - 150]

    def run(self):
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()

            screen.fill((255, 255, 255))
            self.update()
            pygame.draw.rect(screen, (255,0,0), (*self.character_pos, 50, 50))
            pygame.draw.rect(screen, (0,0,255), (*self.goal_pos, 50, 100))
            pygame.display.flip()            

    def update(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            self.character_pos[0] = self.character_pos[0] - 5 if self.character_pos[0] > 5 else 0
        if keys[pygame.K_RIGHT]:
            self.character_pos[0] = self.character_pos[0] + 5 if self.character_pos[0] < SCREEN_WIDTH -10 else SCREEN_WIDTH -10
        if keys[pygame.K_SPACE]:
            self.character_pos[1] -= 10
        # 重力効果
        self.character_pos[1] = self.character_pos[1] + 5 if self.character_pos[1] < SCREEN_HEIGHT - 100 else SCREEN_HEIGHT - 100

# ゲームの開始
if __name__ == "__main__":
    game = Game()
    game.run()

前回との差分

# ゲームのメインクラス
class Game:
    ...
    def update(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            self.character_pos[0] = self.character_pos[0] - 5 if self.character_pos[0] > 5 else 0
        if keys[pygame.K_RIGHT]:
            self.character_pos[0] = self.character_pos[0] + 5 if self.character_pos[0] < SCREEN_WIDTH -10 else SCREEN_WIDTH -10
        if keys[pygame.K_SPACE]:
            self.character_pos[1] -= 10
        # 重力効果
        self.character_pos[1] = self.character_pos[1] + 5 if self.character_pos[1] < SCREEN_HEIGHT - 100 else SCREEN_HEIGHT - 100

Gameクラスにupdateメソッドを追加し、このupdateメソッドでキーの受付とキャラクターの位置を設定します。画面の両端を超えないように3項式を使ってポジションにリミットをかけています。
あと、スペースを押したら上昇して、スペースを離したら勝手に床面まで下降するようになっています。スペースを押してる間、果てしなく上昇していきますが、それは後で修正したいと思います。

で、このupdateメソッドをrunメソッドの無限ループの中で呼び出します。

実行してみて起きた問題

この状態で実行してみると、こりゃ無いよね。ってことが2つ見つかりました。

  • とてつもなくキャラクターの動きが速い

  • スペースを押している間、キャラクターが上昇し続ける(スペース押しても1回ジャンプして床に落ちるという動きにしたい)

問題の解決(その1)

とてつもなくキャラクターの動きが速い問題の解決。原因は、Gameクラスにあるrunメソッド内の無限ループは、CPUが処理できるだけ処理してしまうのでキャラクターの更新速度が速い。ちゃんと適切な更新速度に設定する必要があるのである。(知ったかぶり)

大体今の4Kテレビとかで60フレーム/秒程度らしいので、60フレームにしとけばいいと思う。(適当)

Gameクラスのイニシャライザでクロックオブジェクトなるものを生成して、runメソッドで60フレームに設定しようと思う。

すると、こんな処理を加えることになる。

class Game:
    def __init__(self):
        ...
        # フレームレート設定のためのクロックオブジェクト作成
        self.clock = pygame.time.Clock()
    def run(self):
        while True:
            ...
            # フレームレートを制御         
            self.clock.tick(60)

問題の解決(その2)

スペースを押している間、キャラクターが上昇し続ける動作ではなく、スペースを押したら1回だけジャンプして床に落ちるという動きにしたい。

ジャンプ中かどうかを判定して、未ジャンプ(?)の場合は、スペースキーが押されたらジャンプ中ステータスに変え、一定量だけ上昇。ジャンプ中はキーは関係なく、床に落ちるまで下に落とす。床に落ちたらジャンプ中ステータスを解除。

言葉でよりコードで説明した方が早いかも。

まずはジャンプに必要なパラメータをクラス変数で定義。

  • jump_speed:初速。-20から徐々に値を増やして0になった時点で頂点。プラスに転じたら床に着くまで下に下げていく

  • gravity:どれくらい値を増やしていくか

  • velocity_y:上昇下降の現在速度。初期値として0を入れている

  • is_jumping:ジャンプ中かどうか。ジャンプ中はスペースキーを受け付けない

class Game:
    def __init__(self):
        ...
        self.jump_speed = -20
        self.gravity = 1
        self.velocity_y = 0
        self.is_jumping = False

次に、update関数でスペースキーで行っていたジャンプ処理と重力による下降処理を以下のように変更。

    def update(self):
        ...
        if keys[pygame.K_SPACE]:
            if not self.is_jumping:
                self.velocity_y = self.jump_speed
                self.is_jumping = True
                self.character_pos[1] += self.velocity_y

        # 重力を適用
        if self.is_jumping:
            self.velocity_y += self.gravity
            self.character_pos[1] += self.velocity_y
            # 地面に着地したときの処理
            if self.character_pos[1] > SCREEN_HEIGHT - 100:
                self.character_pos[1] = SCREEN_HEIGHT - 100
                self.is_jumping = False
                self.velocity_y = 0

最終的なコードはこうなった。だいぶ動きがマシになりました。

import pygame
import sys

# 定数の設定
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

# Pygameの初期化
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("ゲームタイトル")

# ゲームのメインクラス
class Game:
    def __init__(self):
        self.character_pos = [50, SCREEN_HEIGHT - 100]
        self.goal_pos = [SCREEN_WIDTH - 70, SCREEN_HEIGHT - 150]
        self.jump_speed = -20
        self.gravity = 1
        self.velocity_y = 0
        self.is_jumping = False

        # フレームレート設定のためのクロックオブジェクト作成
        self.clock = pygame.time.Clock()

    def run(self):
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()

            screen.fill((255, 255, 255))
            self.update()
            pygame.draw.rect(screen, (0,0,255), (*self.goal_pos, 50, 100))
            pygame.draw.rect(screen, (255,0,0), (*self.character_pos, 50, 50))
            pygame.display.flip()            
            # フレームレートを制御         
            self.clock.tick(60)

    def update(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            self.character_pos[0] = self.character_pos[0] - 5 if self.character_pos[0] > 5 else 0
        if keys[pygame.K_RIGHT]:
            self.character_pos[0] = self.character_pos[0] + 5 if self.character_pos[0] < SCREEN_WIDTH -10 else SCREEN_WIDTH -10
        if keys[pygame.K_SPACE]:
            if not self.is_jumping:
                self.velocity_y = self.jump_speed
                self.is_jumping = True
                self.character_pos[1] += self.velocity_y

        # 重力を適用
        if self.is_jumping:
            self.velocity_y += self.gravity
            self.character_pos[1] += self.velocity_y
            # 地面に着地したときの処理
            if self.character_pos[1] > SCREEN_HEIGHT - 100:
                self.character_pos[1] = SCREEN_HEIGHT - 100
                self.is_jumping = False
                self.velocity_y = 0

# ゲームの開始
if __name__ == "__main__":
    game = Game()
    game.run()


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