ゾンビとUnityとUE4

ゾンビネタとUnity/UE4でのゲーム制作についてつづるブログです。

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Unity 動く床

unity moving floor

きっかけ

Facebook の Unity助け合い所に、動く床が上手く作れないという相談がありました。

動く床は、遥か昔に誕生したスーパーマリオブラザーズに既にあったギミックです。
Unity で作った経験がないため、私は何もアドバイスできませんでした。
せっかくなので、私も作ってみよう。


注意点

とりあえず、Cube か何かで床を作って、transform.Translate() で一定方向に動かしてあげれば、動く床はできそうです。
相談内容にもあったのですが、このままだと1つ問題があります。

キャラクターが動く床に乗った後、床の動きと連動してキャラクターが動いてくれない。
なので、キャラクターを床の進行方向に移動させないと、床だけ動いて行ってしまう。


そういう仕様なら問題はないんですが、そうでないなら困ります。

一番楽そうなのは、キャラクターが床の上に立っていたら、床の進行方向にキャラクターを動かします。
考え方はとてもシンプルです。誰でも考え付く方法です。


まずは結果から



できました。


この処理のキモ

相談者へのアドバイスにもあったのですが、床とキャラを連動させる処理での難しいところは、キャラクターが床の上に立っているかどうかを判定する方法です。

私がとった方法は、床の上面(キャラとの接地面)に、厚さ 0.1 の Box Collider を追加しました。

unity moving floor 2

このボックスコライダーは Is Trigger を ON にしておきます。
床にもともと付いているボックスコライダーの Is Trigger は OFF のままです。
こうすれば、接地面のボックスコライダーだけ OnTriggerEnter() で拾うことができます。

これで、床の上面に接触した物体があるか?は分かります。


掘り下げよう

これだけでは、まだ足りません。

接地面のボックスコライダーは床と同じ広さに設定してあるので、上から落ちてきた物体を検出することはできますが、横からぶつかってきた物体も検出してしまいます。
これを防ぐために、接地面のボックスコライダーを床より少し小さい広さに設定する方法もあるかも知れません。
でも、このコライダーは厚さが 0.1 あって、床の上に少し飛び出ているため、飛んできた物体がボックスコライダーにだけ接触してそのまま飛びぬけて行くことはあり得ます。
それがたったの数フレームしろ、設置用のコライダーをかすった物体を、床の上に乗ったと判断しても良いものでしょうか?

その物体の移動スピードが動く床のスピードよりもずっと速いなら、影響はほとんどないかも知れません。
逆に言うと、わずかであっても、確実に影響は受けます。
これが許容できるなら、今のままの処理で問題ありません。
許容できないなら、ぶつかった物体が床の上に乗っているか?を調べる必要もあります。
今回は必要ないので、その処理はしません。

まだあります。

床と連動して動くのはキャラクターだけでしょうか?

これはゲームの仕様で決める部分ですが、キャラクター以外でも、とにかく床の上に乗っている物体は全て床と連動して動くようにします。
そうなると、床の上に乗った物体を全て保存しておく必要があります。


もういいや

まだ掘り下げる余地はあります。

床の上に乗っている物体に、更に別の物体が乗った場合、その物体は床と連動して動くのか?
更に更に別の物体が乗った場合…。

動く床の上に木箱を乗せて、その木箱の上からジャンプしないと進めない…なんてステージも作る場合があるかも知れません。

ただ、この場合は、動く床で処理するのではなく、木箱が処理するべきでしょう。
木箱の上に乗っている物体が木箱と連動して動く処理を木箱がすれば、動く床が処理する必要はありません。


動く床のスクリプト
using UnityEngine;
using System.Collections.Generic;

public class MovingFloor : MonoBehaviour {

public Vector3 speed = Vector3.zero; //1フレームで動く距離(マイナスは逆方向)
public Vector3 distance = Vector3.zero; //この距離まで動く

//distanceまで動いた後に反対方向へ折り返して動くか?
//falseだとdistanceまで動いたらそこで止る
public bool turn = true;

private Vector3 moved = Vector3.zero; //移動した距離を保持
private List ride = new List(); //床に乗ってるオブジェクト

void Update() {
//床を動かす
float x = speed.x;
float y = speed.y;
float z = speed.z;
if (moved.x >= distance.x) x = 0;
else if (moved.x + speed.x > distance.x) x = distance.x - moved.x;
if (moved.y >= distance.y) y = 0;
else if (moved.y + speed.y > distance.y) y = distance.y - moved.y;
if (moved.z >= distance.z) z = 0;
else if (moved.z + speed.z > distance.z) z = distance.z - moved.z;
transform.Translate(x, y, z);
//動いた距離を保存
moved.x += Mathf.Abs(speed.x);
moved.y += Mathf.Abs(speed.y);
moved.z += Mathf.Abs(speed.z);
//床の上のオブジェクトを床と連動して動かす
foreach (GameObject g in ride) {
Vector3 v = g.transform.position;
g.transform.position = new Vector3(v.x + x, v.y, v.z + z); //yの移動は不要
}
//折り返すか?
if (moved.x >= distance.x && moved.y >= distance.y && moved.z >= distance.z && turn) {
speed *= -1; //逆方向へ動かす
moved = Vector3.zero;
}
}

void OnTriggerEnter(Collider other) {
//床の上に乗ったオブジェクトを保存
ride.Add(other.gameObject);
}

void OnTriggerExit(Collider other) {
//床から離れたので削除
ride.Remove(other.gameObject);
}
}

transform.Translate(x, y, z);
床を動かしている部分です。

g.transform.position = new Vector3(v.x + x, v.y, v.z + z); //yの移動は不要
床の上に乗っている物体を動かしている部分です。
ここでの注意点は transform.Translate() を使うと、物体の角度によって動く方向がバラバラになってしまいます。
そうなると、物体の角度に応じて Translate() に指定する値を調整する…という面倒な処理が必要になってしまいます。
そもそも、transform.position に直接移動先を指定してあげれば、物体の角度を考える必要はありません。

yの移動が不要なのは、床が空に向かって上昇しているなら、床に乗っているオブジェクトは床に押し上げられて、床と一緒に空へ上昇します。
床が地面に向かって下降しているなら、床が下降して物体から離れたとき、床の上に乗っていた物体は重力で床の上に落下します。
だから、y軸の連動は不要です(重力の方向が違う場合は必要になるかも)。


微調整

すっごい滑るよ!

テストプログラムは、ランダムな位置に赤い箱が落ちてきますが、赤い箱が動く床に乗った場合、わずかに滑って少しずつ動き、最終的には床から落ちてしまいます。
どう考えても摩擦が足りてません。

ユニティちゃんはピタっと静止してくれます。
Rigidbody の Freeze Rotation が効いているんでしょうか?

赤い箱は自由に回転して欲しいので、Freeze Rotation は設定できません。
試しに設定してはみたのですが、何故かやっぱり滑ります。

どうしてユニティちゃんは滑らないのか?

とりあえず、今は置いておきます。
今重要なのは摩擦です。

コライダーには Physics Material を設定できるので、超滑らない物理特性マテリアルを作って、動く床のボックスコライダーに設定します。

unity moving floor 3

「ちょっとくらいなら滑ってもいいよ」という場合は、この作業は必要ありません。
この記事に貼り付けてある動画は摩擦なしバージョンなので、床の上の箱がガタガタ動きます。


動く床に Rigidbody を設定しないわけ

Rigidbody を設定すると、物体と接触したときに動いてしまうからです。
ユニティちゃんで走ってぶつかれば、どんなに床の質量を大きくしても、床は押されて動いてしまいます。
ユニティちゃんがぶつかったり、床の上に乗ったり、床の上でジャンプするだけで、動く床の軌道がズレます。
AddForce() が使えなくなるものの、使わなくても動く床の処理はできます。

動く床に物体をぶつけて軌道を変えられる…という仕様なら、Rigidbody を付けた方が楽だと思います。


ダウンロード

今回作ったプロジェクト一式(ユニティちゃんデータが含まれているので重いです) 約160M


ユニティちゃんライセンス

このアセットは、『ユニティちゃんライセンス』で提供されています。
このアセットをご利用される場合は、『キャラクター利用のガイドライン』も併せてご確認ください。


 
スポンサーサイト

- 0 Comments

Add your comment

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。