traP Advent Calendar 2016 23日目の記事です。
はじめまして、traPのsatorikuと申します。
現在titeQuestのプログラム班のリーダーをさせていただいています。
titeQusetはJavaで書いているのですが、最近Unityに興味を持って触ってみたところ、当たり判定とかめちゃくちゃ楽に書けるじゃん!ってなりました。初めからこれ使えばよかったんじゃね...ってなるくらいに。なので今回はUnityでtiteQuestっぽいものを作ってみようと思います。
*本当にちょこっとかじっただけなので間違ってるところなどあると思いますがご了承ください。
Unityとは
Unityは、自分の思い描くゲームを自由に制作できるゲームエンジンです。作ったゲームはモバイルからコンソールまで、多彩なプラットフォームにワンクリックで展開可能。また、ゲーム内に効果的な広告を掲出したり、ゲームプレイ状況を分析したり、クラウドによるビルド作業を行なうなど、クリエイターのニーズに応える多様な機能も自由に使えます。楽しくゲームを作り、たくさんのユーザーに遊んでもらう……Unityならその夢がかないます。
引用 https://unity3d.com/jp/unity
だそうです。
こんな感じで実際のゲーム画面を見ながら色々いじったり、当たり判定や重力などを簡単に実装することが出来ます。スクリプトはC#とjavaScriptが使えます。どっちも書いたことがないですが今回はC#を使います。
今回作るもの
titeQuestっぽいものを作るといったな、あれは嘘だ(ごめんなさい)
流石に同じものを作るのは無理なので、今回は簡単な横スクロールゲームに必要な最低限の機能を作ります。
とりあえず必要な機能は
- プレイヤーと敵が移動する
- プレイヤーと敵の壁、床との当たり判定
- プレイヤーと敵の当たり判定
こんなところでしょうか。
Javaで書くと当たり判定、特に壁や床との判定が非常に面倒なのでUnityでどれくらい楽にかけるかやってみようと思います。
実際作ってみる
1.とりあえず色々おいてみる
これがUnityの画面です。左の大きい部分がゲーム画面を作るところです。他のカラムは左から順にオブジェクトの一覧、プロジェクトのファイル、一番右が各オブジェクトの設定をするところです。
こんな感じで、適当に長方形をおいてステージのようなものを作ります。titeQuestではエディタで作ったファイルを読み込んでステージを生成するために32*32のチップを基準として作っていますが、今回は読み込んだりしないので適当に作ります。
チップでやると出来ない傾斜してる床も作ってみました。簡単ですね。
これが壁のオブジェクトの詳細です。オブジェクトはコンポーネントと言うものをつけることで色々な機能を追加してあげることができます。ここにそのコンポーネントが表示されます。一番下のAdd Componentというボタンでコンポーネントを追加することが出来ます。
2.色を付ける
灰色じゃ味気ないので色をつけてみました。materialという見た目の設定ファイルの・ようなものを用意してそれを各ブロックのオブジェクトに関連付けてあげるだけです。簡単ですね。色を変えるくらいなら一瞬です。見た目の情報とブロックの情報を分けて作れるのは便利そうです。
これがmaterialの設定です。色以外にも色々いじれます。
3.玉を転がしてみる
プレイヤーを作る前に玉を作って転がしてみましょう。当たり判定はもともとついているので重力で落ちるようにしてあげれば転がるようになります。Physics/Rigidbodyというコンポーネントを追加します。
これだけで重力ができるので勝手に落下して転がるようになります。
Use Gravityというところにチェックが入ってると重力で落下します。重力加速度も別の所の設定で変えることが出来ます。
なんとここまで5分くらいです。さらっと当たり判定を流してしまいましたがtiteQuestのときはこれ凄く面倒でした。シューティングゲームのように当たったことを判定するだけではだめで、進んだ先に壁がないかどうかを判定して壁があったらどこまでなら進めるか、というのを計算する必要があります。チップの座標は左上を基準にしているので方向によって計算が少しちがったり、大きさが32の倍数だけだと細かい表現ができないのでチップの外側を削って小さい当たり判定もできるようにする、などなど面倒くさい要素が満載です。
さあ、これからプレイヤーを作っていきます。
4.プレイヤーをつくる
適当に書いた絵をプレイヤーとして動かしてみましょう。画像をドラッグするだけで配置できます。先程の球と同じようにまずは重力で落下できるようPhysics/Rigidbodyを付けます。
動かす方向はx,y方向のみで回転はしない、という設定をします。下の方のConstraintsで設定できます。Freeze Positionのほうが各軸の方向の移動、Freeze Rotationが各軸の回転です。
玉のときとは違って勝手に当たり判定は作ってくれないようなので作ってあげましょう。
PhysicsのBox Colliderというコンポーネントで長方形の当たり判定、Sphere Colliderで丸い当たり判定をつけられます。さっきの玉にはSphere Colliderが、壁や床にはBox Colliderがもともとついていました。プレイヤーは長方形なので今回はBox Colliderを採用します。
大きさなど色々変更できますが今回はいじらずそのまま使います。
5.プレイヤーを動かす
次にキー入力を受け付けられるようにしますがこれはスクリプトを書く必要があります。といってもそんなにたくさん書くことはないです。初めから初期化用のメソッドと毎フレーム呼び出されるメソッドが書いてあるのでその中に適当に処理を書くだけです。C#を書いたのは初めてでしたがJavaっぽくて結構すんなり書けました。
using UnityEngine;
using System.Collections;
public class player : MonoBehavio<del datetime="2016-12-19T15:43:15+00:00">ur {
public Rigidbody a;
void Start()//初期化用メソッド
{
a = GetComponent&amp;amp;amp;lt;Rigidbody&amp;amp;amp;gt;();
}
// Update is called once per frame
void Update()//毎フレーム呼び出されるメソッド
{
if (System.Math.Abs(a.velocity.x) &amp;amp;amp;lt; 5) {//最高速度制限
a.AddForce(
transform.right * Input.GetAxisRaw("Horizontal") * 0.5f,
ForceMode.Impulse);//左右方向に力を加える
}
if (Input.GetKeyDown(KeyCode.Space) &amp;amp;amp;amp;&amp;amp;amp;amp; a.velocity.y==0)
//スペースキーが押された瞬間かつジャンプ中でない(速度のy成分が0)
{
a.AddForce(transform.up * 7,ForceMode.Impulse);//上方向に力を加える
}
}
}
速度を設定するのではなく力を加えることで動かす、というやりかたでベクトルを使って操作するので物理みたいです。気持ち良い感じに加減速してくれます。プレイヤーの担当は私ではありませんでしたが、titeQuestでは加減速まで全部書いたので結構長いコードになってました。
速くなりすぎないように最高速度も設定してあります。今回はx方向にしか設定していませんが落下にも終端速度を設定すると気持ち良くプレイできます。
6.敵をつくる
プレイヤーと同じように敵を作ります。重力と当たり判定を設定したら勝手に動くためのスクリプトを少しだけ書いて完成です。
using UnityEngine;
using System.Collections;
public class enemy : MonoBehaviour {
public GameObject refObj;
player p;
private float v = 0.01f;
void Start () {
refObj = GameObject.Find("player");
p = refObj.GetComponent&lt;player&gt;();
}
void Update() {
transform.position += new Vector3(v, 0f, 0f);
}
void OnCollisionEnter(Collision collision)//当たり判定のときに呼び出される
{
if (collision.gameObject.tag == "kabe") { //kabeに当たったら速度を反転
v *= -1;
}
}
}
壁に当たったら反転するようにします。void OnCollisionEnter(Collision collision)という関数で何かと当たったときの処理をすることが出来ます。この時何と当たったかを判定必要があるので、壁にkabeというタグを付けて何と当たったかを判定できるようにします。
このUntaggedというのを選んで、
こんなのが出てきたら一番したのAdd Tag...を選ぶと
タグを追加できます。
もう一回先程のUntaggedをクリックするとちゃんとkabeというタグが追加されてるのでこれを選びます。これで壁にkabeというタグが付いて、敵のオブジェクトは自分が何に当たったのかを判定することが出来ます。
7.敵とプレイヤーの当たり判定をつくる
プレイヤーとの当たり判定は敵側に作ります。先程の壁と当たったときの判定と同じようにプレイヤーにplayerというタグを付けて、プレイヤーと当たったことを判定し、踏まれた場合と普通に当たった場合で条件分岐して自分が倒されるかプレイヤーを倒すかの処理をします。
プレイヤーのスクリプトは以下の二つのメソッドを追加します。
public void Dead()//プレイヤーが死んだときに呼び出される
{
Destroy(this.gameObject);
}
public float getVy()//Javaしか書けないってはっきり分かんだね
{
return a.velocity.y;
}
敵のスクリプトはOnCollisionEnterの中身にプレイヤーと当たったときの処理を追加します。
void OnCollisionEnter(Collision collision)//当たり判定のときに呼び出される</pre>
<pre>
{
if (collision.gameObject.tag == "kabe") { //kabeに当たったら速度を反転
v *= -1;
}
if (collision.gameObject.tag == "player")//Playerと当たったときの処理
{
if (p.getVy() > 0)//プレイヤーの速度のy成分が正(下向きに移動している)とき
{
Destroy(this.gameObject);//自分が死亡
}else
{
p.Dead();//プレイヤーが死亡
}
}
}
完成!
これで最初に上げた
- プレイヤーと敵が移動する
- プレイヤーと敵の壁、床との当たり判定
- プレイヤーと敵の当たり判定
これらの機能は実装出来ました。とりあえずUnityでtiteQuestっぽいものを作ってみるという目的は達成できました。今回はここで力尽きましたが、壁に引っかかったりするバグが残ってたり気持ちよくプレイできるようにするにはもう少し調整が必要そうです。
まとめ
えー、Unity便利スギィ!いいゾ~これ。今回は物理演算と当たり判定しか使いませんでしたがコンポーネントは他にも色々あったので調べて使ってみたら便利そうです。スクリプトも結構簡単に書けますし、公式サイトや他の入門サイトが充実しているので情報にはそんなに困らなかったです。
titeQuestでは当たり判定やステージなどのシステムを私が作ってからみんなでひたすら敵のclassを作っていく、という方針でやっているので一番人数が多いプロジェクトですがそれなりに上手く分担できて効率よく進んでいると思います。今回はチーム製作など全く考えていないのでわかりませんがその辺はどうなんでしょう。また今回は壁と床を一つずつ作るという方法でしたがプレファブなどを使うとまとめて作れるので(ちょっとしかないのに作るの面倒だった)ちゃんと使えばもっと便利にできそうです。そのへんはこっちのほうが詳しいです。ちゃんと完成させてゲームとして起動させるところまで解説してくれてます。
今回は本当に触りの部分しかいじってないのでまだまだやれることはいっぱいあるはずです。これから勉強して使いこなせるようになりたいですね。
明日のtraP Advent Calendar 2016はgen_n、kriw、tubotuの3名がお送りいたします。
参考
Unity公式サイト
https://unity3d.com/jp/unity
Unityスクリプトリファレンス
https://docs.unity3d.com/ja/current/ScriptReference/
Unity2D】Unityで2Dミニゲームを作るチュートリアル(第1回)http://qiita.com/2dgames_jp/items/11bb76167fb44bb5af5f
UnityのInputで入力を扱う
http://qiita.com/yando/items/c406690c9ad87ecfc8e5
[超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(1)ステージ配置
http://qiita.com/JunShimura/items/cbb0db8087a5cc75735e
Unityで他のスクリプトの変数や関数を利用する
http://hiroyukitsuda.com/archives/1702