0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unity LearnのCreate with Codeで使ったコード

Last updated at Posted at 2024-05-22

背景

Unity LearnのCreate with Codeで使ったコードを自分用にまとめています。
Unityでゲームを作り始めたけど、プログラミングが難しいと感じている方にとって、有用な記事になればと思います。
より正確な情報については公式マニュアルを参照ください。

コード

MonoBehaviour(基本クラス)

StartやUpdateなど、重要なイベントを使う際に継承する。

FixedUpdate()

固定フレームレートごとに呼び出される。
また、物理挙動の更新の直前に呼び出される。
(例)Rigidbodyに力を加える。

Update()

Physics (Collisionなど、Unityに備わっている物理関係の処理) の後、
マウスなどからの入力を受けた直後に呼び出される。
(私の理解: 固定フレームレートではないため、負荷が大きいフレームでは、最悪、呼び出されない)

LateUpdate()

Game logicの最後(物理計算、アニメーションなどが終了し、レンダリングの本当に直前)に呼び出される。
(例)プレイヤーに追従するカメラの位置の更新

※参考: イベント関数の実行順序

Collider.OnCollisionEnter(Collision)

衝突する2つのオブジェクトの両方とも、
collider/rigidbodyをアタッチしておく必要がある。
かつ、どちらか一方のrigidbodyのkinematicを無効にしておく必要がある。
(私の理解: Rigidbodyをアタッチされている物体同士の衝突の検出に使う)

敵を弾き飛ばすサンプルコード
private void OnCollisionEnter(Collision collision)
{
    if(collition.gameObject.CompareTag("Enemy")){
        Rigidbody enemyRigidbody = collision.gameObject.GetComponent<Rigidbody>();
        Vector3 awayFromPlayer = (collision.gameObject.transform.position - transfrom.position);
        enemyRigidbody.AddForce(awayFromPlayer, ForceMode.Impulse);
}

OnCollisionExit(Collision)

接している状態から離れるときに呼びたければ、こちらを使う

上下の壁で跳ね返るボールのサンプルコード
private void OnCollisionExit(Collision other)
{
    var velocity = m_Rigidbody.velocity;
        
    //after a collision we accelerate a bit
    velocity += velocity.normalized * 0.01f;
        
    //check if we are not going totally vertically as this would lead to being stuck, we add a little vertical force
    if (Vector3.Dot(velocity.normalized, Vector3.up) < 0.1f)
    {
        velocity += velocity.y > 0 ? Vector3.up * 0.5f : Vector3.down * 0.5f;
    }

    //max velocity
    if (velocity.magnitude > 3.0f)
    {
        velocity = velocity.normalized * 3.0f;
    }

    m_Rigidbody.velocity = velocity;
        

Collider.OnTriggerEnter(Collider)

衝突する2つのオブジェクトの両方とも、
Colliderコンポーネントをアタッチしておく必要がある。
かつ、どちらか一方はCollider.isTriggerを有効にして、
さらに、rigidbodyをアタッチしておく必要がある。
(私の理解: インターフォンを押す人(Trigger)とインターフォンのように役割を分ける)

アイテムを取得するサンプルコード
private void OnTriggerEnter(Collider other)
{
    if(other.CompareTag("Item")){
        hasItem = true;
        Destroy(other.gameObject);
    {
}

OnMouseDown()

コライダーを持つオブジェクトの上でマウスのボタンが押されたときに呼ばれる

InvokeRepeating(〇〇〇, float time, float repeatRate)

設定した秒数timeでメソッドを呼び出し、repeatRate秒ごとにリピートする。
〇〇〇はメソッドの名前。

InvokeRepeatingで一定秒数ごとにスポーンさせる
void Start()
{
    playerControllerScript = GameObject.Find("Player").GetComponent<PlayerController>();
    InvokeRepeating("SpawnObstacle", startDelay, repeatRate);
}

void SpawnObstacle()
{
    if (playerControllerScript.gameOver == false)
    {
        Instantiate(obstaclePrefab, spawnPos, obstaclePrefab.transform.rotation);
    }
}

StartCoroutine(IEnumerator routine)

IEnumeratorに記述した処理を1フレームずつ進める(同期処理)。
反復子yeildを使って、処理の継続(returnの後に反復処理の次の値)や終了(break)を指示する。

(例)処理の継続時間をWaitForSecondで秒数指定する
パワーアップアイテムに時間制限を設ける
private void OnTriggerEnter(Collider other)
{
    if(other.CompareTag("Powerup")){
        hasPowerup = true;
        Destroy(other.gameObject);
        StartCoroutine(PowerupCountdownRoutine()}
}

IEnumerator PowerupCountdownRoutine()
{
    yield return new WaitForSecond(7);
    hasPowerup = false;
}

Transform (位置、回転などに関するクラス)

transform.Translate(〇〇〇, △△)

〇〇〇にはVector3が入る。
△△には座標系の指定(Space.SelfまたはSpace.World)が入り、省略するとローカル座標になる。

Ridgidbody

Rigidbody型の変数はStartで代入する。
※参照型
(例)playerRbという名前のRigidbody型の変数を宣言しておいて、

Rigidbodyの代入
private void Start(){
playerRb = GetComponent<Rigidbody>();}

Rigidbody.AddRelativeForce("〇〇〇")

〇〇〇にはfloat型の値が入る。
ローカル座標に対して力を加える。
※AddForceはグローバル座標に対応

Collision(衝突したオブジェクトの情報を扱うクラス)

衝突した際に接した点、速度などが含まれる。
これらの情報はCollider.OnCollisionEnterなどのイベントに渡される。

Physics

Physics.SphereCast

球状のレイが任意のコライダーと交わる場合にtrue、それ以外はfalseを返す。

(例)groundLayersのレイヤーを使って接地判定する
スフィアレイキャストで接地判定する
//Ground check by Raycast 
    [SerializeField] float groundCheckRadius = 0.4f;
    [SerializeField] float groundCheckOffsetY = 0.45f;
    [SerializeField] float groundCheckDistance = 0.2f;
    [SerializeField] LayerMask groundLayers = 0;
    RaycastHit hit;
bool CheckGroundStatus()
    {
        return Physics.SphereCast(transform.position + groundCheckOffsetY * Vector3.up, groundCheckRadius, Vector3.down, out hit, groundCheckDistance, groundLayers, QueryTriggerInteraction.Ignore);
    }

Physics.Raycast

左クリックした対象のコンポーネントを取得する

レイキャストが当たったオブジェクトがUnitクラスを持ち、かつ、`interface'で定義された情報をもつなら、情報を表示する

if (Input.GetMouseButtonDown(0))
{
    var ray = GameCamera.ScreenPointToRay(Input.mousePosition);
    RaycastHit hit;
    if (Physics.Raycast(ray, out hit))
    {
        //the collider could be children of the unit, so we make sure to check in the parent
        var unit = hit.collider.GetComponentInParent<Unit>();
        m_Selected = unit;

        //check if the hit object have a IUIInfoContent to display in the UI
        //if there is none, this will be null, so this will hid the panel if it was displayed
        var uiInfo = hit.collider.GetComponentInParent<UIMainScene.IUIInfoContent>();
        UIMainScene.Instance.SetNewInfoContent(uiInfo);
    }

    m_Selected.MovingBehaviour();
}

Input

Input.GetAxis("〇〇〇")

〇〇〇にはHorizontalやVerticalが入る。
Horizontalが何なのかなどをInput Managerで設定する。
(例)float型の変数に代入しておいて、Rotateの変更などで使う。

Input.GetKeyDown(KeyCode.〇〇〇)

〇〇〇にはSpaceなど、キーの名前が入る。
※戻り値はbool型

Input.GetMouseButtonDown(0)

左クリックをするとtrueが返ってくる。

Animator

※参照型
(例)Animatorで作っておいた変数の値をUpdateで変更する。
ジャンプした時に、空中にいる時のアニメーションが開始するようにする。
playerAnim.SetBool("Jump_b",true);

AudioSource

PlayOneShot()などでスクリプトからAudioClipを再生できる。
コンポーネントをアタッチすると、
あらかじめPlay On Awakeが有効になっていることに注意する。
シーンを跨いで再生させ続けるなら、DontDestroyOnLoad(this)などで対応する。

Camera

Camera.ScreenPointToRay(Input.mousePosition)

スクリーン上に入力した点から、
カメラを通してレイを飛ばす。

ParticleSystem

Play()Stop()でスクリプトから再生・停止できる。

VisualEffect

冒頭にusing UnityEngin.VFX;と書いておく。
SendEvent("OnPlay")SendEvent("StopPlay")でスクリプトから再生・停止できる。

UnityEngine.UI

UI関連のデータ型を使うときは、
スクリプトの冒頭にusing UnityEngine.UIを書いておくといい。

InputField

ユーザーがキーボード入力可能なフィールドを扱う
(例)InputField.textで、ユーザーが入力した文字列を扱う

UnityEngine.SceneManagement

冒頭にusing UnityEngine.SceneManagementを付ける。
シーンのロード、アンロードなどでシーンを管理する。

SceneManager.LoadScene()

ビルド設定のインデックスか名前を使ってシーンを読み込む。

SceneManager.GetActiveScene()

現在のアクティブなシーンを読み込む。

buildIndexを取得する例
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);

UnityEditor

開発画面で使用する。

Exitボタンの挙動を開発専用にする例
#if UNITY_EDITOR
using UnityEditor;
#endif

public void Exit()
{

#if UNITY_EDITOR
    EditorApplication.ExitPlaymode();
#else
    Application.Quit();  //original code to quit Unity Player
#endif
}

概念

usingディレクティブ

スクリプトの冒頭でusing 名前空間;と書くことで、「名前空間.クラス名」の「名前空間.」を省略できる。
※名前空間(namespace)とはクラスが属するグループ名のこと。
(例)using TMProと書いておくことで、
TextMeshProUGUI型の変数が使えるようになる。

参照型と値型

参照型 値型
メモリの情報 値がある場所 値そのもの
データ型の例 class、配列、コレクション(List型など)、string struct(Vector3など)、int、float、bool
実体 new演算子でインスタンスを作成する* インスタンスがなくても変数を代入できる (実体がある)
内容 中身が変わる 中身は固定
容量 軽い 重い

*stirngは特別で、インスタンスなしで変数を代入できる。

メソッドで引数を使うときの注意点

メソッドに変数を渡すとき、引数はコピーして渡される。
引数に変更を加えて、戻り値を得たい場合は以下に注意する。

参照型を引数として渡せば、呼び出し元の変数にも影響を与える。
一方で、値型を引数として渡しても、コピーが変更されるだけで、呼び出し元の変数に影響がない。

解決方法としては、メソッドを定義するときに、
引数の前にref修飾子またはout修飾子を付けることで、値型を参照渡しできる。
refは引数に値が入った変数を渡す必要があり、outは引数の変数に値が入っていなくていい。
(例)ref修飾子を付けているので、関数の引数は参照渡しになり、2が表示される。

int x = 1;
void Update()
{
    func(ref x);
    Debug.Log(x);
}

void func(ref int x)
{
    x = 2;
}

オブジェクトプール

オブジェクトをいくつか生成しておき、表示の切り替えで、あたかもオブジェクトを生成、削除しているように見せる。
シューティングゲームの球など、最適化の手段の1つ。

オブジェクトプーラーのサンプルコード

(例)オブジェクトプーラーを用意しておき、他のスクリプトから呼び出す。

オブジェクトプーラーを呼び出す
GameObject pooledProjectile = ObjectPooler.SharedInstance.GetPooledObject();
            if (pooledProjectile != null)
            {
                pooledProjectile.SetActive(true); // activate it
                pooledProjectile.transform.position = transform.position; // position it at player
            }
オブジェクトプーラーのスクリプト
public class ObjectPooler : MonoBehaviour
{
    public static ObjectPooler SharedInstance;
    public List<GameObject> pooledObjects;
    public GameObject objectToPool;
    public int amountToPool;

    void Awake()
    {
        SharedInstance = this;
    }

    // Start is called before the first frame update
    void Start()
    {
        // Loop through list of pooled objects,deactivating them and adding them to the list 
        pooledObjects = new List<GameObject>();
        for (int i = 0; i < amountToPool; i++)
        {
            GameObject obj = (GameObject)Instantiate(objectToPool);
            obj.SetActive(false);
            pooledObjects.Add(obj);
            obj.transform.SetParent(this.transform); // set as children of Spawn Manager
        }
    }

    public GameObject GetPooledObject()
    {
        // For as many objects as are in the pooledObjects list
        for (int i = 0; i < pooledObjects.Count; i++)
        {
            // if the pooled objects is NOT active, return that object 
            if (!pooledObjects[i].activeInHierarchy)
            {
                return pooledObjects[i];
            }
        }
        // otherwise, return null   
        return null;
    }

}

カプセル化

クラス内のメンバーを隠して、利用してほしい機能だけを使えるようにする。

  • プロパティを使う
プロパティの書き方

プロパティの名前の後ろに、{get; private set;]を付ける。
これで、クラスの外から呼び出すことができ、クラス内でのみ値を設定できる。
これは自動実装プロパティと呼ばれる。

または、

public プロパティの型名 プロパティの名前(頭文字は大文字にするといい)
{
    set{〇〇〇 = value;}
    get{return 〇〇〇;}
}
  • privateとアクセサ(publicのメソッドでメンバーにアクセスする)の併用
  • public static: 参照することができる。インスタンス化できない
    (私の理解:実体返し(コピーを渡すこと)をするから、元の変数は変えられない)
  • protected:基本クラス、派生クラスからのみアクセス可能
  • [SerializeField]: インスペクターに表示させる。

継承

基本(親)クラスの持つメンバー変数や関数を引き継ぐ形で、新たに派生(子)クラスを作成する。
例えば、MonoBehaviourを継承することで、Start()等の関数を使える。

ポリモーフィズム(多態性)

子クラスで、メソッドをオーバーライドすることで、親クラスとは異なるメソッドを使うことが出来る。
例えば、親クラスでpublic virtual void 関数()としておき、子クラスでpublic override void 関数()として中身を変える。
※下記のabstructと使い分ける。

抽象化

メソッドやクラスを使いやすい形で作成しておく。
例えば、AddForceメソッドを呼ぶだけで、(中身を知らなくても、)オブジェクトに力を加えることが出来る。
子クラスに継承する前提の、インスタンス化できないクラスを作成することも出来る。
抽象クラスと呼ばれ、abstract classとする。

オーバーロード

メソッドを定義するとき、引数の個数や引数の型が異なっていれば、同じ名前のメソッドを複数定義することが出来る。
例えば、transform.Translateには4種類の引数の組み合わせが用意されている。

その他、つまづいたところ

  • 配列は参照型で、new演算子でインスタンスを作成する。int[] 〇〇 = new[]{△, △,...} []の中は要素数

  • クラスの中にあるクラス名と同じ名前のメソッドは、コンストラクタという。
    クラスの初期化処理(値の代入など)を担い、インスタンス化するときに必然的に呼ばれる。
    〇〇〇 ○○ = new 〇〇〇(); の 〇〇〇() がコンストラクタになっているから。

  • interfaceはメソッドやプロパティのコレクションである。
    (別にUIに関わるとも限らない。)
    interface内のメソッドやプロパティは自動的にパブリックになる。
    定義は宣言されない。
    なぜなら、継承されてから定義されるから。
    使いどころとしては、
    ①同じ機能は同じinterfaceを使って実装しておくと、
    後からこのグループをまとめて探すときに便利。
    interfaceを継承していると、[SerializeReference]を使用して、
    シリアル化できる(?)。

  • $文字はコードとして意味のない文字列を補間する。
    例えば、return $"Producing at the speed of {m_ProductionSpeed}/s"; は{}内の変数だけを返す。

  • Vector3.Dot(Vector3, Vector3)は内積

  • as演算子はパターンマッチングに使う。
    例えば、A = B as Typeでは、BがType型ならAをBにする。

  • ?は3項演算子という。
    (式1? 式2:式3)の式1がtrueなら式2、falseなら式3を使う。Ifを使わない条件分岐。

  • switchを使うと、If~elseを使わずに条件分岐できる。

swich文の例
PointValueごとに色分けする
        switch (PointValue)
        {
            case 1 :
                block.SetColor("_BaseColor", Color.green);
                break;
            case 2:
                block.SetColor("_BaseColor", Color.yellow);
                break;
            case 5:
                block.SetColor("_BaseColor", Color.blue);
                break;
            default:
                block.SetColor("_BaseColor", Color.red);
                break;
        }

 

  • あるスクリプトで別のスクリプトの関数や値を使いたい

1. 他のスクリプトをGetComponentで使う

スクリプトを取得する例
PlayerControllerスクリプトを読み込む
private PlayerController playerControllerScript;

void Start()
{
    playerControllerScript = GameObject.Find("Player").GetComponent<PlayerController>();
}

2. シーンのコールバックを介してイベントの発生を伝える。UnityEventでシーンにイベントを保存する。

int型の引数を持つUnityEventを介してやりとりする例
衝突すると、int型の引数を持つonDestroyedというコールバックが登録される
public class Brick : MonoBehaviour
{
    public UnityEvent<int> onDestroyed;
    public int PointValue;

    private void OnCollisionEnter(Collision other)
    {
        onDestroyed.Invoke(PointValue);
        
        //slight delay to be sure the ball have time to bounce
        Destroy(gameObject, 0.2f);
    }
}

シーンにonDestryoedが登録されたことを他のスクリプトで確認できるようにリスナーを設定しておく
void Start(){
brick.onDestroyed.AddListener(AddPoint);
}

void AddPoint(int point)
{
       m_Points += point;
       ScoreText.text = $"Score : {m_Points}";
}

 

  • シーンをまたいで変数を使いたい

1.静的(static)クラスメンバーを使う
static classのメンバー変数はstaticである必要がある。
インスタンス変数がないため、静的クラスのメンバーにアクセスするには、クラス名自体を使用する。
例えば、Time.deltaTimeやVector3.forwardは静的クラスメンバーである。
(私の理解: クラスだけれど値型のように扱える。
インスタンス化する手間がないので、他のクラスから使う機会があるときに便利)

staticを使うサンプルコード
静的クラスと静的クラスメンバーが定義される
public static class GlobalVariables
{
    public static string playerName = "nanashi";
    public static string bestPlayerName;
    public static int bestScore = 0;

}
クラス名を使ってメンバー変数を呼び出す
   private void Update()
    {
        GlobalVariables.playerName = inputField.text;
    }

 
2.DontDestroyOnLoadを使う
シーンをロード、アンロードするときにも、ゲームオブジェクトをメモリに保存しておくことが出来る。

シーンをまたぐマネージャーのサンプルコード
public static MainManager Instance;
private void Awake()
{
    Instance = this;
    DontDestroyOnLoad(gameObject);
}

3.ゲームを中断・再開しても保持したければ、PlayerPrefsを使う。
GetStringGetIntで取得し、SetStringSetIntで値を入れた後にSaveで保存できる。
(私の理解: 例えば、PREFSファイルという拡張子はプリファレンス値に関する情報を格納する。同様に、PlayerPrefsはシーン開始時に優先してアクセスするべき情報を格納しておくことができる)
 
4.JSONを使う方法もある。

対象Version

Unity 2020 LTSおよびUnity 2022 LTS

参考文献

結び

少しでも役に立つ記事になっていれば幸いです。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?