Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9304

【Unity(C#)】3次元空間にオブジェクトを重なりなく生成(表示)する方法

$
0
0

デモ

まずはデモです。

特定の範囲内にオブジェクトがランダムかつ、
メッシュ同士が重なって表示されることなく出現します。

GunShot3.gif

生成位置(表示位置)をランダムにすることは簡単なのですが、
立体的なオブジェクトをメッシュの重なりなく表示するのは
少々入り組んだロジックを考える必要がありましたのでメモしときます。

コード

まずはコード全文です。
今回は最初からHierarchyにオブジェクトを非表示で配置しておきました。
GenerateRandom.PNG

usingSystem;usingSystem.Collections.Generic;usingCysharp.Threading.Tasks;usingUnityEngine;usingRandom=UnityEngine.Random;/// <summary>/// ブロックをランダムな位置にランダムなタイミングで表示/// 位置被りナシ/// </summary>publicclassActivateBlock:MonoBehaviour{[SerializeField]privateGameObject_blockParent;[SerializeField]privateBoxCollider_blockBoxCollider;privateconstfloat_MIN_INTERVAL_VALUE=0.5f;privateconstfloat_MAX_INTERVAL_VALUE=2.0f;privateconstfloat_MIN_X_VALUE=-1.0f;privateconstfloat_MAX_X_VALUE=1.0f;privateconstfloat_MIN_Y_VALUE=0.5f;privateconstfloat_MAX_Y_VALUE=2.0f;privateconstfloat_MIN_Z_VALUE=-1.0f;privateconstfloat_MAX_Z_VALUE=1.0f;privatebool_isGameStart=true;privatebool_isSetablePositionX;privatebool_isSetablePositionY;privatebool_isSetablePositionZ;privateint_randomNumber;privatefloat_randomInterval;privatefloat_randomValueX;privatefloat_randomValueY;privatefloat_randomValueZ;privatereadonlyList<Vector3>_usePositionList=newList<Vector3>();voidStart(){//空同然だけどリスト作っとくforeach(Transformchildin_blockParent.transform){_usePositionList.Add(child.position);}DelayInitBlock();}privateasyncUniTaskDelayInitBlock(){while(_isGameStart){//ランダムな値_randomNumber=Random.Range(0,_blockParent.transform.childCount);_randomInterval=Random.Range(_MIN_INTERVAL_VALUE,_MAX_INTERVAL_VALUE);_randomValueX=Random.Range(_MIN_X_VALUE,_MAX_X_VALUE);_randomValueY=Random.Range(_MIN_Y_VALUE,_MAX_Y_VALUE);_randomValueZ=Random.Range(_MIN_Z_VALUE,_MAX_Z_VALUE);//選ばれたブロックの位置Vector3selectedBlockPosition=_blockParent.transform.GetChild(_randomNumber).gameObject.transform.position;//選ばれたブロックの位置は比較対象から一旦削除_usePositionList.Remove(selectedBlockPosition);//現在使用中のポジションのリストから今利用検討中のポジションが利用可能か判定foreach(Vector3positionin_usePositionList){//表示位置被りがないかオブジェクトの大きさでチェック_isSetablePositionX=Mathf.Abs(position.x-_randomValueX)>_blockBoxCollider.bounds.size.x;_isSetablePositionY=Mathf.Abs(position.y-_randomValueY)>_blockBoxCollider.bounds.size.y;_isSetablePositionZ=Mathf.Abs(position.z-_randomValueZ)>_blockBoxCollider.bounds.size.z;//座標のうち、全ての軸で被っていたら置けないのでやり直しif(!_isSetablePositionX&&!_isSetablePositionY&&!_isSetablePositionZ){_usePositionList.Add(selectedBlockPosition);break;}}//位置被りがどれか1つの軸で無ければ実行するif(_isSetablePositionX||_isSetablePositionY||_isSetablePositionZ){Vector3randomPosition=newVector3(_randomValueX,_randomValueY,_randomValueZ);//ランダムな間隔でDelayawaitUniTask.Delay(TimeSpan.FromSeconds(_randomInterval));_blockParent.transform.GetChild(_randomNumber).gameObject.transform.position=randomPosition;//新しい使用中のポジションをリストに追加_usePositionList.Add(randomPosition);}}}}

流れとしては下記です。
⓪"オブジェクトの座標を保存しておくリスト"を作成
①ランダムな座標を作成
②ランダムにオブジェクトを選択
③選んだオブジェクトの座標を"オブジェクトの座標を保存しておくリスト"の中から削除
④XYZの3軸それぞれで位置が被っていないか比較
⑤1軸でも座標が被っていないなら重なりなく表示可能なので表示する
⑥"オブジェクトの座標を保存しておくリスト"に今回利用した座標を追加

Bounds

オブジェクトの座標同士を比較するだけでは、オブジェクト同士の重なりを防ぐことができません。
ですので、今回はBoundsと呼ばれるオブジェクトの領域を指すもの利用することにしました。

BoxCollider.bounds.sizeでオブジェクトの持つコライダーの大きさが取得できます。

このオブジェクトの大きさを利用して、
新しく配置しようとしている座標既に利用済みの座標と比べてBounds一個分離れているか
どうかを判定すれば、位置被りをなくすロジックを組むことができます。

//表示位置被りがないかオブジェクトの大きさでチェック_isSetablePositionX=Mathf.Abs(現在利用中のX座標-ランダムに生成したX座標)>_blockBoxCollider.bounds.size.x;_isSetablePositionY=Mathf.Abs(現在利用中のY座標-ランダムに生成したY座標)>_blockBoxCollider.bounds.size.y;_isSetablePositionZ=Mathf.Abs(現在利用中のZ座標-ランダムに生成したZ座標)>_blockBoxCollider.bounds.size.z;

まとめ

結構必要となるロジックだと思いましたが、意外と実装録を見つけられませんでした。
もっとスマートな方法があれば教えてください。


Viewing all articles
Browse latest Browse all 9304

Trending Articles