【第4回】キー入力でボールを斜方投射するUnityチュートリアル

【第4回】キー入力でボールを斜方投射するUnityチュートリアル

前回はゲームをスタートするとボールが斜方投射される機能を実装しました。

動きの確認ができたので、よりゲームらしくするためにキー入力を受け取る処理を実装してみます。

今回もバリバリスクリプトを書いていきますが、なるべく解説を挟むようにする予定です。

前回のチュートリアルはこちらから。

 

今回の目的

ゲーム実行後、プレイヤーがスペースキーを入力したタイミングでボールを斜方投射する処理を実装します。

ゲーム画面にボタンを表示するのは次回以降に。

プロジェクトの準備

前回のチュートリアルで作成したプロジェクトをそのまま使います。

このページに先にたどり着いた方は、チュートリアルの初回から追っていただけるといいかもしれません。

前回のおさらい

前回は、ゲームを開始するとボールが斜方投射される機能を実装しました。

斜方投射
無事斜方投射が実装できた

 

この状態だと、ゲーム開始直後にしかボールを飛ばせないので、キー入力で飛ばせるようにします。

キー入力を拾う

今回もスクリプト書くよー。

Unityでは、Inputクラスを使ってキー入力を検知することができます。

詳細は公式のマニュアルを見ていただくとして、ざっくり説明するとキーボードからの入力があった時や、スマホで画面をタップした時の処理を実装するためのクラスです。

ゲームで決定ボタンを押す -> 決定ボタンを押した時の処理が実行される、って時を思い出してもらえればいいかも。

今回やりたいのは、スペースキーが押された時にボールを飛ばすこと。

なので、まずはスペースキーの入力を検知してみましょ。

// Update is called once per frame
void Update () {
// Input.GetKeyUpはキーが一度押された後、それが離された時にTrueを返す
// KeyCode.Spaceはスペースキーを表す
if (Input.GetKeyUp(KeyCode.Space)){
// Debug.LogでUnityのコンソールに出力できる
Debug.Log("スペースキーが押されたよ。");
}
}

前回使ったSphereBooster.csを開いて、上記のように編集。ここではUpdate()のみ載せています。

Inputクラス内のGetKeyUpメソッドを使うことで、キーが一度押された後、離される時にTrueを返してくれます。特にスマホだと、タップしたけど間違えたからスライドして回避したい! なんて時があるので、指が離れるときに処理を行うようにするとユーザーフレンドリー。

KeyCode.Spaceはスペースキーを示すコード。引数として渡すことで、どのキーの入力を検知するのかを指定します。

キー入力の検知を行うif文はUpdate()の中に記述します。というのは、Input.GetKeyUpはキーから指が離れたフレームにのみTrueを返すため、毎フレーム確認しないといけないのです。

これがStart()の中に書かれようものなら、「初期化処理の途中でキー入力があったか?」となり、その後一切呼ばれないことに……。

こうしたUpdate()やStart()はUnityではイベント関数と呼ばれており、呼ばれるタイミングが決まっています。詳細についてはマニュアルのイベント関数へGO!

また、今回はキー入力を確認するために、コンソールにデバッグ文を出すようにしています。Debug.Logを呼べば出力可能で、開発中はめちゃくちゃお世話になります。

スクリプトを編集したら、ゲームを実行してスペースキーを押してみます。Start()をいじっていないのでボールは飛んでいきますが、今回は放置。

うまくいけば、設定したデバッグ文がコンソールに出力されます。何度か押すと、その度に出力されるのが確認できます。

デバッグ文の出力
3回ほどスペースキーを押してみた

 

これでキー入力が拾えそうなので、次はデバッグ文の代わりにボールを飛ばす処理を入れましょ。

キー入力でボールを飛ばす

ボールに力を加える処理はStart()の中に書かれているので、これをUpdate()のキー入力判定部分に移動させます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SphereBooster : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
// Input.GetKeyUpはキーが一度押された後、それが離された時にTrueを返す
// KeyCode.Spaceはスペースキーを表す
if (Input.GetKeyUp(KeyCode.Space)){
// ボールを飛ばす処理をStart()からここに移動させる
// 力を加える方向
Vector3 forceDirection = new Vector3(1.0f, 1.0f, 0f);
// 加える力の大きさ
float forceMagnitude = 10.0f;
// 向きと力の計算
Vector3 force = forceMagnitude * forceDirection;
// SphereオブジェクトのRigidbodyコンポーネントへの参照を取得
Rigidbody rb = gameObject.GetComponent<Rigidbody>();
// 力を加えるメソッド
rb.AddForce(force, ForceMode.Impulse);
}
}
}

コメント文は少し整理しました。デバッグ文はサクッと消しています。この状態でゲームを実行し、スペースキーを押すと……よし、飛びました。

スペースキーで飛ぶ
スペースキーで飛ぶようになった

 

……気付いた方もいると思いますが、このままだとある問題が生じます。

そう、スペースキーを押すたびに力が加わるんです。あと飛んでったら戻ってこないし。

二段ジャンプ
二段ジャンプ

 

実はUnityで物理的な運動を実装する上でもスクリプトの書き方に問題があったりと、修正点がいっぱい。これはなんだかイケてないので、ちょっと実装方法を考えましょ。

 

問題点の修正

今ある問題は、

  • スペースキーを押すたびに力が加わる
  • 一度ボールが飛んでいくと戻ってこない
  • Update()に物理運動を書いているのでなんだかカクカクする

の3つ。

ボールを飛ばしていいタイミングを考えるのと、ボールを戻す処理を考える必要があります。いちいちゲームを停止して再実行するのもめんどいですもんね。

これを解決するために、スペースキーの動作を切り替えるフラグを用意しましょうか。

方針としては、

  • スペースキーを押すと、飛行中フラグがTrueになるようにする
  • 飛行中フラグがTrueの時にスペースキーを押すと、ボールを初期位置に戻し、飛行中フラグをFalseにセット

かな。

また、UnityでRigidbodyを使った運動を実装するときにはFixedUpdate()を使え! という推奨方法があるのでそちらも実装します。

FixedUpdate()は固定秒数でフレーム更新が行われるイベント関数で、デフォルトでは1秒間に50フレーム呼ばれます。

このイベント関数はその性質上、画面の更新と同期しておらず、Inputをうまく拾ってくれないことがあるので、その点も盛り込んでコードを修正します。

スクリプトを書いてみるとこんな感じ。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SphereBooster : MonoBehaviour {
// 飛行中フラグ
bool isFlying = false;
// ボタン押下フラグ
bool isBoostPressed = false;
// Sphereオブジェクトの初期位置格納用ベクトル
Vector3 initPosition = Vector3.zero;
void Start(){
initPosition = gameObject.transform.position;
}
void Update(){
// Input.GetKeyUpはキーが一度押された後、それが離された時にTrueを返す
if (Input.GetKeyUp(KeyCode.Space)){
isBoostPressed = true;
}
}
void FixedUpdate(){
if (isBoostPressed){
if (isFlying){
// 飛行中の処理
// 運動の停止
Rigidbody rb = gameObject.GetComponent<Rigidbody>();
rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
// 初期位置に移動させる
gameObject.transform.position = initPosition;
} else {
// ボールを飛ばす処理
// 力を加える方向
Vector3 forceDirection = new Vector3(1.0f, 1.0f, 0f);
// 加える力の大きさ
float forceMagnitude = 10.0f;
// 向きと力の計算
Vector3 force = forceMagnitude * forceDirection;
// 力を加えるメソッド
Rigidbody rb = gameObject.GetComponent<Rigidbody>();
rb.AddForce(force, ForceMode.Impulse);
}
// 飛行中フラグの切り替え
isFlying = !isFlying;
// どちらの処理をしてもボタン押下フラグをfalseに
isBoostPressed = false;
}
}
}

飛行中フラグはゲーム実行中にずっと保持して欲しいので、メンバ変数として追加しました。また、『Sphere』オブジェクトの初期位置を保持するinitPositionもメンバ変数として追加。カクつき対策のボタン押したよフラグもメンバ変数に。

Start()が呼ばれた時に『Sphere』オブジェクトの初期位置をinitPositionに代入しています。初期位置に戻すときはこれを使えばOKですね。

次に、Update()内では、キー入力検知後にフラグを設定しています。FixedUpdate()ではそのフラグを確認し、キー入力があれば飛行と停止の切り替えを行います。飛行と停止のどちらを行うかは飛行中フラグを見ています。

飛行中フラグがFalseならこれまでのようにボールを飛ばし、飛行中フラグがTrueなら運動を停止させて、ボールの位置を初期位置に戻しています。

物体の運動を停止する処理は、Rigidbodyの変数にあるvelocity(速度ベクトル)とangularVelocity(角速度ベクトル)をゼロにしています。実行してみると、奈落の底まで落ちていった後にスペースキーが押された場合、初期位置から10-8とか10-12といったオーダーでずれることがありますが、誤差だよ誤差。多分勢いを止めきれてないんだと思います。

分岐が終わった後は!isFlyingでフラグを反転させています。キー入力フラグはどちらの分岐でも確定でFalseにします。

動作確認

んじゃ動きを確認してみましょ。

スペースキーの動作修正
スペースキーを押すと初期位置に

スペースキーを押すたびにボールが初期位置に戻っています。GIFは24FPSにしてるので若干カクカクですが、私のUnityではスムーズにボールが飛んでいます。ぜひあなたのマシンで再現して欲しいところ。

これで任意のタイミングでボールを飛ばすことができました。

次は、PCでの操作だけでなく、キーボードのないデバイス(スマホなど)でも操作できるようにしてみます。

まとめ

今回はスペースキーを押して任意のタイミングでボールを飛ばす機能を実装しました。ゲームを作るなら、やはりユーザーの入力を受け付けてなんぼですもんね。

また、新しいイベント関数FixedUpdate()が出てきたりと、内容がいっぱいだったかも。

次回はスマホでも入力できるように、画面にボタンを表示してみたいと思います。

     

ゲーム開発の攻略チャートを作りました!

CTA-IMAGE

「ゲームを作ってみたいけど、何から手を付けていいか分からない!」


そんなお悩みをお持ちの方向けに、todoがアプリをリリースした経験を中心に、ゲーム作りの手順や考慮すべき点をまとめたe-bookを作成しました。ゲーム作りはそれ自体がゲームのように楽しいプロセスなので、「攻略チャート」と名付けています。


ゲームを作り始めた時にぶつかる壁である「何をしたら良いのか分からない」という悩みを吹き飛ばしましょう!