かわいい耳を育てる癒し系放置ゲームを作成していきます。
プロジェクトフォルダの作成
まずは、プロジェクトフォルダを作成します。
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がないので少し寂しいですが、それなりにタイトル画面らしさが出ているのではないでしょうか!