0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

成功だけでは足りなかった――成功・部分成功・失敗の3段階にした理由【第20回】

0
Last updated at Posted at 2026-08-30

4Hえんぴつ|React+TypeScriptでWeb RPGを作る

第4章「戦闘・交渉・調査と状態管理」
第20回:成功だけでは足りなかった――成功・部分成功・失敗の3段階にした理由

連載トップ・第0回はこちら

本連載では、設計・実装・テスト・改善にAIを活用しながら、React+TypeScriptによるWeb RPG開発を進めています。

1. 今回のテーマ

前回までで、

プレイヤー側
AbilitySet
↓
どれくらい得意か

イベント側
ResolutionDefinition
↓
どれくらい難しいか

という判定の材料を用意しました。

次に必要なのは、

判定した結果をどう表現するか

です。

最初に思いつくのは、

成功
失敗

の2種類です。

しかしRPGを作っていくと、この2つだけでは表現しにくい場面が出てきました。

例えば、

依頼の目的は達成した。
ただし、HPを失った。

あるいは、

完全には解決できなかった。
それでも次へ進むことはできる。

こうした結果です。

そこで今回のRPGでは、

export type ResolutionOutcome =
  | "success"
  | "partial_success"
  | "failure";

という3段階にしています。


2. 成功と失敗だけなら分かりやすい

2段階の判定には大きな利点があります。

単純です。

例えば、

能力値 >= 難易度
↓
成功

能力値 < 難易度
↓
失敗

とすれば、処理も説明もしやすくなります。

プロトタイプとして動作確認するだけなら、この形でも十分です。

しかし、ゲームとしてイベントを増やしていくと、

成功ではない
でも完全な失敗でもない

という結果が欲しくなりました。


3. 「失敗=何も得られない」だと進行が止まりやすい

例えば、

壊れた荷車を助ける

イベントがあるとします。

戦闘、交渉、調査のどれかを選び、失敗したとします。

そこで、

失敗
↓
荷車を助けられなかった
↓
依頼終了

としてしまうと、プレイヤーの一度の選択だけで進行が大きく途切れることがあります。

もちろん、

失敗によって依頼そのものが終わる

ゲーム設計もあります。

ただ、今回作っている小規模RPGでは、

失敗した
↓
何かを失う
↓
それでも物語は続く

という展開も作りたいと考えました。


4. そこで部分成功を入れる

partial_successは、

成功した部分と、うまくいかなかった部分が両方ある結果

として使います。

例えば、

荷車は救出できた
↓
しかし荷物の一部を失った

あるいは、

必要な情報は手に入った
↓
しかし評判が下がった

という結果です。

単純に、

成功の弱い版

というより、

目的はある程度達成したが、代償や不利益が残る状態

として考えています。


5. 3段階にすると結果の幅が広がる

例えば、一つの調査について、

success
↓
手掛かりを発見
追加情報も得る

partial_success
↓
手掛かりは発見
ただし時間やHPを失う

failure
↓
手掛かりを得られない
別の不利益が発生

という違いを作れます。

同じイベント、同じ「調査」という選択でも、結果によってその後の状態が変わります。


6. ResolutionOutcomeで型として限定する

現在の設計では、結果種別を次のように定義しています。

export type ResolutionOutcome =
  | "success"
  | "partial_success"
  | "failure";

文字列を自由に書けるようにはしていません。

例えば、

const outcome: ResolutionOutcome =
  "almost_success";

のような値は使えません。

これによって、

success
partial_success
failure

という3種類が、このRPGにおける正式な判定結果であることを型でも表現できます。


7. ResolutionDefinitionから3つの結果へつなぐ

前回紹介したResolutionDefinitionには、

successResultId: string;
partialSuccessResultId: string;
failureResultId: string;

があります。

つまり、

ResolutionDefinition
├─ success
├─ partial_success
└─ failure

それぞれについて、別の結果データへつなげます。

これにより、

戦闘で成功した結果
戦闘で部分成功した結果
戦闘で失敗した結果

を個別に定義できます。


8. 結果そのものはResultDefinitionへ分離する

結果は、ResolutionDefinitionへ直接書き込まず、

ResultDefinitionとして分離しています。

現在の設計は次の形です。

export interface ResultDefinition {
  id: string;
  resolutionId: string;
  outcome: ResolutionOutcome;

  title: string;
  narrative: string;

  effects: EventEffect;
  questContinuation: QuestContinuation;

  nextEventId?: string;
}

ここでは、

どういう結果だったか
何を表示するか
何が変化するか
依頼をどう続けるか

を定義します。


9. 「判定」と「結果」を分ける

ここまでの構造をまとめると、

AbilitySet
↓
プレイヤー能力

ResolutionDefinition
↓
方法・能力・難易度

判定
↓
success / partial_success / failure

ResultDefinition
↓
具体的に何が起きるか

となります。

つまり、

判定結果がpartial_successだった

ことと、

HPが2減って依頼は継続する

ことは別の情報です。

これらを一つにまとめていません。


10. 同じ部分成功でも内容はイベントごとに違う

例えば、二つのイベントで同じpartial_successになったとします。

イベントAでは、

目的達成
+
HP -2

かもしれません。

イベントBでは、

目的達成
+
評判 -1

かもしれません。

つまり、

partial_success
=
必ずHPを失う

というルールではありません。

partial_successは結果の分類であり、

具体的な影響はResultDefinition側で決めます。


11. 依頼の進み方も3段階とは別にする

結果には、

export type QuestContinuation =
  | "continue"
  | "complete"
  | "complete_with_penalty"
  | "fail";

という別の分類があります。

ここも重要です。

例えば、

ResolutionOutcome
success

だからといって、

必ず、

Quest
complete

になるとは限りません。

途中イベントなら、

success
↓
continue

もあります。


12. 「部分成功=依頼継続」とも限らない

同じように、

partial_success

だから、

continue

と固定するわけでもありません。

例えば最終イベントなら、

partial_success
↓
complete_with_penalty

という結果にできます。

つまり、

判定結果
ResolutionOutcome

と

依頼進行
QuestContinuation

も分けています。


13. 失敗してもゲーム全体を止めなくてよい

failureについても同じです。

失敗したからといって、

ゲームオーバー

しか選べないわけではありません。

例えば、

failure
↓
HPを失う
↓
評判も下がる
↓
依頼は失敗
↓
町へ戻る

という展開にできます。

別のイベントなら、

failure
↓
情報を得られない
↓
それでも依頼は継続

という設計も可能です。

判定結果とゲーム進行を一対一に固定しないことで、結果の組み合わせを増やせます。


14. 部分成功は「救済」だけではない

部分成功を入れると、

プレイヤーを失敗させないための救済措置

のようにも見えます。

しかし、それだけではありません。

例えば、

成功
↓
安全だが情報は少ない

部分成功
↓
ダメージを受けるが重要情報を得る

のようにすれば、部分成功にも別の意味を持たせられます。

つまり、

成功・部分成功・失敗を単純な点数順にしなくてもよい

わけです。


15. 結果を文章だけで表現しない

例えば結果文章に、

傷を負ったが、なんとか荷車を助け出した。

と書けば、人間には部分成功だと分かります。

しかしプログラム側で、

narrative.includes("なんとか")

のように判定するわけにはいきません。

そのため、

outcome: "partial_success"

として、結果分類を明示します。

ここでも、

プレイヤーへ見せる文章

と

プログラムが使う状態

を分離しています。


16. UIでも結果を扱いやすくなる

結果種別が型として決まっていれば、画面側でも、

以下は考え方を説明するため、実装を簡略化しています。

function getOutcomeLabel(
  outcome: ResolutionOutcome
): string {
  switch (outcome) {
    case "success":
      return "成功";

    case "partial_success":
      return "部分成功";

    case "failure":
      return "失敗";
  }
}

のように扱えます。

また、

成功
部分成功
失敗

で表示方法を変える場合にも、文章解析ではなくoutcomeを基準にできます。


17. 3段階に増やした分、定義漏れには注意する

3段階にすると表現力は増えます。

一方で、必要な結果データも増えます。

現在の設計では、一つのResolutionDefinitionについて、

成功結果
部分成功結果
失敗結果

の3つが必要です。

どれか一つだけ定義し忘れると、

判定はできた
↓
しかし表示する結果がない

という状態になります。

そのため、正式設計でも1つのResolutionDefinitionにつき3結果を持つことを制約としています。


18. 2段階から始めて、必要になってから増やす

今回も、

最初から3段階にすべきだった

というより、

ゲームを広げる過程で3段階が必要になった

と考えています。

最初のプロトタイプでは、

成功
失敗

でも基本ループを確認できます。

その後、

完全成功ではない
↓
でも失敗として切り捨てたくない

ケースが見えてきたため、

partial_success

を明確な状態として扱うようにしました。


19. 3段階にすると「結果の先」も設計できる

結果が3種類になると、

単に結果画面を変えるだけではありません。

例えば、

success
↓
評判 +2
↓
依頼完了

partial_success
↓
評判 +1
↓
ゴールド減少
↓
ペナルティ付き完了

failure
↓
評判 -1
↓
依頼失敗

のように、その後のゲーム状態まで変えられます。

ここから、

HP
ゴールド
評判
フラグ
所持品

などの更新へつながります。


20. 今回のポイント

今回のポイントは3つです。

  • 成功/失敗だけでは表現しにくい「目的は達成したが代償がある」状態を、partial_successとして明示する
  • ResolutionOutcomeは判定結果の分類であり、HP減少などの具体的な影響とは分離する
  • 判定結果と依頼の継続・完了・失敗も別々に管理する

第18回では、

プレイヤーの能力

第19回では、

解決方法ごとの難易度

を用意しました。

そして今回は、

能力
+
難易度
↓
成功
部分成功
失敗

という結果の形を決めました。

次は、この結果によって、

HP・評判・ゴールドなどを実際にどう変化させるか

へ進みます。


21. 次回

次回は、

判定結果でHP・評判・ゴールドを変える――ResultDefinitionとEventEffect【第21回】

です。

success、partial_success、failureが決まっただけでは、まだゲーム状態は変わっていません。

そこで、

export interface EventEffect {
  hpDelta?: number;
  goldDelta?: number;
  reputationDelta?: number;
  addFlags?: string[];
  removeFlags?: string[];
  addItemIds?: string[];
  removeItemIds?: string[];
}

を使って、

HPが減る
ゴールドを得る
評判が上がる
フラグが立つ
アイテムを得る

といった変化をデータとして表現します。

次回は、

「結果の文章」と「ゲーム状態への影響」をどう分離するか

を中心に見ていきます。

 
 
この記事を最後まで読んでいただき、ありがとうございます。

「♡ いいね」を押していただけるとうれしいです。これからの記事づくりの励みになります。

もし気に入っていただけましたら、フォローもよろしくお願いします。


前の記事

第19回 判定条件をイベントごとに変える――ResolutionDefinitionで難易度をデータ化する

次の記事

第21回 判定結果でHP・評判・ゴールドを変える――ResultDefinitionとEventEffect

連載トップ

第0回 連載の目的と開発ロードマップ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?