大学生が有料スマホゲームを作った全てを公開するよ(2)開発環境とゲームの構成
の続き。
第一回はこちら。
大学生が有料スマホゲームを作った全てを公開するよ(1)イントロダクション
今回は、メインのゲームシーンについて。
いわゆる、インゲームと呼ばれる部分。
逆に、メニュー画面とかはアウトゲームって呼ばれる。
pertica(この記事で紹介するゲーム)の場合は、パズルとアクションの仕組みを中心に解説していくよ。
パズルと言っても謎解きみたいな感じだから、もしかしたらみんなのイメージとは少し違うかもしれない。
#ちょっとずつUnityも出てくる
ここから先は、ちょっとずつUnityの話になる。
プログラミングも出てくるし、
初めて見る人には結構難しいかもしれない。
Unityがどんなものかの説明も書いてるから、
なんとなーくでも理解してもらえると嬉しい。
後でUnityを使うようになったらまた見に来ても良いよ!
Unityを知っている人や、ゲームを作った事がある人は参考にしてみて。
ボクはこうやってゲームを作ったよ。
この記事だと、後になるほど技術的になるから記事の下の方を見ると面白いかも。
これは前編で、後編はもっと技術的な話をするつもり。
アドバイスも待ってます!
#主要なゲームオブジェクト
前回の記事でも触れたけど、ゲームシーンは大きく3つの機能を持っている。
- メニューで選択したステージを生成する
- ステージが遊べる
- クリアしたら保存する
この3つだ。
そして、これを実現するために、
このゲームは**10個の「部品」**で出来ている。
- プレイヤー [Player]
- ブロック [Blocks]
- 敵 [Enemies]
- アイテム [Items]
- その他のステージオブジェクト [Others]
- カメラ [Main Camera]
- ゲームマネージャー [GameController]
- デコードオブジェクト [DecodeObject]
- UI(リトライボタン、戻るボタン) [UI Canvas]
- BGM・SE [BGMSource, SESource]
これらの**「部品」を今後は、「ゲームオブジェクト」と呼ぶことにするよ。
Unityの中でも同じ名前で呼ばれるんだ。
でも長いから短く「オブジェクト」**って言う時があるかも。
ちなみに、[ ]の中はUnityの中での名前だ。
それぞれ説明するね。
###プレイヤー
ゲームを遊ぶ人が操作するキャラクターだ。
- タップした場所に弾を飛ばす
- 弾が当たったオブジェクトと入れ替わる
- 敵に触れたら死んでしまう
この3つの機能を持っているよ。
###ブロック
基本的に四角くて動かないもの。
壁のこと。
マリオのブロックみたいな、四角くて動かないオブジェクトだ。
###敵
プレイヤーは敵に当たると死んでしまう。
プレイヤーに対して攻撃をしてくるオブジェクトだ。
マリオのクリボーとかノコノコと同じだ。
###アイテム
壁を出したり消したりするスイッチとか、ワープホールとか。
プレイヤーが道具として使うもの。
壁と違って動く可能性があって、四角くない。
マリオでいうと、キノコとかスターのことだ。
###その他のステージオブジェクト
今説明した「プレイヤー、ブロック、敵、アイテム」の事を、ボクは「ステージオブジェクト」と呼ぶことにしたんだ。
でも背景の星とか、敵が出すレーザーなんかはその4つでは分類できない。
だからブロック、敵、アイテムに分けにくいものを、例外として分類する為に「その他」を作ったんだ。
これでステージオブジェクトは**「プレイヤー、ブロック、敵、アイテム、その他」の5種類**になった。
###カメラ
カメラはスマホの画面にゲームを映す機能をもってる。
テレビはビデオカメラで撮影した映像を表示してるよね。
あれと同じで、ゲームの中の出来事を撮影してスマホの画面に表示してくれるんだ。
ズームしたり、カメラを複数使うことも出来るよ。
###ゲームマネージャー
ゲーム全体の管理をするオブジェクトだ。
このゲーム(pertica)では、
- リセット
- メニューに戻る
- ステージオブジェクトを作る
- フェードイン
の4つの機能を担当している。
###デコードオブジェクト
ゲームの最初にステージを設置する機能を持ってる。
perticaはパズルゲームだから、パズルのステージを用意しないといけない。
ステージはたくさん欲しいよね。
そしたらボクだけじゃなくて、友達にもステージを作ってもらった方が良さそうだ。
友達がステージを作れるように、ボクは友達に**「ステージを作るツール」を渡す。
友達はステージを作ったら、「保存」ボタンを押す。
そうすると、作ったステージが「どこに何があるか」を文字にしたデータになるんだ。
でもデータでは遊ぶ事は出来ない。
遊ぶ時にはデータを実際にステージに変換しなきゃいけない。
それをするのが、このデコードオブジェクトなんだ。
ゲームが始まった瞬間に、「メニューで選んだステージのデータ**」から「ステージ」を作ってくれる、とっても賢いヤツなんだ。
###UI
リトライボタンとか、メニューに戻るボタンの事だ。
UIっていうのは、人がゲームを操作するために必要な仲介人なんだ。
「ゲームをもう一度やり直したい」とか、「メニューに戻りたい」と思うよね。
UIに頼めば、それを実際にやってくれるんだ。
頼む方法はボタンを押すだけだよ。
###BGM・SE
音を流す機能を持っている。
BGMはゲーム中にずっとなってる音楽
SEは「弾を飛ばす音」「スイッチを押す音」みたいな効果音の事だ。
#実装方法
ゲームが何で出来てるかは何となく分かったかな?
部品が「10個」なのは多いと思った?
それとも少ないと感じたかな。
段々と技術的な話に入っていくよ。
面白くなかったら飛ばして、最後のコメントだけ見ても良いよ。
今回は、ちょっとした制作秘話も書いてみた。
ここからは、Unityを少し知っている人向けになると思う。
でもUnityを知らない人にも楽しんでもらいたいから、
簡単にUnityでのゲームの考え方を書いてみるね。
Unityでは主要なゲームオブジェクトで書いたような部品の事をゲームオブジェクトと呼ぶって言ったね。
それぞれのゲームオブジェクトは、さらにコンポーネントと呼ばれるもっと**「小さな部品」**で出来てるんだ。
例えば、ゲーム内のプレイヤーは
- 今いる座標を定める部品 [Transform]
- 見た目を決める部品 [Particle System]
- 物理挙動を制御する部品 [Rigidbody 2D]
- 当たり判定を調べる部品 [Circle Collider 2D]
- アニメーションを制御する部品 [Animator]
- プレイヤーの**動作(弾を飛ばす、移動するなど)**を制御する部品 [Player(Script)]
みたいな小さな部品(コンポーネント)で出来ている。
このうち、上の5つはUnityが用意してくれてるんだ。
だから、ボクが作るのは一番下の一つだけ。
このゲーム特有の動きを決めるんだ。
脳を作ると言っても良い。
そして、この部品を作るためにプログラミングを使うんだよ。
[ ]の中はUnityの中での呼び方で、Player(Script)はボクが作ったんだ。
こういうプログラミングで、自分で作ったコンポーネントを今後は**「スクリプト」と呼ぶよ。
Unityの考え方では、
「車の部品にタイヤがあって、タイヤの部品にネジがある」みたいに
「ゲームの部品にプレイヤーがあって、プレイヤーの部品にPlayer(Script)**」があるんだ。
少し退屈かもしれないけど、このゲームのUnityでの実装の仕方を書き残しておくね。
##プロジェクトツリー
メインシーンの説明の前に、Unityのプロジェクトのファイルツリーを書いておくよ。
Assets
├─ Animation
├─ Data
├─ Image
├─ Material
├─ Music
├─ ParticleSystem
├─ Prefab
├─ Scene
└─ Script
主要なのはこんな感じ。
さらにResourcesって名前のフォルダを必要に応じてそれぞれのフォルダの下に作るんだ。
##ゲームの仕組みを構造的に見る
ここからゲームシーンの説明だ。
Unity内のメインのゲームシーンは、DecodeStageと呼ばれるシーン名で管理している。
このシーンが実行されると、まずDecodeObjectがデータフォルダに入っているデータをもとにステージを生成する。
次に、Main Cameraがゴールからプレイヤーに向かって移動。
移動が完了すると、ゲームが始まる。
プレイヤーは、キャラクターを操作してゴールを目指す。
ゴールにたどり着くとリザルトシーンに遷移してこのシーンの役目は終わり。
分かりにくいかもしれないけど、上の図を目で追って見て欲しい。
ここからは、10個のゲームオブジェクトについて実装の概要を説明するよ。
Unityの簡単な説明で書いたようにUnityは1つのゲームオブジェクトがたくさんのコンポーネントから出来ている。
前編の今回は、そのたくさんのコンポーネントのうちとても大事な**「脳」の部分「スクリプト」を中心に解説するよ。
「スクリプト」はプログラミングを使ってボクが作ったもの**だったね。
###プレイヤー
プレイヤーの構造はこんな感じだ。
そしてプレイヤーの持つスクリプトは
void IdlingAnimation()
踊るアニメーションを行うよ
プレイヤーが一定時間何もしなかったときに使う。
public void Shift(GameObject obj)
弾が当たったものと位置を入れ替える
飛ばした弾が当たった時に使う
public void Die()
プレイヤーが死ぬ時に使う
・死ぬ時のエフェクトを表示
・死んだ回数を保存
・プレイヤーのゲームオブジェクトを非アクティブにする
みたいな事をするよ
public void Fire()
タップした位置に弾を発射する
画面をタップした時に使う
IEnumerator TypeAheadCoroutin()
先行入力を行う
弾のクールタイム中に画面がタップされた時に使う
クールタイムの終了を待って弾を発射する準備をする
こんな感じ。
「void IdlingAnimation()」とか「public void Shift(GameObject obj)」とかはその下に書いてある機能の名前だと思ってくれたら良い。
プログラミングを触ったことが無い人は、分かりにくいよね。
プログラミングを触ったことがある人にとっては、関数の事だ。
日本語で書いてある説明を、UnityではC#っていう言葉を使って書くんだよ。
基本的に、先頭にpublicが書いてある関数は他のスクリプトから呼び出される。
書いていないのは、自身のUpdate関数から呼ばれる。
Update関数っていうのは、毎フレーム呼び出される関数のこと。
フレームっていうのは、ゲームの世界での時間みたいな感じだ。
フレームは1秒に60回くらい更新されるんだ。
だから、Updateも1秒に60回くらい呼び出される。
Updateの中でif文で条件を書いて関数を呼び出すんだ。
ブロックは次の3種類だ。
#####通常の壁 [Wall]
四角いオブジェクトでどんなものがぶつかっても動かない。
プレイヤーの放つ弾が壁に当たると、弾が消滅する。
#####反射壁 [Reflection Wall]
四角いオブジェクトでどんなものがぶつかっても動かない。
プレイヤーの放つ弾が壁に当たると、弾が跳ね返る。
#####スイッチ壁 [Switch Wall]
レーザーのような見た目をしている。
後で紹介するスイッチボタンによって、壁が出現している状態と消えている状態の2つが切り替わる。
壁が出現している時の機能は通常の壁と一緒で、弾が当たるとその弾は消滅する。
壁が消えている状態では、壁の機能は発揮せずに弾はそのまま通過するよ。
さて、それぞれが持つスクリプトを説明するよ。
まずは**通常の壁 [Wall]**から。
何もない。
実際は少しはあるんだけど、エフェクトを出すとか名前をつけるくらいの簡単なものだけだ。
通常の壁[Wall]はほとんどUnityの機能だけで出来ているんだ。
次は反射壁 [Reflection Wall]。
も、何も無い。
反射するはずの壁が、反射するスクリプトを持っていない。
実は、弾が反射するかどうかは壁ではなく、弾の方で処理しているんだ。
じゃあ最後、スイッチ壁 [Switch Wall]
public override void SetStats(int[] stats)
初期状態で表示か非表示かを設定する。
public void SwitchEnabled()
壁の表示非表示を切り替える。
スイッチボタンの状態が切り替わった時に使う。
スイッチ壁はスイッチボタンが押されたときに、状態を変える。
でも壁の状態を切り替える処理自体は、スイッチ壁の機能として用意しておくんだ。
敵
今のところ敵は2種類。
名前はドッグ[Dog]とピッグ[Pig]だ。
名前の由来は...まぁそのうち話すよ...。
ドッグは直線移動して、壁に当たったら反対向きになる。
ピッグもそれは同じだけど、2つのピッグの間にはレーザーが出るんだ。
ドッグが持つスクリプトは、
public override void SetStats(int[] stats)
最初の状態を決定する。
上下左右とその間の斜め、8種類のうち最初に移動する方向を設定する。
void OnCollisionEnter2D(Collision2D coll)
他のゲームオブジェクトとぶつかった時に使う。
ぶつかったゲームオブジェクトがプレイヤーだったらプレイヤーを倒す。
そして、ピッグはこれに加えて
void SetLinePosition()
レーザーの端っこの位置を決める。
壁に当たったら向きを変える処理は書いていないんだ。
そういうのは全部Unityにやってもらってる。
アイテム
アイテムは5種類。
- キューブ [Cube]
- クリアーホール [Clear Hole]
- スイッチボタン [Switch Wall Button]
- ワープホール [Dimension Crack]
- シフト弾 [Shift Bullet]
キューブはその場で静止しているだけのゲームオブジェクト。
クリアーホールはその位置にプレイヤーが到着すると、ゲームクリアー。
要するに、ゴールの事だ。
ワープホールは2つ1組で使う。
プレイヤーの飛ばす弾や敵が片方のワープホールに入るともう片方のワープホールに瞬間移動するんだ。
シフト弾はプレイヤーが発射する弾の事だよ。
順番に見ていこう。
キューブ [Cube] のスクリプトは、
何もない。
これもUnityのコンポーネントだけで出来てる。
**クリアーホール [Clear Hole]**が持つスクリプトは
void OnTriggerEnter2D(Collider2D coll)
データの保存、ゲームシーンの遷移をする。
プレイヤーが同じ位置に来た時に使う。
クリアーした時のデータの保存もこのクリアーホールで行う。
保存専用のゲームオブジェクトがあるわけでは無いんだ。
スイッチボタン[Switch Wall Button]は、さっき紹介したスイッチ壁の状態を切り替える事が出来るんだ。
スイッチボタンに敵のドッグかピッグがぶつかると、そのスイッチボタンと繋がってる壁の状態が変わる。
表示されてる壁は消えて、表示されてない壁が出現するんだ。
スイッチボタンはこんな感じのスクリプトを持ってる。
void Switch()
繋がってるスイッチ壁の状態を切り替える。
敵がぶつかった時に使う。
次はワープホール[Dimension Crack]
void OnTriggerEnter2D(Collider2D coll)
ステージオブジェクトがこのゲームオブジェクトに重なった時に使う。
void SetVisible(Vector3 viewpos)
このゲームオブジェクトが現在カメラに写っているかの情報を更新する。
void SetPairDC()
現在このゲームオブジェクトと接続しているDimensionCrackを更新する。
ワープホールは開発中は、DimensionCrack(次元の亀裂)って呼んでる。
クリアーホールもシーンをワープする感覚だから、ワープホールって名前だと分かりにくかったんだ。
今回の説明では逆に、次元の亀裂って言ってもイメージ出来ないと思ったからワープホールって書いたよ。
最後はシフト弾[Shift Bullet]だ。
シフト弾はプレイヤーが発射するエネルギーの玉の事だ。
タップした方向に飛んでいって、「入れ替われるオブジェクト」にぶつかるとプレイヤーとぶつかったオブジェクトの位置を入れ替える。
スクリプトを見てみよう。
void OnCollisionEnter2D(Collision2D coll)
何かとぶつかった時に使われる。
ぶつかったオブジェクトが入れ替われるならプレイヤーの入れ替わる処理をする。
IEnumerator DieCoroutine()
弾が発射されてからしばらくたつと弾を消滅させる。
public override Die()
弾を消滅させる。
弾が反射壁以外のものとぶつかった時に使う。
反射壁は反射する処理を書かないで、逆に壁に当たった時に弾が消える処理を書くことで実現してる。
反射する機能はUnityが用意してくれてるからボクはそれを上手に使うだけで良いんだ。
その他のステージオブジェクト
「プレイヤー, ブロック, 敵, アイテム, その他」をステージオブジェクトと呼ぶって言ったね。
その他のステージオブジェクトには、背景の星とかピッグのレーザー[PigLazer]とか予測線[Prediction Line]がある。
他にもあるけれど、それはこのゲームシーンでは登場しないから今度話すね。
背景の星は、Unityのコンポーネントだけで出来てる。
ピッグのレーザーは、
void OnTriggerEnter2D(Collider2D coll)
プレイヤーにぶつかった時にプレイヤーを倒す。
これだけのスクリプトを持ってる。
予測線は、画面を長押ししたときに弾が飛ぶ軌道を赤い線で教えてくれる。
void SetLine()
予測線の軌道を設定する。
予測線は、プレイヤーからタップした方向にに直進する。
予測線はものにあたるとその先までは貫通して表示されない。
こんな感じのスクリプトだけで出来てる。
###カメラ [Main Camera]
perticaでのカメラは基本的にプレイヤーが画面の中心になるように追う事だ。
でもステージの始めは、ゴールを映して徐々にプレイヤーにカメラを動かす演出がいる。
プレイヤーが入れ替わるときにも、少し遅らせ気味にプレイヤーを追うように演出する。
これを実装するために、ボクはこんな感じにいくつかの状態(ステート)で考えたよ。
持ってるスクリプトは
void SetState(CameraState state)
状態(ステート)を切り替える関数
void LookClearHole_Move()
ゴール(ClearHole)からプレイヤーの位置にカメラを移動させる。
ゲームの開始時に使う。
LookClearHoleという状態(ステート)のときに使われる。
void TracePlayer_Move()
プレイヤーのカメラの中心に捉えるように、プレイヤーを追いかけ続ける。
プレイヤーが静止しているときに使う。
TracePlayerという状態(ステート)のときに使われる。
void GoToTarget_Move(GameObject objTarget, float speed)
プレイヤーの方向にゆっくりと移動する。
プレイヤーが入れ替わるときに使う。
GoToTargetという状態(ステート)のときに使われる。
実はカメラの制御は結構難しい。
良い演出が思いついても、難しくて実現できてないものもあるんだ。
特に、ズームとかが入ってくると厄介で...。
###ゲームマネージャー
ゲーム全体の管理をすると言ったね。
このオブジェクトはボクが作ったスクリプトだけで出来てる。
public void Reset(float WaitTime = 1)
ステージをリセットする。
ゲームシーンを最初から読み込み直す。
リトライボタンを押したり、死んでしまったときに使うよ。
public void LoadMenu()
ゲームシーンを終了して、メニューシーンに移動する。
メニューボタンを押したときに使うよ。
public void FadeIn()
ゲームシーンを終わるときに、画面全体を徐々に暗くするよ。
ゲームをクリアーしたり、メニューに戻ったりするときに使う。
と、
void Update()
ゲームを初めてプレイする場合、チュートリアルを表示する。
主にこの2つのスクリプトだ。
初めてスクリプトを2つもったゲームオブジェクトが出てきたね。
ゲームオブジェクトは2つどころか、スクリプトをいくつでも持てるんだ。
ゲーム全体を管理すると言ったけど、
このゲームでは、ゲームマネージャーの仕事は少なめだね。
###デコードオブジェクト
Dataフォルダに入っている、StageCodeDataという名前のステージのデータを元に実際にステージを生成するよ。
データの仕組みや構造については別の回で説明するね。
デコードオブジェクトはスクリプトだけで出来ているよ。
ちなみに、これは先輩が作ってくれたんだ。
void Awake()
メニュー画面で選択したステージのデータをロードする。
データを解読(デコード)して、対応するゲームオブジェクトを次々に生成していく。
生成したオブジェクトによっては、初期状態を指定する。
###UI
UIは、ボタンの事だったね。
UIは基本的にスクリプトを持っていないんだ。
これは全部Unityが用意してくれているコンポーネントで出来ている。
ボタンを押すと、ゲームマネージャーが持ってるスクリプトの
public void Reset(float WaitTime = 1)
とか
public void LoadMenu()
を使うように設定できるんだ。
###BGM・SE
音を鳴らす機能を持っていたね。
これは、スクリプトを持っているよ。
まず、SEは
public static void PlaySE(string keyname)
指定したSEを再生するよ。
流すSEは、事前に曲のファイルとキーワードを紐付ける必要がある。
そして、BGMは
public static void FadeinBGM(string keyname,float pertime=0.008f)
指定したBGMをフェードインしながら再生するよ。
曲が変わるときの違和感をなくすために、曲の入りで徐々に音を大きくするんだ。
流すBGMは、事前に曲のファイルとキーワードを紐付ける必要がある。
public static void FadeoutBGM(float pertime=0.009f)
指定したBGMをフェードアウトしながら終了するよ。
曲が変わるときの違和感をなくすために、曲の終わりで徐々に音を小さくするんだ。
大事なのはこんな感じだ。
例えばプレイヤーが球を発射するときは音が鳴る。
この時、SEPlayerのPlaySEを使うんだよ。
他にも、音を出したいときにはこのスクリプトを使って音を鳴らすんだ。
ちなみに、これも先輩が作ってくれたよ。
#まとめ
ゲームは**「10個の部品」で出来ていた。
さらに「10個の部品」はもっと「小さな部品」で出来ている。
Unityではこの「10個の部品」を「ゲームオブジェクト」**、
**「小さな部品」を「コンポーネント」**と呼ぶ。
**「コンポーネント」はUnityが用意してくれているものと、自分で作るものがある。
自分で作るコンポーネントの事を「スクリプト」**と呼ぶことにしたね。
「スクリプト」は大事な機能を持っていて「脳」みたいなものなんだ。
だから今回は、そんなスクリプトについて説明したよ。
実際には、パソコンが理解できるようにC#という言葉を使って書くんだ。
その言葉を書く作業がプログラミングだよ。
なんとなく分かったかな?
#制作秘話
perticaの制作を初めて2ヶ月くらいたったある日。
帰り道でボクが唐突に言った。
「先輩、重力を消しましょう」
先輩「?????」
ボクが真顔で言うもんだから、ついに頭がおかしくなったかと思われた。
今のperticaは宇宙感のあるゲームで無重力だ。
でも昔は重力があったんだよ。
昔のperticaは入れ替わった直後に重力で下に落ちたんだ。
でもそれだと、画面の動きが目まぐるしくて落ち着いて遊べなかった。
そこで、perticaでは重力をなくした方が良いんじゃないかと思ったんだ。
先輩はオーケーしてくれて、それから**「無重力計画」**が始まった。
結構な変更だったから大変だったよ。
先輩もよく付き合ってくれた、本当に感謝してる。
無重力にしたのが良かったのか悪かったのかは分からない。
でも面白いゲームを作るために思い切って変えたことに後悔はしていないよ。
他にこんな話もある
メインシーンは「ゲームを作ろう」と思って最初に作り始めた部分だった。
「プレイヤー」「Dog」「通常の壁」「反射壁」「ClearHole」「ワープホール」「キューブ」「スイッチ壁」
これは機能だけなら最初の1ヶ月くらいで出来たんだ。
見た目とかは、適当な画像で代用してたけどね...。
実は最初の頃は「Cat」とか「Ojisan」とかもいたんだよ。
その辺は今後「デザインについて」の記事も書くからその時に話そうかな。
多分その記事は神回になるよ。
#コメント
ここまで読んでくれてありがとう。
今回は、メインシーンについて触れた。
少し文量が多かったね...。
Unityを知らない人にも、ゲームの仕組みが分かるように書いてみたんだけど、どうだったかな?
今このシリーズ記事の全体の構成をぼんやりと考えてるんだけど、多分全部で10回くらいになると思う。
なるべくこのゲームを全部公開したいんだけど、どうすると上手く伝えられるかな。
もし、気になる事とか書いてほしい事があったらコメントしてね。
そろそろ実物があった方が分かりやすいんじゃないかな?
もし興味をもってくれたら、よろしくお願いします。
perticaは下のリンクからインストール出来るよ。
大学生が有料スマホゲームを作った全てを公開するよ(1)イントロダクション
大学生が有料スマホゲームを作った全てを公開するよ(2)開発環境とゲームの構成
大学生が有料スマホゲームを作った全てを公開するよ(3)ゲームの構造・パズルとアクションの仕組み(前編)
大学生が有料スマホゲームを作った全てを公開するよ(4)ゲームの成り立ち・パズルとアクションの仕組み(後編)