【チュートリアル】スライム50体から学ぶオブジェクト指向

~本当に手で書いて、OOPのありがたみを体感しよう~

🎯 チュートリアルの目的

このチュートリアルでは、「スライム50体を管理する」という課題を通して、オブジェクト指向(OOP)の必要性と有効性を実感します。


✅ 前提

  • C#の基本構文(変数・if文・for文)を理解している
  • Visual Studio または C#が動作する環境がある

🧱 Step 0. 手続き型でスライムを50体作る

プロジェクト名 Slime50Manager

🔍 目的:

「クラスを使わずに」スライムを50体管理する大変さを体験する


📝 手順:

以下のコードのように、スライム1〜10体分の変数と処理を手で書き出してください

残りの 11〜50 体分も、同じように書き足していく前提で行います。

string name1 = "スライム1";
int hp1 = 10;

string name2 = "スライム2";
int hp2 = 12;

string name3 = "スライム3";
int hp3 = 14;

// ……省略……

string name50 = "スライム50";
int hp50 = 60;

hp1 -= 5;
hp2 -= 5;
// ……hp3~hp50も同様に

Console.WriteLine($"{name1} のHP: {hp1}");
Console.WriteLine($"{name2} のHP: {hp2}");
// ……name3~name50も同様に

💡 考察:

  • 少しずつ変わる数字を1つずつ打つのがとにかく面倒
  • 「あと5体追加して」と言われたら?
  • 「全スライムのHPを2倍にして」と言われたら?

🧰 Step 1. Enemy クラスを作って再設計しよう

🔍 目的:

スライムの情報と行動を1つのまとまり(クラス)にする


📝 手順:

Visual Studio に Enemy.cs を追加し、以下のクラスを作成します。

class Enemy
{
    public string Name;
    public int HP;

    public Enemy(string name, int hp)
    {
        Name = name;
        HP = hp;
    }

    public void TakeDamage(int damage)
    {
        HP -= damage;
        if (HP < 0) HP = 0;
    }
}

🧪 Step 2. Enemy を使って50体をループで作ろう

🔍 目的:

クラスとリスト、for 文を使ってスライムを効率よく量産する


📝 手順:

以下のコードを Main メソッド内に記述します。

List<Enemy> enemies = new List<Enemy>();

for (int i = 1; i <= 50; i++)
{
    int hp = 10 + (i % 5) * 2;  // HPにばらつき
    enemies.Add(new Enemy($"スライム{i}", hp));
}

✅ 解説:

  • List<Enemy> は複数のスライムを1つにまとめる箱。
  • for 文で名前とHPを連番付きで自動生成。

⚔️ Step 3. まとめて攻撃、まとめて表示

🔍 目的:

繰り返し処理で一括管理する便利さを体験する


📝 手順:

foreach (var enemy in enemies)
{
    enemy.TakeDamage(5);
}

foreach (var enemy in enemies)
{
    Console.WriteLine($"{enemy.Name} の残りHP: {enemy.HP}");
}

💡 考察:

  • たった数行で50体分の処理が完了
  • コードが短く・読みやすく・変更しやすい

🧬 Step 4. 継承とオーバーライドを試してみよう

🔍 目的:

クラスを拡張し、「種類ごとの振る舞いの違い」を作る


📝 手順:

Goblin.cs を作成して以下のように記述します。

class Goblin : Enemy
{
    public Goblin(string name) : base(name, 20) {}

    public override void TakeDamage(int damage)
    {
        Console.WriteLine($"{Name} は防御した!");
        base.TakeDamage(damage - 1);
    }
}

🧪 敵リストにゴブリンを追加:

enemies.Add(new Goblin("ゴブリンA"));
enemies.Add(new Goblin("ゴブリンB"));

✅ 解説:

  • Goblin クラスは Enemy を継承
  • TakeDamage をオーバーライドして、動きを変えた
  • それでも Enemy 型として扱える → ポリモーフィズム

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Slime50Manager
{
    internal class Program
    {
        static void Main(string[] args)
        {
            string name1 = "スライム1";
            int hp1 = 10;

            string name2 = "スライム2";
            int hp2 = 12;

            string name3 = "スライム3";
            int hp3 = 14;

            // ……省略……

            string name50 = "スライム50";
            int hp50 = 60;

            hp1 -= 5;
            hp2 -= 5;
            // ……hp3~hp50も同様に

            Console.WriteLine($"{name1} のHP: {hp1}");
            Console.WriteLine($"{name2} のHP: {hp2}");
            // ……name3~name50も同様に

            List<Enemy> enemies = new List<Enemy>();

            for (int i = 1; i <= 50; i++)
            {
                int hp = 10 + (i % 5) * 2;  // HPにばらつき
                enemies.Add(new Enemy($"スライム{i}", hp));
            }


            enemies.Add(new Goblin("ゴブリンA"));
            enemies.Add(new Goblin("ゴブリンB"));

            foreach (var enemy in enemies)
            {
                enemy.TakeDamage(5);
            }

            foreach (var enemy in enemies)
            {
                Console.WriteLine($"{enemy.Name} の残りHP: {enemy.HP}");
            }

        }
    }

    class Enemy
    {
        public string Name;
        public int HP;

        public Enemy(string name, int hp)
        {
            Name = name;
            HP = hp;
        }

        public virtual void TakeDamage(int damage)
        {
            HP -= damage;
            if (HP < 0) HP = 0;
        }
    }

    class Goblin : Enemy
    {
        public Goblin(string name) : base(name, 20) { }

        public override void TakeDamage(int damage)
        {
            Console.WriteLine($"{Name} は防御した!");
            base.TakeDamage(damage - 1);
        }
    }
}

🔚 Step 5. まとめと振り返り

💡 手続き型との比較

観点手続き型オブジェクト指向
敵の数を増やす地獄のコピペ作業for 文で無限に対応
処理の追加すべてに個別で追加メソッドで一括処理
表示やダメージ処理個別に1体ずつforeach で一括
拡張性極めて低い継承・オーバーライドで高い

📝 実践課題

ぜひ、自分の力で以下にチャレンジしてください。

  1. 手続き型でスライム10体を作って攻撃&表示処理を実装
  2. Enemy クラスを使って書き直し(リファクタリング)
  3. List<Enemy> と for 文で50体生成
  4. Goblin クラスを作成して TakeDamage をオーバーライド
  5. すべての敵を Enemy 型で一括管理して攻撃処理を行う

以下に、「📝 実践課題」に対応した 5つのC#サンプルコード を順を追って提示します。

すべて実行可能な形で構成されており、手続き型 → OOP → 継承・多態性へとステップアップできる流れになっています。


✅ 1. 手続き型でスライム10体を作って攻撃&表示処理を実装

string name1 = "スライム1"; int hp1 = 10;
string name2 = "スライム2"; int hp2 = 12;
string name3 = "スライム3"; int hp3 = 14;
string name4 = "スライム4"; int hp4 = 16;
string name5 = "スライム5"; int hp5 = 18;
string name6 = "スライム6"; int hp6 = 20;
string name7 = "スライム7"; int hp7 = 22;
string name8 = "スライム8"; int hp8 = 24;
string name9 = "スライム9"; int hp9 = 26;
string name10 = "スライム10"; int hp10 = 28;

// 5ダメージを与える
hp1 -= 5; hp2 -= 5; hp3 -= 5; hp4 -= 5; hp5 -= 5;
hp6 -= 5; hp7 -= 5; hp8 -= 5; hp9 -= 5; hp10 -= 5;

// 結果表示
Console.WriteLine($"{name1} のHP: {hp1}");
Console.WriteLine($"{name2} のHP: {hp2}");
Console.WriteLine($"{name3} のHP: {hp3}");
Console.WriteLine($"{name4} のHP: {hp4}");
Console.WriteLine($"{name5} のHP: {hp5}");
Console.WriteLine($"{name6} のHP: {hp6}");
Console.WriteLine($"{name7} のHP: {hp7}");
Console.WriteLine($"{name8} のHP: {hp8}");
Console.WriteLine($"{name9} のHP: {hp9}");
Console.WriteLine($"{name10} のHP: {hp10}");

✅ 2. Enemy クラスを使って書き直し(リファクタリング)

class Enemy
{
    public string Name;
    public int HP;

    public Enemy(string name, int hp)
    {
        Name = name;
        HP = hp;
    }

    public void TakeDamage(int damage)
    {
        HP -= damage;
        if (HP < 0) HP = 0;
    }

    public void ShowStatus()
    {
        Console.WriteLine($"{Name} のHP: {HP}");
    }
}

// 使用例
Enemy slime1 = new Enemy("スライム1", 10);
Enemy slime2 = new Enemy("スライム2", 12);

slime1.TakeDamage(5);
slime2.TakeDamage(5);

slime1.ShowStatus();
slime2.ShowStatus();

✅ 3. List<Enemy> と for 文で50体生成

List<Enemy> enemies = new List<Enemy>();

for (int i = 1; i <= 50; i++)
{
    int hp = 10 + (i % 5) * 2;
    enemies.Add(new Enemy($"スライム{i}", hp));
}

// 全員に5ダメージ
foreach (var enemy in enemies)
{
    enemy.TakeDamage(5);
}

// 結果表示
foreach (var enemy in enemies)
{
    enemy.ShowStatus();
}

✅ 4. Goblin クラスを作成して TakeDamage をオーバーライド

class Goblin : Enemy
{
    public Goblin(string name, int hp) : base(name, hp) {}

    public override void TakeDamage(int damage)
    {
        Console.WriteLine($"{Name} は防御した!");
        base.TakeDamage(damage - 1);  // 1だけ軽減
    }
}

// 使用例
enemies.Add(new Goblin("ゴブリンA", 25));
enemies.Add(new Goblin("ゴブリンB", 30));

✅ 5. すべての敵を Enemy 型で一括管理して攻撃処理を行う

foreach (Enemy enemy in enemies)
{
    enemy.TakeDamage(5);  // Goblinなら防御付き処理になる
}

foreach (Enemy enemy in enemies)
{
    enemy.ShowStatus();
}

🧩 補足:全体構成のクラスファイル例

Project/
├── Program.cs         // Mainメソッド、リスト生成と処理
├── Enemy.cs           // 基本クラス
├── Goblin.cs          // 継承クラス(オーバーライドあり)

🎓 最後に

オブジェクト指向のありがたみは、「ない世界を体験してから」初めて心に響きます

スライム50体を手書きすることで、あなたはプログラマーとして最初の“本質”に触れました。

クラス、メソッド、継承――それは人間がコードを快適に扱うための知恵の結晶です。

これから先、あなたが書くコードが、もっとスッキリ・スマートになることを願っています。


訪問数 3 回, 今日の訪問数 3回