🎮【C#入門】「モブ」は継承で作れ!~敵も村人も動くオブジェクト~
目次
はじめに
RPGやアクションゲームを作っていると、敵キャラやNPC(村人など)を「動く存在(ムービングオブジェクト)」として扱うことがよくあります。
こうした「共通して動くキャラ」=モブ(mob)を、どのように効率よくコードで表現するか?
答えは、「継承」と「多態性(ポリモーフィズム)」を使うことです。
🧭「モブ(mob)」って何?
もともと「mob」は mobile object(移動するオブジェクト) の略語で、MMORPGなどで敵キャラやNPCの総称として使われてきました。
日本でも「雑魚モブ」「敵モブ」などと使われるようになり、プレイヤー以外の動くキャラ全般を指す言葉として定着しています。
🏗️ 設計の基本方針
今回は以下の3種類を設計します:
- MovingObject(基底クラス):共通の移動機能を持つ
- Enemy(派生クラス):敵キャラとしての振る舞い
- Villager(派生クラス):村人NPCとしての振る舞い
💻 サンプルコード

using System;
class MovingObject
{
public string Name { get; set; }
public MovingObject(string name)
{
Name = name;
}
public virtual void Move()
{
Console.WriteLine($"{Name} はゆっくり動いています。");
}
}
class Enemy : MovingObject
{
public Enemy(string name) : base(name) {}
public override void Move()
{
Console.WriteLine($"{Name} はプレイヤーに向かって走ってきます!");
}
public void Attack()
{
Console.WriteLine($"{Name} は攻撃してきた!");
}
}
class Villager : MovingObject
{
public Villager(string name) : base(name) {}
public override void Move()
{
Console.WriteLine($"{Name} は村の中をのんびり歩いています。");
}
public void Talk()
{
Console.WriteLine($"{Name}:こんにちは!");
}
}
class Program
{
static void Main()
{
MovingObject[] mobs =
{
new Enemy("ゴブリン"),
new Villager("村人A"),
new Enemy("オオカミ"),
new Villager("村人B")
};
foreach (var mob in mobs)
{
mob.Move(); // 多態性により適切なMove()が呼ばれる
}
// 特定の機能はキャストして使う
((Enemy)mobs[0]).Attack();
((Villager)mobs[1]).Talk();
}
}
🔍 ポイント解説
キーワード | 説明 |
---|---|
virtual / override | Move() メソッドを各クラスで上書きできるように設定 |
base(name) | 基底クラスのコンストラクタを呼び出し、名前をセット |
多態性(ポリモーフィズム) | MovingObject型の配列でも、Enemy や Villager の Move() が正しく呼ばれる |
🤔 なぜ継承を使うのか?
- 共通部分(例:名前、基本の移動)を一箇所で定義できる
- 新しいモブを追加する際に使い回しやすい
- 複数の種類を1つのコレクション(Listなど)で一括管理できる
✍️ 練習問題
次のようなクラスを追加してみましょう!
【練習A】BossEnemy クラス
- Enemy を継承し、特別な Move() と Attack() を実装せよ
- 例:「ゆっくりと重々しい足音で近づいてくる…」「超強力な攻撃を繰り出した!」
【練習B】モブに座標を追加せよ
- X, Y プロパティを持たせ、座標に応じた行動をとるようにしてみよう
- 例:Move() の中で X++ などの処理を入れる
練習問題【練習A】【練習B】に対する サンプル解答(C#コンソールアプリ版)を示します。
先ほどの「モブ = MovingObject」設計をベースにして拡張しています。
✍️ 練習A:BossEnemyクラスの実装

class BossEnemy : Enemy
{
public BossEnemy(string name) : base(name) {}
public override void Move()
{
Console.WriteLine($"{Name} はゆっくりと重々しい足音で近づいてくる…");
}
public new void Attack()
{
Console.WriteLine($"{Name} は超強力な攻撃を繰り出した!");
}
}
🔍 解説
- Enemy を継承し、Move() を override で上書き
- Attack() は Enemy のものと違う動作をするため、new キーワードで隠蔽して定義
✍️ 練習B:座標の追加
class MovingObject
{
public string Name { get; set; }
public int X { get; set; } // 横方向座標
public int Y { get; set; } // 縦方向座標
public MovingObject(string name)
{
Name = name;
X = 0;
Y = 0;
}
public virtual void Move()
{
X++;
Console.WriteLine($"{Name} は ({X}, {Y}) に移動しました。");
}
}
🔍 解説
- X, Y を追加して移動先を記録
- 各派生クラスで Move() を上書きしていても、必要に応じて X++ を共通処理として活かせます
🧪 動作テスト用メイン(拡張版)
class Program
{
static void Main()
{
MovingObject[] mobs =
{
new Enemy("ゴブリン"),
new Villager("村人A"),
new BossEnemy("ドラゴンキング")
};
foreach (var mob in mobs)
{
mob.Move(); // 多態性により適切なMove()が呼ばれる
}
// BossEnemy の攻撃(明示的なキャスト)
((BossEnemy)mobs[2]).Attack();
}
}
✅ 実行結果(例)
ゴブリン はプレイヤーに向かって走ってきます!
村人A は村の中をのんびり歩いています。
ドラゴンキング はゆっくりと重々しい足音で近づいてくる…
ドラゴンキング は超強力な攻撃を繰り出した!
🔁 さらに拡張するなら…
- Move() 内で X, Y をランダムに変化させて「歩き回る」表現を追加
- 画面外への移動制限(境界チェック)を入れる
- List<MovingObject> を使って複雑なゲームマップ上の動作を模擬
🧩 おわりに
「モブ」はゲームの中でプレイヤーと並ぶ重要な要素です。
彼らを効率よく定義・管理するためにも、継承と多態性を活かす設計をぜひ覚えておきましょう。
次はこの仕組みをUnity上のスクリプトとして活用してみると、さらに理解が深まりますよ。
🧠 関連リンク
訪問数 4 回, 今日の訪問数 1回
ディスカッション
コメント一覧
まだ、コメントがありません