概要
こんにちは、しがないエロゲーマー蟹丸です。Ren'Pyを使った2D RPG制作第二回です。
今回はパーティーとステータスを作っていきたいと思います。
本記事の範囲
Ren'Pyでパーティーとステータスの管理ができるようにする、ステータス画面を作る
背景
RPGと言ったらまずバトルがしたい!と思うんですが、その前に必要なものがあります。ステータスです。体力も攻撃力もわからないままバトルはしたくないですからね。
またDQ1みたいなケースを除いてパーティーが必要になってくるでしょう。
ステータスは一人分作ったら同じようにほかの仲間の分も制作できると思うので、このあたりを一緒に進めていきます。
1. デフォルトでできることを確認する
システム実装
ビジュアルノベルであれば好感度を使う場合があるので、ステータス管理がデフォルトであるかも?と思ったのですが、見た感じRen'Pyコード中にaffection
という変数を定義してそこで操作しているようでした。
登場人物数人の好感度を管理するのであればそれで十分ですが、多くのキャラクターと敵に共通する要素であるステータスをこれで実装するのは大変です。わかりやすい実装をする必要がありそうです。
UI周り
ゲームプレイ中にメニューを開いて仲間の状態やステータスが見られると便利です。
デフォルトのゲームメニューを見てみると、ヘルプが「上部に表示されているキーボード/マウス(/ゲームパッド)をクリックするとそれぞれに対応するヘルプが表示される」というUIになっています。
これを利用したら「パーティーメンバーに対してそれぞれのステータスを表示する」ということができそうです。
2. 作る
システム実装
この部分はRen'PyじゃなくてただのPythonだろ!という実装になっているので、Pythonは使えるよ、という方は流し見してください。
Statsクラス
まずはステータス用にStatsクラスを作ります。
英語だとステータスのことstatsとかstatisticsとか呼ぶみたいですね。言われれば確かにです。
class Stats():
def __init__(self, name, default_value, max_value):
if default_value > max_value:
raise ValueError("default_value must be equal to or less than max_value")
self.name = name
self.current_value = default_value
self.max_value = max_value
self.max_flag = False
def get_name(self):
return self.name
def get_current_value(self):
return self.current_value
def add_value(self, diff):
if not self.max_flag:
if self.current_value + diff >= self.max_value:
self.max_flag = True
self.current_value = self.max_value
else:
self.current_value += diff
簡単に以下を実装しています。後で他の機能が欲しくなったら追加します。
- 名前、デフォルト値(初期値)、最大値でインスタンス生成
- キャラクターによってデフォルト値や最大値が違う場合に対応。
- 名前と現在値を取得するメソッド
- 基本。
- デフォルト値、最大値、名前はインスタンス生成後変更不可。
- 最大値を超えないように現在値を増やすメソッド
- レベルアップやステータスアップのアイテムに対応。
味方キャラクターにも敵にも使えるようになっています。敵にはmax_value
はいらないかもしれませんが、味方が強くなると敵も強くなるタイプのゲームなら使えそうなので残しておきます。
PartyMemberクラス
次に味方キャラクター用にPartyMemberクラスを作ります。
class PartyMember():
def __init__(self, name, default_stats = {"体力": 100, "攻撃力": 30, "防御力": 15}, max_stats = {"体力": 1000, "攻撃力": 100, "防御力": 100}, skills = None):
if default_stats.keys() != max_stats.keys():
raise ValueError("stats keys not corresponding")
self.name = name
self.default_stats = default_stats
self.max_stats = max_stats
# self.skills = skills
self.stats = []
for stats_name in default_stats.keys():
self.stats.append(Stats(stats_name, default_stats[stats_name], max_stats[stats_name]))
def get_name(self):
return self.name
def get_all_stats(self):
return self.stats
以下を実装しています。スキルについてはあとから必要になると思うので形だけ作り、メソッドは定義していません。
- 名前、デフォルトステータス、最大ステータス、初期スキルを与えてインスタンス生成
- ここはやや特殊で、自分の作ろうとしているゲームはキャラクターによって持っているステータスの種類が違う(壁は魔力ステータスがないみたいなイメージです)ため、ステータスをdictで渡します。ステータスの種類が共通であれば
default_hp = 100
のような渡し方もできると思います。 - 渡された値を元に、
stats
リストにStatsクラスのインスタンスを追加します。
- ここはやや特殊で、自分の作ろうとしているゲームはキャラクターによって持っているステータスの種類が違う(壁は魔力ステータスがないみたいなイメージです)ため、ステータスをdictで渡します。ステータスの種類が共通であれば
- 名前を取得するメソッド
- 基本。
- ステータスを取得するメソッド
-
stats
リストを返します。要素はStatsクラスのインスタンスです。
-
パーティーメンバーの追加
クラスができたので、パーティーメンバーを追加してみます。
init:
$party = []
label join_member:
$party.append(PartyMember(name = "バランス型の人"))
これでjoin_member
ラベルに飛んだとき、パーティーにデフォルトステータスを持った「バランス型の人」が追加されるようになりました。
UI実装
ここからはRen'Pyらしいことをやっていきます。
上述のとおり、ヘルプメニューのデフォルト実装を参考に作ってみます。
screen stats():
tag menu
python:
party_member_list = []
stats_list = []
for party_member in party:
party_member_list.append(party_member.get_name())
stats_dict = {}
for stats in party_member.get_all_stats():
stats_dict[stats.get_name()] = stats.get_current_value()
stats_list.append(stats_dict)
default member = party_member_list[0]
default member_stats = stats_list[0] # dict型
use game_menu(_("ステータス"), scroll="viewport"):
style_prefix "stats"
vbox:
spacing 15
hbox:
for i, party_member in enumerate(party_member_list):
textbutton _(party_member) action [SetScreenVariable("member", party_member), SetScreenVariable("member_stats", stats_list[i])]
use stats_screen(member, member_stats)
screen stats_screen(member, member_stats):
for k, v in member_stats.items():
hbox:
label _(k)
text str(v)
以下のような流れになっています。
- まずPythonコードで各パーティーメンバーに対して名前とステータスのmapを取得する
- 押されたら
member
変数とmember_stats
変数をセットするテキストボタンを配置する -
member
とmember_stats
を渡して画面にステータスを表示する
member_stats
をRen'Pyコード内で扱えるdict型にしてから渡しているのは、screenに渡されるargsをPythonコード内で使う方法がわからなかったためです……。
ゲームメニューから上で作ったstatsスクリーンを表示できるようにします。メインメニューでは必要ないのでhistoryやsaveと同じところに追加します。
# 文言をデフォルトからありがちなものに変えています。
# 直近でやっていたエロゲに寄せたなんてことはありません。
screen navigation():
vbox:
style_prefix "navigation"
xpos gui.navigation_xpos
yalign 0.5
spacing gui.navigation_spacing
if main_menu:
textbutton _("はじめから") action Start()
textbutton _("つづきから") action ShowMenu("load")
else:
textbutton _("ステータス") action ShowMenu("stats") # これを追加
textbutton _("ログ") action ShowMenu("history")
### 後略 ###
メニューを開くとまずセーブが開いているのはあまりRPGライクではないので、ステータスを開くようにします。
################################################################################
## 初期化
################################################################################
init offset = -1
default _game_menu_screen = "stats" # ここを追加
これでゲームメニューからパーティーメンバーのステータスを確認できるようになりました。
1人だけのとき:
2人に増えたとき:
結論と今後
今回はRen'Pyを使ってパーティーとステータスの管理・確認ができるようになりました。
まだまだバトルができるには程遠いので、道具やスキルなどをゆっくり実装していきたいと思います。