【C#学習法】完成コードを上から写さない|ターン制バトルを大きなブロックから組み立てる写経術
プログラミングの写経というと、完成コードを1行目から順番に入力する方法を思い浮かべるかもしれません。しかし、長いコードを上からそのまま写すだけでは、「今どの機能を作っているのか」が見えにくくなります。
ターン制バトルのように複数の処理が組み合わさるプログラムでは、まず大きなブロック(骨格)だけを作り、中身を後から埋める方法がおすすめです。この記事では、Characterクラスとバトルループを題材に、構造を理解するための写経手順を紹介します。
今回使用するターン制バトルの完成コードと基本解説は、はじめての C# ミニ RPG ― クラス × ループで動かすターン制バトルで確認できます。完成版を一度動かしてから、この記事の手順で骨格から再構築する学習方法もおすすめです。
- 完成コードを機能ごとのブロックに分解できる
- クラスやメソッドの「外側」を先に書ける
- 空の骨格を少しずつ動くプログラムへ育てられる
- 写経をタイピング練習ではなく設計の練習に変えられる
結論:外側から内側へ書くと構造が見える
完成コードを上から順番に写す方法が必ず悪いわけではありません。文法や入力に慣れる段階では役立ちます。しかし、学習の目的が「自分で似たプログラムを作れるようになること」なら、次の順番が効果的です。
最初に完成コードを5つのブロックへ分ける
今回のターン制バトルは、細かい行を見る前に、次の5つへ分けられます。
- キャラクターのデータ:名前、現在HP、最大HP、生存判定
- キャラクターの行動:攻撃、ダメージ、回復
- 登場人物の生成:プレイヤーと敵を作る
- バトルの進行:生きている間、交互に行動する
- 終了処理:勝利または敗北を表示する
この説明を自分の言葉で言えれば、コードを書く前にプログラムの設計図ができています。
ステップ1:空の骨格だけを書く
最初は攻撃力や回復量を書きません。名前空間、クラス、Main、必要なメソッドの外枠だけを作ります。
using System;
namespace TurnBasedGame
{
class Character
{
// データは後で書く
public int Attack()
{
// 後で実装する
return 0;
}
public void TakeDamage(int damage)
{
// 後で実装する
}
public void Heal()
{
// 後で実装する
}
}
class Program
{
static void Main()
{
// キャラクター生成
// バトルループ
// 勝敗表示
}
}
}
この段階ではゲームは動きません。それで構いません。まず、Characterが「行動を持つもの」、Program.Mainが「ゲーム全体を進行する場所」だと確認します。
return 0;が必要なのは、戻り値を持つメソッドを一時的にコンパイル可能にするためです。
ステップ2:Characterのデータだけを入れる
class Character
{
public string Name;
public int HP;
public int MaxHP;
private static Random _rand = new Random();
public bool IsAlive => HP > 0;
// メソッドはまだ仮のまま
}
ここでは「キャラクターが何を持つか」だけに集中します。
Name:画面に表示する名前HP:現在の体力MaxHP:回復の上限_rand:攻撃力や回復量を決める乱数IsAlive:HPが0より大きいかを返す生存判定
ステップ3:Mainで登場人物を作る
Character player = new Character
{
Name = "ヒロト",
HP = 100,
MaxHP = 100
};
Character enemy = new Character
{
Name = "スライム",
HP = 60,
MaxHP = 60
};
Character[] participants = { player, enemy };
int turn = 0;
この段階で、次の確認用コードを一時的に追加して実行してみましょう。
Console.WriteLine($"{player.Name}: {player.HP}/{player.MaxHP}");
Console.WriteLine($"{enemy.Name}: {enemy.HP}/{enemy.MaxHP}");
ヒロトとスライムの情報が表示されれば、キャラクター生成は成功です。確認できたら、この仮表示は削除して構いません。
ステップ4:バトルループの外枠を作る
攻撃や入力処理をいきなり書かず、まず「戦闘を続ける条件」「現在の行動者」「攻撃される相手」「手番交代」だけを書きます。
while (player.IsAlive && enemy.IsAlive)
{
Character current = participants[turn];
Character target = participants[1 - turn];
Console.WriteLine($"現在の行動者: {current.Name}");
Console.WriteLine($"攻撃される相手: {target.Name}");
turn = 1 - turn;
}
ただし、このまま実行すると無限に表示され続けます。骨格の確認時は、Console.ReadLine();を一時的に入れるか、回数を制限してください。
Console.ReadLine();
turn = 1 - turn;
なぜ 1 – turn で交代できる?
| 現在のturn | 1 – turn | 次の手番 |
|---|---|---|
| 0 | 1 | 敵 |
| 1 | 0 | プレイヤー |
配列の添字を0と1に限定しているため、短い式で交互に切り替えられます。
ステップ5:Characterの行動を1つずつ完成させる
攻撃
public int Attack()
{
int damage = _rand.Next(10, 21);
Console.WriteLine($"{Name} の攻撃! {damage} ダメージ!");
return damage;
}
Next(10, 21)の21は含まれないため、ダメージは10〜20です。まず攻撃だけ実装し、戻り値を表示して確認します。
被ダメージ
public void TakeDamage(int damage)
{
HP = Math.Max(HP - damage, 0);
}
Math.Maxにより、HPが0未満になるのを防ぎます。HPが5のとき10ダメージを受ける、という境界値を試しましょう。
回復
public void Heal()
{
int heal = _rand.Next(5, 16);
HP = Math.Min(HP + heal, MaxHP);
Console.WriteLine(
$"{Name} は回復魔法! {heal} 回復(HP: {HP}/{MaxHP})"
);
}
Math.Minにより、最大HPを超えて回復しません。HPが95、最大HPが100のときに試すと役割が分かります。
ステップ6:ループの中にプレイヤーと敵の行動を入れる
if (current == player)
{
Console.WriteLine("\n=== あなたのターン ===");
Console.WriteLine($"敵 HP: {target.HP}/{target.MaxHP}");
Console.Write("1)攻撃 2)回復 > ");
string input = Console.ReadLine();
if (input == "2")
{
current.Heal();
}
else
{
target.TakeDamage(current.Attack());
}
}
else
{
Console.WriteLine("\n--- 敵のターン ---");
target.TakeDamage(current.Attack());
}
ここで初めて、先ほど作った骨格の中へ具体的な行動を入れます。すでにcurrentとtargetの意味が確認できているため、処理を読み解きやすくなります。
ステップ7:最後に勝敗表示を入れる
Console.WriteLine(
player.IsAlive ? "\n★ 勝利!" : "\n★ 敗北…"
);
バトルループを抜けた時点で、プレイヤーか敵のどちらかが倒れています。条件演算子を使い、プレイヤーが生きていれば勝利、そうでなければ敗北と表示します。
ブロック学習で毎回行う3つの確認
- 予想する:実行前に何が表示されるか、変数がどう変わるかを書く
- 小さく実行する:1ブロック追加するたびに動作を確認する
- 説明する:そのブロックの役割をコードを見ずに日本語で説明する
「入力できた」だけで終わらず、予想と結果を比べることが学習につながります。
上から写す方法との使い分け
| 方法 | 向いている目的 | 注意点 |
|---|---|---|
| 上から順に写す | 文法や開発環境の操作に慣れる | 全体構造を見失いやすい |
| ブロックごとに外側から書く | 設計・処理の流れ・役割分担を学ぶ | 仮実装や小さな動作確認が必要 |
初学者には、最初に完成版を一度動かしてゴールを見せた後、白紙からブロック単位で再構築する二段階の授業も効果的です。
練習課題
- 設計図を作る:完成コードを見ずに、5つのブロック名だけ書いてください。
- 仮実装:
Attackが常に10を返す状態でバトルを動かしてください。 - 境界値:HPが5のキャラクターに10ダメージを与え、HPが0で止まることを確認してください。
- 防御を追加:プレイヤーの選択肢に「3)防御」を追加する前に、必要なブロックを日本語で設計してください。
- 敵を増やす:敵を2体にする場合、現在の配列と
1 - turnのどこを変更する必要があるか考えてください。 - 再構築:完成コードを閉じ、コメントで骨格を書いてから自力で中身を埋めてください。
まとめ
- 長いコードは、最初に機能ごとの大きなブロックへ分ける
- クラス、メソッド、ループなどの外枠を先に書く
- 中身は攻撃、ダメージ、回復のように1機能ずつ実装する
- ブロックを追加するたびに、予想・実行・説明を行う
- 写経のゴールは、同じコードを入力することではなく、構造を理解して再構築できること
完成コードは答えではなく、設計を読み取るための教材です。「上から写す」から「骨格を作り、機能を組み立てる」へ変えると、次のゲームを自分で作る力につながります。








ディスカッション
コメント一覧
まだ、コメントがありません