UnityUnityメモ

Unityのコードをリファクタリング!初心者でもできる最適化&整理術

Unity

はじめに

Unityでゲーム開発をしていると、最初はシンプルだったコードがどんどん複雑になってしまうことはよくあります。機能を追加するたびにコードが長くなり、「どこに何が書いてあるのかわからない…」「少し修正したらバグだらけになった…」という状況に陥ることも珍しくありません。

そんなときに役立つのが リファクタリング です。リファクタリングとは、プログラムの動作を変えずに コードを整理し、読みやすく、修正しやすくする作業 のことです。これを行うことで、次のようなメリットがあります。

可読性の向上:他の開発者や未来の自分がコードを見ても理解しやすくなる
保守性の向上:バグの修正や機能追加が楽になる
パフォーマンスの最適化:無駄な処理を減らし、ゲームの動作を軽くする

特にUnity開発では、Update() の中で無駄に処理を繰り返していたり、スクリプトの責務が曖昧になっていたりすると、パフォーマンスが低下したり、デバッグが困難になったりします。そこで、この記事では 初心者でもすぐに実践できるリファクタリングの具体的なテクニック を紹介していきます。

「とりあえず動く」コードから「スマートに動く」コードへ!
ゲーム開発をスムーズに進めるために、リファクタリングの基本を学んでいきましょう!




1. リファクタリングとは?

リファクタリングとは、コードの動作を変えずに整理し、分かりやすくすること を指します。簡単に言えば、「とりあえず動くコード」を「誰が見ても理解しやすいコード」に変える作業です。

たとえば、以下のようなコードを書いたことはありませんか?

void Update() {
if (Input.GetKeyDown(KeyCode.Space)) {
GameObject.Find("Enemy").SetActive(false);
}
}

このコードは、スペースキーを押すと “Enemy” という名前のオブジェクトを非表示にする処理ですが、いくつかの問題があります。

  • GameObject.Find("Enemy") は毎回オブジェクトを検索するため、処理が遅くなる可能性がある
  • "Enemy" というハードコーディングされた文字列は、オブジェクトの名前が変わると動かなくなる
  • SetActive(false) の意味が分かりづらい(「敵を消すのか?倒したのか?」といった意図が不明)

このコードをリファクタリングすると、次のように改善できます。

public class EnemyManager : MonoBehaviour {
[SerializeField] private GameObject enemy;

void Update() {
if (Input.GetKeyDown(KeyCode.Space)) {
HideEnemy();
}
}

private void HideEnemy() {
enemy.SetActive(false);
}
}

こうすることで… ✅ GameObject.Find を使わずに SerializeField を利用し、検索のコストを減らす
HideEnemy() というメソッドを作ることで、コードの意図が明確になる
enemy の参照をInspectorで設定できるので、オブジェクト名が変わっても動作する


なぜリファクタリングが必要なのか?

リファクタリングを行うと、コードがスッキリして開発が楽になります。具体的なメリットを3つ紹介します。

① 可読性向上

コードが整理されていれば、自分だけでなく他の開発者もスムーズに理解できます。特に、数ヶ月後に自分のコードを見直したときに「何を書いたんだっけ…?」と悩むことがなくなります。

② 保守性向上

コードの構造が整理されていると、バグ修正や新機能の追加がしやすくなります。「ここを直したら別の部分が壊れた…」という状況を防ぐことができます。

③ パフォーマンス向上

不要な処理や重複したコードを減らすことで、ゲームの動作が軽くなります。特に Update() 内の無駄な処理を減らすことで、フレームレートが改善されることが多いです。


リファクタリングは、「コードを書いたら終わり」ではなく「書いた後に整理する」作業です。小さな改善を積み重ねることで、開発がどんどんスムーズになっていきます!




2. Unityにおけるリファクタリングの基本

Unityでの開発では、コードの整理がとても大切です。特に長期的なプロジェクトでは、「とりあえず動くコード」ではなく、「分かりやすく、修正しやすいコード」 を書くことが重要になります。

ここでは、Unity開発におけるリファクタリングの基本的なポイント を紹介します。


2.1 変数や関数の命名を分かりやすくする

「名前を見ただけで意味が分かるコード」 は、リファクタリングの基本です。
例えば、次のようなコードを見たときに、どんな役割の変数や関数なのか分かるでしょうか?

❌ NG例

int x;
void DoSomething() {
x += 10;
}

このコードだと、x が何を意味するのか分かりませんし、DoSomething() も何をする関数なのか分かりません。


OK例

int playerScore;
void CalculatePlayerScore() {
playerScore += 10;
}

このように変数や関数の名前を明確にすると、コードの意図がすぐに伝わります。

また、C#の命名規則を意識すると、より統一感のあるコードになります。

C#の命名規則

  • 変数やメソッドの命名にはPascalCaseまたはcamelCaseを使用
    • PascalCase:クラス名やメソッド名に使用(例: PlayerControllerCalculateScore()
    • camelCase:変数や引数名に使用(例: playerScoreenemyHealth

💡 ポイント:
コードの「読みやすさ」は、「書きやすさ」よりも重要です!
チーム開発では特に、命名ルールを統一しておくと、コードの管理が楽になります。


2.2 メソッドの長さを短くする

1つのメソッドに 複数の処理を詰め込みすぎる と、可読性が落ち、バグの原因になります。
例えば、次のような Update() メソッドを見てみましょう。

❌ NG例

void Update() {
if (Input.GetKeyDown(KeyCode.Space)) {
GameObject enemy = GameObject.Find("Enemy");
if (enemy != null) {
enemy.SetActive(false);
Debug.Log("Enemy disappeared!");
}
}
}

このメソッドは、「キー入力の判定」「オブジェクトの検索」「敵の非表示化」「デバッグログの出力」 という4つの異なる処理を1つのメソッドに詰め込んでいます。

こういう場合は、メソッドを分割(Extract Method) して整理しましょう。


OK例

void Update() {
if (Input.GetKeyDown(KeyCode.Space)) {
HideEnemy();
}
}

void HideEnemy() {
GameObject enemy = GameObject.Find("Enemy");
if (enemy != null) {
enemy.SetActive(false);
LogEnemyHidden();
}
}

void LogEnemyHidden() {
Debug.Log("Enemy disappeared!");
}

分割することで、メソッドの役割が明確になりました!
Update() は「スペースキーを押したときの処理」だけを担当し、実際の処理は HideEnemy()LogEnemyHidden() に任せる形になっています。




2.3 クラスの責務を明確にする

リファクタリングの中でも特に重要なのが、「1つのクラスに複数の役割を持たせないこと」 です。
これは 単一責務の原則(SRP: Single Responsibility Principle) に従うべきという考え方です。

例えば、プレイヤーを操作するスクリプト PlayerController に、スコアの管理まで含めてしまう と、以下のようになります。

❌ NG例(責務が多すぎるクラス)

public class PlayerController : MonoBehaviour {
private int score;

void Update() {
if (Input.GetKeyDown(KeyCode.Space)) {
Jump();
}
}

void Jump() {
Debug.Log("Player jumped!");
}

void AddScore(int points) {
score += points;
}
}

この PlayerController は、「プレイヤーの操作」と「スコア管理」 という2つの責務を持っています。
こうなると、後からスコアの管理方法を変更したくなった場合、PlayerController を修正する必要があり、コードの見通しが悪くなります。


OK例(クラスを分割する)

public class PlayerController : MonoBehaviour {
[SerializeField] private ScoreManager scoreManager;

void Update() {
if (Input.GetKeyDown(KeyCode.Space)) {
Jump();
}
}

void Jump() {
Debug.Log("Player jumped!");
scoreManager.AddScore(10);
}
}
public class ScoreManager : MonoBehaviour {
private int score;

public void AddScore(int points) {
score += points;
}
}

こうすることで、PlayerController は「プレイヤーの操作」、ScoreManager は「スコアの管理」と、クラスごとに責務が明確に分かれます。
もしスコア管理の方法を変えたいときも、ScoreManager だけを修正すればOKになります。


まとめ

  • 変数や関数の命名を分かりやすくする
    • 名前を見ただけで意味が分かるようにする
    • C#の命名規則(PascalCase, camelCase)を活用
  • メソッドの長さを短くする
    • 1つのメソッドに複数の処理を詰め込まない
    • Extract Method でメソッドを分割
  • クラスの責務を明確にする
    • 単一責務の原則(SRP) に従う
    • PlayerController がスコア管理まで担当しないようにする

こうしたリファクタリングを意識することで、コードが整理され、開発がスムーズになります!
次のステップでは、さらに実践的なリファクタリングテクニックを紹介していきます!




3. 実践!リファクタリングテクニック

ここからは、実際に UnityのC#スクリプトを改善するための具体的なリファクタリングテクニック を紹介します。これらを実践することで、コードの可読性が向上し、バグが減り、パフォーマンスも改善 できます!


3.1 マジックナンバーを使わない

コードの中に直接数値を埋め込むと、「この数字は何を意味しているのか?」 という問題が発生します。これを 「マジックナンバー」 と呼び、可読性を大きく損なう原因になります。

NG例(マジックナンバーを使っている)

if (score > 1000) {
Debug.Log("ハイスコア達成!");
}

このコードでは 1000 という数値が突然登場し、何を基準にしているのか分かりません。


OK例(定数を使う)

private const int HighScoreThreshold = 1000;

if (score > HighScoreThreshold) {
Debug.Log("ハイスコア達成!");
}

このように定数 (const) を使うことで、「1000」がハイスコアの基準であることが明確になり、コードの意図が伝わりやすくなります


3.2 GetComponentを減らす

Unityでは、GetComponent<T>() を使ってコンポーネントを取得することがよくありますが、これを毎フレーム呼ぶとパフォーマンスが悪化 します。
なぜなら、GetComponent<T>() はオブジェクト内のコンポーネントを検索する処理が必要になるため、負荷がかかるからです。

NG例(毎回GetComponentを呼び出す)

void Update() {
GetComponent<Rigidbody>().velocity = new Vector3(0, 5, 0);
}

毎フレーム GetComponent<Rigidbody>() を呼んでしまっているため、パフォーマンスが低下 します。


OK例(事前にキャッシュしておく)

private Rigidbody rb;

void Start() {
rb = GetComponent<Rigidbody>(); // 最初に取得してキャッシュする
}

void Update() {
rb.velocity = new Vector3(0, 5, 0);
}

こうすることで、Start() で一度だけ取得して rb に保存するため、毎回検索する負荷がなくなります!
特に Update() 内で頻繁に GetComponent<T>() を使っている場合は、必ずキャッシュするようにしましょう




3.3 Update関数の最適化

Update()毎フレーム(1秒間に60回以上)実行 されるため、ここに不要な処理があるとパフォーマンスが低下します。
例えば、毎フレームスコアを更新する処理 を書くと、無駄に負荷がかかる可能性があります。

NG例(毎フレーム不要な処理を実行)

void Update() {
if (Time.frameCount % 60 == 0) {
UpdateScore();
}
}

このコードは、フレームカウントを使って定期的にスコアを更新していますが、Update() 内で処理する必要はありません。


OK例(InvokeRepeating() や Coroutine を使う)

InvokeRepeating() を使う

void Start() {
InvokeRepeating(nameof(UpdateScore), 1f, 1f); // 1秒ごとにUpdateScoreを実行
}

Coroutine を使う

void Start() {
StartCoroutine(UpdateScoreRoutine());
}

IEnumerator UpdateScoreRoutine() {
while (true) {
UpdateScore();
yield return new WaitForSeconds(1f); // 1秒ごとに実行
}
}

このように、InvokeRepeating()Coroutine を使うと、毎フレームではなく、必要なタイミングで処理を実行 できるため、パフォーマンスが向上します。


3.4 コードの重複を減らす

コードの中に 同じような処理を何度も書いていると、修正が大変 になります。
例えば、プレイヤーの攻撃やエネミーの攻撃など、似た処理をコピペしてしまう ことはよくあります。

NG例(コードが重複している)

void PlayerAttack() {
Debug.Log("プレイヤーの攻撃!");
}

void EnemyAttack() {
Debug.Log("エネミーの攻撃!");
}

このコードでは、「攻撃」の処理がそれぞれのメソッドに分かれていますが、処理の内容はほぼ同じ です。


OK例(共通処理をメソッド化)

void Attack(string attacker) {
Debug.Log(attacker + "の攻撃!");
}

void PlayerAttack() {
Attack("プレイヤー");
}

void EnemyAttack() {
Attack("エネミー");
}

こうすると、Attack() という共通のメソッドを作ることで、コードの重複をなくし、修正が簡単になります!

さらに、オブジェクトごとに異なる攻撃処理を持たせたい場合は、基底クラスを作成 するのも有効です。


OK例(基底クラスを使う)

public class Character : MonoBehaviour {
protected virtual void Attack() {
Debug.Log(gameObject.name + "の攻撃!");
}
}

public class Player : Character {
protected override void Attack() {
Debug.Log("プレイヤーの攻撃!");
}
}

public class Enemy : Character {
protected override void Attack() {
Debug.Log("エネミーの攻撃!");
}
}

このように、共通の処理は Character クラスにまとめ、各キャラクターごとに個別の処理を追加 できます。
こうすることで、コードの重複を最小限にし、柔軟に管理 できるようになります!


まとめ

  • マジックナンバーを使わず、定数を利用する
    const を使ってコードの意味を明確にする
  • GetComponent を減らし、キャッシュする
    Start() で取得し、変数に保存する
  • Update() の負荷を減らす
    InvokeRepeating()Coroutine を活用する
  • コードの重複を減らす
    共通処理をメソッド化 or 基底クラスにまとめる

これらのリファクタリングテクニックを意識するだけで、コードが整理され、開発がスムーズ になります!
次のステップでは、さらにリファクタリングをサポートするツール について解説していきます!




4. Unity開発に役立つリファクタリングツール

Unityでの開発を効率化するために、コードの整理や最適化を支援してくれるツール を活用するのが効果的です。
ここでは、UnityのC#スクリプトをリファクタリングする際に便利なツールを3つ紹介します。


4.1 RiderやVisual Studioのリファクタリング機能

C#の開発には、Visual StudioJetBrains Rider などのIDE(統合開発環境)が使われます。
これらには、リファクタリングを簡単に行うための機能 が搭載されています。

Visual Studio のリファクタリング機能

Visual Studioには、ワンクリックでリファクタリングを行うツール が用意されています。
例えば、以下のような操作が簡単にできます。

  • メソッドの分割(Extract Method)
    • 長いメソッドを選択して Ctrl + R, M を押すと、新しいメソッドに分割
  • 変数名の一括変更(Rename)
    • F2 を押すだけで、スクリプト全体の変数名を一括変更
  • インターフェースの実装(Implement Interface)
    • Ctrl + . を押して、未実装のメソッドを自動生成

💡 ショートカットキーを覚えると、リファクタリングがよりスムーズに!


Rider のリファクタリング機能

JetBrains Rider は、C#開発に特化した強力なIDEで、Visual Studioよりも高度なリファクタリング機能 を備えています。

  • コードの最適化(Code Cleanup)
    • Alt + Shift + Enter でコードを自動的に整理
  • メソッドの自動抽出(Extract Method)
    • Ctrl + Alt + M でメソッドの分割
  • 冗長なコードの削除(Remove Unused Code)
    • 使われていない変数やメソッドを自動的に削除

💡 特に大規模なUnityプロジェクトでは、Riderを使うと作業がスムーズに!


4.2 Resharper(コード解析&リファクタリング支援)

Resharper は、JetBrainsが提供する Visual Studio向けの拡張機能 で、リファクタリングを強力にサポートしてくれます。

Resharperの主な機能

  • コードの自動修正(Quick Fix)
    • Alt + Enter を押すだけで、改善できるコードを提案
  • コードの解析(Code Analysis)
    • コードの非効率な部分をハイライト表示
  • 無駄なコードの削除(Optimize Imports)
    • 使われていない using 文を整理

💡 Visual Studioを使っているなら、Resharperを導入するだけでリファクタリングが大幅に効率化!




4.3 Odin Inspector(エディター拡張&デバッグ支援)

リファクタリングの一環として、Unityのインスペクターを整理し、スクリプトの管理を楽にするツール も活用できます。その中でも 「Odin Inspector」 は、特に便利なアセットです。

Odin Inspectorの主な機能

  • カスタムエディターを簡単に作成
    • スクリプトに [SerializeField] を追加しなくても、インスペクターで変数を表示できる
  • 属性(Attributes)を活用して、エディターの見た目や挙動をカスタマイズ
    • [Button] で簡単にボタンを追加
    • [ShowInInspector] で非公開変数を可視化
  • スクリプトのデバッグがしやすくなる
    • コードを書かずに、データ構造をインスペクターで直感的に操作可能

💡 Odin Inspectorを活用すれば、エディター作業の効率が大幅にアップし、スクリプトの整理がしやすくなります!
👉 Odin Inspector and Serializer(アセットストアリンク)


まとめ

リファクタリングを効率よく行うために、以下のツールを活用しましょう!

ツール名主な機能おすすめポイント
Visual Studioコード補完・メソッド抽出・変数名の一括変更Windowsユーザー向けの基本ツール
Rider高度なコード分析・自動整理・リファクタリング機能大規模なUnityプロジェクト向け
Resharperコードの解析・最適化・自動修正Visual Studioと組み合わせると最強
StyleCopコーディング規則のチェックコードの統一性を維持するのに最適

💡 これらのツールを活用することで、コードの品質が向上し、開発スピードもアップします!
リファクタリングを意識しながら、スムーズなUnity開発を進めていきましょう!




5. まとめ

Unity開発をスムーズに進めるためには、「とりあえず動くコード」から「読みやすく、修正しやすいコード」に変えていくことが大切です。
そのために、リファクタリングの基本を学び、実践すること が重要になります。


✅ リファクタリングのポイントをおさらい

変数や関数の命名をわかりやすくする
長すぎるメソッドを分割する
クラスの責務を明確にする(単一責務の原則)
マジックナンバーをなくし、定数を活用する
GetComponentを減らしてパフォーマンスを最適化
Update() 内の処理を減らし、Coroutine や InvokeRepeating を活用
コードの重複をなくし、共通処理をメソッド化・基底クラスにまとめる
リファクタリングツール(Rider、Resharper、StyleCop)を活用する


✅ 小さな改善を積み重ねることが重要

リファクタリングは、一度やれば終わりというものではありません。
日々の開発の中で 「このコード、もっとわかりやすくできるかな?」 という視点を持ち、小さな改善を積み重ねていくことが大切です。

特に、長期的なプロジェクト ではリファクタリングを怠ると、後から修正が大変になり、開発効率が大きく下がってしまいます。
定期的にコードを見直し、整理する習慣をつけましょう!


💡 わかりやすいコードは最強の武器!

「プログラムは動けばOK」ではなく、「誰が見ても理解しやすいコード」を書くことが大切です。
わかりやすいコードは、チーム開発でも、個人開発でも、最強の武器になります!

ぜひ、この記事で紹介したリファクタリングテクニックを活用し、スッキリしたコードで快適なUnity開発 を目指しましょう!



よくある質問(FAQ)

Q
リファクタリングはいつやるべき?
A

大きな機能を追加する前や、バグ修正のタイミングで行うと良いです。毎回少しずつ改善するのがベストです。

Q
既存のコードを壊してしまうのが怖い…
A

ユニットテストや**バージョン管理(Git)**を活用すると、安全にリファクタリングできます。

Q
リファクタリングするとゲームの処理速度が遅くなる?
A

逆に最適化が進み、パフォーマンスが向上することが多いです。ただし、不要な変更は避けるようにしましょう。

タイトルとURLをコピーしました