//tips
//Physicsを使用しているオブジェクトを飛ばす
Physicsを使用したオブジェクトをAddForceで飛ばす方法も確認する。
public class IgaguriController :MonoBehavior
{
public void Shoot(Vector3 dir)
{
GetComponent<Rigidbody>().AddForce(dir);
}
void Start()
{
Shoot(new Vector3(0,200,2000));
}
}
ここではShootメソッドを作成しており、Shoot()とし、Vector3の座標をメソッドの引数に取ることで、その座標がAddForceの力に変換されて、オブジェクトを飛ばす形になっている。
//物体衝突時点でオブジェクトにかかる力を無効化する
的当てでは的に当てたオブジェクトを的に刺さったままにしておきたいので、投げるオブジェクトの力が的に当たった時点で消滅し、刺さったように表現されるようにする。
双方ともにColliderをアタッチしておけば、双方の接触時にOnCollisionEnterメソッドを投擲物にから呼ぶことができ、そのトリガーを使って、物体にかかる力を無効化する。
void onCollisionEnter(Collision other)
{
GetComponent<Rigidbody>().isKinematic = true;
}
オブジェクトに働く力を無効化するために接触時「isKinematic」をtrueにする操作を加える。
//パーティクル粒子を利用したエフェクトの使用
粒子を使用したエフェクトはUnityでは比較的簡単に使用することができ、付加したいオブジェクトのAddComponentからEffects→Particle Systemを選択する。
パーティクルの表現をオブジェクトが的に接触した瞬間弾けるエフェクトになるまで整えるため、パラメータを調整する。
パーティクルの形状を変更したい場合、Rendererをクリックし、その中にあるMaterialをクリックする。
Select Materialウィンドウが開くので、Default-Particleを選択することで、白い粒形状の使いやすいエフェクトに変わる。
パーティクルの放出形状を球状に広がるように変えたい場合は、Particle SystemのShapeからSphereに変更する。ここでの放出開始時の半径はRadiusの値で調整できる。
現状では、常時大量のエフェクトが放出されている状態になっているので、生成パターンを変化させるためにEmissionの中の、マイフレーム生成されるパーティクル数Rateと指定した時間に生成するパーティクル数を表すBurstsを操作する。
EmissionのRate over Timeを0にし、Burstsの+ボタンからTime「0」、Count「50」に設定する。
Rate over Time モードに設定されていたら、親オブジェクトがどのように動いているかに関係無く毎秒ごとに設定した数のパーティクルが放射される。
Burstsのタイムは、バーストを放出する時間を指すので、接触の一瞬にエフェクトを表示すれば良いので今回は0で、Countは放出されるパーティクルの数である。
パーティクルの再生時間が長い場合は、Particle Systemのエフェクトの再生時間Durationとパーティクルの表示時間Start Lifetimeで調整する。
パーティクルの粒子のサイズを時間経過で小さくするにはsize over Lifetimeにチェックし、Sizeの減衰カーブを選択する。
最後に、LoopingとPlayOnAwakeのチェックも外す。
これにより意図するパーティクルができたので、あとはこのパーティクルの再生タイミングをプログラミングに書き込めば良い。
public class IgaguriController :MonoBehavior
{
public void Shoot(Vector3 dir)
{
GetComponent<Rigidbody>().AddForce(dir);
}
void Start()
{
Shoot(new Vector3(0,200,2000));
}
void OnCollisionEnter(Collision other)
{
GetComponent<Rigidbody>().isKinematic = true;
GetComponent<ParticleSystem>().Play();
}
}
的に当たったオブジェクトが静止した瞬間、パーティクルを再生するようにしている。
GetComponent<ParticleSystem>().Play();で再生できる。
//タップした場所をスクリーン座標からワールド座標に変換し、反映する
3Dを採用した際に、スマホのスクリーンという2Dの情報しか得られないものから、その情報を3Dの座標に置き換える方法を確認する。
例えば、的に向かって、イガグリを投げる場合、的の近くをタップすることになるが、そのタップした座標Input.mousePositionメソッドで得られたものは、スクリーン座標系で3Dのワールド座標系に変換する必要がある。
この座標を変換する際に、ScreenPointToRayというメソッドを使用することになる。
原理としては、カメラを基準として、タップしたスクリーン座標へのベクトルに変換することで3次元に拡張させる。つまり、カメラとの距離も加味されることで三次元に直している。
カメラの位置からタップした位置へのベクトルという力の与え方をする。
void Update()
{
if(Input.GetMouseButtonDown(0))
{
GameObject igaguri = Instantiate(igaguriPrefab) as GamaObject;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Vector3 worldDir = ray.direction
igaguri.GetComponent<IgaguriController>().Shoot(worldDir.normalized*2000);
}
}
ScreenPointToRay(Input.mousePosition)でタップ座標をScreenPointToRayメソッドに渡してベクトルであるRay光線クラスを返している。
Rayは光線であり、光源の座標originと光線の方向directionをメンバ変数に持っており、Colliderがアタッチされたオブジェクトとの接触を感知し、当たり反応を返すことができる。
今回はoriginがMain Cameraの座標、directionがカメラからタップされた位置へ向かう座標になる。
なので、ray.directionを計算しやすいように3次元ベクトルworldDir移し替えて、Shoot()メソッドの引数に入れている。
引数の中にあるnormalizedとは、worldDirのベクトルの長さを1とおくことができるもので、これにより掛け算を行いやすくし、力をかけやすくしている。
//ライトを設定して影をつける
光源であるライトを利用することでオブジェクトの落下位置などに影を追加することができる。
ライトには種類があり、
Directional Light:太陽光のように並行に光を放つライトで、光源から離れても光の強さは変わらない。
Point Light:全方向に同時に光を放つライトで、光源から離れると光の強さは減衰する。
Spot Light:特定の方向に放射状の光を放ち、光源から離れると光の強さは減衰する。
あとはArea Lightというベイトと言う特殊な処理をするときのみ使えるライトがある。
ライトがシーン上に配置されていれば、影の位置や見え方はUnityが自動的に計算して表示してくれる。
3DのプロジェクトではDirectional Lightが初期配置されており、影も自動的に表示される。
影を使ってアイテムの落下位置を表示したい場合は、光源の位置が重要になり、アイテムの真上に光源を設定することで、アイテムの真下に影が配置される。
Directional Lightを真下に向けたいときにはTransformのRotationを「90,0,0」にすることでライトを下に向けることができる。
ライトの光の強さはインスペクターのLightの項目のIntensityで設定する。
影の輪郭にも拘りたい場合はEditのProjectSettingsからQualityを選択し、ShadowDistanceの数値を変更する。
//タップした座標でオブジェクトを動かす
3×3のマスの上のオブジェクトをタップしたマスに動かす。
正方形の中心を軸の交差点とした場合、x座標で考えると、左のマスから順に
左マス:-1.5 <=x<= -0.5
中央マス:-0.5 <=x<= 0.5
右マス:0.5 <=x<= 1.5
と範囲が分けられる。
これを利用して、それらの範囲内にタップ時の座標が収まっているときに、座標小数点を四捨五入したxの値に移動させる処理をすれば良い。
左マスのx=-1へ移動:-1.5 <=x<= -0.5
中央マスのx= 0へ移動:-0.5 <=x<= 0.5
右マスのx= 1へ移動:0.5 <=x<= 1.5
四捨五入のメソッドはMathf.RoundToIntを用いる。
void Update()
{
if(Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray, out hit, Mathf.Infinity ))
{
float x = Mathf.RoundToInt(hit.point.x);
float z = Mathf.RoundToInt(hit.point.z);
transform.position = new Vector(x, 0, z);
}
}
}
タップされた座標をRay ray = Camera.main.ScreenPointToRay(Input.mousePosition);でベクトルとして取得する。
RaycastHitはレイキャストによる情報を得るための構造体でヒット地点の座標を取得して入れたいため変数で宣言する。
その後に、Raycast メソッドを使い、
Physics.Raycast(①Ray ray, ②out RaycasatHit hit, ③float maxDistance, ④int layerMask)
①どのRayについて判定するか
②RaycastHitに格納されている衝突したオブジェクトの座標情報を得る
③Rayの長さ(何も指定しないと、Mathf.Infinity(無限)になる)
④Rayが衝突するレイヤー(何も指定しないと、「Ignore Raycast」レイヤー以外が判定対象レイヤーになる)
を利用する。
このメソッドはorigin = directionの時にtrueとして実行されるbool型である。
originをカメラからのベクトルrayとし、directionをhit地点の座標、ただこの時点ではhitの座標はRaycastHitのメソッドの中で計算されており、その構造体の処理結果を反映して欲しいのでoutとつけてメソッドの中で値を詰めて返してもらっている。
ここは少々わかりづらいがRaycastHitの裏の処理の計算を表に出してあげるようにoutを使っていると考えれば良い。
最後にMathf.RoundToIntを使ってRaycastHit構造体として取得できた座標のポジションの小数点を四捨五入して、オブジェクトの移動場所としている。
https://docs.unity3d.com/Manual/CameraRays.html
https://docs.unity3d.com/ScriptReference/Physics.Raycast.html