掲示板<36>2026/2/11
from=TOZ

Pythonを利用⑫「jump_game.py」

マリオゲームのようなものを作りたいと孫が言うので、試行錯誤しながら 作成しました。
ホームページのタグの関係で、プログラム中の「<」「>」は全角表示しています。
import pygame
import random
import os

# --- 設定 ---
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60
GROUND_Y = SCREEN_HEIGHT - 50
UPPER_GROUND_Y = GROUND_Y - 150
TOP_GROUND_Y = UPPER_GROUND_Y - 150 
SCORE_FILE = "highscore.txt"

# 状態管理
STATE_TITLE = 0
STATE_PLAYING = 1
STATE_GAMEOVER = 2

# 色の定義
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
SKY_BLUE = (135, 206, 235)
SUN_YELLOW = (255, 215, 0)
GRAY = (180, 180, 180)
SKIN_COLOR = (255, 220, 177)
SHIRT_COLOR = (30, 144, 255)
PANTS_COLOR = (50, 50, 50)
ONI_RED = (220, 20, 60)
STAR_GOLD = (255, 255, 0)
FEATHER_WHITE = (240, 248, 255)
SNAKE_GREEN = (34, 139, 34)
ELEPHANT_GRAY = (169, 169, 169)
RABBIT_PINK = (255, 192, 203)

# --- 記録の保存・読み込み ---
def load_high_score():
    if os.path.exists(SCORE_FILE):
        try:
            with open(SCORE_FILE, "r") as f:
                return int(f.read())
        except: return 0
    return 0

def save_high_score(score):
    with open(SCORE_FILE, "w") as f:
        f.write(str(score))

# --- 背景クラス ---
class BackgroundElement:
    def __init__(self, x, y, speed, type):
        self.x, self.y, self.speed, self.type = x, y, speed, type
    def update(self):
        self.x -= self.speed
        if self.x < -200: self.x = SCREEN_WIDTH + 100
    def draw(self, screen):
        if self.type == "cloud":
            pygame.draw.circle(screen, (245, 245, 245), (int(self.x), self.y), 20)
            pygame.draw.circle(screen, (245, 245, 245), (int(self.x+20), self.y-5), 25)
            pygame.draw.circle(screen, (245, 245, 245), (int(self.x+40), self.y), 20)
        else:
            pygame.draw.polygon(screen, (100, 150, 100), [(self.x, GROUND_Y), (self.x+100, GROUND_Y-80), (self.x+200, GROUND_Y)])

# --- アイテムクラス ---
class Feather:
    def __init__(self):
        self.x, self.y = SCREEN_WIDTH, UPPER_GROUND_Y - 50
    def update(self): self.x -= 4
    def draw(self, screen):
        pygame.draw.line(screen, (200, 200, 200), (self.x, self.y+10), (self.x+30, self.y-5), 2)
        pygame.draw.ellipse(screen, FEATHER_WHITE, (self.x, self.y-5, 30, 15))

class Coin:
    def __init__(self):
        self.x, self.y = SCREEN_WIDTH, TOP_GROUND_Y - 40
    def update(self): self.x -= 6
    def draw(self, screen):
        pygame.draw.circle(screen, STAR_GOLD, (int(self.x), int(self.y)), 12)
        pygame.draw.circle(screen, (218, 165, 32), (int(self.x), int(self.y)), 12, 3)

# --- プレイヤークラス(リアル版) ---
class Player:
    def __init__(self):
        self.width, self.height = 30, 60
        self.reset()
    def reset(self):
        self.x, self.y = 80, GROUND_Y - self.height
        self.vel_y, self.vel_x = 0, 0
        self.speed, self.jump_count = 5, 0
        self.max_jumps = 2 
        self.is_falling_forced = False
        self.invincible_time = 0
    def jump(self):
        if self.jump_count < self.max_jumps:
            self.vel_y = -14 
            self.jump_count += 1
            return True
        return False
    def update(self):
        if self.invincible_time > 0: self.invincible_time -= 1
        self.x += self.vel_x
        if self.x < 0: self.x = 0
        if self.x > SCREEN_WIDTH - self.width: self.x = SCREEN_WIDTH - self.width
        self.vel_y += 0.8 
        self.y += self.vel_y
        if self.y >= GROUND_Y - self.height:
            self.y, self.vel_y, self.jump_count, self.is_falling_forced = GROUND_Y - self.height, 0, 0, False
            self.max_jumps = 2 
        if not self.is_falling_forced and 200 <= self.x <= 800:
            foot_y = self.y + self.height
            if self.vel_y > 0 and UPPER_GROUND_Y - 10 < foot_y < UPPER_GROUND_Y + 15:
                self.y, self.vel_y, self.jump_count = UPPER_GROUND_Y - self.height, 0, 0
            if self.vel_y > 0 and TOP_GROUND_Y - 10 < foot_y < TOP_GROUND_Y + 15:
                self.y, self.vel_y, self.jump_count = TOP_GROUND_Y - self.height, 0, 0
    def draw(self, screen):
        x, y = self.x, self.y
        b_color, s_color = SHIRT_COLOR, SKIN_COLOR
        if self.invincible_time > 0 and (self.invincible_time // 5) % 2 == 0:
            b_color, s_color = STAR_GOLD, (255, 255, 200)
        # 足
        pygame.draw.line(screen, PANTS_COLOR, (x + 10, y + 45), (x + 10, y + 60), 4)
        pygame.draw.line(screen, PANTS_COLOR, (x + 20, y + 45), (x + 20, y + 60), 4)
        # 体
        pygame.draw.rect(screen, b_color, (x + 5, y + 20, 20, 25))
        # 顔
        pygame.draw.circle(screen, s_color, (x + 15, y + 10), 10)
        pygame.draw.circle(screen, BLACK, (x + 11, y + 8), 2)
        pygame.draw.circle(screen, BLACK, (x + 19, y + 8), 2)
        # 羽(3段ジャンプ中)
        if self.max_jumps == 3:
            pygame.draw.ellipse(screen, FEATHER_WHITE, (x-15, y+20, 20, 10))
            pygame.draw.ellipse(screen, FEATHER_WHITE, (x+25, y+20, 20, 10))

# --- 障害物クラス(リアル版) ---
class Obstacle:
    def __init__(self, speed_mult, is_oni=False):
        self.is_oni, self.passed = is_oni, False
        if is_oni:
            self.name, self.width, self.height = "Oni", 45, 55
            self.speed = 5 * speed_mult
            self.color = ONI_RED
            self.score_val = 8
            self.y = UPPER_GROUND_Y - self.height
        else:
            data = random.choice([["Rabbit", 35, 35, 10, RABBIT_PINK, 5], 
                                  ["Snake", 80, 20, 6, SNAKE_GREEN, 3], 
                                  ["Elephant", 60, 70, 4, ELEPHANT_GRAY, 10]])
            self.name, self.width, self.height, self.speed, self.color, self.score_val = data
            self.speed *= speed_mult
            self.y = GROUND_Y - self.height
        self.x = SCREEN_WIDTH
    def update(self): self.x -= self.speed
    def draw(self, screen):
        x, y, w, h = self.x, self.y, self.width, self.height
        if self.is_oni:
            pygame.draw.rect(screen, self.color, (x, y+10, w, h-10))
            pygame.draw.rect(screen, self.color, (x-5, y-5, w+10, 30))
            for i in range(2):
                hx = x + 5 + i*25
                pygame.draw.polygon(screen, STAR_GOLD, [(hx, y-5), (hx+5, y-25), (hx+10, y-5)])
            pygame.draw.rect(screen, WHITE, (x+10, y+25, w-20, 5))
        elif self.name == "Rabbit":
            pygame.draw.ellipse(screen, self.color, (x, y+5, w, h-5))
            pygame.draw.ellipse(screen, self.color, (x+5, y-15, 8, 25))
            pygame.draw.ellipse(screen, self.color, (x+w-13, y-15, 8, 25))
        elif self.name == "Elephant":
            pygame.draw.rect(screen, self.color, (x, y+15, w, h-15), border_radius=10)
            pygame.draw.circle(screen, self.color, (x+w//2, y+20), 25)
            pygame.draw.ellipse(screen, self.color, (x-10, y+5, 20, 40))
            pygame.draw.ellipse(screen, self.color, (x+w-10, y+5, 20, 40))
        elif self.name == "Snake":
            for i in range(5): pygame.draw.circle(screen, self.color, (x+i*15+10, y+10), 10)

# --- スタークラス(リアル版) ---
class Star:
    def __init__(self):
        self.x, self.y = SCREEN_WIDTH, random.choice([GROUND_Y - 80, UPPER_GROUND_Y - 80])
    def update(self): self.x -= 5
    def draw(self, screen):
        pygame.draw.polygon(screen, STAR_GOLD, [
            (self.x, self.y-20), (self.x+5, self.y-5), (self.x+20, self.y-5), 
            (self.x+10, self.y+5), (self.x+15, self.y+20), (self.x, self.y+10), 
            (self.x-15, self.y+20), (self.x-10, self.y+5), (self.x-20, self.y-5), 
            (self.x-5, self.y-5)
        ])

# --- メイン関数 ---
def main():
    pygame.init()
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption("孫と遊ぶ!リアル動物アドベンチャー")
    clock = pygame.time.Clock()
    font_large = pygame.font.SysFont("notosanscjkjp,sans", 50)
    font_small = pygame.font.SysFont("notosanscjkjp,sans", 24)

    high_score = load_high_score()
    game_state = STATE_TITLE
    
    player = Player()
    bg_elements = [BackgroundElement(100,450,0.5,"mt"), BackgroundElement(500,450,0.5,"mt"), BackgroundElement(200,100,2,"cloud")]
    obstacles, stars, feathers, coins = [], [], [], []
    spawn_timer, score = 0, 0

    running = True
    while running:
        screen.fill(SKY_BLUE)
        pygame.draw.circle(screen, SUN_YELLOW, (700, 60), 40)
        for bg in bg_elements: bg.update(); bg.draw(screen)

        for event in pygame.event.get():
            if event.type == pygame.QUIT: running = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_q: running = False
                
                if game_state == STATE_TITLE:
                    if event.key == pygame.K_SPACE: game_state = STATE_PLAYING
                
                elif game_state == STATE_PLAYING:
                    if event.key == pygame.K_SPACE:
                        if player.jump(): score += 20 if player.invincible_time > 0 else 10
                    if event.key == pygame.K_RIGHT: player.vel_x = player.speed
                    if event.key == pygame.K_LEFT: player.vel_x = -player.speed
                
                elif game_state == STATE_GAMEOVER:
                    if event.key == pygame.K_r:
                        player.reset(); obstacles, stars, feathers, coins, score = [], [], [], [], 0
                        game_state = STATE_PLAYING

            if event.type == pygame.KEYUP:
                if event.key in (pygame.K_RIGHT, pygame.K_LEFT): player.vel_x = 0

        if game_state == STATE_PLAYING:
            player.update()
            speed_mult = 1.0 + (score / 4500)
            spawn_timer += 1
            if spawn_timer > 60:
                if random.random() < 0.3: obstacles.append(Obstacle(speed_mult, True))
                else: obstacles.append(Obstacle(speed_mult, False))
                if random.random() < 0.2: feathers.append(Feather())
                if random.random() < 0.08: stars.append(Star())
                if random.random() < 0.2: coins.append(Coin())
                spawn_timer = 0

            # アイテムと障害物の更新・判定
            for f in feathers[:]:
                f.update()
                if pygame.Rect(player.x, player.y, player.width, player.height).colliderect(pygame.Rect(f.x, f.y, 30, 15)):
                    player.max_jumps = 3; feathers.remove(f)
                elif f.x < -30: feathers.remove(f)
            for c in coins[:]:
                c.update()
                if pygame.Rect(player.x, player.y, player.width, player.height).colliderect(pygame.Rect(c.x-10, c.y-10, 20, 20)):
                    score += 50; coins.remove(c)
                elif c.x < -30: coins.remove(c)
            for s in stars[:]:
                s.update()
                if pygame.Rect(player.x, player.y, player.width, player.height).colliderect(pygame.Rect(s.x-15, s.y-15, 30, 30)):
                    player.invincible_time = 300; stars.remove(s)
                elif s.x < -20: stars.remove(s)
            for ob in obstacles[:]:
                ob.update()
                if not ob.passed and ob.x + ob.width < player.x:
                    ob.passed = True; score += ob.score_val
                    if ob.is_oni: player.is_falling_forced = True
                if ob.x + ob.width < 0: obstacles.remove(ob)
                if player.invincible_time <= 0 and pygame.Rect(player.x+5, player.y+5, player.width-10, player.height-10).colliderect(pygame.Rect(ob.x+5, ob.y+5, ob.width-10, ob.height-10)):
                    game_state = STATE_GAMEOVER
                    if score > high_score: high_score = score; save_high_score(high_score)

        # 描画
        pygame.draw.rect(screen, (100, 100, 100), (0, GROUND_Y, SCREEN_WIDTH, 50)) 
        pygame.draw.rect(screen, (150, 150, 150), (200, UPPER_GROUND_Y, 600, 15)) 
        pygame.draw.rect(screen, (200, 200, 200), (200, TOP_GROUND_Y, 600, 10)) 
        
        player.draw(screen)
        for ob in obstacles: ob.draw(screen)
        for s in stars: s.draw(screen)
        for f in feathers: f.draw(screen)
        for c in coins: c.draw(screen)

        # 画面状態による表示
        if game_state == STATE_TITLE:
            overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
            overlay.fill((0, 0, 0, 150))
            screen.blit(overlay, (0,0))
            title_txt = font_large.render("わくわく動物アドベンチャー", True, WHITE)
            screen.blit(title_txt, (SCREEN_WIDTH//2 - 280, 150))
            start_txt = font_small.render("SPACEキーでスタート!", True, STAR_GOLD)
            screen.blit(start_txt, (SCREEN_WIDTH//2 - 120, 300))
        elif game_state == STATE_PLAYING:
            screen.blit(font_small.render(f"得点: {score}", True, BLACK), (10, 10))
            screen.blit(font_small.render(f"最高: {high_score}", True, (255, 50, 0)), (10, 35))
            if player.max_jumps == 3: screen.blit(font_small.render("!! 3段ジャンプOK !!", True, (255, 255, 255)), (150, 10))
        elif game_state == STATE_GAMEOVER:
            msg = font_large.render("GAME OVER", True, (255, 0, 0))
            screen.blit(msg, (SCREEN_WIDTH//2 - 140, 150))
            retry_txt = font_small.render(f"今回の得点: {score}  [R]でやりなおし", True, WHITE)
            screen.blit(retry_txt, (SCREEN_WIDTH//2 - 180, 250))

        pygame.display.flip()
        clock.tick(FPS)
    pygame.quit()

if __name__ == "__main__":
    main()


← 一覧へ戻る