【Unity/2D】任意の地点を中心にキャラクターへ重力を掛ける

円の内周を移動するプレイヤーと、外周を移動する敵キャラ

任意の地点に重力ポイントを設置。
円の内周を移動するプレイヤーと、外周を移動する敵キャラを実装。
横スクロールで有りながら、見下ろし型ゲームのように、画面を上まで余す所なく使える。

サンプル動画

プレイヤーのジャンプ力が高過ぎて、重力反転のようになった。
面白いと思ったので、そのままにしてみた。

前提

  • キャラクター画像は、各自任意のアセットで。
    (この例では、無料アセットの[Ansimuz] Sunny Landを使用)
  • プロジェクト作成時に、2Dテンプレートを選択。
    (細かい所の設定が2D向けになり、2D用の機能が導入される)

ざっくりな解説

  1. 地面(フレーム)作成。
    1. Sprite RendererのSpriteに画像を設定。
      フレーム画像(512 x 512)
    2. 小さいので、TransformのScaleを(2, 2, 2)に変更。
    3. Polygon Collider 2Dをアタッチ。
  2. プレイヤー作成。
    1. Sprite RendererのSpriteに画像を設定。
    2. Capsule Collider 2Dをアタッチ。
      1. 画像に合わせて、位置とサイズを調整。
    3. Rigidbody 2Dをアタッチ。
      1. Gravity Scaleを0に設定(基本の重力の無効化)。
      2. ConstraintsでRotationを固定(転倒防止)。
    4. 新規スクリプトに、後述のPlayer用コードを記述。
  3. 敵キャラ作成。
    1. Sprite RendererのSpriteに画像を設定。
    2. Capsule Collider 2Dをアタッチ。
      1. 画像に合わせて、位置とサイズを調整。
    3. Rigidbody 2Dをアタッチ。
      1. Gravity Scaleを0に設定(基本の重力の無効化)。
      2. ConstraintsでRotationを固定(転倒防止)。
    4. 新規スクリプトに、後述のEnemy用コードを記述。

詳細な解説

地面(フレーム)の画像をダウンロード

フレーム画像(512 x 512)

  • ライセンス:CC0
  • 圧縮済み

地面(フレーム)を作成&設定

  1. 上部メニュー -> GameObject -> 2D Object -> Spriteを選択、Frameと命名。
    1. インスペクター -> Transformコンポーネント
      1. Position:(0, 0, 0)に変更。
      2. Scale:(2, 2, 2)に変更。
    2. インスペクター -> Sprite Rendererコンポーネント
      1. Sprite:インポートした画像”Frame512″を選択。
      2. インスペクター -> Add Component -> Polygon Collider 2Dをアタッチ。

プレイヤーを作成&設定

  1. 上部メニュー -> GameObject -> 2D Object -> Spriteを選択、Playerと命名。
    1. インスペクター -> Transformコンポーネント。
      1. Position:(0, 0, 0)に変更。
    2. インスペクター -> Add Component -> Capsule Collider 2Dをアタッチ。
      1. Offset:ズラす値。キャラクターに合わせて調整。
      2. Size:キャラクターの大凡のサイズ。
    3. インスペクター -> Add Component -> Rigidbody 2D
      1. Gravity Scale:0
      2. Constraints -> 左の三角をクリック -> Freeze Rotation -> Z:有効化
    4. インスペクター -> Add Component -> 新規スクリプトを作成&アタッチ。

敵キャラを作成&設定

前項の「プレイヤーを作成&設定」と同じ手順を繰り返す。
オブジェクトや、スクリプトの命名のみ「Enemy」に変更。

プレイヤーのコード

Playerにアタッチしたスクリプトへ、以下を記述。


//使わなかった為、一応コメントアウト。
//using System.Collections;
//using System.Collections.Generic;
using UnityEngine;


public class Player : MonoBehaviour
{

//キャラクターのRigidbody2Dをインスペクターから紐付けしておく。
	[SerializeField]
	Rigidbody2D rb2d;

//キャラクター画像の表示用コンポーネントをインスペクターから紐付けしておく。
	[SerializeField]
	SpriteRenderer sr;


//ジャンプ時に掛ける力。任意の値。
	float jumpForce = 1000.0f;

//移動時に掛ける力。任意の値。
	float moveForce = 30.0f;

//スピード制限。任意の値。余り高過ぎると、ちょっとバグる。
//「Rigidbody2D.velocityのsqrMagnitudeが、speedLimit以上出ていた場合は、AddForceしない」という仕様なので、スピードの上限としては厳密に守られていない。
	float speedLimit = 100.0f;
	
//移動キーの入力値。
	float moveDirection;

//重力の中心となる任意の座標。
//ロングステージの任意の地点に重力ポイントを設置したい場合は、シングルトンに座標を持たせておいて適宜、読み込むと良い。
	Vector3 gravityPoint = Vector3.zero;

//通常の重力値。変えないなら定数にしていても良い。
	float defaultGravity = -9.81f;

//Physics2D.gravityを使うと共通の重力値になってしまう為、キャラクターそれぞれに個別の重力を持たせている。
	Vector3 gravity;



	void FixedUpdate()
	{
//キャラクターの相対的な上を変更、そこを基準としてrotationを決定している。内部で自動的にノーマライズされる。
		transform.up = gravityPoint - transform.position;


//十字キーが入力されていたら、キャラクター画像の向きを変更&キャラクターを移動。
		if (moveDirection != 0) {

//本当は、相対的なvelocityのX値のみで判定したかったが、色々やっても抽出出来なかった為、妥協。
//ジャンプや落下のスピードも判定に含まれてしまう。
			if (rb2d.velocity.sqrMagnitude < speedLimit) {

//移動の入力値に応じて、キャラクター画像の向きを変更。
				sr.flipX = moveDirection < 0;

//キャラクターと相対的に移動。
				rb2d.AddForce(transform.right * moveDirection * moveForce, ForceMode2D.Force);
			}
		}


//(先に重力に合わせた向きにしているので、)向きに合わせて重力を設定。
		gravity = transform.up * defaultGravity;
		rb2d.AddForce(gravity, ForceMode2D.Force);
	}

//入力の受付はFixedUpdate内でやると、取得しそびれが発生した。
	void Update()
	{
//キーボードの十字キー左右の入力を-1.0f ~ 1.0fまでの値として取得。
		moveDirection = Input.GetAxis("Horizontal");

//Zキーを押すと、適当にジャンプする。ジャンプ力が高過ぎて重力反転みたいな感じになった。
		if (Input.GetKeyDown("z")) {
//相対的な上にジャンプ。
			rb2d.AddForce(transform.up * jumpForce, ForceMode2D.Force);
		}
	}

}

敵キャラのコード

Enemyにアタッチしたスクリプトへ、以下を記述。


//使わなかった為、一応コメントアウト。
//using System.Collections;
//using System.Collections.Generic;
using UnityEngine;


public class Enemy : MonoBehaviour
{

	[SerializeField]
	Rigidbody2D rb2d;

	[SerializeField]
	SpriteRenderer sr;


//ジャンプ力。余り高過ぎると、どこかに飛んでいく。
	float jumpForce = 300.0f;

	float moveForce = 10.0f;
//スピード制限。余り高過ぎると、バグる。この値でも若干、跳ねてしまっている。
//これ以上の速さにするなら、Transform.RotateAroundを使った方が良いかも。
	float speedLimit = 30.0f;

//テストとして右に入力しっ放しにしている。    
	float moveDirection = 1.0f;

	Vector3 gravityPoint = Vector3.zero;
	float defaultGravity = -9.81f;
	Vector3 gravity;



	void FixedUpdate()
	{
		transform.up = transform.position - gravityPoint;

		if (moveDirection != 0) {
//敵の画像が左向きだったので、プレイヤー側と条件式を逆にしている。
			sr.flipX = 0 < moveDirection;

			if (rb2d.velocity.sqrMagnitude < speedLimit) {
				rb2d.AddForce(transform.right * moveDirection * moveForce, ForceMode2D.Force);
			}
		}


		gravity = transform.up * defaultGravity;
		rb2d.AddForce(gravity, ForceMode2D.Force);
	}

	void Update()
	{
//【テスト用】Xキーを押すと、適当にジャンプする。ジャンプフラグと接地判定は割愛した為、各自で追加してください。
		if (Input.GetKeyDown("x")) {
			rb2d.AddForce(transform.up * jumpForce, ForceMode2D.Force);
		}
	}

}

カジュアルゲームや、宇宙アクションゲーム等にどうぞ。

タイトルとURLをコピーしました