LoginSignup
18
20

More than 5 years have passed since last update.

物理移動の適正化、理不尽な動きの変化を止める:「はじめてのUnity」のブロック崩しを改造しながら学ぶ

Last updated at Posted at 2015-07-03

この解説は

Unityのサイトに昔からあるチュートリアルに、以下があります。
「はじめてのUnity」(旧)
http://tutorial.unity3d.jp/archive/my-first-unity/

これを独自にC#化したものをベースとしています。以下を
この解説は以下の段階があります、前の内容を実行した上でのものとして参照して下さい。現時点(20151026)では、新しい「はじめてのUnity」の玉転がしゲームが公開されています。本サイトのブロック崩しと併せて学習すると広がりがあり、初期の学習にはとても有効です。

物理移動の適正化

現状の処理での問題点があります、状況によって、ゲーム性に影響が出る部分があるからです。

現状の問題点:処理速度に依存して変化する

このブロック崩しをプレイすると、PCの性能によって動きに変化がある部分があります。現状だと、以下の状況でRacketの動作が速くなります。実験できる場合、それぞれ試してみてください。

  • より性能の高いPCでプレイした場合
  • ゲーム起動後のダイアログで、速い動作のFastestを選んだ場合
  • ゲーム中の物体が少なくなった場合

ここで動作が変化するのはRacketだけで、Ballには影響が出ません。ラケットが速いと操作性に影響が大きいので、プレイして貰いたい状態を出来る限り維持するよう、注意を払うと親切です。

処理速度に依存する部分の特定

今のゲームで処理速度に依存してしまうのは、以下の部分が原因です。

Racket.cs(抜粋)
    // Update is called once per frame
    void Update () {
        //力を加える
        this.GetComponent<Rigidbody>().AddForce(
            transform.right*Input.GetAxisRaw("Horizontal")*accel,
            ForceMode.Impulse);
    }

Update関数の呼び出されるタイミングは、画像を生成する毎になります。これがどう変化するかは、Unityエディタでは、[Game]タブの左上にある[Status]ボタンで表示できます。

Gyazo

ここで表示される「フレームレート」は、画面が更新されるタイミングになります。そして、この値が、1秒間当たりにUpdate関数が呼び出される回数になります。プレイ中、この値は頻繁に変化します。表示しているポリゴンが少なければ速くなり、多い場合は遅くなります。現状のプログラムでは、フレームレートが高い場合にUpdate関数が呼び出されAddforce関数の実行頻度も高くなります。つまり、フレームレートが高いとRacketが速く動くという状態です。

使っているPCやその時の表示内容で速さが変わることがプレイヤーに許容されるか、考えてみましょう、好まざる状況であることが大半なはずです。クリエイターは、同じ条件でプレイしてもらうことに配慮しましょう。

動作の適正化の方法

処理速度にUpdate関数の実行頻度が依存していることを考えて、処理を変えます。これには幾つか方法があります。

Update関数での処理でAddforce関数の力の大きさを変える

Update関数での処理をするときに、時間当たりの力を平均化します。

  • 前回の呼び出しから時間経過が少ないときは力を少なくする
  • 前回の呼び出しから時間経過が多いときは力を多くする

Update関数の呼び出される間隔は、Time.deltaTimeで取得できます。これが大きいときは時間経過が大きいので力をより大きく、小さい場合は時間経過が少ないのでより小さくします。要するに、比例させれば良いので、乗算します。

Racket.cs
    // Update is called once per frame
    void Update () {
        //力を加える
        this.GetComponent<Rigidbody>().AddForce(
            transform.right*Input.GetAxisRaw("Horizontal")*accel
            *Time.deltaTime,  //経過時間を追加
            ForceMode.Impulse);
    }

これを乗算すると、経過時間に比例してVector3の大きさが変化します。Time.deltaTimeについては、以下を参照してください。

Unity - スクリプトリファレンス:Time.deltaTime
http://docs.unity3d.com/ja/current/ScriptReference/Time-deltaTime.html

この変数の返す値は秒数なので、通常、50fpsで0.02などの小数になります。他のパラメータで適宜、調整し、Racketの動作を適正化してください。

FixedUpdate関数での処理でAddforceを実行する

別の考え方で、物理演算するフレームレートで実行するタイミングでAddforceする方法があります。Unityの使い方としては、本来、こちらが正当な方法です。
物理演算の度に呼ばれる関数はFixedUpdate関数で、これをオーバーライドして定義します。
このとき、入力状態はUpdate関数に依存することにPCではなっているため、Inputの状態を読み取り、保存しておくことになります。これらを実装すると、以下になります。

Racket.cs
using UnityEngine;
using System.Collections;

public class Racket : MonoBehaviour {

    private float accel = 1000.0f;//加える力の大きさ
    private Vector3 inputVector;
    private Rigidbody rig;

    // Use this for initialization
    void Start() {
        rig = this.GetComponent<Rigidbody>();
        inputVector = new Vector3(0.0f, 0.0f, 0.0f);
    }

    // Update is called once per frame
    void Update() {
        //入力を拾う
        inputVector = transform.right * Input.GetAxisRaw("Horizontal") * accel;
    }

    void FixedUpdate() {
        //力を加える
        rig.AddForce(inputVector, ForceMode.Impulse);
    }
}

これで物理演算ごとに処理をすることで、適正な動作にすることができました。

対応方法の選択

上記の二つの方法でどちらを選択するのが妥当か、というと、後者になります。以下に説明があります。

Unity - スクリプトリファレンス:MonoBehaviour.FixedUpdate()
http://docs.unity3d.com/jp/current/ScriptReference/MonoBehaviour.FixedUpdate.html

物理演算のあるタイミングで処理することで、一番、無駄が無い状態になります。Update関数を用いる方法も可能ですが、それぞれ独立で動いているため、フレームの状態によって同期が取れないために表示がチラつく場合などがあるかも知れません。例えば今回の処理では、Racketが壁に一瞬だけめり込んですぐに戻る、などです(必ず起こるかは判りません)。こうした状態は望まれるものではないので、きちんと対処すべきです。


まとめ

  • Update関数の呼び出し頻度は処理系に依存するので一定しない
  • 物理演算を伴うものはFixedUpdate関数を用いる
  • 入力系はUpdate関数を用いる

重要なことは、これに尽きます。
プレイヤーが理不尽に感じたり違和感となる部分は排除する

18
20
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
18
20