こんにちは、やまだたいし( やまだ たいし@ソシャゲプログラマ (@OrotiYamatano) | Twitter )です。
Unity1WeekJamの時にTileMapを消す処理を書いた時に苦戦したので
今回はその時の知見を共有したいと思い記事化しました。
目次
前提
Unity2018.4.11f1で動作確認
UniRxだけど、分かる人はわかると思う。
使用結果
↓こんな感じで使いました。
といっても、実装そのままだとコードが少し複雑なので皆様には、もう少し簡略化したものをお見せします。
カーソルとかその他諸々を消しました。
Tileを消すプログラム全体
using Script.Util.input; using UniRx; using UniRx.Triggers; using UnityEngine; using UnityEngine.Tilemaps; using Zenject; namespace Script { public class GetReyObject : MonoBehaviour { [Inject] private InputService _inputService; private RaycastHit2D _hit; private Vector2 _hitPos; [SerializeField] private GameObject cursorObj; [SerializeField] Tilemap blockTilemap; private void Start() { //画面をクリックしたら呼ばれる _inputService.GetClickPos.Subscribe(val => { //クリックするか、クリックを離すと反応 if (val == Vector3.zero){ //クリック話したときはマウスの位置が0で来る return; } var position = gameObject.transform.position; if (Camera.main == null) return; Vector3 diff = (Camera.main.ScreenToWorldPoint(val) - position).normalized; _hitPos = diff; _hit = Physics2D.Raycast(position,diff/*方向*/, 1/*距離*/,LayerMask.GetMask("Block")); }).AddTo(gameObject); //毎フレーム呼ばれる this.UpdateAsObservable().Subscribe(_ => { Action(); }).AddTo(gameObject); } private void Action(){ if (_hit.collider == null) return; var tilePos = blockTilemap.WorldToCell(_hit.point+_hitPos); blockTilemap.SetTile(tilePos, null); //消去 } } }
切り取って説明したいと思います。
Rayを飛ばす
_inputService.GetClickPos.Subscribe(val => { //クリックするか、クリックを離すと反応 if (val == Vector3.zero){ //クリック話したときはマウスの位置が0で来る return; } var position = gameObject.transform.position; if (Camera.main == null) return; Vector3 diff = (Camera.main.ScreenToWorldPoint(val) - position).normalized; _hitPos = diff; _hit = Physics2D.Raycast(position,diff/*方向*/, 1/*距離*/,LayerMask.GetMask("Block")); }).AddTo(gameObject);
キャラクターの位置からクリックした方向までRayを飛ばします。
_hitPos
という変数にどういう角度でRayを飛ばしたか格納しておきます。
Debug.DrawRay((Vector2)position,diff*10, Color.white,1);
をすると以下のようになります。
消去処理
private void Action(){ if (_hit.collider == null) return; var tilePos = blockTilemap.WorldToCell(_hit.point+_hitPos); blockTilemap.SetTile(tilePos, null); //消去 }
今回Actionメソッドは毎フレーム呼ばれています。
Destroy(_hit.collider.gameObject);
としたいところですが、それをするとTileマップ全体が消えてしまいます。
Tilemapのあたった箇所を Vecter3Int
に変換し該当箇所のtileをnullに設定することでTileを消去できます。
_hitPos
を足す理由としては_hit.pointだけだと、Tilemapの表面にあたってしまい、上手く変換できないためです。
_hitPos
という変数を使わずにあたった箇所から一番近いTileを削除というロジックでも良いかも知れません。
以上です。
まとめ
とりあえず、まとめてみました。
一つのマップを拡縮させたり等は現状では厳しそうです。
今後もっと使い安くなっていってほしいです。