【Unity/2D】UI位置からワールド座標へプレハブを生成

ボタンの位置からワールド空間へ、オブジェクトプール化したコインを投下

スクリーン座標にある、ボタン等のUI要素の位置から、ワールド座標を取得。
オブジェクトプール化したプレハブを生成する。

UI要素やオブジェクトを、画面の左右に往復させるコードもあります。

  • 「Coin」という命名だと名前が衝突しそうなので、スクリプト名は「Puck」とした。
  • 使わなかったusing宣言も、一応コメントアウトしつつ残した。
    (自分でコードを加える場合にエラーが出たらコメントアウトを外してください)

サンプル動画

サンプルのコイン落としミニゲームです。

【注意】Render ModeがScreen Space – Cameraの場合

CanvasコンポーネントのRender Modeを変更していた場合は、座標の変換の必要がない。
なので、ボタンのRectTransform.positionを直接使えば良い。

但し、Z方向にカメラ位置とPlane Distanceの分だけズレる。
(-10(デフォルトカメラ位置) + 100(デフォルトPlane Distance)で、90になる)

Physics2D.OverlapCircle等で、深度のオプションを使う場合は、Zを0に修正しておく。
通常のOnCollisionEnter2D等は正常に動作した。

【コード】Puck

生成するプレハブ用コード。
オブジェクトプール化する為に、画面外に落ちたかの判定も適当に行っている。

  1. 2D Object -> Spriteを作成「Puck」と命名。
  2. 任意の画像を設定し、適度な大きさにScaleを弄る。
  3. Circle Collider 2D等をアタッチして、画像のサイズに合わせる。
  4. Rigidbody 2Dをアタッチ。
  5. 新規スクリプト「Puck」を作成、コードを貼り付け。
  6. インスペクターから、スクリプトの変数へ、各コンポーネントを紐付け。
  7. プレハブ化。


//using System.Collections;
//using System.Collections.Generic;
using UnityEngine;

public class Puck : MonoBehaviour
{

//各コンポーネントをインスペクターから紐付けしておく。
	public GameObject go;
	public Transform tf;
	public Rigidbody2D rb2d;

//アクティブか、非アクティブかのチェック用フラグ。
	public bool isActive;

//これ以下の低さまで落ちたら、非アクティブ化する。任意の値。
	static readonly float KillThreshold = -10.0f;



	void Update()
	{
		CheckKill();
	}

	public void SetActive(bool b)
	{
		isActive = b;
		go.SetActive(b);
	}

	void Reset()
	{
//Rotationを初期化。Rigidbody2D.Constraintsを固定していれば省略可能。
		tf.rotation = Quaternion.identity;
//加速度を初期化。オブジェクトプール&Rigidbody使用時には必須。
		rb2d.velocity = Vector2.zero;
	}

//非アクティブ化判定。
	void CheckKill()
	{
		if (tf.position.y <= KillThreshold) {
			SetActive(false);
			Reset();
		}
	}
}

【コード】PuckSpawner

オブジェクトプール管理用コード 。
ボタンへのリスナー登録用メソッドもコレに記述している。

*ワールド空間に存在するオブジェクトを子として管理するので、ボタン自体には付けない。

  1. ボタンを作成。
  2. Canvasの設定を変更。
    1. UI Scale Mode:Scale With Screen Size
    2. Reference Resolution:(X 1920, Y 1080)
  3. 新しくオブジェクトを作成して、新規スクリプト「PuckSpawner」を作成、コードを貼り付け。
  4. インスペクターから、スクリプトの変数へ、ボタンとプレハブを紐付け。



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


public class PuckSpawner : MonoBehaviour
{

//インスペクターから対象のボタンを紐付けしておく。
	[SerializeField]
	RectTransform buttonRt;

//インスペクターから対象のボタンを紐付けしておく。
	[SerializeField]
	Button button;

//インスペクターからPuckのプレハブを紐付けしておく。
	[SerializeField]
	GameObject puckPrefab;

//1回で使用出来るPuckの上限数。任意の値。
	static readonly int PuckCountMax = 10;

//各コンポーネントを弄るので、PuckクラスにまとめておいてPuckをリストとして保持。
	List<Puck> puckList = new List<Puck>(PuckCountMax);

//毎回Camera.mainから読み取ると負荷が掛かるので、こっちに参照を保持。
	Camera mainCamera;

	Puck tempSpawnPuck;
	Vector3 tempSpawnPuckPosition;
	


	void Awake()
	{        
		mainCamera = Camera.main;

//ボタンリスナーにメソッドを登録。
		button.onClick.AddListener(ButtonListenerPuckSpawner);

//オブジェクトプールを初期化。
		for (int i = 0; i < PuckCountMax; i++) {
			puckList.Add(Instantiate(puckPrefab, transform).GetComponent<Puck>());
			puckList[i].SetActive(false);
		}
	}


//ボタンリスナーに登録するメソッド。publicにして、ボタンのインスペクターから登録しても良い。
	void ButtonListenerPuckSpawner()
	{
		tempSpawnPuck = GetPuck();

//全てのPuckが使用中ならばnullが返されるので、その場合はPuckの出現をキャンセル。
		if (tempSpawnPuck == null)
			return;

//ボタンの位置に移動させる。
		tempSpawnPuckPosition = mainCamera.ScreenToWorldPoint(buttonRt.position);
//Zがカメラの位置(デフォルトなら-10)になり、カメラの範囲外になる為、0に変更。
		tempSpawnPuckPosition.z = 0;
		tempSpawnPuck.tf.position = tempSpawnPuckPosition;

		tempSpawnPuck.SetActive(true);
	}


//オブジェクトプールから非アクティブなPuckを取得。
	Puck GetPuck()
	{
		for (int i = 0; i < PuckCountMax; i++) {
			if (!puckList[i].isActive) {
				return puckList[i];
			}
		}

		return null;
	}
}

【コード】MoveUI

ボタンの移動用コード 。
厳密にスクリーンの端を計算している訳ではない。

*MoveButtonとしたら移動用ボタンと混同するので、MoveUIと命名した。

  1. 前項で作ったボタンを選択。
  2. 新規スクリプトを作成「MoveUI」と命名、コードを貼り付け。


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

public class MoveUI : MonoBehaviour
{

	RectTransform rt;

//ボタンが往復する左端と右端の位置。Anchorを変更していない場合は、画面中心からの距離。
	Vector3 buttonLeftPosition = new Vector3(-800, 300, 0);
	Vector3 buttonRightPosition = new Vector3(800, 300, 0);
	
//ボタンが1往復する秒数。任意の値。
	float moveDuration = 2.0f;



	void Awake()
	{
		rt = GetComponent<RectTransform>();
	}


	void Update()
	{
//ボタンを左右に移動させる。
//Anchorと相対的な位置を変更。        
		rt.anchoredPosition = Vector3.Lerp(buttonLeftPosition, buttonRightPosition, Mathf.PingPong(Time.time / moveDuration, 1.0f));
	}
}

【コード】Bucket

バケツの移動用コード。
厳密にスクリーンの端のワールド座標を計算している訳ではない。

  1. 2D Object -> Spriteを作成「Puck」と命名。
  2. 用意した、コの字状の画像を設定。
  3. Polygon Collider 2Dをアタッチ。
  4. 新規スクリプトを作成「Bucket」と命名、コードを貼り付け。


//using System.Collections;
//using System.Collections.Generic;
using UnityEngine;

public class Bucket : MonoBehaviour
{

	Transform tf;   

//バケツが往復する左端と右端の位置。
	Vector3 leftPosition = new Vector3(-7, -4, 0);
	Vector3 rightPosition = new Vector3(7, -4, 0);

//バケツが1往復する秒数。任意の値。
	float moveDuration = 3.0f;



	void Awake()
	{
		tf = transform;
	}


	void FixedUpdate()
	{
//バケツを左右に移動させる。当たり判定を持っているので、一応FixedUpdate内で動かしている。
		tf.position = Vector3.Lerp(leftPosition, rightPosition, Mathf.PingPong(Time.time / moveDuration, 1.0f));
	}
}

タイトルとURLをコピーしました