1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Unity】Photon Fusion 2のSimple KCCを試す

1
Posted at

はじめに

Unity向けオンラインゲーム作成SDK「Photon Fusion 2」には、キャラクター操作を簡単に実装できる公式アドオン Simple KCC が用意されています。
今回は Simple KCC の挙動を理解するために、

  • 俯瞰の三人称視点カメラ
  • クリックした地点への移動
  • スペースキーでジャンプ

といった基本操作ができる ミニサンプル を作成しました。

公式ドキュメント

環境

  • Unity 6000.3.19f1
  • Fusion 2.1.1

作成した内容

今回作成したサンプルでは、以下のような挙動を実装しています。

  • マウスクリックした地点へキャラクターが移動
  • 移動方向へキャラクターが自然に向きを変える
  • スペースキーでジャンプ
  • Simple KCC の Move / Jump / LookRotation を利用

Fusion のネットワーク同期を使いながら、Simple KCC の基本機能を確認できる構成です。

作業手順

※ Photon のアプリケーションID作成・設定は省略します。

Simple KCC インストール

  1. メニューから Tools -> Photon -> Fusion Hub を選択する
  2. Addonsから "Simple KCC" をインストールする

image.png

シーンとNetwork Runnerの準備

  1. 新しいシーンを作成する
  2. Hierarchyを右クリックでメニューを開く
  3. Fusion -> Scene -> Setup Networking in the Scene で簡易ネットワーク接続処理を追加

image.png

ステージオブジェクト作成とカメラの準備

  1. 地面と適当に障害物のオブジェクトを設置する
  2. カメラを俯瞰の三人称視点になるように調整する
    image.png

プレイヤーオブジェクト作成

  1. プレイヤー用オブジェクトを作成する
  2. 以下のコンポーネントをアタッチ
    • Network Object
    • SimpleKCC
    • Rigidbody
  3. 動的にスポーンするために Player をプレハブ化する
    image.png

プレイヤースクリプト作成

  1. SimpleKCCを使った、キャラクターを動かすスクリプトを作成する
  2. 作成したSimpleKCCSamplePlayerPlayerプレハブにアタッチする
SimpleKCCSamplePlayercs
using Fusion;
using Fusion.Addons.SimpleKCC;
using UnityEngine;

namespace SimpleKCCSample
{
    // クリックで移動先を指定し、スペースでジャンプできるようにするプレイヤー制御スクリプト。
    // 役割としては入力受付 → ネットワーク同期状態の更新 → SimpleKCCへ移動/回転/ジャンプを反映する流れです。
    public class SimpleKCCSamplePlayer : NetworkBehaviour, IBeforeUpdate
    {
        [SerializeField] private SimpleKCC _simpleKCC;

        // 移動・ジャンプの挙動を調整するためのパラメータ。
        [SerializeField] private float _moveSpeed = 8f;
        [SerializeField] private float _jumpImpulse = 8f;
        [SerializeField] private float _upGravity = -22f;
        [SerializeField] private float _downGravity = -40f;
        [SerializeField] private float _groundAcceleration = 45f;
        [SerializeField] private float _groundDeceleration = 20f;
        [SerializeField] private float _airAcceleration = 20f;
        [SerializeField] private float _airDeceleration = 1.5f;
        [SerializeField] private float _clickRaycastDistance = 100f;
        [SerializeField] private float _turnSpeed = 12f;
        [SerializeField] private float _stopDistance = 0.2f;

        // ネットワーク同期対象の移動状態。
        // これらはサーバー側で管理され、他クライアントへ反映される。
        [Networked] private Vector3 _moveVelocity { get; set; }
        [Networked] private Vector3 _moveDestination { get; set; }
        [Networked] private bool _hasMoveDestination { get; set; }
        [Networked] private bool _jumpRequested { get; set; }

        // オブジェクト生成時に必要な初期化処理を行う。
        public override void Spawned()
        {
            if (_simpleKCC == null)
            {
                _simpleKCC = GetComponent<SimpleKCC>();
            }

            ResetMovementState();
        }

        // 入力を受け取り、次の固定更新で使う状態を記録する。
        // ここでは「クリックで移動先を決める」「スペースでジャンプ要求する」の2つを処理する。
        void IBeforeUpdate.BeforeUpdate()
        {
            if (Object == null || HasStateAuthority == false)
            {
                return;
            }

            if (Input.GetMouseButtonDown(0) == true)
            {
                if (TryGetClickTarget(out Vector3 target) == true)
                {
                    _moveDestination = target;
                    _hasMoveDestination = true;
                }
            }

            _jumpRequested = Input.GetKeyDown(KeyCode.Space);
        }

        // 固定タイムステップで実行されるメイン処理。
        // ここで「目標地点に向かう速度」を決め、移動・回転・ジャンプをまとめて適用する。
        public override void FixedUpdateNetwork()
        {
            if (_simpleKCC == null)
            {
                return;
            }

            Vector3 desiredMoveVelocity = Vector3.zero;

            if (_hasMoveDestination == true)
            {
                Vector3 currentPosition = _simpleKCC.Transform.position;
                Vector3 delta = _moveDestination - currentPosition;
                delta.y = 0f;

                if (delta.sqrMagnitude > _stopDistance * _stopDistance)
                {
                    desiredMoveVelocity = delta.normalized * _moveSpeed;
                }
                else
                {
                    StopMovement();
                }
            }

            ApplyMovement(desiredMoveVelocity);
            ApplyRotation();
            _jumpRequested = false;
        }

        // 現在の状態から実際の移動速度を算出し、SimpleKCCへ反映する。
        // 地上/空中で加速度を変え、必要ならジャンプもここで付与する。
        private void ApplyMovement(Vector3 desiredMoveVelocity)
        {
            float acceleration = desiredMoveVelocity == Vector3.zero
                ? (_simpleKCC.IsGrounded == true ? _groundDeceleration : _airDeceleration)
                : (_simpleKCC.IsGrounded == true ? _groundAcceleration : _airAcceleration);

            _moveVelocity = Vector3.Lerp(_moveVelocity, desiredMoveVelocity, acceleration * Runner.DeltaTime);

            _simpleKCC.SetGravity(_simpleKCC.RealVelocity.y >= 0f ? _upGravity : _downGravity);

            Vector3 moveVelocity = _moveVelocity;
            if (_simpleKCC.ProjectOnGround(moveVelocity, out Vector3 projectedMoveVelocity) == true)
            {
                moveVelocity = Vector3.Normalize(projectedMoveVelocity) * _moveSpeed;
            }

            float jumpImpulse = 0f;
            if (_jumpRequested == true && _simpleKCC.IsGrounded == true)
            {
                jumpImpulse = _jumpImpulse;
            }

            _simpleKCC.Move(moveVelocity, jumpImpulse);
        }

        // 移動方向に向けてキャラクターの向きを滑らかに変える。
        private void ApplyRotation()
        {
            if (_moveVelocity.sqrMagnitude <= 0.001f)
            {
                return;
            }

            Vector3 forwardDirection = new Vector3(_moveVelocity.x, 0f, _moveVelocity.z);
            if (forwardDirection.sqrMagnitude <= 0.001f)
            {
                return;
            }

            Quaternion targetRotation = Quaternion.LookRotation(forwardDirection, Vector3.up);
            Vector2 currentLookRotation = _simpleKCC.GetLookRotation(true, true);
            Quaternion currentRotation = Quaternion.Euler(currentLookRotation.x, currentLookRotation.y, 0f);
            Quaternion blendedRotation = Quaternion.Slerp(currentRotation, targetRotation, _turnSpeed * Runner.DeltaTime);
            Vector2 blendedLookRotation = new Vector2(blendedRotation.eulerAngles.x, blendedRotation.eulerAngles.y);
            _simpleKCC.SetLookRotation(blendedLookRotation);
        }

        // 目標地点に到達したら移動状態をリセットする。
        private void StopMovement()
        {
            _hasMoveDestination = false;
            _moveVelocity = Vector3.zero;
        }

        // スポーン時や移動開始前に状態を初期化する。
        private void ResetMovementState()
        {
            _moveVelocity = Vector3.zero;
            _moveDestination = transform.position;
            _hasMoveDestination = false;
            _jumpRequested = false;
        }

        // 画面上のマウス位置から、地面上のターゲット位置を取得する。
        // まず地面平面に対するレイキャストを試み、失敗した場合は近くのコライダーにヒットした位置を使う。
        private bool TryGetClickTarget(out Vector3 target)
        {
            target = Vector3.zero;

            Camera camera = Camera.main;
            if (camera == null)
            {
                return false;
            }

            Ray ray = camera.ScreenPointToRay(Input.mousePosition);
            Plane groundPlane = new Plane(Vector3.up, Vector3.zero);

            if (groundPlane.Raycast(ray, out float distance) == true)
            {
                target = ray.GetPoint(distance);
                return true;
            }

            if (Physics.Raycast(ray, out RaycastHit hit, _clickRaycastDistance) == true)
            {
                target = hit.point;
                return true;
            }

            return false;
        }
    }
}

image.png

PlayerSpawnを作成する

  1. シーン内に、スポーン設定を入れるオブジェクトPlayerSpawnを作成する
  2. Assets\Photon\FusionDemos\Fusion-Intro-Shared\Scripts\PlayerSpawner.csNetwork Objectをアタッチする
  3. PlayerSpawnerに先ほど作成したPlayerを設定する

image.png

動作動画

Animation03.gif

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?