【Unity】Cinemachineのカメラの向きをスクリプトで取得する方法

導入

CinemachineVirtualCameraの垂直方向の向き(VericalAxis.Value)を

this.gameObject.GetComponent<CinemachineVirtualCamera>().Aim.VerticalAxis.Value

で取得しようとしたところ、「CinemachineVirtualCameraにはAimという名前のプロパティはありません」というエラーが発生。

じゃあどうすれば取得できるのか、Unity公式リファレンスを当たったところ、なんとか正答に到達。その正答にたどり着く作業は意外と大変だったので、ここで共有しておこうと思います。

コード

これがCinemchineVirtualCameraの垂直方向の角度の値を取得するのに必要なコードです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;

public class LESSON : MonoBehaviour
{
    CinemachineVirtualCamera camera;

    // Start is called before the first frame update
    void Start()
    {
        camera = this.gameObject.GetComponent<CinemachineVirtualCamera>();
        
    }

    public float GetVerticalAngle()
    {
        return camera.GetCinemachineComponent(CinemachineCore.Stage.Aim).GetComponent<CinemachinePOV>().m_VerticalAxis.Value;
    }
 
}

補足

GetCinemachineComponent とは

定義は

public CinemachineComponentBase GetCinemachineComponent(CinemachineCore.Stage stage)

「CinemachineCore.Stage」に応じた「CinemachineComponentBase」を返します。

CinemachineCore.Stage とは

CinemachineVirtualCameraコンポーネントにある、「Aim」「Body」「Noise」などを含む列挙型メンバーです。

CinemachinePOV とは

CinemachineComponentBaseの継承クラスの一つ。Aimの項目を「POV」に設定したときに継承されます。

水平方向の場合

m_VerticalAxisのところをm_HorizontalAxisに変えればOKです。

参考文献

Unity公式ドキュメント – ChinemachineCore.Stage

Unity公式ドキュメント – CinemachinePOV

Unity公式ドキュメント – CinemachineVirtualCamera

Unity公式ドキュメント – CinemachineComponentBase

【Unity】Unityで作ったゲームをandroid端末で実行する手順を紹介

導入

Unityで作ったゲームをandroid端末向けにビルドしようとしたとき、「CommandInvokationFailure: Gradle build failed.」や「UnityEditor.BuildPlayerWindow+BuildMethodException」などのエラーが発生し、ビルドが途中で中断してしまった。その原因を調べては試し、また調べては.試したが、正解は中々見つからなかった。そんなとき、「unityでエクスポートして、それをandroid studioでビルドすれば出来ました。」という文章を発見。自分もその方法を試したところ、見事に成功。

…というわけで、今回は、私もお世話になった、Unityで作ったゲームをandroid端末向けにビルドする方法を紹介します。

目次

今回使うもの

  • android studio
  • android 端末

流れ

①JDK、SDK、NDKをインストール → ②プロジェクトのエクスポート → ③Android studioで開く → ④android端末にビルド

①JDK、SDK、NDKをインストール

Unity2019以降のversionを使う場合

Unity Hub」(Unityのプロジェクト起動時に使うやつ)を開き、「インストール」タブを選択します。すると、Unityのマークと共に今までインストールしたUnityのversionが表示されます(2019.4.19f1 など)。

自分が使いたいUnityのversionを選び、「・・・」が縦に並んでいるボタンを押し、「モジュールを加える」を選択します。すると、モジュールがずらーっと出てくるので、「Android Build Support」と、その下の階層にある「Android SDK & NDK Tools」と「OpenJDK」にチェックを入れ、「次へ」を選択します。

そして、ライセンスの同意にチェックを入れ、「実行」します。すると、ダウンロードが始まるので、終了するまで待てばOKです。

Unity2018以前のversionを使う場合

JDK、SDK、NDKをそれぞれ特定の場所でインストールする必要があります。

>> JDK、SDK、NDKのインストール方法(公式ドキュメント)

②プロジェクトのエクスポート

UnityのProjectを開き、「File」→「Build Settings」を選択し、Build Settingsを開きます。Platformが「Android」になっていなければ、「Android」を選択した後、左下の「Switch Platform」を押します。すると、少し長いロードの後、PlatformがAndroidに切り替わります。

このとき、Platformの右に、「TextureCompression」や「ETC2fallback」など、沢山の項目が出てこない場合は、Unityを一回閉じて、開き直して下さい(①でインストールしたやつが反映されていないので)。

①が反映されていれば、「ExportProject」という項目にチェックを入れ、左下の「Export」を押して下さい。すると、エクスポート先のファイルを選択する画面が出てくるので、パスに日本語が使われていない階層のファイル内にエクスポートして下さい(もしエクスポート先のファイルパスに日本語が含まれている場合は、③でgradleの同期に失敗します。)。

これで、プロジェクトのエクスポートは完了です。

③Android Studioで開く

Android Studioを開き、「ファイル」→「開く」を選択します。そして、先ほどエクスポートしたプロジェクトの、一番上の階層のフォルダを開きます(このとき、先ほどのプロジェクトのエクスポートに成功していれば、プロジェクトのアイコンが「ドロイド君」の絵になっているので探しやすいです)。

プロジェクトを開いたとき、Android Studioで使用しているSDKとプロジェクトで使用しているSDKのパスが異なる場合、「どちらのSDKを使うか」問われることがありますが、プロジェクトの方を選択して下さい。

④android端末にビルド

USBケーブルでandroid端末とパソコンを繋ぎます。

端末側の準備

端末の開発者モードがONになっていない場合は、「設定」→「システム」→「端末情報」を開き、「ビルド番号」を7回タップして、開発者モードをONにします(端末によっては最初から開発者モードがONになっている場合があります)。開発者モードがONであれば、「設定」→「システム」→「開発者向けオプション」を開き、「USBデバッグ」をONにして下さい

ここで、端末とパソコンをUSBケーブルで繋ぎます。

ケーブルで繋ぐとすぐに、端末側に反応が現れます。パソコンからのアクセスを許可していない場合には、端末の方で「許可しますか」と問われることがあるので、許可して下さい。接続の用途を問われた場合は、「充電のみ」ではなく「ファイルを転送」を選択して下さい。

パソコン側の準備

Android Studioの上のツールバーにある「実行」(緑色の三角)を押すと、プロジェクトのビルドが始まります。ビルドが完了したら、端末側でプロジェクトのアプリケーションが作成されます。

これで、目的達成です!おめでとう!

【Unity】スクリプトだけでドット絵を作る方法

導入

ドットゲームの中には、装備によってキャラクターの見た目が変わるものがあります。そのシステムを実装する際、全てのキャラクターの見た目を作っていたらキリがありません。

…もしスクリプトでドット絵を作ることができたら、その膨大とも思える作業量やメモリをコンパクトにできるでしょう。というわけで今回は、スクリプトのみでドット絵を表示させる方法を解説します。

こんな感じで、コードからドットの画像を作ることができます。(完全ランダムにも関わらず、既視感のある画像が何度も生成されているのは、Unityの疑似乱数の性質によるものです)

コード

以下のコードは、動画で使われているコードの内、無駄な部分を全て省いたバージョンです。なので、RawImageをアタッチして、変数riに「RawImageがアタッチされたゲームオブジェクト自身」をアタッチすれば、そのまま使えます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class BrogIntroduce : MonoBehaviour
{
    public RawImage ri;

    void Start()
    {
        ri = GetComponent<RawImage>();

        SetMarioTexture();
        
    }

    void Update()
    {
        
        
    }

    // ーーーーーーーーーーーーーーーーーーーー
    //    関数一覧
    // ーーーーーーーーーーーーーーーーーーーー

    public string[,] CreateMarioPicture()
    {
        string BBB_ = "#000f";
        string rrr_ = "#f00f";
        string _e_e = "#ff0f";
        string x___ = "#0000";

        string[,] picture_str = new string[,]
        {
            {x___,x___,x___,rrr_,rrr_,rrr_,rrr_,rrr_,x___,x___,x___,x___},
            {x___,x___,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,x___},
            {x___,x___,BBB_,BBB_,BBB_,_e_e,_e_e,BBB_,_e_e,x___,x___,x___},
            {x___,BBB_,_e_e,BBB_,_e_e,_e_e,_e_e,BBB_,_e_e,_e_e,_e_e,x___},
            {x___,BBB_,_e_e,BBB_,BBB_,_e_e,_e_e,_e_e,BBB_,_e_e,_e_e,_e_e},
            {x___,BBB_,BBB_,_e_e,_e_e,_e_e,_e_e,BBB_,BBB_,BBB_,BBB_,x___},
            {x___,x___,x___,_e_e,_e_e,_e_e,_e_e,_e_e,_e_e,_e_e,x___,x___},
            {x___,x___,BBB_,BBB_,rrr_,BBB_,BBB_,BBB_,x___,x___,x___,x___},
            {x___,BBB_,BBB_,BBB_,rrr_,BBB_,BBB_,rrr_,BBB_,BBB_,BBB_,x___},
            {BBB_,BBB_,BBB_,BBB_,rrr_,rrr_,rrr_,rrr_,BBB_,BBB_,BBB_,BBB_},
            {_e_e,_e_e,BBB_,rrr_,_e_e,rrr_,rrr_,_e_e,rrr_,BBB_,_e_e,_e_e},
            {_e_e,_e_e,_e_e,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,_e_e,_e_e,_e_e},
            {_e_e,_e_e,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,rrr_,_e_e,_e_e},
            {x___,x___,rrr_,rrr_,rrr_,x___,x___,rrr_,rrr_,rrr_,_e_e,x___},
            {x___,BBB_,BBB_,BBB_,x___,x___,x___,x___,BBB_,BBB_,BBB_,x___},
            {BBB_,BBB_,BBB_,BBB_,x___,x___,x___,x___,BBB_,BBB_,BBB_,BBB_},
        };
        return picture_str;
    }

    public Texture2D ToTexture2D(string[,] picture_str)
    {
        Texture2D picture = new Texture2D(picture_str.GetLength(1),picture_str.GetLength(0));
        
        for (int x = 0; x < picture_str.GetLength(1); x++)
        {
            for (int y = 0; y < picture_str.GetLength(0); y++)
            {
                string colorCode = picture_str[y,x];
                Color color = default(Color);

                if(ColorUtility.TryParseHtmlString(colorCode, out color))
                {
                    picture.SetPixel(x, picture_str.GetLength(0)-1-y, color);
                }                
            }
        }

        return picture;
    }

    public void SetTexture(Texture2D texture) 
    {
        texture.filterMode = FilterMode.Point;
        texture.Apply();
        ri.texture = texture;
    }

    public void SetMarioTexture()
    {
        string[,] picture_str = CreateMarioPicture();
        Texture2D picture = ToTexture2D(picture_str);
        SetTexture(picture);
    }
}

↓ ちなみにコードの結果はこうなります(右はさっき居た奴です)

コードの説明

大筋

①マリオの絵をカラーコードで描く→②UnityEngine.Texture2Dに置き換える→③ゲームオブジェクトのRawImageのテクスチャを置き換える

①マリオの絵をカラーコードで描く

CreateMarioPictureの部分で担当。

BBB_とか_e_eとか変な名前が付いていますが、「遠くから見たらそれっぽく見える」以外に深い理由はありません。

②UnityEngine.Texture2Dに置き換える

ToTexture2Dの部分で担当。

GetLength(int i)は配列のi次元の要素数を取得する関数。

ColorUtility.TryParseHtmlString(string colorCode, Color outcolor)は、「colorCodeがUnityEngineColorに変換できるか」の判定をbool(真偽)を返し、trueの場合、outcolorに変換後の色を代入する関数。

SetPixel(int x, int y, Color color)は、Texture2Dの座標(x, y)のピクセルの色をcolorに変更する関数。Unityの座標はx座標が→方向、y座標が↑方向になっている一方、コード内のpicture_strはx座標が→方向、y座標が↓方向になっているので、y座標をpicture_str.GetLength(0)-1-yで置換しています。

③ゲームオブジェクトのRawImageのテクスチャを置き換える

SetTextureの部分で担当。

filterModeをPointに変更しているのは、ドットがぼやけないようにする為。

Apply()最も重要な関数。この関数は、Texture2Dの変更内容を適用する関数。これが無ければ、変更が適用されないので、灰色の画像が現れます。

補足

Textureの画像はピクセル数が増えても、大きさが変わらないので、大きさを不変にするか、大きさを変えるコードを作っておいた方が良いです。

また、作ったピクセルを動かしたい場合は、UIのRawImageではなく、ゲームオブジェクトにRawImageをアタッチして下さい。

Q&A

Q1. SpriteRendererのSprite.textureを使用せず、RawImage.textureを使用しているのは何故?

A1. SpriteRendererのSprite.textureは読み取り専用だからです。

Q2. ピクセルが表示されない。

A2. テクスチャを表示するにはキャンバスが必要なので、キャンバスの階層の下に移動させて下さい。

【Unity】「ドット絵がぼやける」対処法を紹介

導入

spriteのぼやけを直すときは、素材となる画像の設定をいじれば良かったが、コードで作った(素材となる画像が存在しない)textureのぼやけを直すときは…あれ?どこをいじれば良いんだろう…。

――というわけで「texture ぼやける」と検索。

しかし、解決策が出てこない…。仕方なくUnityの公式ドキュメントで検索したところ、使えそうなコードを発見!

というわけで、使えそうなコードを見つけたので、Unityドット勢には是非知っておいてほしいコードを共有したいと思います。

コード

やっていることは、コードを使ってtextureのfilterModeをPointに変えているだけ。

テクスチャのぼやけを消したい場合

// どのtextureに対して行うか選ぶ
// (例1)
Texture texture = GetComponent<RawImage>().texture;
// (例2)
Texture texture = GetComponent<Renderer>().material.mainTexture;

// 今回紹介するコード(フィルターモードをpointに変更する)
texture.filterMode = FilterMode.Point;

スプライトのぼやけを消したい場合

スプライトのtextureに対して、さっきの処理をするだけです。

// どのspriteに対して行うか選ぶ
// (例1)
Texture texture = GetComponent<Renderer>().sprite.texture;

// さっきと同じ
texture.filterMode = FilterMode.Point;

いつ使うの?

冒頭でも触れましたが、「いつ使うか?」と聞かれたら、コード内で作ったtextureのfilterModeをPointにしたいときです。それか「ゲームの途中でfilterModeを変えるとき」とか(『Evoland』っぽいゲームを作りたいなら使うかも)。

あと、なぜコード内でtextureを作っているかといえば、「2DObjectの4方向から見たimageを作る手間を省くため」です。

↓ 実際に使う場面はこのような場面です。

https://nazenavi.com/2021/02/24/%e3%80%90unity%e3%80%91%e3%83%97%e3%83%ad%e3%82%b0%e3%83%a9%e3%83%a0%e3%81%ae%e3%81%bf%e3%81%a7%e3%83%89%e3%83%83%e3%83%88%e7%b5%b5%e3%82%92%e8%a1%a8%e7%a4%ba%e3%81%95%e3%81%9b%e3%82%8b%e6%96%b9/

参考文献

今回の参考文献は冒頭でも触れた通り、「Unityの公式ドキュメント(2021.1)」>「Filter Mode」

Unity公式ドキュメント – Filter Mode

【Unity】プレイヤーが壁に引っかかる理由とその対処法

導入

プレイヤーを動かす設定はできたが、今度はプレイヤーが壁にくっつくようになってしまった。…そんな人の為に、解決策とその現象が発生する原因を解説します。

以下に紹介するのは2Dでの対処方法ですが、3Dでも応用できるように原因を解説しているので、3Dで困っている方は「原因の解説」を読むことをお勧めします。

▼問題の現場はこんな感じ

解決策

①引っかかりを直したいゲームオブジェクトにRigidBody2DもしくはBoxCollider2Dをアタッチする。

②Project > Asset を開き、左上の「+」を押して「PhysicMaterial2D」を選択する。もしくは、右クリックして、「Create > PhysicMaterial2D」を選択する。今作成したファイルを開き、Frinction、Bouncinessを共に0にする。

③RigidBodyもしくはBoxColliderのMaterialという項目に、「Friction(摩擦)、Bounciness(反発)を両方0にしたもの」をドラッグ&ドロップで設定する。

結果

普通の横スクロールアクションのように、壁に引っかからずにプレイヤーを操作することができています。

原因の解説

原因は、「摩擦(Friction)」です。ある物体が平面に接しているとき、ある物体をその平面上で動かそうとする向きと逆向きに、その物体に対して摩擦力が働きます。そのとき働く力の大きさは、「平面が物体から垂直方向に受ける力の大きさ」×「物体と平面間の摩擦係数」で表されます。

つまり、原因は摩擦力となるわけですが、…Unityでは摩擦力の計算が少し違うことに注意して下さい。Unityでは、2Dならば、「摩擦力の大きさ=PhysicMaterialFrinctionの大きさ」です。より正確にいえば、「Frinctionが0なら摩擦力は0、Frictionが1なら力を加えない限りオブジェクトは動かないような摩擦力がはたらく(中間値はその間)」という感じに定義されています。

そして、デフォルトの設定(None)では、「Friction=0.4、Bounciness=0」である為に、今回の現象が起きるのです。

補足

Unityの摩擦力は、先ほども述べた通り、「摩擦力の大きさ=PhysicMaterialのFrinctionの大きさ」なので、「平面が物体から垂直方向に受ける力の大きさ」には比例しません。従って、物体が重力の向きに垂直に広がる平面上を滑っていたとしても、質量(Mass)に関係なく、Frinctionの値が同じである限り、同じ摩擦力がはたらきます。

2Dでは動摩擦力(Dynamic Friction;物体が動いているときにかかる摩擦力)と静止摩擦力(Static Friction;物体が静止しているときにかかる摩擦力)は同じFrictionで定義されますが、3Dでは個別に設定することができます。