【Unity】壁の移動処理で悩む

前回、Rayの設定まで出来たので、
キャラクターの移動処理を考え行きます。

横スクロールのアクションと言う事で、
横に移動しながら、ジャンプ・壁を登る・スライディング・落下
こんな動きを実装したいなと考えています。

そこで、キャラクターを移動させる方法を勉強してみたいと思います。

簡単に移動実装させるならCharacterControllerがあるみたいですが、
折角なんで自前で考えてみようと思います。

ちなみにAndroidアプリを意識しているので、
横移動は、強制的に右移動させる予定です。


まず移動処理なんですが、今まではTransformを使っていました。
しかし、Rigidbodyを積んでるキャラには使えないって言うか向いてません。

なぜかと言うと、Transformは座標移動を強制的に行うので、
物理計算が効きません。

単純に言うと壁をすり抜けたりします。

また、Transform移動中でも物理計算が行われている為、
物理計算の移動と違う移動量だと物理計算が再計算を繰り返す為、
リソースを取るんだとか、
なのでTransformを使う場合はKinematicにする必要があります。

アクション系は、物理計算を使いたいので、
Transformは封印して、違う方法を探す事に…

Google先生に聞いたところ、
Rigidbodyには、直接移動命令を出す事ができるって事です。
これなら物理計算の恩恵を受けながら操作できそうでっすヽ(´▽`)/~♪

まずは、横強制移動の処理を作ります。

移動処理としては、
・Velocity 速度ベクトル
・AddForce 物理力を加える

この辺りを使う事になります。

横移動は等速で行いたいので、力を加えるAddForceだと
スピードがどんどん上がってしまうので、Velocityを使います。

Velocityは、速度ベクトルなんですが、
横だけでなく全方位に計算されます。

例えば落下の場合、落ちていく物は加速していくと思います。
この落下スピードがVelocityです。

ようは、物理計算上で動かされた物のスピードとなります。

なので、下手に触ると動きがおかしくなるのでリファレンスでは、
触らない方がいいとなっています。
ですが、強制的に等速移動させたい場合は、変更しても問題ないと思います。

ただ、注意点も
Velocityで右移動を行っていて、壁にぶつかったとします。
本来は、反動で左に跳ね返る所なんですが、
Velocityに数値を設定すると跳ね返りが起こりません。

跳ね返りの速度なんかもVelocityとなるので、固定すると物理要素がなくなります。
なので、物理的な要素を残しておきたい場合はVelocityを触らない方がいいです。

前置きが長くなったのでスクリプトを少々…
RayTest

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

public class RayTest : MonoBehaviour
{
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()
{
//床に接触してるか判断する
if (underHit)
{
moveSpeed = 5f;
}
}

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

 Linecastの返り値をunderHitに取っています。
}

前回Rayで作ったスクリプトの一部なんですが、
Velocityに速度を代入する事で動かしています。

Y軸は、基本さわらないので、そのままのY軸の速度を代入して
X軸のみ速度を設定してやります。

moveSpeedをマイナスにすると左方向に移動します。

ちなみにRigidbodyの移動の場合、FixedUpdateを使います。
Updateで命令するとフレームのバラつきや飛びで、動きがカクカクになるので、
定期フレームのFixedUpdateを使う必要があるそうです。

横移動は、これで行えるようになったので、
続いて、ジャンプの処理を作ります。

ジャンプは、移動ではなくて上昇と落下をするので、
物理計算が欲しい所です。

なのでVelocityを使うと変な事になるので、AddForceを使います。
上向きに力を加えれば跳ね上がる状態を作れます。
RayTest

private float jumpPow=300f;
private bool jump; //ジャンプフラグ

// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.UpArrow)) jump = true;
}

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

if (jump)
{
androidRB.AddForce(Vector2.up * jumpPow);
jump = false;
}
}

AddForceで上向きに300fの力を加えるって感じです。
これでジャンプするようになりました。

ここまでは、本や説明サイトなどで書かれている事なので、
横移動やジャンプは、そこまで難しくはないと思います。

ここから問題の壁登りです。

アクションなので簡単に登れると面白くないので、
壁からずり落ちないように上昇操作で登って行く、
そんな処理を考えてみようと思います。

イメージとしては、
移動処理⑩.jpg
壁をジャンプする感じです。
RayTest

private bool climb; //壁登りフラグ
private float climbPow = 500f; //壁ジャンプ力

// Update is called once per frame
void Update()
{
//壁に接触したか判断する
if (rightHit)
{
if (Input.GetKeyDown(KeyCode.UpArrow))
climb = true;
}
}

void FixedUpdate()
{
//壁を登らせるか
if (climb)
{
ballRB.AddForce(new Vector2(-1100f, climbPow));
climb = false;
}
}

 Linecastの返り値をrightHitに取っています。
}

通常ジャンプと違って、壁から浮き上がるようなジャンプなので、
左斜め上方向に力を加えてみました。

velocityを右に掛けているので、左斜めに飛び上がると
壁に戻る感じになります。

これで壁が登れるので、テストPLAYでっす。
移動処理①.jpg
球が壁に張り付いて、簡単に登れてしまいます。(T-T) グスッ

壁には摩擦の設定があるので、球が落ちて行かない状態でっす。
これでは、落ちないように操作する意味がないので、
少し摩擦の状態を変更してみようと思います。

壁にマテリアルを設定して様子を見ます。
AssetsフォルダにMaterialsフォルダを作成して、
移動処理②.jpg
右クリックから物理マテリアル2Dを追加します。
追加したマテリアルをwallとでもリネームしておきます。

Inspectorから設定します。
移動処理③.jpg
Frictionが摩擦、Bouncinessが跳ね返りを設定できるので、
Frictionを変更します。

0では、ジャンプ時の掛かりが悪いので0.002くらいに設定します。
移動処理④.jpg
作ったマテリアルをwallのRigidbodyとColliderに設定します。

これで壁に張り付いた時の滑りを確認してみます。
若干、軽いずり落ち方ですがフリック操作を考えると
これくらいでいいかなって感じです。

もう少し早く落ちる感じにしたい場合は、球のRigidbodyにある重力スケールを
大きくすると早くなりそうです。

この辺りは、用途に合わせて変更する必要がありそうです。

これで、壁登りができるので連続壁を設定して動きを見てみます。
移動処理⑤.jpg
壁を越えて、次の壁に張り付くって感じで移動してみます。

落下スピードが付くと壁の摩擦が小さいので一気に下へ落ちます。
ジャンプ処理が追い付かない感じです。

これでは、壁から壁へ飛び移る事ができません。(T-T) グスッ

少しマテリアルを変更する必要があるんですが…

壁を登る際に摩擦を上げるとずり落ちが無くなります。
移動処理⑪.jpg
とりあえず落下時の摩擦を上げる為に新しいマテリアルを用意して、
ContactWallとでもしておきます。
最初に作ったWallマテリアルもNormalWallにリネームします。

球のファーストコンタクト時には、ContactWallをチョイスしておいて、
壁に捕まったらNormalWallに変更すれば、ずり落ちも設定できそうです。

マテリアルを変更するスクリプトを用意します。
FrictionManager

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

public class FrictionManager : MonoBehaviour
{
public PhysicsMaterial2D normalwall;

private Rigidbody2D wallRB;
private BoxCollider2D wallBC;


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

private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ball")
{
wallRB.sharedMaterial = normalwall;
wallBC.sharedMaterial = normalwall;
}
}

CollisionEnterで球が接触したらマテリアルを変更するように設定しました。
このスクリプトをwallにアタッチして、変数にNormalWallをアタッチします。
WallのInspectorから
移動処理⑫.jpg
最初はContactWallを選択しておきます。

最後は”Ball"タグを追加して、球のタグに設定しておきます。
タグ忘れで無反応が一番の難敵でっす。(^.^; オホホホ

準備ができたので、PLAYでっす。

しかし…
衝突した瞬間は、摩擦が効いてブレーキが掛かるのですが、
瞬時にマテリアルが変更されるので、まったく止まりません(T^T) ヒック

ならばとCollisionExitで、一度ジャンプしたらマテリアル変更をしてみたのですが、
壁に着壁した時にずり落ちが起こらないので、そのままの状態で止まっています。

これでは意図した動きにならないので、
着壁後にブレーキが効いた状態を一定時間設定して、
その後にマテリアルを変更してみます。
FrictionManager

private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ball")
{
StartCoroutine("MaterialChange");
}
}

IEnumerator MaterialChange()
{
yield return new WaitForSeconds(0.1f);
wallRB.sharedMaterial = normalwall;
wallBC.sharedMaterial = normalwall;
}

コルーチンで変更処理を0.1秒遅延してみました。

これで着壁時にブレーキが掛かって止まりますが、
0.1秒後にはずり落ち出します。

おおむね思った通りの処理になってきました。
ならばと壁の落差を大きくして挑戦してみます。
移動処理⑦.jpg
三枚目の壁をかなり下げてみました。

ContactWallの摩擦では、耐えきれないので、
新しくStopWallを追加して様子をみます。
移動処理⑧.jpg
無事に受け止めてくれましたヽ(´▽`)/~♪

これで壁渡りなんかもできるので、
いろいろとコースバリエーションがつくれそうです。

しかし気になる点もあります。
落下速度が上がるとジャンプ力が足りないので、その場で壁から離れただけになります。
落下速度に関係なくジャンプの高さを揃えたいのですが、
物理の知識がないので計算ができません(T^T) ヒック

調べてみましたが、こんな状況を設定してないのか落下速度に対しての
ジャンプ力調整が見つかりませんでした。

なので適当に処理を考えてみたいと思います。

落下速度はvelocityで分かるので、速度に合わせてジャンプ力を変えてみようと思います。

まずは、ジャンプ力500fを基準に落下速度からジャンプ力を大きくしてみます。

//壁を登らせるか
if (climb)
{
float wallJump = 0f;

if (ballRB.velocity.y <= 0)
wallJump = Mathf.Abs(ballRB.velocity.y) * 50f + climbPow;

ballRB.AddForce(new Vector2(-1100f, wallJump));
climb = false;
}

落下時のみジャンプ力を調整するので、velocityのマイナスを取り出して、
velocityのスピードを加算してみました。

50倍していますが、この数値を調整すると雰囲気が変わります。

本来は正確な物理計算が必要なんですが、ゲームなんで適当で
勉強不足で計算できません。
(;^o^) \(ToT )あんたほんとにそれでいいの

大体の動きが作れたのでテストPLAYでっす。

なんとか狙いの動きになってきました。

まだまだ微調整が必要ですが、雰囲気は出てきたと思います。
壁登りの処理って、ゲームの内容によっていろいろなんで、
正直、今回の処理は、あまり使えないかなと思っています。

落下と摩擦の参考にでもなれば幸いでっす。(^ё^) ♪♪

少し動きも作れたので、そろそろモーション連動なんかも勉強して行こうと思います。
それではこの辺で ( ^ 0 ^ )/~~~~see you again

この記事へのコメント