#この解説は
Unityのサイトに昔からあるチュートリアルに、以下があります。
「はじめてのUnity」(旧)
http://tutorial.unity3d.jp/archive/my-first-unity/
これを独自にC#化したものをベースとしています。以下を
この解説は以下の段階があります、前の内容を実行した上でのものとして参照して下さい。現時点(20151026)では、新しい「はじめてのUnity」の玉転がしゲームが公開されています。本サイトのブロック崩しと併せて学習すると広がりがあり、初期の学習にはとても有効です。
- [超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(1)ステージ配置
- [超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(2)色を変える
- [超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(3)動くボール
- [[超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(4)ラケットを動かす]
(http://qiita.com/JunShimura/items/5fac20966bba6b223427) - [超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(5)消えるブロック
- [超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(6)ブロックを手作業で並べる
#物理移動の適正化
現状の処理での問題点があります、状況によって、ゲーム性に影響が出る部分があるからです。
##現状の問題点:処理速度に依存して変化する
このブロック崩しをプレイすると、PCの性能によって動きに変化がある部分があります。現状だと、以下の状況でRacket
の動作が速くなります。実験できる場合、それぞれ試してみてください。
- より性能の高いPCでプレイした場合
- ゲーム起動後のダイアログで、速い動作の
Fastest
を選んだ場合 - ゲーム中の物体が少なくなった場合
ここで動作が変化するのはRacket
だけで、Ball
には影響が出ません。ラケットが速いと操作性に影響が大きいので、プレイして貰いたい状態を出来る限り維持するよう、注意を払うと親切です。
##処理速度に依存する部分の特定
今のゲームで処理速度に依存してしまうのは、以下の部分が原因です。
// Update is called once per frame
void Update () {
//力を加える
this.GetComponent<Rigidbody>().AddForce(
transform.right*Input.GetAxisRaw("Horizontal")*accel,
ForceMode.Impulse);
}
Update
関数の呼び出されるタイミングは、画像を生成する毎になります。これがどう変化するかは、Unityエディタでは、[Game]タブの左上にある[Status]ボタンで表示できます。
ここで表示される「フレームレート」は、画面が更新されるタイミングになります。そして、この値が、1秒間当たりにUpdate
関数が呼び出される回数になります。プレイ中、この値は頻繁に変化します。表示しているポリゴンが少なければ速くなり、多い場合は遅くなります。現状のプログラムでは、フレームレートが高い場合にUpdate
関数が呼び出されAddforce
関数の実行頻度も高くなります。つまり、__フレームレートが高いとRacket
が速く動く__という状態です。
使っているPCやその時の表示内容で速さが変わることがプレイヤーに許容されるか、考えてみましょう、好まざる状況であることが大半なはずです。クリエイターは、同じ条件でプレイしてもらうことに配慮しましょう。
##動作の適正化の方法
処理速度にUpdate
関数の実行頻度が依存していることを考えて、処理を変えます。これには幾つか方法があります。
###Update
関数での処理でAddforce
関数の力の大きさを変える
Update
関数での処理をするときに、時間当たりの力を平均化します。
- 前回の呼び出しから時間経過が少ないときは力を少なくする
- 前回の呼び出しから時間経過が多いときは力を多くする
Update
関数の呼び出される間隔は、Time.deltaTime
で取得できます。これが大きいときは時間経過が大きいので力をより大きく、小さい場合は時間経過が少ないのでより小さくします。要するに、比例させれば良いので、乗算します。
// 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]
(http://docs.unity3d.com/ja/current/ScriptReference/Time-deltaTime.html)
この変数の返す値は秒数なので、通常、50fpsで0.02などの小数になります。他のパラメータで適宜、調整し、Racket
の動作を適正化してください。
###FixedUpdate
関数での処理でAddforce
を実行する
別の考え方で、物理演算するフレームレートで実行するタイミングでAddforce
する方法があります。Unityの使い方としては、本来、こちらが正当な方法です。
物理演算の度に呼ばれる関数はFixedUpdate
関数で、これをオーバーライドして定義します。
このとき、入力状態はUpdate
関数に依存することにPCではなっているため、Input
の状態を読み取り、保存しておくことになります。これらを実装すると、以下になります。
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]
(http://docs.unity3d.com/jp/current/ScriptReference/MonoBehaviour.FixedUpdate.html)
物理演算のあるタイミングで処理することで、一番、無駄が無い状態になります。Update
関数を用いる方法も可能ですが、それぞれ独立で動いているため、フレームの状態によって同期が取れないために表示がチラつく場合などがあるかも知れません。例えば今回の処理では、Racket
が壁に一瞬だけめり込んですぐに戻る、などです(必ず起こるかは判りません)。こうした状態は望まれるものではないので、きちんと対処すべきです。
#まとめ
-
Update
関数の呼び出し頻度は処理系に依存するので一定しない - 物理演算を伴うものは
FixedUpdate
関数を用いる - 入力系は
Update
関数を用いる
重要なことは、これに尽きます。
プレイヤーが理不尽に感じたり違和感となる部分は排除する