LoginSignup
1
3

More than 1 year has passed since last update.

【Unity】公式プロジェクトから、デザインパターンを学んでみよう(Part 1:単一責任の原則)

Last updated at Posted at 2022-10-28

●はじめに

前: 【Unity】公式プロジェクトから、デザインパターンを学んでみよう(Part 0)

今回は、SOLID原則のSの部分である、単一責任の原則(SingleResponsibility)について、Unityの公式プロジェクトを参照しながら学んでいきます!(プロジェクトのリポジトリへのリンクは、前回の記事に記載しています。)

●単一責任の原則とは?

  • めちゃくちゃ簡単に言うと、1つのクラスに詰め込み過ぎるなっていう原則です。(間違ってたら指摘ください)
  • 詰め込みすぎずに細分化すれば、多人数での開発などで担当を分担したり、バグの数を減らすことができます。

●プロジェクトの内容

では、実際にUnity公式のプロジェクトから単一責任の原則のデザインパターンの例を見てみましょう。

  • Projectビューの、[1 SingleResponsibility]のScripts内に、今回のデザインパターンにならったスクリプトがあります。
    image.png

  • 内容はこんな感じです。

    1. Player
    2. PlayerAudio
    3. PlayerInput
    4. PlayerMovement
    5. UnrefactoredPlayer

○Player

  • 以下3つのスクリプトの管理とかをするクラスっぽい。
  • デフォルトだと、特に何もしないクラス。

○PlayerAudio

  • プレイヤーに関する音声を再生するクラス。

○PlayerInput

  • プレイヤーに関する入力判定をするクラス。

○PlayerMovement

  • プレイヤーの移動に関するクラス。

○UnrefactoredPlayer

  • 訳してみると、「整理されていないプレイヤー」 みたいな感じです。
  • このスクリプトは、やってはいけないクラスの見本です。

●コードを見てみる

○UnrefactoredPlayer

まずは、ダメな例である「UnrefactoredPlayer」を見てみます。

クリックして展開
UnrefactoredPlayer.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.SRP
{
    // Even though this class is short, it violates single-responsibility.
    // Too many things will cause the class to update, and extending the class will be more difficult.

    public class UnrefactoredPlayer : MonoBehaviour
    {

        [SerializeField] private string _inputAxisName;

        [SerializeField] private float _positionMultiplier;

        private float _yPosition;

        private AudioSource _bounceSfx;

        private void Start()
        {
            _bounceSfx = GetComponent<AudioSource>();
        }

        private void Update()
        {
            float delta = Input.GetAxis(_inputAxisName) * Time.deltaTime;

            _yPosition = Mathf.Clamp(_yPosition + delta, -1, 1);

            transform.position = new Vector3(transform.position.x, _yPosition * _positionMultiplier, transform.position.z);
        }

        private void OnTriggerEnter(Collider other)
        {
            _bounceSfx.Play();
        }
    }

}

上部のコメントの翻訳を見てみましょう

このクラスは短いですが、単一責任に違反しています。
あまりにも多くのことがクラスの更新を引き起こし、クラスの拡張がより困難になります

入力、移動、当たり判定などを1つのクラスにまとめちゃってる感じですね。
Unity始めたての頃はこんな感じでもいいと思いますが、本格的なゲームを作ろうとしたときにこんな感じだと、コメントの通り拡張性が失われちゃいます…。

入力は入力クラス、移動は移動クラス…などと細分化することが、単一責任の原則に則ったコードになります。

○Player

クリックして展開
Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.SRP
{
    [RequireComponent(typeof(PlayerAudio), typeof(PlayerInput), typeof(PlayerMovement))]
    public class Player : MonoBehaviour
    {
        [SerializeField] private PlayerAudio playerAudio;
        [SerializeField] private PlayerInput playerInput;
        [SerializeField] private PlayerMovement playerMovement;

        private void Start()
        {
            playerAudio = GetComponent<PlayerAudio>();
            playerInput = GetComponent<PlayerInput>();
            playerMovement = GetComponent<PlayerMovement>();
        }
    }


}

  • [RequireComponent]で、このスクリプトをAddComponentしたときに、自動的に指定されたスクリプトもコンポーネントに追加するらしい。付け忘れ防止とかになるので、そこそこ便利かも。
  • デフォルトの状態だと、ただコンポーネント取得するだけで、特に何もしてないです。

〇それ以外のクラス

  • 中身は特に詳しく説明はしないです。細分化することが大切ってことが分かればいいので…。

●さいごに

よくあるのが、Managerっていう名前のクラスを作って、そこになんでも詰め込んじゃうっていうやつですが、単一責任の原則とは反しています(僕も現在進行形でそれしてます…。)
これも危険っぽいので、見直して設計してみます。

前の記事でも紹介しましたが、公式のちゃんとしたPDFの説明書があるので(※英語)、よろしければどうぞ↓

次:【Unity】公式プロジェクトから、デザインパターンを学んでみよう(Part 2:開放・閉鎖の原則)

1
3
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
1
3