一通り画面を作成したら、スクリプトを作成します。
ソースはこちらで公開しています。
本ページ: https://github.com/matsushima-terunao/unity_toride_old
改良版: https://github.com/matsushima-terunao/unity_toride
実際のゲームはこちら
PC 本ページ: http://ter.dip.jp/blog/unity_toride_old/build/web/web.html
PC 改良版: http://ter.dip.jp/blog/unity_toride/build/build.html
Android 本ページ: https://github.com/matsushima-terunao/unity_toride_old/raw/master/build/toride.apk
Android 改良版: https://github.com/matsushima-terunao/unity_toride/raw/master/build/toride.apk
Update() 内で、ゲーム中以外ではラベルを表示し、マウスクリックまたは画面タッチでゲームをスタートさせます。
赤枠内は Player の入力判定処理です。前回押下状態なら移動量を求め、今回離されたなら発射フラグを立てます。
緑枠内の処理で移動量から Player の砲台の回転量を決め、回転させます。
青枠内は相手側砲台の処理です。相手側砲台と前回砲弾着点との角度と、Player 砲台と前回砲弾着地点との角度の差から、Y軸方向の回転量を決めます。また、相手側砲台と前回砲弾着点との距離と、Player 砲台と前回砲弾着地点との距離の差から、X軸方向の回転量を決めます。
オレンジ枠内は砲弾の発射処理です。まず、砲台の角度から砲弾の発射角度を求めておきます。そして、Instantiate() で砲弾の Prefab から砲弾を実体化します。
→ 2-3. Unity で砲台ゲームを作る - 改良
← 2-1. Unity で砲台ゲームを作る
↑ 一覧
ソースはこちらで公開しています。
本ページ: https://github.com/matsushima-terunao/unity_toride_old
改良版: https://github.com/matsushima-terunao/unity_toride
実際のゲームはこちら
PC 本ページ: http://ter.dip.jp/blog/unity_toride_old/build/web/web.html
PC 改良版: http://ter.dip.jp/blog/unity_toride/build/build.html
Android 本ページ: https://github.com/matsushima-terunao/unity_toride_old/raw/master/build/toride.apk
Android 改良版: https://github.com/matsushima-terunao/unity_toride/raw/master/build/toride.apk
MainScript
ゲーム全体を管理するスクリプトです。MainScript は特にオブジェクトに関連したスクリプトではありませんが、オブジェクトに関連付けないとスクリプトが動作しないので、ここでは Main Camera に関連付けます。 Project タブ -> Create -> Javascript -> 名前を MainScript に変更
MainScript をダブルクリック -> MonoDevelop が起動するのでスクリプトを編集
MainScript を Hierarchy タブ -> Main Camera へドロップして関連付ける
スクリプト内に宣言した変数が Inspector タブ -> Game Main (Script) パネル内に表示されます。これをオブジェクトに関連付けます。(スクリプト中の青枠の部分)MainScript をダブルクリック -> MonoDevelop が起動するのでスクリプトを編集
MainScript を Hierarchy タブ -> Main Camera へドロップして関連付ける
Hierarchy タブ -> canon(Player) を Player の右の枠 へドロップ
Hierarchy タブ -> canon(相手側) を Other の右の枠 へドロップ
Hierarchy タブ -> GUI Text を Label の右の枠 へドロップ
Hierarchy タブ -> canon(相手側) を Other の右の枠 へドロップ
Hierarchy タブ -> GUI Text を Label の右の枠 へドロップ
Update() 内で、ゲーム中以外ではラベルを表示し、マウスクリックまたは画面タッチでゲームをスタートさせます。
/**
* ゲームメイン。
*
* @author 2014/08 matsushima
*/
#pragma strict
/** ゲーム状態: 開始 */
static var STATE_START:int = 0;
/** ゲーム状態: ゲーム中 */
static var STATE_RUNNING:int = 1;
/** ゲーム状態: 勝ち */
static var STATE_WIN:int = 2;
/** ゲーム状態: 負け */
static var STATE_LOSE:int = 3;
/** ゲーム状態 */
static var state:int = STATE_START; // ゲーム状態: 開始
/** デバッグテキスト */
static var debugTexts:Hashtable = new Hashtable();
/** プレーヤー */
var player:Transform;
/** 対戦相手 */
var other:Transform;
/** テキストラベル */
var label:Transform;function Start () {
}
function Update () {
switch (state) {
case STATE_START: // ゲーム状態: 開始
label.guiText.text = "START";
label.guiText.enabled = true;
break;
case STATE_RUNNING: // ゲーム状態: ゲーム中
break;
case STATE_WIN: // ゲーム状態: 勝ち
label.guiText.text = "YOU WIN";
label.guiText.enabled = true;
break;
case STATE_LOSE: // ゲーム状態: 負け
label.guiText.text = "WIN LOSE";
label.guiText.enabled = true;
break;
}
// click or touch start
if (label.guiText.enabled) {
if (Input.GetMouseButtonUp(0)
|| (Input.touchCount > 0) && (TouchPhase.Ended == Input.GetTouch(0).phase)) {
state = STATE_RUNNING; // ゲーム状態: ゲーム中
label.guiText.enabled = false;
player.GetComponent(cannonScript).Init();
other.GetComponent(cannonScript).Init();
}
}
}
function OnGUI() {
// デバッグテキスト表示
var y:int = 0;
for (var k:Object in debugTexts.Keys) {
GUI.Label(Rect(0, y * 20, 400, 20), debugTexts[k].ToString());
++ y;
}
}
static function Log(key:String, text:Object) {
// // デバッグテキスト表示登録
// debugTexts[key] = text;
// // デバッグテキストログ出力
// print(text);
}
cannonScript
砲台を制御するスクリプトです。MainScript と同様の方法でスクリプトを Hierarchy タブの cannon(Player, 対戦相手両方)に関連付けます。同様に Inspector タブ上に表示されるスクリプト内変数のうち以下のものを関連付けます。 Ball Prefab: Project -> Assets -> ball
Particle Prefab: Project -> Assets -> Particle System
Terrain: Hierarchy -> Terrain
Update() はフレームごとに定期的に呼び出されますが、Player と相手砲台それぞれで呼ばれます。それで、Inspector タブの Tag に Player が設定されているかをParticle Prefab: Project -> Assets -> Particle System
Terrain: Hierarchy -> Terrain
if (gameObject.CompareTag("Player")) {
で識別しています。赤枠内は Player の入力判定処理です。前回押下状態なら移動量を求め、今回離されたなら発射フラグを立てます。
緑枠内の処理で移動量から Player の砲台の回転量を決め、回転させます。
青枠内は相手側砲台の処理です。相手側砲台と前回砲弾着点との角度と、Player 砲台と前回砲弾着地点との角度の差から、Y軸方向の回転量を決めます。また、相手側砲台と前回砲弾着点との距離と、Player 砲台と前回砲弾着地点との距離の差から、X軸方向の回転量を決めます。
オレンジ枠内は砲弾の発射処理です。まず、砲台の角度から砲弾の発射角度を求めておきます。そして、Instantiate() で砲弾の Prefab から砲弾を実体化します。
Object.Instantiate
static function Instantiate(original: Object, position: Vector3, rotation: Quaternion): Object;
static function Instantiate(original: Object): Object;
パラメータ
original: 複製したい既存オブジェクト
position: 新規オブジェクトの位置
rotation: 新規オブジェクトの回転
戻り値
original のオブジェクトをクローンしたうえで返り値とします
オブジェクトをエディタ上の Duplicate コマンドと同様の方法で複製します。
AddForce() で砲弾に発射角度の力を加えて移動させます。Rigidbody.AddForce
AddForce(force: Vector3, mode: ForceMode = ForceMode.Force): void;
パラメータ
force: 加える力のベクトル
mode: どのように力を適用するか
AddForce(x: float, y: float, z: float, mode: ForceMode = ForceMode.Force): void;
パラメータ
x: 加える力のベクトルの X 成分
y: 加える力のベクトルの Y 成分
z: 加える力のベクトルの Z 成分
mode: どのように力を適用するか
Rigidbodyオブジェクトに力を加える。加えることでオブジェクトを動かすことができる。
数フレームに渡って力を加えるには Update でなく FixedUpdate の中で適用すべきです。
砲弾側のスクリプトの変数に砲台を代入します。「オブジェクト.GetComponent(スクリプト名).変数名または関数名」で別スクリプトの変数・関数にアクセスできます。GameObject.GetComponent
GetComponent(type: Type): Component;
パラメータ
コンポーネントの型
GetComponent(type: string): Component;
パラメータ
コンポーネントの型の名前
コンポーネントを取得します。コンポーネントを取得することで、Rigidbodyクラスやスクリプトなどを参照したりすることが可能になります。
OnCollisionEnter() 内は砲台がほかのオブジェクトと衝突したときの処理です。砲弾と衝突した場合、破壊エフェクトを生成し、砲台が Player か相手かによって勝ち負けを判断します。MonoBehaviour.OnCollisionEnter(Collision)
OnCollisionEnter(collision : Collision)
パラメータ
collision: 接触地点、衝突強さなどの情報
collider/rigidbody がほかの collider/rigidbody に接触し始めたときに呼ばれる。
Init() は対戦開始時に MainScript から呼ばれる砲台初期化ルーチンです。/**
* 砲台。
*
* @author 2014/08 matsushima
*/
#pragma strict
var BALL_FORCE:float = 1800;
/** 砲弾 */
var ballPrefab:Transform;
/** 爆発 */
var particlePrefab:ParticleSystem;
/** 爆発 */
var particle:ParticleSystem = null;
/** 地形 */
var terrain:Terrain;
/** 砲弾存在 */
var ballExists:boolean = false;
/** 砲弾位置 */
var ballPosition:Vector3;
/** マウスボタン */
var mouseDown:boolean = false;
/** マウス位置 */
var mousePos:Vector3;
/** タッチ */
var touchDown:boolean = false;
/** タッチ位置 */
var touchPos:Vector3;
function Start () {
}
function Update () {
if (MainScript.STATE_RUNNING != MainScript.state) { // not ゲーム状態: ゲーム中
return;
}
var angleResolution:float = 180.0 / Mathf.Min(Screen.width, Screen.height); // 操作回転量: 画面短辺 -> 180度
var distance:Vector3 = Vector3.zero; // 入力移動量
var fire:boolean = false; // 発射
if (gameObject.CompareTag("Player")) {
// 自分
// mouse drag: 移動量を求める
var mousePosPrev:Vector3 = mousePos;
mousePos = Input.mousePosition;
if (mouseDown) {
distance = mousePos - mousePosPrev;
}
// mouse down
if (Input.GetMouseButtonDown(0)) {
mouseDown = true;
}
// mouse up: 発射
if (Input.GetMouseButtonUp(0)) {
fire = mouseDown;
mouseDown = false;
}
// touch swipe
var touchPosPrev:Vector3 = touchPos;
if (Input.touchCount > 0) {
var touch = Input.GetTouch(0);
switch (touch.phase) {
// touch down
case TouchPhase.Began:
touchDown = true;
touchPos = touch.position;
break;
// touch swipe: 移動量を求める
case TouchPhase.Moved:
touchPos = touch.position;
distance = touchPos - touchPosPrev;
break;
// touch up: 発射
case TouchPhase.Ended:
fire = touchDown;
touchDown = false;
touchPos = touch.position;
distance = touchPos - touchPosPrev;
break;
}
} // mouse or touch 移動量 -> 回転
// 縦方向: x軸回転
// 横方向: y軸回転
if (Vector3.zero != distance) {
var angle:Vector3 = transform.eulerAngles - Vector3(distance.y, distance.x, 0) * angleResolution;
angle.x = Mathf.Max(0, Mathf.Min(80, angle.x));
angle.y = Mathf.Max(80, Mathf.Min(190, (angle.y + 180) % 360)); // -100..10
angle.y = (angle.y + 180) % 360;
transform.eulerAngles = angle;
MainScript.Log("eulerAngles", "eulerAngles=" + angle + "," + transform.eulerAngles);
} } else {
// 相手
if (!ballExists) { // not 砲弾存在
var player:GameObject = GameObject.FindGameObjectWithTag("Player");
// 横回転量 = (プレイヤーとの角度 - 砲弾との角度) / 2 + ランダム
var playerAngle = Mathf.Atan2(
player.transform.position.z - transform.position.z,
player.transform.position.x - transform.position.x) * 180 / Mathf.PI;
var ballAngle = Mathf.Atan2(
ballPosition.z - transform.position.z,
ballPosition.x - transform.position.x) * 180 / Mathf.PI;
distance.y -= (playerAngle - ballAngle) / 2 + Random.value * 2 - 1;
// 縦回転量 = (プレイヤーとの距離 - 砲弾との距離) / 10 + ランダム
var magnitude:float
= (player.transform.position - transform.position).magnitude
- (ballPosition - transform.position).magnitude;
distance.x += magnitude / 10 + Random.value * 2 - 1;
// 回転
transform.eulerAngles += Vector3(distance.x, distance.y, 0);
// 発射。
fire = true;
} }
// 発射。
if (fire && !ballExists) {
// 発射角度
var y:float = Mathf.Cos(2 * Mathf.PI * transform.eulerAngles.x / 360);
var r:float = Mathf.Sin(2 * Mathf.PI * transform.eulerAngles.x / 360);
var x:float = r * Mathf.Sin(2 * Mathf.PI * transform.eulerAngles.y / 360);
var z:float = r * Mathf.Cos(2 * Mathf.PI * transform.eulerAngles.y / 360);
angle = Vector3(x, y, z);
// インスタンスの生成
var ball:Transform = Instantiate(ballPrefab, transform.FindChild("Cylinder").position + angle * 5, Quaternion.identity);
ballExists = true;
// 力を加える
ball.rigidbody.AddForce(angle * BALL_FORCE);
MainScript.Log("AddForce", "AddForce=" + angle * BALL_FORCE + "," + (angle * BALL_FORCE).magnitude);
// 発射元の砲台
ball.GetComponent(ballScript).cannon = transform;
}}
function OnCollisionEnter(collision: Collision) {
MainScript.Log("cannon collision", "cannon collision: " + collision.gameObject.name + "," + collision.contacts[0].point);
if ("ball(Clone)" == collision.gameObject.name) {
// 砲台非表示
gameObject.SetActive(false);
// 破壊エフェクト。
// インスタンスの生成
particle = Instantiate(particlePrefab, transform.position, Quaternion.identity);
// 勝ち負け
if (MainScript.STATE_RUNNING == MainScript.state) {
MainScript.state = (gameObject.CompareTag("Player") ? MainScript.STATE_LOSE : MainScript.STATE_WIN);
}
}
}
/**
* 初期化。
*/
function Init() {
ballExists = false;
mouseDown = false;
touchDown = false;
// 破壊エフェクト破棄
if (null != particle) {
Destroy(particle);
particle = null;
}
// 砲台角度・位置
if (gameObject.CompareTag("Player")) {
// プレイヤー
transform.eulerAngles = Vector3(45, 315, 0);
transform.position.x = 100 - 20 * Random.value;
transform.position.z = 20 * Random.value;
} else {
// 対戦相手
transform.eulerAngles = Vector3(45, 135, 0);
ballPosition = Vector3(100 * Random.value, 0, 100 * Random.value);
transform.position.x = 20 * Random.value;
transform.position.z = 100 - 20 * Random.value;
}
// 砲台の地形の高さ
transform.position.y = terrain.terrainData.GetHeight(
transform.position.x * terrain.terrainData.heightmapWidth / terrain.terrainData.size.x,
transform.position.z * terrain.terrainData.heightmapHeight / terrain.terrainData.size.z)
- transform.localScale.x;
MainScript.Log("height", "height=" + transform.position.y);
// 砲台表示
gameObject.SetActive(true);
}
ballScript
砲弾を制御するスクリプトです。MainScript と同様の方法でスクリプトを Project タブの Assets 内の ball に関連付けます。/**
* 砲弾。
*
* @author 2014/08 matsushima
*/
#pragma strict
/** 発射元の砲台 */
var cannon:Transform;
function Start () {
}
function Update () {
// 高さ < 0 なら破棄
cannon.GetComponent(cannonScript).ballPosition = transform.position;
if (transform.position.y < 0) {
Destroy(gameObject);
}
}
function OnCollisionEnter(collision: Collision) {
MainScript.Log("ball collision", "ball collision: " + collision.gameObject.name + "," + collision.contacts[0].point);
// 地形なら破棄
if ("Terrain" == collision.gameObject.name) {
Destroy(gameObject, 3); // 3秒後に破棄
}
}
function OnDestroy() {
// 破棄されたら砲台側の砲弾生存フラグをクリア
if (null != cannon) {
cannon.GetComponent(cannonScript).ballExists = false; // 砲弾存在
}
}
→ 2-3. Unity で砲台ゲームを作る - 改良
← 2-1. Unity で砲台ゲームを作る
↑ 一覧
0 件のコメント:
コメントを投稿