スクリーン座標にある、ボタン等の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
生成するプレハブ用コード。
オブジェクトプール化する為に、画面外に落ちたかの判定も適当に行っている。
- 2D Object -> Spriteを作成「Puck」と命名。
- 任意の画像を設定し、適度な大きさにScaleを弄る。
- Circle Collider 2D等をアタッチして、画像のサイズに合わせる。
- Rigidbody 2Dをアタッチ。
- 新規スクリプト「Puck」を作成、コードを貼り付け。
- インスペクターから、スクリプトの変数へ、各コンポーネントを紐付け。
- プレハブ化。
//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
オブジェクトプール管理用コード 。
ボタンへのリスナー登録用メソッドもコレに記述している。
*ワールド空間に存在するオブジェクトを子として管理するので、ボタン自体には付けない。
- ボタンを作成。
- Canvasの設定を変更。
- UI Scale Mode:Scale With Screen Size
- Reference Resolution:(X 1920, Y 1080)
- 新しくオブジェクトを作成して、新規スクリプト「PuckSpawner」を作成、コードを貼り付け。
- インスペクターから、スクリプトの変数へ、ボタンとプレハブを紐付け。
//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と命名した。
- 前項で作ったボタンを選択。
- 新規スクリプトを作成「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
バケツの移動用コード。
厳密にスクリーンの端のワールド座標を計算している訳ではない。
- 2D Object -> Spriteを作成「Puck」と命名。
- 用意した、コの字状の画像を設定。
- Polygon Collider 2Dをアタッチ。
- 新規スクリプトを作成「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));
}
}