Unityを触り始めた頃って、つい Update() に「移動処理」「アニメーション」「向きの反転」「入力処理」…と、全部まとめて書いてしまいがちですよね。最初は動くので満足できますが、あとから「敵にも同じ処理を使いたい」「武器だけ向きを変えたい」となった瞬間に、巨大なスクリプトをコピペする地獄が待っています。
とくに2Dゲームでよくあるのが、「キャラの向き(左右反転)」ロジックを移動スクリプトの中にベタ書きしてしまうパターンです。velocity.x を見て SpriteRenderer.flipX を切り替えるだけなのに、毎回同じようなコードを書いていませんか?
この記事では、「向きの反転」だけに責務を絞った小さなコンポーネント SpriteFlipper を作り、親オブジェクトの移動方向に合わせて自動でスプライトを左右反転させる方法を解説します。プレイヤーでも敵でも動く床でも、「とりあえずこれを付けておけば向きは勝手に合わせてくれる」 という状態を目指しましょう。
【Unity】移動方向で自動反転!「SpriteFlipper」コンポーネント
今回作る SpriteFlipper は、次のような前提で設計します。
- 親オブジェクト(例: Player, Enemy)が
Rigidbody2Dを持っている - このコンポーネントは「子オブジェクト」に付けて、
SpriteRendererだけを左右反転させる - 親の
velocity.xが正なら右向き、負なら左向きとしてflipXを切り替える - 微小な揺れ(0.0001 みたいな値)では向きを変えないように、しきい値を用意する
SpriteFlipper.cs(フルコード)
using UnityEngine;
/// <summary>
// 親オブジェクトの移動方向(Rigidbody2D.velocity.x)に合わせて
// アタッチ先の SpriteRenderer の左右反転を自動で切り替えるコンポーネント。
//
// 想定構成:
// Parent (例: Player, Enemy)
// ┗ Child (SpriteFlipper + SpriteRenderer)
//
// ・Parent 側に Rigidbody2D が付いていることが前提
// ・Child 側に SpriteRenderer が付いていることが前提
/// </summary>
[RequireComponent(typeof(SpriteRenderer))]
public class SpriteFlipper : MonoBehaviour
{
[Header("移動元の Rigidbody2D(通常は親を指定)")]
[SerializeField]
private Rigidbody2D sourceRigidbody;
[Header("左右判定のしきい値(絶対値がこれ未満なら向きを変えない)")]
[SerializeField]
private float flipThreshold = 0.01f;
[Header("初期向き(右を向いている状態がデフォルトか?)")]
[SerializeField]
private bool isRightFacingDefault = true;
// 反転対象の SpriteRenderer
private SpriteRenderer spriteRenderer;
// 現在の向き(true: 右向き, false: 左向き)
private bool isFacingRight;
private void Awake()
{
// 同じ GameObject 上の SpriteRenderer を取得
spriteRenderer = GetComponent<SpriteRenderer>();
// sourceRigidbody が未設定なら、親から自動取得を試みる
if (sourceRigidbody == null)
{
// まずは親に直接付いている Rigidbody2D を探す
sourceRigidbody = GetComponentInParent<Rigidbody2D>();
if (sourceRigidbody == null)
{
Debug.LogWarning(
$"[SpriteFlipper] 親階層に Rigidbody2D が見つかりませんでした。" +
$" 手動で sourceRigidbody を設定してください。対象: {gameObject.name}",
this
);
}
}
// 初期向きの設定
isFacingRight = isRightFacingDefault;
ApplyFlip();
}
private void LateUpdate()
{
// Rigidbody2D が設定されていない場合は何もしない
if (sourceRigidbody == null)
{
return;
}
// 親オブジェクトの現在の速度
Vector2 velocity = sourceRigidbody.velocity;
// X方向の絶対値がしきい値未満なら、向きは変えない
if (Mathf.Abs(velocity.x) < flipThreshold)
{
return;
}
// 速度の正負で向きを判定
bool shouldFaceRight = velocity.x > 0f;
// 既に同じ向きなら何もしない(無駄な flip を避ける)
if (shouldFaceRight == isFacingRight)
{
return;
}
// 向きの更新
isFacingRight = shouldFaceRight;
ApplyFlip();
}
/// <summary>
/// isFacingRight の状態に応じて SpriteRenderer.flipX を反映する
/// </summary>
private void ApplyFlip()
{
if (spriteRenderer == null)
{
return;
}
// isRightFacingDefault が true のとき:
// ・右向き = flipX = false
// ・左向き = flipX = true
//
// isRightFacingDefault が false(左向きがデフォルト)のとき:
// ・右向き = flipX = true
// ・左向き = flipX = false
//
// つまり「現在の向き」と「デフォルト右向きフラグ」が異なるときに反転する。
bool shouldFlipX = (isFacingRight != isRightFacingDefault);
spriteRenderer.flipX = shouldFlipX;
}
/// <summary>
/// 外部から強制的に向きを指定したいとき用の公開メソッド。
/// 例: 攻撃演出の都合で一時的に向きを固定したい場合など。
/// </summary>
/// <param name="faceRight">true なら右向き、false なら左向き</param>
public void ForceSetFacing(bool faceRight)
{
isFacingRight = faceRight;
ApplyFlip();
}
/// <summary>
/// 親の Rigidbody2D を後から差し替えたい場合に使うメソッド。
/// 例: ランタイムで乗り物に乗る / 降りるなど。
/// </summary>
public void SetSourceRigidbody(Rigidbody2D newSource)
{
sourceRigidbody = newSource;
}
}
使い方の手順
-
シーン構成を用意する
例として2Dプレイヤーを想定します。Player(親)Rigidbody2D- (任意の移動スクリプト)
Sprite(子)SpriteRendererSpriteFlipper← ここにアタッチ
プレイヤーの見た目用スプライトを子オブジェクトにしておくと、武器やエフェクトなども同じ階層に整理しやすくなります。
-
SpriteFlipper をアタッチする
- 子オブジェクト(
Spriteなど)を選択 Add ComponentからSpriteFlipperを追加SpriteRendererは自動で取得されますsourceRigidbodyが空の場合、親階層から自動でRigidbody2Dを探します
自動取得がうまくいかない構成の場合は、
sourceRigidbodyに親のRigidbody2Dをドラッグ&ドロップで指定しましょう。 - 子オブジェクト(
-
初期向きを設定する
- インスペクター上の
Is Right Facing Defaultにチェックが入っている場合:
「スプライトが右を向いている状態」をデフォルトとみなします。 - もしインポートしたスプライトが左向きなら、チェックを外してください。
- これにより、
velocity.x > 0のときに「右向き」として扱われます。
- インスペクター上の
-
実際に動かして確認する
プレイヤーの移動スクリプトは、普通にRigidbody2D.velocityを更新するだけでOKです。
例えばこんなシンプルな移動スクリプトでも、SpriteFlipper が勝手に向きを合わせてくれます。using UnityEngine; using UnityEngine.InputSystem; [RequireComponent(typeof(Rigidbody2D))] public class SimplePlayerMover : MonoBehaviour { [SerializeField] private float moveSpeed = 5f; private Rigidbody2D rb; private Vector2 moveInput; private void Awake() { rb = GetComponent<Rigidbody2D>(); } // 新Input SystemのActionから呼ばれる想定 public void OnMove(InputAction.CallbackContext context) { moveInput = context.ReadValue<Vector2>(); } private void FixedUpdate() { // X方向だけを使うシンプルな横移動 Vector2 velocity = rb.velocity; velocity.x = moveInput.x * moveSpeed; rb.velocity = velocity; } }この状態でゲームを再生し、左右に移動すると、子オブジェクトのスプライトが自動で左右反転してくれるはずです。
メリットと応用
SpriteFlipper を分離したことで、次のようなメリットがあります。
- 移動ロジックと見た目の向きを分離できる
プレイヤーの移動スクリプトには「速度をどう決めるか」だけを書き、
「どのスプライトをどう反転させるか」はSpriteFlipperに任せられます。
これにより、移動スクリプトを敵や動く床に流用するときも、
向きの反転ロジックをコピペする必要がなくなります。 - プレハブの再利用性が上がる
例えば「左右にパトロールする敵」のプレハブを作るとき、- 親にパトロール用の移動スクリプト +
Rigidbody2D - 子に
SpriteRenderer+SpriteFlipper
という構成にしておけば、別の敵にスプライトだけ差し替えても、向き反転は自動で動きます。
- 親にパトロール用の移動スクリプト +
- 複数の見た目を個別に反転できる
例えば、プレイヤー本体と武器で別々の向きを管理したい場合、BodySpriteにSpriteFlipperWeaponSpriteに別のSpriteFlipperまたは手動制御
といった構成がとりやすくなります。
「胴体だけ向きを変えたい」「武器は常に右向き」などの表現もやりやすいですね。 - レベルデザイン時に「とりあえず動く」安心感
レベルデザイナーがプレハブをシーンにポンと置いて、
「あ、向き反転もちゃんと動いてる」となれば、細かい実装を気にせず配置に集中できます。
コンポーネント指向で小さな責務に分けておくと、こうした「安心して使える部品」が増えていきます。
さらに、ちょっとした改造で使い勝手を上げることもできます。
改造案:向きが変わった瞬間にイベントを発火する
例えば、「向きが変わった瞬間にエフェクトを出したい」「武器の持ち手を入れ替えたい」といったケース向けに、
向き変更時にコールバックを呼ぶ処理を追加する案です。
// SpriteFlipper 内に追加する例
[SerializeField]
private UnityEngine.Events.UnityEvent onFlipDirectionChanged;
/// <summary>
/// 向きが変わったときに呼び出すヘルパー
/// </summary>
private void InvokeDirectionChangedEvent()
{
onFlipDirectionChanged?.Invoke();
}
そして LateUpdate() 内で向きが変わったタイミング(isFacingRight を更新した直後)に InvokeDirectionChangedEvent() を呼べば、
インスペクターからエフェクト再生用メソッドなどを紐付けられるようになります。
このように、小さな責務のコンポーネントに「イベントフック」を足していくと、さらに柔軟な設計がしやすくなりますね。
