hildsoftのコード置き場

プログラム関連で調べたことやコードの保管場所です

Unityでクリックした位置にprefabを作成する(Perspective編)

Unityでクリックした位置にprefabを作成する(Perspective編)

検証環境

Unity:5.6.1f1

カメラの設定

Unityのカメラ設定でProjectionという項目があります。

どのように空間を映すかという設定なのですが、

  • Orthographic
  • Perspective

の2パターンがあります。

この記事ではPerspectiveの場合について説明します。

Orthographicはこちら code.hildsoft.com

Perspective

f:id:hildsoft:20170704073703p:plain

設定はカメラのProjectionで変更可能です。

OrthographicもPerspectiveも3次元を2次元に投影する方法ですが、
PerspectiveではPerspectiveとは違いカメラに対して前後の概念も奥行きも影響してきます。

遠くにあるものは小さく、近くにるものは大きく表現されます。

f:id:hildsoft:20170704073846p:plain

Z正方向を見たカメラに2つのキューブを置きました。

シーンビューで斜め上からPerspectiveで見た図がこちらです。

赤いキューブのZ座標は-5で、緑のキューブのZ座標は0です。

緑から青色の長方形で構成される四角錘台に含まれるものがカメラに映されます。


f:id:hildsoft:20170704073923p:plain

実際にカメラに映し出されるのがこの図になります。

同じ大きさのキューブですが、赤は遠くにあるため小さく、緑は近くにあるため大きく見えます。これがPerspectiveの特徴です。

通常のカメラでの撮影や人の目の見え方はこの方式になります。

クリックした座標のとり方

画面のクリック位置を取る方法はこちらの記事を参照してください。

code.hildsoft.com

prefabを作成

public GameObject prefab; // prefabはインスペクタから与えてください。

private Camera cam;

void Start ()
{
    this.cam = FindObjectOfType<Camera>();
}
    
void Update () {
    if (Input.GetMouseButtonUp(0))
    {
        var mousePosition = Input.mousePosition;

        mousePosition.z += 18f;  // カメラのZ座標に+18
        //mousePosition.z -= this.cam.transform.position.z; // XY平面上に
        var worldPoint = this.cam.ScreenToWorldPoint(mousePosition);

        GameObject.Instantiate(this.prefab, worldPoint, Quaternion.identity);
    }
}

Input.mousePositionのZは常に0です。

Camera.ScreenToWorldPoint(Vector3 v)

で変換すると、カメラが見ている範囲のx,y座標に変換してくれますが、Zはカメラの座標からの相対値が返されます。

Orthographicとは違い、カメラからの距離によって大きさが変わるため、Zの値によってx,y座標も変わってきます。

Zの値を変更しないとカメラと同じ位置にインスタンスを作成されて見えないので、少し前に移動させます。

Orthographicでもそうですが、ScreenToWorldPointを使用しても座標が変わらない、オブジェクトが見えないなどはこの辺が原因であることが多いです。

この例ではカメラのZ=-20に対して+18に設定し、赤と緑のキューブの間に生成されるようにしました。

f:id:hildsoft:20170704074817p:plain

実行して適当にクリックしてみたのがこの図です。

赤と緑のキューブの間にprefabで指定したキューブが作成されています。

奥行きについて

f:id:hildsoft:20170704075527p:plain

画面上は同じ座標になっても、奥に行くほどX,Y座標は変わっていきます。

同じ大きさの比較できる物体があれば前後感は分かるようになります。

しかし、遠くに大きな物、近くに小さなものを配置すると画面上の大きさは同じようになります。 Orthographicと同様プログラム内で位置を知りたい場合はraycastなどで調べることになります。

補足

Perspectiveを使う時に大事なパラメータにField of Viewという項目があります。

これは視野を調整するもので、カメラを動かしたときに人間の目との違和感を感じやすい部分になるので3D酔いの原因になります。

人により差があるため、できればユーザーが設定できるようにプログラムを作成した方が良いでしょう。

その際にクリック位置などの補正が必要になることもあるので注意してください。

参考サイト

tsubakit1.hateblo.jp

関連リンク

Unity - スクリプトリファレンス: Camera

Unity - スクリプトリファレンス: Camera.orthographic

Unityでクリックした位置にprefabを作成する(Orthographic編)

Unityでクリックした位置にprefabを作成する(Orthographic編)

検証環境

Unity:5.6.1f1

カメラの設定

Unityのカメラ設定でProjectionという項目があります。

どのように空間を映すかという設定なのですが、

  • Orthographic
  • Perspective

の2パターンがあります。

この記事ではOrthographicの場合について説明します。

Perspectiveはこちら code.hildsoft.com

Orthographic

f:id:hildsoft:20170621203458p:plain

設定はカメラのProjectionで変更可能です。

OrthographicもPerspectiveも3次元を2次元に投影する方法ですが、
Orthographicではカメラに対して前後の概念はあっても「奥行き」というものがありません。

遠くにあっても、近くにあっても同じ大きさで表現されます。

f:id:hildsoft:20170621203824p:plain

Z正方向を見たカメラに2つのキューブを置きました。

シーンビューで斜め上からPerspectiveで見た図がこちらです。

片方のZ座標は-5で、もう片方は0です。


f:id:hildsoft:20170621204042p:plain

実際にカメラに映し出されるのがこの図になります。

奥にあるにもかかわらず、同じ大きさに見えます。これがOrthographicの特徴です。

2Dゲームなどではこの投影方式を選択することが多いですが、Perspectiveでも画角を調整することで奥行きの感じられない2D的なゲームを作成することも可能です。

クリックした座標のとり方

画面のクリック位置を取る方法はこちらの記事を参照してください。

code.hildsoft.com

prefabを作成

public GameObject prefab; // prefabはインスペクタから与えてください。

private Camera cam;

void Start ()
{
    this.cam = FindObjectOfType<Camera>();
}
    
void Update () {
    if(Input.GetMouseButtonUp(0))
    {
        var mousePosition = Input.mousePosition;
        mousePosition.z += 1f; // カメラのZ座標に+1
        // mousePosition.z -= this.cam.transform.position.z; // XY平面上に
        var worldPoint = this.cam.ScreenToWorldPoint(mousePosition);

        GameObject.Instantiate(this.prefab, worldPoint, Quaternion.identity);
    }
}

Input.mousePositionのZは常に0です。

Camera.ScreenToWorldPoint(Vector3 v)

で変換すると、カメラが見ている範囲のx,y座標に変換してくれますが、Zはカメラの座標からの相対値が返されます。

Zの値を変更しないとカメラと同じ位置にインスタンスを作成されて見えないので、少し前に移動させます。

Perspectiveでもそうですが、ScreenToWorldPointを使用しても座標が変わらない、オブジェクトが見えないなどはこの辺が原因であることが多いです。

この例では+1だけしていますが、他のオブジェクトとの前後関係を考慮した値を設定してください。

f:id:hildsoft:20170621211901p:plain

実行して適当にクリックしてみたのがこの図です。

カメラのすぐ前方に緑のキューブが作成されています。

奥行きについて

f:id:hildsoft:20170621211042p:plain

画面上は点でも、実際には奥行きがある事を理解しておいてください。

また、線上のどこにあるかは画面からは判断できません。

プログラム内でレイヤーに分けて管理するか、raycastなどで調べることになります。

補足

Orthographicを使う際は、Z軸に平行で、x,y平面を映すようにした方が良いです。

カメラに角度をつけてしまうと上記の方法ではズレが生じてしまいます。

もちろん補正してしまえば問題ないのですが、バグの原因にもなりかねませんので必要が無ければ避けた方が良いです。

2次元の被写体全部を傾けるよりは楽なので、その場合はカメラを傾けた方が楽になります。

参考サイト

tsubakit1.hateblo.jp

関連リンク

Unity - スクリプトリファレンス: Camera

Unity - スクリプトリファレンス: Camera.orthographic

Unityでクリックした座標を取得する

Unityでクリックした座標を取得する

検証環境

Unity:5.6.1f1

Inputクラス

InputクラスのStaticメソッドやフィールドを使うことで、入力系の処理を知ることができます。

座標の取得

マウスの座標はInput.mousePositionで参照できます。

Vector3 mousePosition = Input.mousePosition;

Input.mousePositionは常に更新されているので、クリックしたときだけ処理をしたい場合は条件を加える必要があります。

座標の始点

f:id:hildsoft:20170621144006p:plain

座標は左下が原点で、右方向がX軸のプラス、上方向がY軸のマイナスになります。

マウスの状態

マウスは

  • 押していない状態
  • 押している状態
  • 離した状態

の3つの状態があります。

それに対してUnityでは、3つのマウスボタンの動作を検知できます。

  • 押した瞬間(GetMouseButtonDown)
  • 押している状態(GetMouseButton)
  • 離した瞬間(GetMouseButtonUp)

の3つです。

クリック操作というのは、押した時ではなく離した時に決定することがほとんどだと思います。
ですので、今回はボタンを離した時のサンプルを書いておきます。

サンプルコード

// 左ボタンクリック
if (Input.GetMouseButtonUp(0))
{
    Vector3 mousePosition = Input.mousePosition;
    Debug.Log("LeftClick:"+mousePosition );
}
// 右ボタンクリック
if (Input.GetMouseButtonUp(1))
{
    Vector3 mousePosition = Input.mousePosition;
    Debug.Log("RightClick:"+mousePosition );
}
// 中ボタンクリック
if (Input.GetMouseButtonUp(2))
{
    Vector3 mousePosition = Input.mousePosition;
    Debug.Log("MiddleClick:"+mousePosition );
}

このように、マウスクリックが発生していることを確認してから座標を取得します。

補足

ボタンを押した位置から動かしてボタンを離す(ドラッグ&ドロップ)のような操作は、

押した瞬間(GetMouseButtonDown)に座標を取得して保存しておき、
離した瞬間(GetMouseButtonUp)にも座標を取ることで、距離なども計算できます。

uGUIなど、画面上のボタンなどについてはクリックイベントがありますので、イベント時に取得するのが良いでしょう。

関連リンク

Unity - スクリプトリファレンス: Input

Unity - スクリプトリファレンス: Input.GetMouseButtonUp

Unityの物理演算(Rigidbody)を設定したオブジェクトが崩れたりバラけたりして安定しない

UnityのRigidbodyが崩れたりバラけたりするため上手く積めない

検証環境

Unity:5.6.1f1

Rigidbodyコンポーネント

f:id:hildsoft:20170621091637p:plain

RigidbodyはUnityで物理演算で物を動かす時などに使います。

ただ、初期設定のままで使うと、上手く積むことができません。

意図したところでピタっと止めたい場合の対処法です。

Cubeを縦に10個、横に4個積んでみました。

f:id:hildsoft:20170621091755p:plain

左側はキッチリと積めていますが、右側は揺れていて安定しません。

微妙な力がお互いに影響し合っている状態ですね。

Dragの値で調整

設定調整で簡単に動きを止めることはできます。

f:id:hildsoft:20170621092454p:plain

何をしたかというと、Dragの値を調整しました。

公式のヘルプによるとDragは

力により動く際に、オブジェクトに影響する空気抵抗の大きさ。0 の場合、空気抵抗が 0 で、無限の場合、オブジェクトは動きを止めます。
「 Drag 」値が低いと、オブジェクトが重く見えるようになります。この値が高いと、軽く見えます。「 Drag 」の通常の値は、.001 (金属の塊) と 10 (羽) の間です。

とのことです。

今回は右側のオブジェクトはDrag値:0
左側のオブジェクトはDrag値:20
にしました。

確かにDrag値を増やせば動きにくくなり安定します。

しかし、安定するということは、動かしたいときに動かしにくいということにもなります。

公式のヘルプでも"通常の値は、.001 (金属の塊) と 10 (羽) の間"との記載があります。

Dragの値での動きにくさ

同じ重さの球を同じ速さでぶつけてみます。

Drag:0

f:id:hildsoft:20170621093253g:plain

Drag:20

f:id:hildsoft:20170621093453g:plain

このように、キッチリと積むことはできても、思ったような物理演算を実現できなくなる可能性があります。

力を加えても空気抵抗が大きすぎるため、すぐに止まってしまうのです。

水中で物を動かすのに近いですね。

Rigidbodyの物理動作を止める

Rigidbodyは計算量節約のため、動作時(Awake)と停止時(Sleep)の状態を持っています。

停止状態では物理演算が行われないので、初期位置から動くことはありません。

f:id:hildsoft:20170621094556p:plain

起動時に強制的に停止状態にしておくことで、初期配置でグラグラ動いたり、はじけ飛んだりすることを防げます。

各Cubeに、下記のスクリプトを追加しました。

void Start () {
    this.GetComponent<Rigidbody>().Sleep();
}

停止状態は、新たにColliderが接触したりWakeUpメソッドなどで解除できます。

f:id:hildsoft:20170621094856g:plain

先ほどと同じように球をぶつけてみると、Drag:0の時と同様に崩れていきます。

関連リンク

Unity - マニュアル: Rigidbody

Unity - スクリプトリファレンス: Rigidbody

UnityでスクリプトからTransformコンポーネントを取得する方法

UnityでスクリプトからTransformコンポーネントを取得する方法

検証環境

Unity:5.6.1f1

Transformコンポーネント

Transformコンポーネントは、すべてのGameObjectに追加される基本的なコンポーネントです。

位置、回転、スケール、親子情報などシーン内のGameObjectにアクセスするために使用します。

空のオブジェクトを作成しても追加されます。

f:id:hildsoft:20170616093504j:plain

アタッチされたゲームオブジェクトのTransformコンポーネントにアクセス

f:id:hildsoft:20170616100333j:plain

アタッチされているC#スクリプトから自身のTransformへは簡単にアクセスできます。

MonoBehaviourを継承していればフィールドにありますので、

Transform myTransform = this.transform;

だけで使用可能です。

親子間のTransformコンポーネントにアクセス

親の場合

var parentTransform = this.transform.parent;

子の場合

var childTransform = this.transform.Find("child_name");

// 古い取得方法
//var childTransform_old = this.transform.FindChild("child_name");

“child_name"部分に、Hierarchyに表示されている子要素の名前を設定してください。

それ以外のTransformコンポーネントにアクセス

親や子を伝って取ることがほとんどだと思いますが、親子関係にないオブジェクトは検索して取得することになります。

var anotherTransform1 = GameObject.Find("object_name").transform;
var anotherTransform2 = GameObject.Find("/parent/child").transform;

/で区切ることによって階層を指定して検索することもできます。

名前が同じものが複数ある場合は、最初に見つかったもの(Hierarchyの順とは限らない)が取得できますが、同じ階層で重複する名前は極力使わないようにしましょう。

また、Hierarchy上で灰色(Inspectorで使用をoff)にしている状態だと探せませんので、NullReferenceExceptionに注意してください。

関連リンク

Unity - スクリプトリファレンス: Transform

Unity - スクリプトリファレンス: GameObject.Find

プライバシーポリシー

プライバシーポリシー

広告について

当サイトでは、第三者配信の広告サービス(GoogleアドセンスA8.netAmazonアソシエイトバリューコマース)を利用または利用を予定しています。 広告配信事業者は、ユーザーの興味に応じた広告を表示するためにCookie(クッキー)を使用することがあります。 Cookieを無効にする設定およびGoogleアドセンスに関する詳細は「こちら」をご覧ください。

アクセス解析ツールについて

当サイトではアクセス解析ツール「Google Analytics」を利用しトラフィックデータ収集のためにCookieを使用しています。 またトラフィックデータは匿名で収集されており、個人を特定するものではありません。 もし収集を拒否したい場合はCookieを無効にしてください。 この規約に関して、詳しくはこちら、またはこちらをクリックしてください。