【Unity】Linecastを使って床や壁を判断する

横スクロールアクションを作ってみると言う事で
AnimationのBone&IKを勉強しました。

モーションも作れるようになってきたので、
いよいよ実際のアクションを作っていこうと思います。

アクションとしては、走る・ジャンプ・スライディング・登るを想定していて、
状況に合わせて、Animationを切り替えていければと考えています。

さて、アクションを切り替えるには?って事なんですが…

当然、床の上なら走るモーションです。
壁が来たら登るモーションに切り替えたいのですが、
どうすればいいかとなります。

ColliderのisTriggerで接触判定を出せば、壁と判断はできそうですが…
そうなんです、isTriggerを設定するとオブジェクトをすり抜けて行くんですよね。
(~ヘ~;)ウーン

なので、isTriggerを設定しなくても壁を認識させる必要があります。

そこで便利な物が用意されています。
レイキャスターです。

Rayとは、日本語なら光線の意味になるんですが、
Unityでは、見えない線と考えてもらうと分かりやすいかと思います。

このRayは、センサーの役割をします。
種類もいろいろあるのですが、

・Raycast 指定座標から指定方向へ一直線の線を飛ばします。
・Linecast 指定座標から指定座標まで線を張ります。
・Boxcast 箱型のRayを指定座標・指定方向・指定距離にセットします。
・Spherecast 球型のRayを指定座標・指定方向・指定距離にセットします。

Rayは、接触したCollider持ちのオブジェクト情報を取得するように出来ているらしいので、
これを使って、壁や床を判断するようにしていきます。

3Dと2Dでは、少し使い方が変わるそうですが、
基本的には、同じ使い方ができるようです。

あと2Dと3Dでは、互換性がないそうで、2Dのオブジェクトに対して
3DのRayは反応しない。逆もまた反応しないそうです。

ざっくりと説明しましたが、Rayについては情報が豊富なんで、
Google先生で検索してもらうと細かい部分は理解できるかと思います。

さて、早速Rayを設定してみようと思います。
使うタイプは、Linecastです。

まずプロジェクトを立ち上げて、オブジェクトを用意します。
Rayテスト①.jpg
2Dスプライトの円を追加します。

このスプライトにRigidbody2DとCircleCollider2Dをアタッチします。
Rayテスト②.jpg
これにRayを設定していきます。

まず、Rayなんですが透明なんでシーンViewでも見る事ができません。
可視化が不可能なんで、位置の確認が出来ないんですよ~(T-T) グスッ

なので、指定座標に線を引くコマンドを設定して位置決めをします。

RayTestスクリプトを用意して、
RayTest

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

public class RayTest : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame
void Update()
{
Debug.DrawLine(始点座標, 終点座標, 表示色);
}
}

Debug.Drawコマンドは、Rayを可視化すると説明されている事がありますが、
あくまで、シーンView上の指定座標に線を引くコマンドなんで、
Rayの座標とデバッグの座標が違えば、意味がなくなります。

なのでケロは、先に線を引いて位置だしをしてから、同じ座標にRayを張るようにしています。

さてRayを張るのに座標が必要と言う事で、
用意した球にRayを張るわけなんですが、球自体動くので、どう設定するか考え処になります。

そこで、ある本に書いてあったLinecastの張り方を使わせてもらおうと思います。
RayTest

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

public class RayTest : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame
void Update()
{
//Linecastの始点を求める
Vector2 underStart = transform.position - (transform.right * 0.5f) - (transform.up * 0.51f);
//Linecastの終点を求める
Vector2 underEnd = transform.position + (transform.right * 0.5f) - (transform.up * 0.51f);

//Linecastの座標デバッグ
Debug.DrawLine(underStart, underEnd, Color.blue);
}
}

このスクリプトを球にアタッチする前提で説明していきます。

Rayの座標は、球の座標から割り出さないといけないのですが、
参考にした本には、ベクトル計算を使うといいと書いてありました。

ベクトルは向きなんですが、transform.rightで右向き1ベクトルとなります。

この数値にオブジェクトの中心点から外周までの距離を掛けると
ベクトルが求められるようなので計算します。

オブジェクトの外周までの距離を調べるには、Colliderのサイズを見ると
分かり易いかと思います。
Rayテスト⑤.jpg
半径が0.5となっています。

なので、transform.right * 0.5fで計算すると球の外周までのベクトルが求められます。

このベクトルを球の座標から引いてやると、左側の座標が計算できます。
Linecastは、球の下に張りたいので、Y軸の座標も設定する必要があります。
なので、transform.up * 0.51fを引く事で下側の座標も設定しています。

これでRayの始点座標が計算できたので、終点座標の計算になります。
終点座標は、球の右側になるのでtransform.right * 0.5fを足してやります。
Y軸の計算は同じになります。

表示座標が計算できたので、Debug.DrawLineに座標を設定してPLAYします。
Rayテスト⑦.jpg
球の下に青い線が表示されました。

この位置にLinecastするわけですが、床と接触させる必要があるので、
Colliderより少し下に位置取りさせています。

これで床とのセンサー位置が決まったので、続いて壁用センサーの位置出しをします。
球は右方向に移動させるので、球の右側に用意します。
RayTest

void Update()
{
//床用のLinecast座標
Vector2 underStart = transform.position - (transform.right * 0.5f) - (transform.up * 0.51f);
Vector2 underEnd = transform.position + (transform.right * 0.5f) - (transform.up * 0.51f);

//壁用のLinecast座標
Vector2 rightStart = transform.position + (transform.right * 0.51f) + (transform.up * 0.5f);
Vector2 rightEnd = transform.position + (transform.right * 0.51f) - (transform.up * 0.5f);

//Linecastの座標デバッグ
Debug.DrawLine(underStart, underEnd, Color.blue);
Debug.DrawLine(rightStart, rightEnd, Color.green);
}

実行すると
Rayテスト⑫.jpg
こんな感じになります。

位置が決まったので、Linecastの設定をします。

Linecastは、特定のレイヤーに属するColliderと接触した時に
反応するそうなので、レイヤーも設定する必要があります。

まず、接触判断する為に床と壁を用意します。
Rayテスト⑧.jpg
2Dオブジェクトのスプライト→正方形で二つのオブジェクトを作ります。

それぞれにRigidbody2DとBoxCollider2Dをアタッチして、
ボディタイプを静的にして固定します。

続いて、レイヤーの編集を行います。
Rayテスト⑨.jpg
レイヤーを追加で、FloorとWallを作ります。
Floorは、元からあったような…

あれば、それを使ってもらってOKでっす。

続いて、追加したオブジェクトのレイヤーをそれぞれに対応したレイヤーに変更します。
Rayテスト⑪.jpg
床ならFloor、壁ならWallを選択。

準備完了でっす。スクリプトを作成します。
RayTest

public class RayTest : MonoBehaviour
{
public LayerMask floorLayer; //床レイヤー
public LayerMask wallLayer; //壁レイヤー


// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame
void Update()
{
//床用のLinecast座標
Vector2 underStart = transform.position - (transform.right * 0.5f) - (transform.up * 0.51f);
Vector2 underEnd = transform.position + (transform.right * 0.5f) - (transform.up * 0.51f);

//壁用のLinecast座標
Vector2 rightStart = transform.position + (transform.right * 0.51f) + (transform.up * 0.5f);
Vector2 rightEnd = transform.position + (transform.right * 0.51f) - (transform.up * 0.5f);

//Linecastを張る
RaycastHit2D underHit = Physics2D.Linecast(underStart, underEnd, floorLayer);
RaycastHit2D rightHit = Physics2D.Linecast(rightStart, rightEnd, wallLayer);

//床に接触してるか判断する
if (underHit) Debug.Log("着地");

//Linecastの座標デバッグ
Debug.DrawLine(underStart, underEnd, Color.blue);
Debug.DrawLine(rightStart, rightEnd, Color.green);
}
}

Physics2D.Linecast(始点座標, 終点座標, 対象レイヤーマスク)
このコマンドで張る事ができます。

指定したレイヤーマスクに接触するとtrueが返ってきます。
なので、レイヤーマスクを設定しておく必要があります。

publicな変数でレイヤーマスクを用意したので、RayTestコンポーネントの
Rayテスト⑬.jpg
レイヤーを設定しておきます。

これで準備が整いました。
球を空中に移動させてPLAYしてみます。
床に着地すれば、デバッグが表示されるはずです。
Rayテスト⑭.jpg
無事に表示できました。ヽ(´▽`)/~♪

上手く反応してくれたので、少し動きを追加してみます。
ついでに壁の接触判定も見たいので、スクリプトを少し変更します。
RayTest

public class RayTest : MonoBehaviour
{
public LayerMask floorLayer; //床レイヤー
public LayerMask wallLayer; //壁レイヤー

private Rigidbody2D ballRB; //球のRigidbody
private float moveSpeed = 0.0f; //球の移動速度


// Start is called before the first frame update
void Start()
{
ballRB = GetComponent<Rigidbody2D>();
}

// Update is called once per frame
void Update()
{
//床用のLinecast座標
Vector2 underStart = transform.position - (transform.right * 0.5f) - (transform.up * 0.51f);
Vector2 underEnd = transform.position + (transform.right * 0.5f) - (transform.up * 0.51f);

//壁用のLinecast座標
Vector2 rightStart = transform.position + (transform.right * 0.51f) + (transform.up * 0.5f);
Vector2 rightEnd = transform.position + (transform.right * 0.51f) - (transform.up * 0.5f);

//Linecastを張る
RaycastHit2D underHit = Physics2D.Linecast(underStart, underEnd, floorLayer);
RaycastHit2D rightHit = Physics2D.Linecast(rightStart, rightEnd, wallLayer);

//床に接触してるか判断する
if (underHit) moveSpeed = 2f;

//壁に接触したか判断する
if (rightHit) Debug.Log("動けない");

//Linecastの座標デバッグ
Debug.DrawLine(underStart, underEnd, Color.blue);
Debug.DrawLine(rightStart, rightEnd, Color.green);
}

void FixedUpdate()
{
ballRB.velocity = new Vector2(moveSpeed, ballRB.velocity.y);
}
}

床に着地したら右方向へ移動していくようにしました。

このままPLAYすると球が回転するので、
Rayテスト⑮.jpg
z軸を固定します。

準備ができたのでPLAYしてみます。

無事に両方のRayが反応してくれてます。

これで床や壁の判断ができるので、移動処理が作って行けそうです。

最後にLinecastについて少し書いておきます。
Physics2D.Linecast(始点座標, 終点座標, 対象レイヤーマスク)は、
bool型を返すようになっています。
なので、スクリプトでは、RaycastHit2D underHitの変数を設定していましたが、
bool型でも受ける事ができます。

返り値がbool型と言う事で、
if(Physics2D.Linecast(始点座標, 終点座標, 対象レイヤーマスク))といきなり
if文に突っ込む事もできます。

この辺りは、スクリプトを組むときの手間で変えてもいいかと思います。

ジャンプの設定時にキーを押したらジャンプする処理だと、
空中でもジャンプができてしまいます。

キーを押し続ければ、ずっとジャンプし続けます。

なので、床に着地してる時だけジャンプできるようにするなら
必須になるかと思います。

また、Raycastの情報は多いのですが、Linecastは意外と見つからないので、
参考になれば幸いかと思います。

Rayが設定できたので、次回からはアクションとモーションの連動あたりを
調整できればと思います。

それでは、今回はこのへんで ( ^ 0 ^ )/~~~~see you again


この記事へのコメント