2022年12月2日6 分

【Unity】回転する地面に重力(引力)を持たせ、地面と垂直にジャンプさせる方法

最終更新: 2022年12月3日

今回のリクエストは、タイトル通り下の要件です。

  1. 回転する地面にプレイヤーが乗った時、その地面に垂直に立つことができる

  2. ジャンプをすると地面に対して垂直に飛び上がる

  3. ジャンプ後、指定秒数、または指定の距離だけ離れると、通常の重力に戻る

完成イメージは下のような感じです。

環境構築

地面

地面は 3DObject→Cube から作成しています。
 
1,tag:「Ground」という名前のタグを作成してセットしておきます。
 
2,BoxCollider2D:特にプロパティの変更は必要ありません。
 
3,Material:地面の色がわかるようなマテリアルをセットしておきましょう。

回転ブロック

プレイヤーは回転ブロックに乗った時、触れている面が表面か裏面かを判定して、接触面に対し垂直に立たなければなりません。地面が回転しなければ、プレイヤーとの位置関係で表面か裏面かを判定できるのですが、今回は計算で表裏を判定するのが厄介なので、回転ブロックにセンサーを付けておきます。

下の図のように3つのCubeを使って 「RotBlock」という実態を持つ回転ブロックと、その子要素となる表面センサー「Omote」、裏面センサー「Ura」を作成します。

RotBlock

tag:RotBlock というタグを作成しセットします。
 
大きさ:任意
 
BoxCollider2Dコンポーネント:プロパティはそのままでOK
 
Rigidbody2Dコンポーネント
 
 BodyType ⇒ Kinematic スクリプトファイルの影響のみ受ける
 
       (プレイヤーが乗った際に回転させたい場合は、Dinamicのまま)
 
 Material ⇒ Friction を30程度にした2D物理マテリアルを追加。
 
 Constrainse ⇒ FreezePosition X Y にチェック、重心の座標を中心に回転。

回転の動きを管理するスクリプトを追加しておきます。
 
スクリプトファイル「RotBlocks」を作成し以下のコードを記述してください。

Rigidbody2D rb; //Rigidbody2Dコンポーネント
 
public float rotSpeed; //回転速度
 

 
void Start()
 
{
 
rb = GetComponent<Rigidbody2D>();
 
}
 

 
void Update()
 
{
 
rb.angularVelocity = rotSpeed; //回転の動き
 
}


 
1つ1つの回転ブロックが任意の速さで回転するように、変数:rotSpeed はパブリック変数にして、Inspectorから指定できるようにしておきます。
 
Rigidbody2D.angularVelocity で常に指定した回転速度を与え、ブロックを回します。

作成したスクリプトファイル「RotBlocks」はRotBlockにアタッチします。
 
表示された変数「Rot Speed」には任意の値を入れておきます。
 
サンプルでは30を指定しました。マイナスの値を指定すると逆回転します。

Omote と Ura

続いて RotBlock の子要素の「Omote」を作成します。
 
Transformコンポーネント
 
Position:y を0.5程度にして親要素(RotBlock)から少しはみ出すような位置関係にします。
 
MeshRendererコンポーネント
 
チェックを外し無効に、見えないようにします。
 
BoxCollider2Dコンポーネント
 
Is Trigger:侵入判定にします。

裏面のセンサーも同様に作成します。

これで回転ブロックは完成です。

プレハブ化して任意の場所に配置、大きさや回転速度を指定しておきましょう。

プレイヤー

コンポーネント

キャラクターやアニメーションの作成は省略します。各自任意のキャラクターを指定しておいてください。必須となるのは次の2つのコンポーネントです。
 
1,Collider2D コンポーネント(Box、Capsule、Circle なんでも大丈夫です)
 
2,Rigidbody2D コンポーネント

スクリプトファイル

「PlayerController」などの任意の名前のスクリプトファイルを作成し、Player にアタッチします。

PlayerController に次のコードを記述します。
 
まずは変数の宣言と、Start()関数内での初期化です。
 
ポイントは次の3つ。
 
1、回転ブロックに乗っているときは無重力にする必要があるので、その判定フラグ「isGravity」を作成
 
2、乗っている回転ブロックを知る必要があるので、GameObject 型の変数「Target」を作成
 
3、乗っているブロックが表化裏かを判定する変数「UraOmote」を作成

float speed; //プレイヤーの速度
 
float jumpPower; //プレイヤーのジャンプ力
 
int jumpCount; //空中ジャンプを制限するための変数
 
bool isGravity = false; //無重力判定
 
bool UraOmote; //表面か裏面か?
 
GameObject target = null; //ターゲット(回転ブロック)
 
Rigidbody2D rb; //Rigidbodyコンポーネント
 

 
void Start()
 
{
 
speed = 3;
 
jumpPower = 350;
 
rb = GetComponent<Rigidbody2D>();
 
}

続いて移動とジャンプの動きを作っておきます。
 
ジャンプは2段ジャンプまでOKにしておきます。変数「JumpCount」でジャンプの数を数えますが、「JumpCount」が0か1、つまり2未満の時はジャンプができるようにジャンプの条件に指定します。

void move() //左右の移動
 
{
 
if (Input.GetKey(KeyCode.RightArrow))
 
{
 
rb.velocity = new Vector2(speed, rb.velocity.y);
 
transform.localScale = new Vector3(1, 1, 1);
 
}
 
if (Input.GetKey(KeyCode.LeftArrow))
 
{
 
rb.velocity = new Vector2(- speed, rb.velocity.y);
 
transform.localScale = new Vector3(-1, 1, 1);
 
}
 
}
 

 
void Jump() //ジャンプ
 
{
 

 
//ジャンプカウントが2未満で上向き矢印キーた押されたら
 
if (Input.GetKeyDown(KeyCode.UpArrow) && jumpCount < 2)
 
{
 

 
rb.AddForce(transform.up * jumpPower); //上向きの力を加える
 
jumpCount++; //JumpCountをインクリメント
 
}
 
}
 

 
void Update()
 
{
 
move();
 
Jump();
 
}

続いて当たり判定に関するコードを追加します。
 
先に、回転ブロックから離れたときに発動するコルーチンを用意しておきます。
 
このコルーチンには何秒後に発動するかの引数を与え、任意の値で元に戻る動きを指定できるようにしておきます。

//回転ブロックから離れたら、0.5秒後に重力発生プログラム
 
IEnumerator ExitBlock(float s)
 
{
 
yield return new WaitForSeconds(s); //引数秒待って
 
isGravity = false; //無重力解除
 
target = null; //ターゲットをnullに
 
rb.gravityScale = 2; //重力を初期状態にもどす
 
transform.rotation = Quaternion.Euler(0, 0, 0); //向きを通常の上向きに
 
}
 

 
void OnCollisionEnter2D(Collision2D collision) //当たり判定
 
{
 
//地面か回転ブロックに触れたら
 
if (collision.gameObject.tag == "Ground"|| collision.gameObject.tag == "RotBlock")
 
{
 
jumpCount = 0; //ジャンプカウントをリセット
 

 
}
 
if(collision.gameObject.tag == "RotBlock") //回転ブロック乗ったら
 
{
 
isGravity = true; //無重力状態に
 
target = collision.gameObject; //ターゲットを乗っている回転ブロック
 
}
 
}
 

 
void OnCollisionExit2D(Collision2D collision) //離脱判定
 
{
 
if (collision.gameObject.tag == "RotBlock") //回転ブロックから離れたら
 
{
 
StartCoroutine(ExitBlock(0.5f)); //指定時間秒後にコルーチン発動
 
}
 
}
 

 
void OnTriggerStay2D(Collider2D collision) //裏表侵入判定
 
{
 

 
if (collision.gameObject.name == "Omote") //表面に触れていたら
 
{
 
UraOmote = true; //UraOmoteをtrue(表)に
 
}
 
if (collision.gameObject.name == "Ura") //裏面に触れていたら
 
{
 
UraOmote = false; //UraOmoteをfalse(裏)に
 
}
 
}

最後は取得した裏表判定に従って、回転ブロックに対して垂直に立つという動きを追加します。

void SetDirection() //向き決定関数
 
{
 
if (isGravity) //無重力状態なら
 
{
 
rb.gravityScale = 0; //重力を0に
 
if (UraOmote) //表面に乗っていたら
 
{
 
//向きをブロックの向きに
 
transform.rotation = target.transform.rotation;
 

 
}
 
else //裏面に乗っていたら
 
{
 
//向きを裏面ブロックの向き(ブロックに対し180度)
 
transform.eulerAngles = new Vector3(0, 0, target.transform.eulerAngles.z + 180);
 
}
 

 
}
 
}
 

 
void Update(
 
{
 
move(); //記述済み
 
Jump(); //記述済み
 
SetDirection(); //向き決定関数の発動
 
}

これでプレイヤーの基本的な動きは完成です。
 
ではスクリプトファイルがプレイヤーにアタッチされているのを確認して、地面にくっつくか試してみてください。
 
下のがイメージの様に、回転ブロックの表でも裏でも垂直に立ち、垂直にジャンプすることができていればOKです。

ファイブボックスでは実際に通って頂いての授業の他、ちょっとしたお困りごとに対するオンライン授業やオンラインサポートも行っております。

お困りごとのある方、ご興味がある方は、ぜひお問い合わせください。

お問い合わせは こちら から。

体験授業のお申込みは こちら から。

    3