【Unity】【初心者向け】実戦風ゲーム開発練習 その2

対象

次のような人向けに課題を作成しています。
 ・Unityの入門書を2~3冊読了済みの人
 ・Unityの基礎力に自身がない人
 ・Unityの勉強をしたいけど何をすればよいか分からない人

課題

次に示す内容をUnityを用いて開発する。

 【作るもの】
  スクリーンショットを撮影する機能

 【仕様】 ※ここに記載されていない部分はどのように実装しても構いません。
  ・ゲーム画面はUI(Canvas)と3Dシーンで構成する。
  ・スクリーンショットボタンはUIに配置する。
  ・スクリーンショットボタンを押すとUIを除くゲーム画面をスクリーンショット画像として取得する。
  ・3Dシーンには撮影したスクリーンショット画像を表示する3Dオブジェクトを配置する。

 【イメージ図】

実装例

仕様に基づいた実装の一例をGitHubに公開しています。
実装方法が分からなかったり、他の人の実装が気になる方は確認してみてください。

github.com   ・Assets/Practice2

【Unity】【初心者向け】実戦風ゲーム開発練習 その1

対象

次のような人向けに課題を作成しています。
 ・Unityの入門書を2~3冊読了済みの人
 ・Unityの基礎力に自身がない人
 ・Unityの勉強をしたいけど何をすればよいか分からない人

課題

次に示す内容をUnityを用いて開発する。

 【作るもの】
  同じ色の点を線でつなぎ、全ての点をつなぐゲーム

 【仕様】 ※ここに記載されていない部分はどのように実装しても構いません。
  ・色は赤、青、黄、緑の計4種類
  ・色の点は各色2つずつ
  ・線はマウスのドラッグアンドドロップでつなぐ
  ・ドラッグ中は始点となる色の点に応じた線を [色の点]-[マウス]間で表示
  ・ドラッグ中にもう一方の同じ色の点上でドロップすると線がつながる
  ・キーボードの"Esc"キーを押すと、すべての線を消し初期状態に戻す

 【イメージ図】

実装例

仕様に基づいた実装の一例をGitHubに公開しています。
実装方法が分からなかったり、他の人の実装が気になる方は確認してみてください。

github.com   ・Assets/Practice1

【Unity】ピンチ操作でUIをズームイン/ズームアウトする

初めに

ピンチ操作でUIをズームするコードを紹介している記事が少なく、表現したい動きがなかなか作れなかったので色々試して一番しっくりくる動きをしたコードを紹介します。

参考サイト

次のサイトのUIの拡縮処理を参考(ベース)にさせていただきました。

kohki.hatenablog.jp

スクリプト

参考サイトのスクリプトから、意図しない移動や拡縮を極力削除したスクリプトになります。

using UnityEngine;
using UnityEngine.UI;

public class PinchScalingManager : MonoBehaviour {

    [SerializeField]
    private ScrollRect scrollRect;    //拡大縮小するコンテンツ
    [SerializeField]
    private RectTransform contentRect;  //拡大縮小するコンテンツ

    //コンテンツのRectTransformの参照
    [SerializeField]
    private float scale;    //現在の拡大率

    [System.Serializable]
    struct RangeClass
    {
        public float min, max;
    }

    [SerializeField]
    private RangeClass RangeScale;          //拡大縮小の範囲

    [SerializeField]
    private float TweenSecond;      //収束するまでにかかる時間

    private bool isPinch = false;       //ピンチ中であればtrue
    private Vector3 center;             //現在の中心座標

#if UNITY_EDITOR
#elif UNITY_IOS || UNITY_ANDROID
    private float max_distance = 0;     //ピンチ開始時の指間の距離
#endif


    void Start () {
        center = contentRect.localPosition / scale;

        //状態の初期化
        UpdateScaling ();

        //表示されている画面の中心を拡大率に合わせて調整する
        contentRect.anchoredPosition *= scale;
    }


    void Update () {

#if UNITY_EDITOR
        bool isZoom = Input.GetAxisRaw("Vertical") != 0;
#elif UNITY_IOS || UNITY_ANDROID
        bool isZoom = Input.touchCount == 2;
#endif

        //タッチ中の処理
        if (isPinch)
        {
            //タッチ終了を感知し、終了処理をする
            if (!isZoom)
            {
                isPinch = false;
                return;
            }

#if UNITY_EDITOR
            float newScale = scale + Input.GetAxisRaw("Vertical") * Time.deltaTime;
#elif UNITY_IOS || UNITY_ANDROID
            float distance = Vector2.Distance (Input.touches [0].position, Input.touches [1].position);
            float newScale = distance / max_distance;
#endif

            SetNewScale(newScale);
            UpdateScaling();
            return;
        }
        else
        {
            scrollRect.enabled = true;
        }

        //タッチ開始時を感知し、初期化処理をする
        if (isZoom)
        {
            center = contentRect.localPosition / scale;
            isPinch = true;
            scrollRect.enabled = false;

#if UNITY_EDITOR
#elif UNITY_IOS || UNITY_ANDROID
            float distance = Vector2.Distance(Input.touches[0].position, Input.touches[1].position);
            max_distance = distance / scale;
#endif
        }
    }

    /// <summary>
    /// 新しい拡大率のバリデートと更新をする
    /// </summary>
    private void SetNewScale(float new_scale){
        // min < 新しい拡大率 < max に設定する
        new_scale = Mathf.Min (new_scale, RangeScale.max);
        new_scale = Mathf.Max (new_scale, RangeScale.min);

        scale = new_scale;
    }


    /// <summary>
    /// 設定された拡大率に基づいてオブジェクトの大きさを更新する
    /// </summary>
    private void UpdateScaling(){
        contentRect.localPosition = center * scale;
        contentRect.localScale = new Vector3(scale, scale, 1);
    }
}

Unity

参考サイトの構成から極力使用しないオブジェクトを削除しました。 ScrollViewのContent以下が拡縮対象になります。

【Unity】FindやGetChildを使用せずに子オブジェクトを取得する方法

初めに

Unityでは子オブジェクトを取得する方法がいくつかあります。

最も楽に実装できる方法にFindやGetChildなどがありますが、これらはHierarchy上のGameObjectの名前や並び順に依存しており、時間が経った後や別の人が編集するといきなりエラーが発生して処理を見直すといったことがあります。

そのためFindやGetChildの使用は保守性の観点からできるだけ避けた方が良いように思います。

そこで、本記事ではFindやGetChildを使用せずに指定の子オブジェクトを取得する方法を紹介します。

取得方法

結論としては、取得対象のオブジェクトに独自のenum型の変数を持つクラスをアタッチし、親オブジェクトはGetComponetsInChildrenによりそのクラスを全て取得するといった方法でオブジェクトを取得します。

実装例

以下のような構成で、"ModelManager"クラスは各子オブジェクトが何の形状をしているかを知る必要がある場合を考えます。



オブジェクト判別用に独自のenum型の変数を持つクラスを作成し、取得対象のオブジェクトにアタッチします。

ModelTag.cs
~~~~~~~~~

using UnityEngine;

public enum MTag
{
    Cube,
    Sphere,
    Capsule,
}

public class ModelTag : MonoBehaviour
{
    public MTag mTag;
}



つぎに"ModelManager"に以下の処理を実装します。

ModelManager.cs
~~~~~~~~~

using UnityEngine;

public class ModelManager : MonoBehaviour
{
    private ModelTag[] models; 

    void Start()
    {
        // 子階層に付与されているModelTagコンポーネントを全て取得
        models = FindObjectsByType<ModelTag>(FindObjectsSortMode.None);

        // ModelTagおよび、GameObjectの参照例
        foreach (var model in models)
        {
            Debug.Log(model.mTag);
            Debug.Log(model.gameObject.transform.position);
        }
    }
}


これにより"ModelManager"クラスは子オブジェクトを取得することができます。 Hierarchy上の各GameObjectの名前を変更したり、並び順が変わったとしてもエラーは生じません。

最後に

今回はモデルの形状をenumとして設定しましたが、enumの値を変えれば何に対しても使用できます。

また、GetComponentsInChilderenではなく、FindObjectsByTypeとすると親子関係に関係なくシーン上の全ての"ModelTag"を持つオブジェクトを取得することもできます。