この記事の査読を担当してくれたMichaela Lehr氏に感謝します。
この記事では、Unityを利用した簡単な2Dタッピングゲームの作り方を説明します。このゲームは、かの有名な移動中の昆虫をタップするとそれが消えて別の虫が出現するゲームTapping Bugsからアイデアを得ました。
簡単なコードをいくつか使用して仕組みを紹介しますので、デザイン性は重視しません。また、このゲームはAndroid、iOS、WebGL用に変換できます、
最初に、Unityが最新バージョンであるかどうか確認してください。記事で使用しているのはバージョン5.3です。
Tapitプロジェクト用のAssetsフォルダーはこちらです。プロジェクトファイル全体を閲覧・ダウンロードするにはこちらを参照してください。
ゲームシーン、Canvas要素、GUI要素の作成
2Dプロジェクトを新規作成し、好きな名前をつけます。新規プロジェクトのAssetsフォルダーに新規フォルダー「Images」を作成します。Imagesフォルダーを右クリックして「Import new Asset」を選び、background.jpgをインポートします。インポートしたイメージをSceneウィンドウ内にドラッグし、使用するスクリーンサイズに合わせて大きさを調節します。例では、モバイルスクリーンを意識した800×1280pxのポートレートスクリーンを使用しています。
同様に、ant_1.pngイメージをインポートします。より見やすく、タップしやすいように幅と高さを2倍に拡大しました。Hierarchyビューを使うようにして、Circle Collider 2Dを1つ追加します。
Hierarchyビューで余白部分を右クリックし、UI > Canvasと選択します。
Canvasメニューでは、Render Modeを選択しScreen Space – Cameraにします。Render Cameraはデフォルトカメラ、つまりMain Cameraです。Plane Distanceは背景距離とMain Cameraの間の値にします。たいていの場合、この設定は0から10の間にします。例では3に設定しました。
Canvas Scaler (Script)メニューで、UI Scale ModeをScale With Screen Sizeに、またScreen Match ModeをExpandに設定します。
HierarchyビューでCanvasを右クリックし、UI > Textと選択します。新ゲームオブジェクト名を「Score」とします。読みやすいフォントサイズに設定してから、任意のコーナーに配置します。
テキストゲームオブジェクトをもう1つ作成し、名前を「Lives」とします。このテキストは残機の数を表示します。オブジェクトをスコアオブジェクトに似せて作成し、別のコーナーに配置します。
これで、すべてのGUIが揃いました。
スクリプト
Unityの最新バージョンでは、UnityScript(JavaScriptに類似したもの)とC#の2つのスクリプト言語しかサポートされていません。例ではUnityScriptを使用します。
Assetsフォルダーに新規フォルダー「Scripts」を作成します。Scriptsフォルダーにゲームの実行に必要なすべての機能が集まります。はじめに変数を作成し、名称とタイプを決定します。
Scriptsフォルダー内に、新規JavaScriptファイル「AntScript.js」を作成します。このファイルは下のような変数の定義から始まります。
var ant : GameObject;
var scoreNumber : int;
var livesNumber : int;
var scoreText : GameObject;
var livesText : GameObject;
var walkingSpeed : double;
このゲームで唯一のキャラクターは、antと呼ばれるGameObjectです。実際、2D Spriteはこのあと説明するコードのように動作し、行動が変化します。
今回は、scoreNumber(プレイヤーのスコア保存用)とlivesNumber(プレイヤーの残機格納用)という2つの整変数を使います。
GameObjectのscoreTextとlivesTextはスコアと残機に関する情報をそれぞれ表示します。
2Dスペースでキャラクターantを動かすには、動作の軸に従って座標を変化させる必要があります。2Dスペースはやや小さめなので、ここでは軸に沿った小さな数値を使用します。
速度はwalkingSpeedという変数によって決定されます。この変数は、たとえば0.01といった低い数値で増加していきますので、その種類はdoubleになります。
Start()関数
UnityScriptの知識が多少あれば、function Start()とfunction Update()という2つの重要な関数は知っていると思います。前者はシーン中に一度しか呼び出されず、後者はゲームフレームごとに呼び出されます(MonoBehaviourが有効な場合)。functionStart()の内容は下記の通りです。
function Start () {
ant = GameObject.Find("Ant");
scoreText = GameObject.Find("Score");
livesText = GameObject.Find("Lives");
//Initialize the values of walking speed
walkingSpeed = 0.0;
livesNumber = 3;
scoreNumber = 0;
//Initialize the GUI components
livesText.GetComponent(UI.Text).text = "Lives Remaining: " + livesNumber;
scoreText.GetComponent(UI.Text).text = "Score: " + scoreNumber;
//Place the ant in a random position on start of the game
ant.transform.position.x = generateX();
ant.transform.position.y = generateY();
}
コードの第1ブロックでは、変数の初期値が設定されています。UnityScriptingのGameObjectsの参照はとても簡単で、GameObject.Find("[Name_of_gameobject]");と記述するだけです。
初期値はwalkingSpeedが0.0、livesNumberが3、scoreNumberは通常0となります。
第2ブロックは、先に作成したUI要素にテキストを設定します。
第3ブロックは、antのxおよびy座標にアクセスします。generateX()とgenerateY()はともに乱数を返す関数です。
generateX()関数、generateY()関数
generateX()とgenerateY()のコードは次のようになっています:
//Generates random x
function generateX(){
var x = Random.Range(-2.54,2.54);
return x;
}
//Generates random y
function generateY(){
var y = Random.Range(-4.0,3.8);
return y;
}
この2つの関数は、Range()以外はコードがよく似ています。それぞれに左右の限界値を入力します。
antがスクリーン内に留まるよう、限界水平位置として左右の限界値を自分のスクリーンサイズに応じて選択します。
返り値はブラケットに入力するパラメーターの種類に依存します。ここではdoubleを入力しているので、doubleの値が返ってきます。同時に、これらの値は実際に二次元で見えるゲーム場面を表します。
Update()関数
この関数はゲームで最も重要な役割を果たします。
function Update () {
if(ant.transform.position.y < -4.35 && livesNumber > 0){
livesNumber -= 1;
livesText.GetComponent(UI.Text).text = "Lives Remaining: " + livesNumber;
generateCoordinates();
}
else if(ant.transform.position.y < -4.35 && livesNumber == 0){
Destroy(GameObject.Find("Ant"));
gameOver();
}
else{
ant.transform.position.y -= walkingSpeed;
}
}
function Update () {
if(ant.transform.position.y < -4.35 && livesNumber > 0) {
livesNumber -= 1;
livesText.GetComponent(UI.Text).text = "Lives Remaining: " + livesNumber;
generateCoordinates();
} else if(ant.transform.position.y < -4.35 && livesNumber == 0) {
Destroy(GameObject.Find("Ant"));
gameOver();
} else {
ant.transform.position.y -= walkingSpeed;
}
}
Update()は、ゲームフレームごとのゲームオブジェクト情報を取得します。antは常に動いているので、プレイヤーがミスをするという設定が必要です。例では、antがスクリーン下端に達したときや、そのy座標が-4.35未満になったときにミスとなります。プレイヤーがミスをし、かつ残機が0より大きければゲーム続行となります。残機は1つ減り残機数がスクリーンに表示さたあと、generateCoordinaes()が呼び出されます。
残機が0で、かつantが画面下端に達した場合、全滅となりgameOver()が呼び出されます。gameOver()によってゲームオーバー画面がロードされます。
さらに、antがスクリーン下端に達していない場合には、Y軸に沿ってフレームごとの変数walkingSpeedのスピードで下方へ移動します。
OnMouseDown()関数
OnMouseDown()はゲームエンジンの内部関数の1つで、親オブジェクトがマウスボタンでクリックされたり、タッチデバイスでタップされたりするたびに呼び出されます。
function OnMouseDown(){
//Place the ant at another point
generateCoordinates();
//Increase the walking speed by 0.01
walkingSpeed += 0.01;
}
コードは、プレイヤーがantをタップして1点獲得したことを意味しています。得点表示は更新され、新規座標が2つランダムに作成されます。antはこの新しい座標の上方に現れ、増加した変数walkingSpeedのスピードで下方へ移動します。
AntScript.jsの全コードはGitHubで参照できます。
このスクリプトをドラッグし、Hierarchyビューのantゲームオブジェクトへドロップします。
Scriptsフォルダー内に新規スクリプト「Functions.js」を作成します。コードは次のようになります。
function loadGame(){
Application.LoadLevel("Game");
}
function loadMenu(){
Application.LoadLevel("Menu");
}
function quit(){
Application.Quit();
}
ゲームシーンはほぼ完成しました。File > Save Scene(訳注:原文ではFile* > Save Scene)と選択して「Game」という名称で保存します。
ゲームオーバー画面
File > New Sceneと選択します。前と同様に、新規Canvasを作成し、新規パネルに好きな色をつけます。
パネル内に、ゲームオーバーを表示するための新規テキスト要素を配置します。
新規ボタンを作成し、その名称をRestartとします。Restartボタンをゲームオーバーテキストの下に配置し、ソースイメージとしてAssetsフォルダーからreload.pngを選択します。
Add Componentをクリックし、Scripts > Functionsを選択します。このボタンをクリックしたらFunctions.loadGameが呼び出されることを確認しましょう。
メニュー画面をロードするボタンの追加もできます。
Restartボタンの下に新規ボタンを作成し、その名称をQuitとします。QuitボタンがクリックされるとFunctions.quitが呼び出されます。
画面が完成したので、GameOverという名前で保存します。
メニュー画面
最後に、新規画面「Menu」を作成します。この画面は、アプリのロード後にユーザーが最初に目にするものです。
新規Canvasを先のように作成し、その中にボタンを2つ作ります。1つはNew Game、もう1つはQuitという名称にします。
図のように、各ボタンに対してFunctions.jsスクリプトから関数を呼び出します。
最後に
記事では、さまざまなゲームに利用可能な要素を紹介してきました。冒頭で述べたように、注目すべきはデザインではなく、アセットを自分がデザインしたものに簡単に変更できることです。メニュー画面を自分好みの独創的な仕上げにもできます。
これでゲームは完成です。あとは実際に作成してみてください。繰り返しになりますが、全コードのダウンロードはGitHubからできます。
(原文:How to Build a 2D Tapping Game in Unity)
[翻訳:Noriko O. Romano]
[編集:Livit]