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()
|