耳農場のタイトル画面を作成する

かわいい耳を育てる癒し系放置ゲームを作成していきます。

プロジェクトフォルダの作成

まずは、プロジェクトフォルダを作成します。

mimiフォルダの中に、以上三つのフォルダと、メインとなるPythonファイルを作成します。

ゲームを作っていくうえで、毎回コマンドプロンプトから作業ディレクトリに移動し、mimi.pyを実行するのは面倒なので、ワンクリックでテストができるようテスト用のbatファイルも作成しておきます。

cd /d %~dp0
python mimi.py
cmd /k

このbatファイルを、mimi.pyと同じ階層に保存しておきます。

これで、テストをしたいときにこのbatファイルをダブルクリックで実行すれば、エラーがあってもその内容を確認することができます。

タイトル画面の作成

それでは、タイトル画面を作成していきます。

一度スペースラビップルを作っていることもあり、一度解説済みの部分に関しては飛ばしてしまうので、詳しくはソースコードをご確認ください。

ここでは、新たに加えた工夫点などをまとめていきます。

点滅する「Press Space」

PCゲームだと、マウスを使って操作するゲームなのか、キーボードを使って操作するゲームなのか、分かりにくい場合があります。

耳農場は主にキーボードを使って操作するゲームになりますが、それをプレイヤーに自然に伝えるため「Press Space」の文字を表示します。

一度スペースキーを押すためにキーボードを触れば、そのまま自然とキーボードで操作してくれるはずです。

class Title():
    def draw(self, screen):
        if self.pressed == False:
            # 点滅
            if int(self.frame / 48 % 2) == 0:
                screen.blit(self.press_txt, (120, 230))

self.pressedという変数を用意し、Falseであれば点滅するようにしています。

キーハンドラーで、スペースキーを押すとTrueになるような処理を記述しておきます。

class Game():
    def title_handler(self, event):
        if event.type == KEYDOWN and event.key == K_SPACE:
            if self.title.pressed == False:
                self.title.pressed = True

セーブデータの有無を確認する

耳農場は放置系ゲームなので、セーブ機能が必要になってきます。

実装はまだまだ先ですが、いざ実装されたときにセーブデータがあるかないかでメニューの表示を少し変えます。

具体的には、セーブデータがない場合はコンティニューの文字をグレーアウトし、選択不可にしましょう。

CONTINUE, NEW, EXIT = range(3)
MAIN_COLOR = (228, 0, 127)
INVALID_COLOR = (128, 128, 128)

class Title():
    def reset(self):
        # セーブデータの有無を確認
        if os.path.exists("data/save.dat") == True:
            self.valid = True
        else:
            self.valid = False

        # フォント
        if self.valid == True:
            self.select = CONTINUE
            self.continue_txt = menu_font.render("CONTINUE", True, MAIN_COLOR)
        elif self.valid == False:
            self.select = NEW
            self.continue_txt = menu_font.render("CONTINUE", True, INVALID_COLOR)

キーハンドラーでも、セーブデータが存在しない場合はコンティニューを選択できないようにします。

class Game():
    def title_handler(self, event):
        (省略)
        elif event.type == KEYDOWN and event.key == K_UP:
            if self.title.pressed == True:
                if self.title.select > 1:
                    self.title.select -= 1
                    self.title.cursor_set()
                elif self.title.select == 1:
                    if self.title.valid == True:
                        self.title.select -= 1
                        self.title.cursor_set()

波をスクロールさせる

スペースラビップルでは、背景画像をスクロールさせましたが、これを応用し波だけをスクロールさせます。

タイトル画面とはいえ、まったく動きがないのも寂しいですからね。

使用するのは、以下の画像です。

class Title():
    scroll_speed = 3

    def __init__(self):
        # 背景画像
        self.bg = load_image("resources/image/bg", "title.png")
        self.wave = load_image("resources/image/bg", "wave.png", -1)
        self.bgx = 0
        self.scr_width = pygame.display.get_surface().get_width()

    def update(self):
        self.frame += 1
        self.bgx = (self.bgx - self.scroll_speed) % self.scr_width

    def draw(self, screen):
        screen.blit(self.bg, (0, 0))
        screen.blit(self.wave, (self.bgx, 440))
        screen.blit(self.wave, (self.bgx - self.scr_width, 440))

仕組みは背景スクロールと全く同じですが、背景を透過させ座標を調整しています。

落下し、波に流される耳を作る

さらにタイトル画面に動きをつけていきます。

画面上の木から耳が育ち、ぽとりと落ちて波に流されていくようにします。

title.pyでMimiクラスを作成します。

class Mimi(pygame.sprite.Sprite):
    SPEED = 1

    def __init__(self):
        super().__init__(self.containers)
        self.image = load_image("resources/image/mimi", "normal_mimi.png", -1)
        self.rect = self.image.get_rect()
        randx = random.randint(0, 50)
        randy = random.randint(0, 50)
        self.rect.center = (680 + randx, 130 + randy)
        self.alpha = 0
        self.image.set_alpha(self.alpha)
        self.grow = True
    
    def update(self):
        if self.grow == True:
            self.alpha += 5
            if self.alpha >= 255:
                self.grow = False
            self.image.set_alpha(self.alpha)
        elif self.grow == False:
            if self.rect.centery < 440:
                self.rect.move_ip(0, self.SPEED)
            elif self.rect.centery == 440:
                self.image = pygame.transform.rotate(self.image, -60)
                self.rect.move_ip(0, self.SPEED)
            else:
                self.rect.move_ip(-self.SPEED * 2, 0)
            if self.rect.right < 0:
                self.kill()

耳は、まず透明な状態で作成されます(self.alpha = 0)。

それをupdateするたびに+5していき、MAX(255)になったら落下が始まります。

指定した座標まで来たら、画像を少し回転させ、次は波の向きに合わせて移動するようにし、最終的に画面外に出たら消去、という処理を行います。

耳を大量発生できるようにする

タイトル画面では、せいぜいニューゲームかコンティニューしかプレイヤーは選べませんが、ちょっと遊び心を追加して、Mキーを押すと耳を追加で作成できるようにします。

class Game():
    def title_handler(self, event):
        (省略)
        elif event.type == KEYDOWN and event.key == K_m:
            self.title.create_mimi()
class Title():
    def create_mimi(self):
        if len(self.all) <= 15:
            Mimi()

さすがに無尽蔵だと重くなってしまうので、15個ぐらいで制御しています。

ちょっとした遊びですが、プレイヤー目線で考えるとうれしいですよね。

完成形

このようになりました!

音楽やSEがないので少し寂しいですが、それなりにタイトル画面らしさが出ているのではないでしょうか!

ソースコードのダウンロード