今日のエッセイ

おなじみの気まぐれゲームプログラミングです。
プログラム言語はPythonのPygameです。

タイトル:「THE SHOOTER」

シューティングパズルゲームです。
上から落ちてくる緑の四角い物体を、水色の四角いのを左右のカーソルキーで操作してスペースキーで撃つと得点です。
緑の四角に水色の四角が接触するか、一番下を通り過ぎるとHPが減ります。
HPが0以下になるとゲームオーバーで、ゲームが止まり、4秒後にウインドウが閉じます。
HPは数値と黄色いバーで表示します。
バーはHPが減るとオレンジに変わっていきます。
10000点でHPが20回復、緑の四角を撃墜するごとに1回復します。


ゲーム画面


ゲームオーバー画面

○プログラム

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((60, 60)) #size of image
        self.image.fill((0,230,230))
        self.rect = self.image.get_rect()
        self.rect.centerx = SCREEN_WIDTH // 2
        self.rect.bottom = SCREEN_HEIGHT - 10
        self.speed_x = 0

    def update(self):
        self.speed_x = 0
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            self.speed_x = -5
        if keys[pygame.K_RIGHT]:
            self.speed_x = 5
        self.rect.x += self.speed_x
        if self.rect.right > SCREEN_WIDTH:
            self.rect.right = SCREEN_WIDTH
        if self.rect.left < 0:
            self.rect.left = 0

プレイヤー(水色の四角)のクラスです。
位置や動き、スピードを設定します。

class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((80, 80))
        self.image.fill((0,220,50))
        self.rect = self.image.get_rect()
        self.rect.x = random.randint(0, SCREEN_WIDTH - self.rect.width)
        self.rect.y = random.randint(-100, -50)
        self.speed_y = random.randint(3, 6) #enemy speed #random.randint(a,b) range [a,b]

    def update(self):
        global hitnumber #calling of global value in class
        self.rect.y += self.speed_y
        if self.rect.top > SCREEN_HEIGHT: #out of enemy for screen height
            self.rect.x = random.randint(0, SCREEN_WIDTH - self.rect.width)
            self.rect.y = random.randint(-100, -50)
            self.speed_y = random.randint(3, 6) #enemy speed

            hitnumber -= 2
            if hitnumber < -1:

              gameover = fontgo.render("GameOver.Try for the Next.", True, (250,150,0))
              screen.blit(gameover, (180, 290))
              pygame.display.flip()  # Update the display

              pygame.time.wait(4000)  # Wait for 4 seconds before exiting
              pygame.quit()
              sys.exit()#Game Exit

敵キャラクターの動きを設定するクラスです。
テキスト表示の時は、pygame.display.flip()関数がないと表示されません。

class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface((5, 10))
        self.image.fill((250,220,0))
        self.rect = self.image.get_rect()
        self.rect.centerx = x
        self.rect.bottom = y
        self.speed_y = -10

    def update(self):

        self.rect.y += self.speed_y

        if self.rect.bottom < 0:
            self.kill()

プレイヤーのショットの動きを設定します。
self.rect.bottom = yは画面の上端です。
上端をすぎたらself.kill()関数でショットを消去します。

class HPBar(pygame.sprite.Sprite):
    def __init__(self, max_health, width, height, color, outline_color):
        super().__init__()
        self.max_health = max_health
        self.current_health = max_health
        self.width = width
        self.height = height
        self.color = color
        self.outline_color = outline_color

        # Create a surface for the HP bar
        self.image = pygame.Surface((width, height))
        self.rect = self.image.get_rect()

        self.update()  # Initial update to set HP bar to full health

    def update(self):
        # Calculate fill ratio
        fill_ratio = self.current_health / self.max_health
        fill_width = int(self.width * fill_ratio)

        # Fill the HP bar
        self.image.fill(self.outline_color)
        pygame.draw.rect(self.image, self.color, pygame.Rect(0, 0, fill_width, self.height))

    def set_health(self, health):
        # Set current health and update the HP bar
        self.current_health = health
        self.update()

    def decrease_health(self, amount):
        # Decrease current health by the given amount
        self.current_health = amount
        #self.current_health -= amount
        if self.current_health < 0:
            self.current_health = 0
        self.update()

    def increase_health(self, amount):
        # Increase current health by the given amount
        self.current_health += amount
        #self.current_health += amount
        if self.current_health > self.max_health:
            self.current_health = self.max_health
        self.update()

HPの減少を設定するクラスです。
def decrease_health(self, amount)でバーが減少します。
self.current_health = amountだとHPの数値を受け取ります。
self.current_health = -amountだと減少し続けます。
def increase_health(self, amount)は、増やす時に使います。
今回は、使用しません。

hpbar = HPBar(max_health=hitnumber, width=250, height=25,
color=(255,210,0), outline_color=(255,150,0))

クラスで設定したHPバーを表示します。
color=(r,g,b)でバーの色をRGBカラー(0〜255)で設定します。
outline_color=(255,150,0)で減少した後の色を設定します。

running = True
while running:
    clock.tick(60)

while running以降でゲーム中の更新を行います。

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                bullet = Bullet(player.rect.centerx, player.rect.top)
                all_sprites.add(bullet)
                bullets.add(bullet)

スペースキーを押した時のショットの設定です。

    hits = pygame.sprite.groupcollide(enemies, bullets, True, True)
    for hit in hits:
        score += 100  # Increment score when enemy is destroyed
        hitnumber += 1
        if score == 10000:
            hitnumber += 20

        enemy = Enemy()
        all_sprites.add(enemy)
        enemies.add(enemy)

ショットが敵(水色の四角)に当たった時の処理です。

    hits = pygame.sprite.spritecollide(player, enemies, False)
    if hits: #Player hits enemy.
        hitnumber -= 1

        if hitnumber < 0:
         gameover = fontgo.render("GameOver.Try for the Next.", True, (250,150,0))
         screen.blit(gameover, (180, 290))
         pygame.display.flip()  # Update the display
         pygame.time.wait(4000)  # Wait for 4 seconds before exiting
         pygame.quit()
         sys.exit() #Game Exit

プレイヤーと敵の衝突判定です。
hitnumberがHPの数値となります。
0を下回るとゲームオーバーの表示をします。
pygame.quit()とsys.exit()でゲームが止まり、ウインドウを閉じますが、プログラムは稼働しているので、ctr+cやエディタの停止でプログラムを止めます。

○全体のプログラム

import pygame
import sys
import random

pygame.init()

# Screen
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
hitnumber = 50

# Set up the screen
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("THE SHOOTER")

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((60, 60)) #size of image
        self.image.fill((0,230,230))
        self.rect = self.image.get_rect()
        self.rect.centerx = SCREEN_WIDTH // 2
        self.rect.bottom = SCREEN_HEIGHT - 10
        self.speed_x = 0

    def update(self):
        self.speed_x = 0
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            self.speed_x = -5
        if keys[pygame.K_RIGHT]:
            self.speed_x = 5
        self.rect.x += self.speed_x
        if self.rect.right > SCREEN_WIDTH:
            self.rect.right = SCREEN_WIDTH
        if self.rect.left < 0:
            self.rect.left = 0

class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((80, 80))
        self.image.fill((0,220,50))
        self.rect = self.image.get_rect()
        self.rect.x = random.randint(0, SCREEN_WIDTH - self.rect.width)
        self.rect.y = random.randint(-100, -50)
        self.speed_y = random.randint(3, 6) #enemy speed #random.randint(a,b) range [a,b]

    def update(self):
        global hitnumber #calling of global value in class
        self.rect.y += self.speed_y
        if self.rect.top > SCREEN_HEIGHT: #out of enemy for screen height
            self.rect.x = random.randint(0, SCREEN_WIDTH - self.rect.width)
            self.rect.y = random.randint(-100, -50)
            self.speed_y = random.randint(3, 6) #enemy speed

            hitnumber -= 2
            if hitnumber < -1:

              gameover = fontgo.render("GameOver.Try for the Next.", True, (250,150,0))
              screen.blit(gameover, (180, 290))
              pygame.display.flip()  # Update the display

              pygame.time.wait(4000)  # Wait for 4 seconds before exiting
              pygame.quit()
              sys.exit()#Game Exit

class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface((5, 10))
        self.image.fill((250,220,0))
        self.rect = self.image.get_rect()
        self.rect.centerx = x
        self.rect.bottom = y
        self.speed_y = -10

    def update(self):

        self.rect.y += self.speed_y

        if self.rect.bottom < 0:
            self.kill()

class HPBar(pygame.sprite.Sprite):
    def __init__(self, max_health, width, height, color, outline_color):
        super().__init__()
        self.max_health = max_health
        self.current_health = max_health
        self.width = width
        self.height = height
        self.color = color
        self.outline_color = outline_color

        # Create a surface for the HP bar
        self.image = pygame.Surface((width, height))
        self.rect = self.image.get_rect()

        self.update()  # Initial update to set HP bar to full health

    def update(self):
        # Calculate fill ratio
        fill_ratio = self.current_health / self.max_health
        fill_width = int(self.width * fill_ratio)

        # Fill the HP bar
        self.image.fill(self.outline_color)
        pygame.draw.rect(self.image, self.color, pygame.Rect(0, 0, fill_width, self.height))

    def set_health(self, health):
        # Set current health and update the HP bar
        self.current_health = health
        self.update()

    def decrease_health(self, amount):
        # Decrease current health by the given amount
        self.current_health = amount
        #self.current_health -= amount
        if self.current_health < 0:
            self.current_health = 0
        self.update()

    def increase_health(self, amount):
        # Increase current health by the given amount
        self.current_health += amount
        #self.current_health += amount
        if self.current_health > self.max_health:
            self.current_health = self.max_health
        self.update()


score = 0  # Initialize score variable

font = pygame.font.Font(None, 40)  # Font for displaying score
fonthp = pygame.font.Font(None, 40)
fontgo = pygame.font.Font(None, 50) # displaying Gameover

# Create an HP bar instance
hpbar = HPBar(max_health=hitnumber, width=250, height=25,
color=(255,210,0), outline_color=(255,150,0))


all_sprites = pygame.sprite.Group()
bullets = pygame.sprite.Group()
enemies = pygame.sprite.Group()

player = Player()
all_sprites.add(player)

for i in range(8):
    enemy = Enemy()
    all_sprites.add(enemy)
    enemies.add(enemy)

clock = pygame.time.Clock()

running = True
while running:
    clock.tick(60)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                bullet = Bullet(player.rect.centerx, player.rect.top)
                all_sprites.add(bullet)
                bullets.add(bullet)

    all_sprites.update()

    hits = pygame.sprite.groupcollide(enemies, bullets, True, True)
    for hit in hits:
        score += 100  # Increment score when enemy is destroyed
        hitnumber += 1
        if score == 10000:
            hitnumber += 20

        enemy = Enemy()
        all_sprites.add(enemy)
        enemies.add(enemy)

    hits = pygame.sprite.spritecollide(player, enemies, False)
    if hits: #Player hits enemy.
        hitnumber -= 1

        if hitnumber < 0:
         gameover = fontgo.render("GameOver.Try for the Next.", True, (250,150,0))
         screen.blit(gameover, (180, 290))
         pygame.display.flip()  # Update the display
         pygame.time.wait(4000)  # Wait for 4 seconds before exiting
         pygame.quit()
         sys.exit() #Game Exit

    screen.fill((250,220,200))
    all_sprites.draw(screen)

    # Display score
    score_text = font.render("1P:" + str(score), True, (0,200,250))
    screen.blit(score_text, (10, 10)) #text position

    #Update the display
    pygame.display.flip()

    hp=fonthp.render("HP:" + str(hitnumber), True, (0,250,150))
    screen.blit(hp, (150, 10))

    pygame.display.flip()

    # Decrease HP by hitnumber
    hpbar.decrease_health(hitnumber)

    # Draw the HP bar
    screen.blit(hpbar.image, (250, 10))

    pygame.display.flip()


pygame.quit()
sys.exit()