Yaminabe

らくがきぶろぐ

てすと:Yaminabeちゃんは生まれたばかりのブログです みんなで仲よく使ってね

カテゴリ: シリーズ講座

だいぶ前回から間が開いてしまいました 今回はオブジェクトの移動と
ゲームプレイモデルに適用するのに必要なものの解説をします。

今回は解説の図がはいってないので少々読みにくいのと 基本しか書いてないので上達してしまった人には
物足りないかと思います 徐々に複雑な工程記事を入れていきますので がまんしてください。

Start()内で初期設定を行います Awake()との違いはAwake()がStart()より早く呼ばれることです。 例えばゲームオブジェクト全体に関わる設定などはAwake()の中で行ってStart()ではゲームオブジェクト内のローカルな設定を行うようにします。
たとえばゲームの開始で初期化したデータをゲームオブジェクトに引き継ぎたい時などAwake()で設定しStart()に引き継ぐようにするとうまくいきます。
各ゲームオブジェクトの実行のメインルーチンとなるUpdate()文にもLateUpdate()という関数が用意されていて これはUpdate()の動作が確定したあとに呼ばれます。カメラなどに仕込んでやるとゲームオブジェクトが移動を終えたあとにカメラを追従させるような動作をさせることができます。 このほかにFixedUpdate()という関数があり例えば
function FixedUpdate () {
rigidbody.AddForce (Vector3.up);
}

とすれば リジッドボディに上向きの力(Vector3.Up)を一定時間ごとに与え続けることになります。
FixedUpdate()の割り込み間隔は EDIT->ProjectSetting->Time の FixedTimestepの値を操作すると変更できます。


Start(){

mytransform=transform; //高速化手法として

//プレイヤーのサーチ
var target:Transform = GameObject.FindWithTag("player").transform;



Transformタイプはその下に position(位置)、rotation(回転)などのメンバーを従えていてそれらは全てVector3タイプです。
Vector3タイプというのはいわゆる(x,y,z)の3要素で、Vector3指定を行うと変数の下にx,y,zの3つの箱ができると考えてもらっていいと思います。
例えば
Var test:Vector3; 
と 宣言すれば "test.x","test.y","test.z"の3つの変数を宣言したことになります。VectorタイプにはVector2=(x,y)、Vector4=(x,y,z,w)などの仲間があります。
Transformタイプは宣言をTransform:で行いますがメンバの指定はtransform.position.x 、 transform.rotation.x
などのようにピリオドで区切って指定します。

初期設定では まずmytransform=transform;として ローカルなTransform型の変数を用意してゲームオブジェクトの値をコピーします。関数の内部でコピーした値つまりtransformのかわりにmytransformを使用することでキャッシュによって高速化が期待できるそうです。

そしてtargetにplayerの移動情報をコピーするのですが この時に使用するのが
Find系の関数です。 例では"player"というTagを指定してシーン全体にFindサーチをかけています。
TagはInspectorの上部Tagのウィンドウを開いて指定することができます。

Find系にも仲間の関数があり

GameObject.FindWithTag ("タグ名");
GameObject.Find("オブジェクト名");
FindObjectOfType(オブジェクトタイプ);
などとして使用することができます。変数を宣言するときにはJavaの場合は
var 変数名;とするだけで 自動判別してくれるのですが
var S:GUITexture = FindObjectOfType(GUITexture);
などときちんと型把握をしてキャスト(型指定)してやることで内部で判別するより高速化が期待できます。
Find系も実行速度が遅いためできる限りStart()内などで行ってしまったほうが処理が軽く済むようです。

では オブジェクトが複数の場合はどうするかというと

var respawns = GameObject.FindGameObjectsWithTag ("Respawn");
for (var respawn in respawns)
Instantiate (respawnPrefab, respawn.transform.position, respawn.transform.rotation);

というようにObjectをObjectsに変更するだけです。respawnsに帰ってくるのは
複数オブジェクトのGameObjectタイプのデータなので正確には
var respawns:GameObject[] = GameObject.FindGameObjectsWithTag ("Respawn");
というように書くのが正式になります。
タイプ指定のあとに[]をつけるとArray型つまり配列宣言したことになります。
簡単にいえば複数のデータを入れる箱が用意されたということです。


次に オブジェクトの移動の基本文です
 transform.Translate(Vector3.forward * Time.deltaTime * speed);
 
transform.Translate( 時機の正面方向 ×フレームあたりの時間 ×スピード);
となります。 始点と終点が決まっている場合はLerp関数が使えます。

transform.position = Vector3.Lerp(start.position, end.position, Time.deltaTime * speed);
Lerp関数は状態を指定した時間分の変化をする関数なので移動以外の変化例えばマテリアルのカラーを変化させるような場合にも使用できます。

リジッドボディに速度を与える方法だと
rigidbody.AddRelativeForce(Vector3.forward * Speed);
またはRigidbody.AddForce()


方向は前方に進むとして機首を狙った方向に向けなければなりません
よく使用される形は
var rotation = Quaternion.LookRotation(target.position ,transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * damping);

Quaternion.LookRotationはQuaternionタイプでtargetの位置と自機の位置(transform.position)を指定することで回転角を返してきます。
Quaternion.SlerpはゆっくりとTime.deltaTime * damping時間をかけて回転運動をさせます。先に出てきたLerp関数との違いはinterporate(補間)の差です。
Lerpを使用したほうが高速ですが補間が粗いため結果の見た目が悪くなります。
というリファレンスの解説があります。

狙った方向を向かせるという関数では transform.LookAt()というのもあります

transform.LookAt(target);   targetはVector3型のposition
のように使用します。モデルのZの+方向が指定した座標に向かいます,この関数は回転のスピードを指定できませんので、例えば常に目標を見続けるカメラ制御
や 弾の発射時の方向を指定するときなどに使用するといいと思います。
ちなみにこのブログの2Dシューティングの作例ではtransform.LookAt()にyieldを使用して回転に遅延をかけています。


ここで よく使われるnomalize(正規化)について話します。高校数学で習うと思いますが、ベクトルの算出は『target.position - transform.position』という
目標の座標から自分の座標を引き算することで算出できます。ただこれだと
長さを持ったベクトルになってしまうので、√x*x+y*y+z*zで長さを持ったベクトルを割ってやると長さが1の単位ベクトルになります。こtれを正規化と読びます。

例えばベクトル方向にスピードを与えて移動させたい場合は
        
  『正規化されたベクトル×移動スピード×TimeDelta.time』と指定することで正しい速度で移動できます。大きさを持ったベクトルを使用すると一瞬であさっての方向。にとんでいってしまうのを経験したことがあると思います。

Vector3.normalizedの使用例は

 Direction = (target.position - transform.position).normalized;

もうひとつよく使う関数がClampです
function Update () {
transform.position = Vector3(Mathf.Clamp(Time.time, 1.0, 3.0), 0, 0);
}

例ではTime.time時間の変数をmin=1.0、max=3.0で挟みつけ(Clamp) ています。
座標データなどが期待する範囲からこぼれないように範囲を決める関数です。


Transformに関する基本はこのくらいで、移動条件に関しては次回解説します
次にtargetへの攻撃の説明です。


targetのサーチ

ショット条件の判断 ex:角度 方向などを検証

弾の発射+音


という流れです

敵のサーチ:
 targetが複数の場合一番近いtargetを探したいときは
   
   var Near:float = Mathf.Infinity;
//Mathf.Infinityは無限に遠い数値を表す

   
var gos:GameObject[] = GameObjects.FindWithTag("enemy");
var target:GameObject;

for(var go in gos){
//go(gameobjectの短縮名)のなかにgos(GameObject型の配列)から順番にデータを受け取ります
var Distance = Vector3.Distance(go.transform.position,transform.position);
//Vector3.Distanceで距離を計測します
if(Near>Distance){
//DistanceがNearより近いと判定されたときにtargetの値を更新する
Near = Distance;
target = go;
}
}


Vector3.Distance(go.transform.position,transform.position)は (go.transform.position-transform.position).magnitude とかくこともできます。

次にtargetとの角度と距離を計算して攻撃条件に合っているかを調べます


例:
var shootAngleDistance = 10.0;

var forward = transform.TransformDirection(Vector3.forward);
var targetDir = target.position - transform.position;
if (Vector3.Angle(forward, targetDir) < shootAngleDistance){
     
    なにかの処理
   }



  正面方向のベクトルforwardとtargetの方向をVector3.Angleで角度をみて処理を行います。例ではshootAngleDistance = 10.0 つまりtargetとの角度が10度以内だったらなにか処理をおこすということになります。
 
さらにチュートリアルから例:

function CanSeeTarget () : boolean
//booleanタイプ(trueかfalseを返す指定)
{
if (Vector3.Distance(transform.position, target.position) > attackRange)
//targetと自分の距離を測ってattackRangeより大きければfalseを返す
return false;
//RAYを飛ばしてhit.transforにtargetが返ってきたらtargetとの間に障害物がないということなので射撃して良い すなわちtrueを返します。
//targetはTransform型です
var hit : RaycastHit;
if (Physics.Linecast (transform.position, target.position, hit))
return hit.transform == target;

return false;
}



これらの判定を終えたあとに弾をショットするファンクションFire ()
を呼びます。

例:
function Fire () {
var fireRate:float=0;

while(true) {
       fireRate += Time.deltaTime;
    if(fireRate >= 2.0){
        FireOneShot();
fireRate = 0;
}
}
}

このように書くと2秒間隔で弾を発射させることになります
FireOneShot()は実際に弾を発射する処理をするファンクションです。

var projectile:Rigidbody;//弾のプレファブ(Rigidbodyが設定されていること)
var initialSpeed:float=100.0;//弾のスピード

function FireOneShot(){

if(projectile){
//射出する弾(ここではprojectile)が登録されていることを確認します
var instantiatedProjectile : Rigidbody = Instantiate (projectile, transform.position, transform.rotation);

//instantiatedProjectileという名前で弾のコピーを作成します
instantiatedProjectile.velocity = transform.TransformDirection(Vector3 (0, 0, initialSpeed));

Physics.IgnoreCollision(instantiatedProjectile.collider, transform.root.collider);
//自分自身に当たるのを防ぐためPhysics.IgnoreCollision()で発射する自身のコリジョンを当たり判定から除外します。この場合transform.root.colliderつまりモデルのルート一番上の階層のコライダーを参照しています

 
 }

}



■自機のダメージ処理:

DamageReceiver 『ゲームオブジェクトが受けるダメージファンクション』

チュートリアルからダメージレシーバーのサンプルですゲームオブジェクトのどこに入っていてもいいので 別スクリプトにして登録しても良いのです。


function ApplyDamage (damage : float) {
// 既にヒットポイントが無かったら何もしない
if (hitPoints <= 0.0)
return;

hitPoints -= damage;
if (hitPoints <= 0.0) {
// 子になっているパーティクルエミッターを
GetComponentInChildrenで検索
var emitter : ParticleEmitter = GetComponentInChildren(ParticleEmitter);
// もしパーティクルエミッターが存在したら エミッターをtrue(表示状態)にします
       if (emitter)
emitter.emit = true;

//Invoke()関数は少し遅らせて実行する関数
Invoke("DelayedDetonate", detonationDelay);
}
}


function DelayedDetonate () {
BroadcastMessage ("Detonate");
}


function Detonate () {
//このゲームオブジェクトの消去
Destroy(gameObject);

// explosionプレファブの表示 if (explosion)としてプレファブがインスペクタでアサインされているかを確認する。Instantiate()関数は表示するオブジェクトがない場合実行が止まってしまうので、必ず確認作業を含める
if (explosion)
Instantiate (explosion, transform.position, transform.rotation);

// ゲームオブジェクトを壊れた状態のプレファブに置き換える
if (deadReplacement) {
var dead : Rigidbody = Instantiate(deadReplacement, transform.position, transform.rotation);
dead.rigidbody.velocity = rigidbody.velocity;
dead.angularVelocity = rigidbody.angularVelocity;
}

// パーティクルエミッターが子にアサインされているかを検出してもしあったならemitter.emit = falseでエミッターを停止emitter.transform.parent = null親から切り離す。

var emitter : ParticleEmitter = GetComponentInChildren(ParticleEmitter);
if (emitter) {
emitter.emit = false;
emitter.transform.parent = null;
}
}


ここにスコアを加算する部分例えば
ダメージをうけたあとに 『GameController.score += 10;』
さらにexplosionのプレファブが表示されるときにAudioリソースがアサインされていれば
var Sound : AudioClip;

if (Sound)
AudioSource.PlayClipAtPoint(Sound, transform.position);

または単に
if (audio) {
  if (!audio.isPlaying)
  audio.Play();
 }

を記述することで 爆発音を入れることができます。

ちなみにApplyDamage (damage : float)の damage は弾から受けるSendMessageの値で
敵の弾には
gameObject.SendMessage ("ApplyDamage", damage);
が記述されている必要があります これは講座みたいなもの3回目の記事を読んでください。



と少し駆け足ではありますが移動から攻撃までの簡単なスクリプトの流れをみてもらいました
次回はモデルデータを使ってそれにスクリプトを適用してみたいと思います。

次回はなるべく早く書くようにしますね あと記事に不明点、記事に希望する点があればコメントしてください 
あと 拍手、拍手コメントありがとうございます 力をいただいてます



それではまた

講座みたいなもの番外編です。
今回は "Unity3D 高速化" という検索ワードが非常に多かったため 公式サイトなどで確認した手法を
まとめてみます。


■『使用頻度の低いファンクションは削る』

■『FindObjectsOfType, FindGameObjectWithTag, GameObject.Find といったFind系は処理が重い』 出来る限りAwake()、Start()文中などの初期化段階で行いましょう

■『static型を使用してください』静的な変数であるStaticタイプを使用することでメモリは固定され処理効率があがります。 これちょっと訳が正確かわかりません 単純に静的な値の割付け(つまりキャストしなさい)ということかもしれません。 



最近のハードウェアでのフレームあたりのドローコールは500回程度ですが あまり多いポリゴン数とライトの数は処理を低下させます 
対策として

■『モデルをコンバインする』 MeshMergerなどを使用して シーン内のメッシュをまとめてしまいます。
まとめるモデルは なるべく同じマテリアルを使用していて 一枚のテクスチャがはられていることが好ましいです。
これはモデル数が多いとCPUの計算コストがあがりGPUに対して複数オブジェクトを何度もレンダリングコールすると
処理負荷が高いということに基づきます。

■『ライティングはライトマップに焼き付ける』これは3.0以降はビーストライトマップが使用できます。もちろん3Dソフトでライトマップを焼き付けるのも可能です。
 
■『ライトはカリングマスクを使用して必要以上のオブジェクトに照射しない』 レイヤーでカリングマスクを使用することでライトがどのオブジェクトにあたるかを指定することができます。必要以上のライティングは行わないようにするほうが高速です。

■『レイキャストは低速なのでを使用するときはかならずカリングマスクをして必要以外のオブジェクトは対象から外す』  これもやはりレイヤーマスクを使用して レイの判定に必要のないオブジェクトは除外します。

■『配列を使うときは静的な配列を使用する』 これはおそらく 配列の指定で 単にvar=Array(); とするより

var[100」などと配列の個数を静的に割り当ててしまったほうがメモリ効率が良いということだと思われます。
■『OnEnable、OnDisable を使用する』カメラに写っていないオブジェクトの処理をしないようにする 

■『カメラから遠くに存在するようなオブジェクトはUpdate()文中でReturnをする』

■『トリガーを使用してプレーヤーが接近した場合にのみ処理するようにする』 

■『coroutineを使用してUpdateを5秒間隔で行う』 例には5秒間隔と書かれているのですが ようするにYieldを使用してコルーチンにディレイを掛けるということだと思います。 たとえば敵のサーチなどの処理は毎フレーム行わなくても良い処理ですのでUpdate文のなかに書かずに コルーチン化して数秒に一回呼んでやるようにすれば処理が軽くなるということです

■『Math関数のサイクル数の目安』
• Add, subtract, multiplyは同程度のサイクル数
• Divide: 30-40 サイクル
• Square root, sin, cos: 60-100 サイクル

■『ループ内での Sqrt と Normalizeの使用を避ける』
Nomalizeは内部にSqrtを含むため
• if (distance > dif.magnitude)
• if (distance * distance > dif.sqrMagnitude)
などは重たいのでしない

■『Cacheを活用する』

例:
private var myTransform : Transform;
function Awake () {
myTransform = transform;
}


■『JavaScriptに関して』
Static typing をすればC#と同様の速度が得られるらしい 
つまり 型のキャストをしっかり手動で行う。
UnityのJavaはC++の50%程度のスピードで動く これはMozillaの20倍の速度
スクリプト内で#pragma strictを宣言すると 動的な変数割付に対してエラーが帰るので参考にして調整する




とここまでが公式の資料によるものです。

ここから下がリファレンスのオプティマイズに関しての記述の解説です。





■キャラクターデータの高速化

『キャラクタモデルには1つのスキンメッシュレンダラーで構成する』
『マテリアル数は可能な限り多く無いほうがいい シェーダー負荷が高いためで 例えば銃などマテリアルを分けなければならない場合は別モデルにすること』
『可能なかぎりボーン数は減らす 通常のゲームで15-60のボーン数 モバイルマシンなどでは30程度が適切な数値』
『ポリゴン数の目安 300-1500がモバイル 500-6000がデスクトップ
Half Life 2 のキャラクターで 2500-5000 , PS3 or Xbox 360 でだいたい 5000-7000 ぐらいが適当。』
『インバースキネマティックはFKにベイクしておく』
『未使用のリグは消しておく』
『正しく骨を命名してください』



■スクリプトの高速化
前の記述とダブるところがありますので注意して読んでください。

1. Use Static Typing

変数の型を静的に割付け つまり使用するときにきちんと宣言すること
宣言しなくても自動で判別して実行してくれるのですが その分処理がかかります

例:
function Start () {
var foo = GetComponent(MyScript);
foo.DoSomething();
}

  
       ↓ このようにする
      
function Start () {
var foo : MyScript = GetComponent(MyScript);
foo.DoSomething();
}



2. Use #pragma strict
『#pragma strict』を宣言して動的宣言しているところのエラーが返るようにして静的宣言に調整する
ただしですが どうしてもコンパイルエラーが返る場合があるので そういう場合は『#pragma strict』はずしてコンパイラに任せたほうがいいです
特にGetComponent()を使用して値を参照するときのエラーが消えないことが多いようです。

例:
#pragma strict
function Start () {
var foo : MyScript = GetComponent(MyScript) as MyScript;
foo.DoSomething();
}



3. コンポーネントの値のキャッシュ使用

transformをmyTransformと自分が用意した変数で定義します

例:
private var myTransform : Transform;
function Awake () {
myTransform = transform;
}

function Update () {
myTransform.Translate(0, 0, 5);
}


4. Use Builtin arrays
配列はあらかじめ使用する配列の個数を指定してしまうことで効率よくメモリ使用されるようです

例:
private var positions : Vector3[];
function Awake () {
positions = new Vector3[100];
for (var i = 0; i < 100; i++)
positions[i] = Vector3.zero;
}


5. 必要のないUpdateコールは避けましょう。

Update ()文は毎フレームコールされるので できれば必要ない条件でreturn;をはさんで何もさせない処理をするといいです
例はターゲットから遠い時に何もしないよう設定しています
またOnBecameVisible()、OnBecameInvisible ()でカメラに映っているときに処理をするように
指定します

例:

var target : Transform;

function Update () {
// Early out if the player is too far away.
if (Vector3.Distance(transform.position, target.position) > 100)
return;
// perform real work work...
}



例:

function OnBecameVisible () {
enabled = true;
}

function OnBecameInvisible () {
enabled = false;
}



2.トリガを使用するときには一番軽い球型(sphere)のコライダを使用すると高速です。
球の公式を見ればわかるのですが オブジェクトの接する判定はトリガ中心とオブジェクトの距離の判定だけです。
ボックスの場合は6面分の接触判定の計算になりますし カプセルの場合は縦横比分の計算コストがかかります。メッシュコライダはもちろん重いです。
3Dゲームの場合たいてい高速処理のために球型のコリジョンで判定します。 四角い場合などはオブジェクトの形に合わせて小さな球コリジョンをいくつか
並べるなどのやり方もあります。 もっとも最近のハードは高速ですので昔のようなこの手法は特別に処理軽減を狙わなければあまり必要ないと思われます。

例:

function OnTriggerEnter (c : Collider) {
if (c.CompareTag("Player"))
enabled = true;
}

function OnTriggerExit (c : Collider) {
if (c.CompareTag("Player"))
enabled = false;
}



■補足
このほかに有効そうな手法を記述します。

『ウェイトの影響範囲を2本以下に』これは プロジェクトのクオリティ設定でできます
『テクスチャの大きさを調整する』Unityのテクスチャの設定項目でサイズを変更できる箇所がありますので FPSを調べて重たい箇所のテクスチャサイズを小さくします
『シェーダーのパス数に注意 シェーダーはあまり重くしない』 シェーダーのソースをひらくとPASS:という箇所があると思いますが これがレンダラーのコール部分なのでこのPASSの合計数があまり多くなりすぎないようにシェーダーを選択します。
『LODモデルの切り替え』
これは前回の記事で解説しました。予めポリゴン数やテクスチャの小さな複数モデルを用意しておきカメラからの
距離に応じて切り替えることで高速化を期待する技術です。
背景には3.0から搭載されたオクルージョンカリングがありますがこれは カメラからの距離や画角などである程度ポリゴンを非表示にして処理軽減をしてくれる技術ですが、遠くから近づいてくるポリゴン数の大きなモデルなどは突然現れてしまうため見かけが悪い場合があります。 複雑なモデルデータはポリゴン数を減らしたモデルを切り替え表示したほうが良いかもしれません。
昔のはなしですが例えばレースゲームの背景でコーナー付近はポリゴン数が非常に多いため何段階かのLODをもつことがありました
ただあまり多くのオブジェクトに設定すると処理が逆に大きく掛かるので限定した方がいいと思います。

『ライトマップをつかう』 よくやりがちなのがシーン全体をテクスチャベイクしてしまうことですライティング情報として影などをテクスチャとして焼きこんでしまうわけですが当然テクスチャサイズが大きくシーン全体をカバーするには量も膨大になります。
テクスチャはタイリングをうまく活用し それほど解像度の大きくない内蔵のライトマップを使用すればこれは回避できます。 

『パーティクルは内蔵のものを使用する』 大量のアニメーションデータベタもちは当然重いです どうしてもやるならモデル的にマージして切り替えアニメにするほうがいいでしょう。
内蔵パーティクルはビルボード処理やソート処理などもしてくれますので やはり内蔵のものを使用するのがいいと思います。スクリプトで制御できる上にあまり重たくなくて性能的にはかなりいいです

これらをGameウィンドウのStats あるいはProfilerを見ながら調整を繰り返します。




この他に いまのところスタンドアローン版にはC++やObjective_CなどでDLLやプラグインを使うことが可能なようです(プロ版)
それがどのくらいの効果があるかは まだ試していないのでわかりませんが、将来的にはPS3やXBOXなどの
家庭用ゲームのプラットフォームにも拡張されると思います。
処理が必要な部分に局所的に使用すれば効果は高いのではないでしょうか


後述:

先日の某所のセミナーでUnityエンジンはスクリプトが低速で 次世代機で使うのは難しいという評価をしている開発者がいたようです。
まだ発展している段階で 大変バージョンアップの間隔も早いUnityエンジンです。
プラットフォーム自体 パワーがアガるスピードも年々はやまっていますので
現在の処理速度でこのゲームエンジンの評価をしてしまうのは早計だと思います。
他社製品のものに比べて重たいという評ですが 値段的に天と地ほども差がありそうな
高価なエンジンと比べれば多少使い勝手が悪いかも知れませんが この値段でこの性能をたたき出してるのは
むしろ驚愕だと思います。
世界中からの興味をひいているゲームエンジンです。日本にとっても低コストでゲーム開発ができるチャンスだと思いますが、世界中の誰にとってもチャンスなので 現在の研究潜伏期間を経て高いクオリティの作品が
登場するのも時間の問題かと思われます。そうなってしまってからでは日本の開発は更に劣勢に立つことになるわけで、 いまこのエンジンについて使える使えないなどという議論をするのはナンセンスです。
将来登場するさらに高性能のゲームエンジンのための布石としてUnityエンジンに慣れておくのはありだと思います。 そのときになって開発者がゲームエンジンの作法がわからないという状況が訪れはしないかというのが一番の懸念です。
ゲーム業界的にみると国内開発の一番の問題点はプロジェクトの大型化により昔のようにゲームを一人で作成した経験がなく開発全般をコントロールできる開発の人材がいないことがあげられます。海外のように映像系などのモノづくりに特化した教育をうけている開発者がディレクションをしている業界と競争していかなければ勝ち残れないわけで  これからのゲーム開発を考える上でゲーム制作をコントロールできる人材の育成は大変重要なことだと思います。 そういう意味を含めてこのブログではUnityでの開発に関するはなしをしていくつもりなのです。 まあ たいした話もできませんがw


■その他の最適化の関連記事

うにばな 講座みたいなもの番外編(高速化(最適化)について2  自分用のメモ)

うにばな 講座みたいなもの番外編(高速化(最適化)について3 モバイル系

うにばな メモリ使用量の最適化・ヒープメモリ ほか

今回も関数の解説です。 解説なんかリファレンス見ればいいじゃんとおっしゃる方もいるかも知れませんが ひとつ

ジャッキーチェンの映画『酔拳』で毎日基礎ばっかりやらされて嫌気が刺したジャッキーがもっと派手な必殺技を教えてくれって師匠に頼むんですね
。それで師匠は一つ技を教えてくれて、ジャッキーがそれをケンカで試すんですけど全く技が決まらないんです。 で失敗に懲りて真面目に教えを乞うわけなんですがいつまでも強くならなかった。ですけどラストで師匠に襲いかかる敵に向かっていままで覚えた技を自分のアレンジで繰り出すことで敵を撃退してしまいます。そこで初めて真の格闘家としてのレールに乗ったんだよー と 終劇を迎えるというおはなしなんですが 勉強したことをそのまま憶えるだけでは力にはならない自分なりのやり方をみつける すなわち『応用』が最も大事なんだよと語ってるわけです 深いでしょ。
リファレンスはもちろん有効ですけど 格ゲーで言えば技一覧表のコマンドを眺めているのと同じで 戦いの仕方を学んだことにはならないわけなのです、どうやって技を組み合わせれば戦えるのか応用を考えていこうとしているのが、うちの記事です。っていうとちょっと大げさですねw たいしたもんじゃないです。


前回のおさらい

ゲームコントローラーっていうのはUnityに提供されるものではなくて ゲーム全体を統率するコントローラーとして定義したものです。 検索されてしまった方誤解を与えて申し訳ありません。
前回は大まかにゲーム全体の仕組みについてざっくりとおはなししました。

今回はオブジェクトの製作をする予定でしたが、すこし都合が変わりましたバレット(弾)の解説をします。経緯の詳細は次の記事を待ってもらうとして

弾のスクリプトの構造ですが 基本的な組み合わせで、弾は 

●移動、
●衝突判定、
●ダメージの送信


のスクリプト部分で構成されています。 ちなみに今回と次回の解説はとても重要です。
『enemyBulletScript.js』を中心にしておはなしをします。主に関数の解説です。



fig10




衝突判定

衝突する側にColliderと移動オブジェクトにはRigidBodyを設定していることが前提です。

■OnTriggerEnter()

fig11



//↓ Collider型のotherObjectという変数にOnTriggerEnterから衝突したコリジョンのデータが帰ります。

相手のオブジェクトColliderコンポーネントにはかならずtriggerにチェックが入っていることが条件です。

var explosion:Transform;

function OnTriggerEnter( otherObject: Collider ){
//↓コリジョンのオブジェクトにつけられたタグが”enemy”だった場合
if(otherObject.gameObject.tag == "enemy"){
//explosionのprefabがアサインされていたら
if(explosion){
// explosionのprefab(爆発のパターン)を表示(Instansiate)します
var tempExplosion: Transform;

tempExplosion = Instantiate(explosion, transform.position, transform.rotation);
}
//衝突したコリジョンのオブジェクトのApplyDamage() という関数にdamageの値を送信します
    otherObject.gameObject.SendMessageUpwards("ApplyDamage",damage,SendMessageOptions.DontRequireReceiver);

// このオブジェクト(弾)を破壊
Destroy(gameObject);

}

}

という流れです。
explosionは爆発のプレファブなのですが、スクリプト先頭でプレファブなどを宣言する場合
Instansiateで使用する前に『if(プレファブ名){}』としておくと なんらかの原因でプレファブがアサインされなかった場合にエラーがでて停止することがありません。Instansiateにnull(空文字=なにもありません)が指定されるとUnityの実行が停止してしまうので気をつけてください。 

衝突判定からあるオブジェクトを除外する場合には

var bulletPrefab : Transform;
function Start () {
var bullet = Instantiate(bulletPrefab) as Transform;
Physics.IgnoreCollision(bullet.collider, collider);
}


リファレンスの例ですが bulletPrefab(弾のプレファブ)を 『Physics.IgnoreCollision(bullet.collider, オブジェクト名.Collider)』オブジェクト名のCollider との計算から省きます。

注) 『as Transform』は Transform型として定義 という指定です。

例えば弾を発射する自分自身に弾が当たらないように計算から除外するなどという使い方です。
1対他(オブジェクト対レイヤー)のような使い方はできないようですが、レイヤー同士の衝突の場合には
Physics.IgnoreLayerCollision(1つ目のレイヤーナンバー,2つ目のレイヤーナンバー,true);
の形で対処できるようです trueをfalseにすればレイヤー同士の衝突判定がおこなわれます。
Ignoreの宣言はゲーム開始で一度呼ばれればいいので Start()関数内で行ってください。


OnTriggerEnter() にはOnTriggerStay()とOnTriggerExit()という仲間があります。
OnTriggerEnter() はトリガーと衝突した瞬間に判定が発生します。
OnTriggerStay()  はトリガーに触れている間判定が出続けます。例えばトリガー内部に入っている間スピードがあがるなどといった使い方です。
OnTriggerExit()  は接触していたトリガーと離れた瞬間に判定が出ます。

■OnCollisionEnter()

トリガーと使用法は似ているのですが 返ってくる値がCollisionInfoな点が違います。
接触したポイント座標や法線がcollision.contacts[0]、collision.contacts[0].normalに
返ります。またcollision.gameObjectで衝突したコリジョンをもつオブジェクトのデータを参照することもできます。Triggerの場合返ってくるのはCollision.Collider だけです。
OnCollisionEnterの場合は
relativeVelocity, rigidbody, collider, transform, gameObject, contacts などの値が返ります。

var explosionPrefab : Transform;

function OnCollisionEnter(collision : Collision) {

var contact : ContactPoint = collision.contacts[0];
var rot : Quaternion = Quaternion.FromToRotation(Vector3.up, contact.normal);
var pos : Vector3 = contact.point;
Instantiate(explosionPrefab, pos, rot);

Destroy (gameObject);
}

これもリファレンスの例からです。 これは衝突したコリジョンの衝突位置と法線の回転方向をとってきてexplosion(爆発のプレファブ)を衝突位置に衝突方向に回転させてInstansiateさせています。

OnTrigger同様に OnCollisionStay()、OnCollisionExit()という仲間があります。


■Physics.Raycast()
ある方向にオブジェクトからRay(英語では光線 )を発射するイメージです。
レーザーサイトのような感じで交差する点からオブジェクトと交点の情報を返します。

var hit : RaycastHit;
if (Physics.Raycast (origin, direction, hit,
distance,layerMask)) {
var distanceToGround = hit.distance;
}


の形で扱うのが基本です。
originはRayを発生するオブジェクト座標 大体は transform.position
directionはRayを発生する方向 Vector3.upなら絶対座標の上(オブジェクトの上ではなくワールド座標の上方向でオブジェクトの上方向を指定するならtransform.upになる)

Vector3クラスの値は以下のとおり
zero    Shorthand for writing Vector3(0, 0, 0)
one    Shorthand for writing Vector3(1, 1, 1)
forward Shorthand for writing Vector3(0, 0, 1)
up    Shorthand for writing Vector3(0, 1, 0)
right    Shorthand for writing Vector3(1, 0, 0) 

forwardは正面ですが後面の場合-forwardと書きます 値は(0,0.-1)
hitはhitInfoです。Rayが当たったオブジェクトからの情報が帰ってきます。
この例の場合戻り値は指定した変数hitに帰ってきて
hit.point  Rayが当たった座標
hit.normal  Rayが当たった法線座標ベクターつまり面の角度
hit.distance Rayの当たった場所までの距離
hit.collider Rayの当たったコリジョン情報。通常hit.collider.gameObject.tagを指定して
例えば
if (hit.collider.gameObject.tag=="BG"){〜}などのようにタグが”BG”なら〜するという処理を記述します。

distance Rayを跳ばす距離です(float型) 。Mathf.Infinityを指定すれば無限。

layerMask  レイヤー番号に従ってRayのヒット判定を検出するオブジェクトを指定します。  少しややこしいのは32ビットのマスクを作成するところで

例えば 1<<9 とすると9番のレイヤーを指定したことになります。<< はビットシフト演算子と呼ばれるもので 2値だと 000000001 がシフトされて100000000 となり9番目のビットが立つイメージでしょうか。9番以外全部を指定する場合は~(1<<9)  ~は除外の演算子です。 複数のビットを立てたいときは(1<<9)||(1<<10)などのように||(or演算子)を使用してください。


fig9


衝突した相手オブジェクトにダメージを与える



OnTriggerEnter()文中でHitしたオブジェクトのCollider情報が検出できるわけです。

var damage:int=100;

function OnTriggerEnter( otherObject: Collider ){
  if(otherObject.gameObject.tag=="Enemy"){

var enemyStatus:enemyDamage = otherObject.GetComponent(enemyDamage);
enemyStatus.ApplyDamage(damage);

}


という手続きを簡略化したものがSendMessageで『講座みたいなもの一回目』でやりましたが

var damage:int=100;

function OnTriggerEnter( otherObject: Collider ){
  if(otherObject.gameObject.tag=="Enemy"){
otherObject.SendMessage ("ApplyDamage", damage);

}

2つの例は同じ動作をしていますがSendMessageをつかうとと少しだけ短いスクリプトで書けます。
あと衝突した相手オブジェクトのスクリプトを調べる必要がありません。




オブジェクト(弾)の移動
サンプルプロジェクトのenemyBulletScript.js の一部ですが

function Start(){

target= GameObject.Find("player").transform;
//ターゲットを探すこの場合はプレイヤーのトランスフォーム(位置座標)
}

function Update () {

if((aimingTimer <= aimingTime )&&(AimingActive==true)){
  //aimingTimer が 一定時間(aimingTime)より小さければ transform.LookAt()関数を使用してプレイヤーの座標に自オブジェクトの先頭を向ける        
transform.LookAt(target);

}

switch( projectType){


case 0:
transform.Translate(Vector3.forward * bulletSpeed * Time.deltaTime);
// transform.Translate(方向ベクター * 弾の速度 *1フレーム当たりの時間)
     break;


case 1:
.........................
case 2:
.........................
case 3:
.........................
case 4:
.........................

}
if(destroyTime!=0){
Destroy(gameObject,destroyTime);
   //destroyTimeが設定されていれば destroyTime秒後に自オブジェクトを破壊する。
     //destroyTimeはfloat型
     }


aimingTimer += Time.deltaTime;
// aimingTimerを1フレーム分加算する
}


}



というところで弾の解説をひととおりしました。ブログの文字数制限などもありますので一旦これでおわりにしてホーミングミサイルなどの話は弾の表示関係のオブジェクトなどがはいったときに一緒に解説しますね あとオブジェクトの移動に関してはエネミーの回で解説でしましょう。


今日はもう一回 日記を更新しますよ(Unity記事じゃないです)

■追記です 8月23日
shinriyoさんから指摘がありました 一部の漢字の間違い(値が帰る => 返る)と
enemyBulletScript.js これはエネミーにアサインするスクリプトです。サンプルプロジェクトをダウンロードした方は理解できると思って手を抜いてしまいました 申し訳ありません。

講座みたいなもの 2回目です。
サンプルゲームを詰めていたため少し更新に間があいてしまいました 申し訳ない。今回はサンプルプロジェクトのスクリプト概要についてです。
ゲーム全体のスクリプトの概要ですが

●ゲームコントローラー
●プレイヤー
●エネミー
●バレット(弾)
●背景スクロール
●背景モデル
●GUI
●サウンドコントローラー
●カメラ

などで構成されています

http://dl.dropbox.com/u/18978109/2DSTG_prj110521.zip

プロジェクトファイルへのリンクです↑


fig8



全体の概念図

■ゲームコントローラー
全体を統括するのがゲームコントローラーでメインループだと考えればいいかと思います。
static型の変数 例えばプレイヤースコア、プレイヤーのライフ、ハイスコア などを記述してあり 『static var XXXX:型』で指定してあれば どのスクリプトからでも『gameController.XXXX = ....』という形でアクセスが可能です。
ゲーム中のイベントを記述することも簡単で、例えば『if(Time.time==120){なにか実行文}』としておけば、プレイ時間120秒の時にメッセージを出すなどのイベントを起こすことになります。
ゲームオーバー処理 や GUI表示関係も まとめてあります。
他にもカメラの移動制御に命令を出したり BGMのコントロールをしたりといった
ゲームを動かす本体となるループが入ったスクリプトです。


■プレイヤー
スクリプトはキーの操作に応じて機体を前後上下に移動する部分と アイテムを取ったときに、弾の撃ち方やオプションを不可する部分 そして敵や背景の衝突時にライフを減らす部分のスクリプトで組まれています。 

■エネミー
最もUnityらしい作り方です。 
エネミーコントローラー =エネミーのアニメーションと移動を制御します。
エネミーウェポン= 弾の発射方式を指定します。
ダメージスクリプト= ダメージを受けたらヒットポイントを減らすスクリプトです。
多少変化はありますが、主にエネミー関係はこれら3種類のスクリプトをインスペクタにアタッチして作成されています。
スクリプトはそれぞれ使い回しが効くように作成しているため 例えば 背景の
柱にダメージスクリプトをアタッチすれば破壊できる柱になり エネミーコントローラーをアタッチすれば移動する背景ができます。

fig7



■バレット(弾)
直進する弾が基本で、プレイヤーを追跡するスクリプトをアタッチすればホーミングミサイルが作成できたり 壁で反射したりなどといった制御が可能です。
エネミーのウェポンは発射するまでの制御で、バレットのスクリプトは弾の跳び方を制御します。
バレットのスクリプトはコリジョンの当たり判定が出たときに 当たったオブジェクトに対してダメージメッセージを送信します。applyDamage(ダメージ量)
が相手のスクリプトに記述されていれば 一定ダメージ量を相手のオブジェクトのヒットポイントから減らします。

■スクロール背景
一番奥の宇宙の背景で 3枚のポリゴンプレーン(星 隕石 宇宙空間)にテクスチャを貼りつけて それぞれスピードを変えながら ゲームカメラの進行方向とは逆の左に向かって移動させています。 テクスチャはループするように描いてあるので、ある程度左に進んだら元のスタート位置に戻っています。
fig6fig5


■背景
背景のモデルにコリジョンが作成されていれば 衝突判定が出ます。
改めて後に解説しますが、タグ及びレイヤーで『BG』を指定してあれば背景のコリジョンに衝突があったという値が返ります。
ゲーム中では OnTriggerEnter()またはPhysics.Raycast()を用いて背景との
判定をとっています。

■カメラ
カメラは2つ以上設置してあります。一つがメインカメラでプレイに応じて右方向に自動で進みます。もう一つがスクロール背景用カメラで こちらは自由に動かすことで様々な背景を表示できます。例えばグラディウスVの立体的な背景を動かすようなことを考えています。プロバージョンの場合エフェクトをカメラ単位でかけることも可能です。 カメラの合成にはレイヤーおよびマスクを使用します。

■GUI
スコアなどの2DGUIの表示関係を制御します。現在のプロジェクトではゲームコントローラーに内包しています。

■サウンドコントローラー
BGMのボリュームコントロールやサウンドをアサインすることで音源のチェンジャーのような動作をします。


など



エネミーの制御などオブジェクトの数が多い物を同じスクリプトの使い回しが可能なように記述するのがUnityエンジンのゲームの作り方としてはスマートなようです。


■例文

すこし補足です。

エネミーの動きを制御するスクリプト(enemyController.JS)のなかで次のような
ルーチンのコールをしていますが

function Update(){

if(flag==true){

A();
}



function A(){
flag=false;

........

flag=true;


これはUpdate()文というのが止まること無くループし続けるためfunction A()の処理が終わらないうちにfunction A()が呼び出されてしまうのを避けています。
具体的にはアニメーションが終わる前に再びアニメーションが呼び出されるために
ガクガクとした動きになってしまうわけです。
同様な実行をするのに以下のような書き方もできます。

function Start(){
.
.
.
.
MainRoutine();
}

function MainRoutine(){
while(true){
yield A();
yield B();
yield C();
}
}
function A(){
yield WaitForSeconds();
}
function B(){
yield WaitForSeconds();
}
function C(){
yield WaitForSeconds();
}

こちらのほうがフラグも使わずスマートです。 ほかにもやり方がありそうなのでいろいろ考えてみてください。ちなみに下の方法だとスクリプトの先頭で
#pragma strict を指定しないとエラーが帰ってくるかもしれません。



プロジェクトの値を変更をして動作を確かめてみてください。

次回からこれらのスクリプトについて解説をしていきます
ついでにモデルとアニメーションを追加してゲームらしい体裁を整えていこうと考えています。

次回もっと早めに更新します。。。。。という予定ですw


追記:ストパンSTGのほうの更新はもちっと待ってください。 ファイルが消されるのを容量のせいだと思っていたんですが、どうやらunity3D形式のファイルのアップをFC2のホームページが認めていないみたいなんです。
だもので、ファイルを他の場所に移します。 
あと当初は初心者でも簡単に動かせることを目指したので 自動照準にしたのですけど 操作性に難有りなので、マウスを使用することも考えています。 といってもイベント関連の制御なども入れ込まねばならないのでしばらく後です。 お待ちいただいてる方々申し訳ないです。

講座みたいなもの 第一回です。

まずUnityでゲームを作成するときに必要な部分をまとめてみました。
Unityエンジンはスクリプトをインスペクタにアタッチすることで作成しますが、スクリプト同士のデータのやりとり
についてあまり解説がなされていないようですので簡単に解説します。


■型宣言

var 変数名:型名;
static var XXXX:int;
public var XXXX:int;
private var XXXX:int;


型名のところには 
int(整数)、float(実数)、String(文字列), Array(配列) の他に
Transform(移動座標系データ)、Rigidbody(物理演算データ)、Renderer(CG描画データ)、Camera(カメラデータ)、Animation(アニメーションデータ)、Collider(コリジョンデータ) ...
などといったコンポーネント型が入ります。
1


var
標準の宣言の仕方です。 インスペクタ上でデータに値を与えることができます
エディタ上での実行時にデータ値を確認することが可能です。(デバッグ時などに)

private var 
スクリプト内部でのみ使用するための宣言でインスペクタに表示されることはありません。
外部から参照することはできません。

static var
静的にメモリが割りつけられます。インスペクタ上に値は表示はされません。
他のスクリプトから main.variable (= スクリプト名.変数名) のように手続き無く
参照することが可能です。新規シーンファイル読み込み時に破壊されることがないので
ゲームのスコアなどデータを引き継ぐときにも使えるようです。

静的ってなに?
fig3



静的なデータはデータが解放されないため メモリを消費することになりますので
上手く活用してください。 Unityエンジンではstatic型を積極使用することで高速化が図れるという説明がされています。

public var
外部から参照するときに使用する宣言 データの参照については次の項で。
fig2



補足
function の{ }の内部で設定された値はそのfunction内でしか有効ではありません
外部参照する場合はその都度 外部から参照できる形の変数にコピーしてやる必要があります。
return文 を使用すれば呼び出した場所に値を引き渡すことができます。これはJavaなどの構文
function Update(){
   XXX=Move();

function Move(){
return 値;

この場合はXXXにMove()内で計算した値が帰ります。

注意:
型名は必ずしも宣言しなければならないわけではなく省略することもできます。
その場合はコンパイラが自動判別を行って最適化してくれます。 ただし手動でキャスト(型変換)しない場合 実行速度に影響が出ますので カウンターなどを表示させて
確認しながら高速化を図ってください。



■他のゲームオブジェクト、コンポーネントへのアクセス
リファレンスの該当ページは次のリンク↓
http://unity3d.com/support/documentation/ScriptReference/index.Accessing_Other_Game_Objects.html
http://unity3d.com/support/documentation/ScriptReference/index.Accessing_Other_Components.html


◯コンポーネントへのアクセス

コンポーネントのアクセスは非常に重要なのですが、リファレンスでは殆ど触れられていないようなので説明します。
リファレンスの説明は次のとおり

1.通常の取得

function Update () {
var otherScript = GetComponent(OtherScript);
otherScript.DoSomething();
}

DoSomething() がよくわからないかもしれませんが、要するにOtherScriptがゲームオブジェクトに設定されたスクリプト名で otherScriptという変数に一度取ってから
DoSomething()(ここには function か 変数名がはいります)。
もちろん
var otherScript = GetComponent(OtherScript).function名();
または
var otherScript = GetComponent(OtherScript).変数名();
とひとつづきに記述しても構いません。

2.ツリー構造中から取得する場合。

ツリー階層の内部を移動して検索する場合
transform.Find("Hand").GetComponent(OtherScript).foo = 2;
この場合階層内の”Hand"というオブジェクトを検索してOtherScriptに宣言された
fooに2を代入することになります。
transform.Find("Hand").GetComponent(OtherScript).function名(XXX);
この場合はfunction名で指定する関数にXXXの値を渡します。

まとめ
gameObject.GetComponent("XXXX").変数名
gameObject.GetComponent("XXXX").ファンクション()

などの形式でXXXXにはコンポーネント名がはいります。


コンポーネントの値を取る場合varの中では
private var 変数名:XXXX
XXXXにはコンポーネント名として宣言しなければなりません。

private var target : SeagullFlightPath;
private var origin : Transform;

function Start(){
origin = transform.parent;
target = origin.GetComponent(SeagullFlightPath);


これは公式のチュートリアルの南の島?のカモメに飛行パスを設定する部分です。
この場合はカモメの親が飛行パスを設定するオブジェクトです。ここでは
origin という変数に transform.parent を代入しています Unityエンジン内ではparentで一つ上の階層のオブジェクトを指定することになっているようです。
(階層の一番上に移動したい場合はrootを指定します)
targetの型宣言でSeagullFlightPath としています これは親のインスペクタに
あるコンポーネント(スクリプト)です。
この例の場合はtarget.XXX のようにXXXにSeagullFlightPath内でVarで宣言された
値をコールできます。XXXは 『public var』の指定をしてください。

子オブジェクトからコンポーネントを取得したい場合は
GetComponentInChildren()を使用します。


Static型のfunction
ちなみにコルーチンを static宣言をすれば 変数のstatic宣言と同様に
static function XXXX(){

とすれば
compo.XXXX (= コンポーネント名.XXXX())
の形で直接呼び出すことができます。

SendMessage・・・・・・・・・・・・・・・・・

手続きを経ずにファンクションコールをする方法としてUnityエンジンには
SendMessageという関数が用意されています。

SendMessage ("ApplyDamage", 5.0);

↓対象のコンポーネントに記述されたファンクション。
function ApplyDamage (damage : float) {
print (damage);
}
特定のゲームオブジェクトに送信する場合は
gameObject.SendMessage ("ApplyDamage", 5.0);

↓対象のゲームオブジェクト内のコンポーネントに記述されたファンクション。
function ApplyDamage (damage : float) {
print (damage);
}

SendMessageの欠点として 引数をひとつしか取れない点が上げられます。
どうしても複数の値を送信したい場合は
var arg1 = "test";
var arg2 = "test2";
var arr = new string[arg1,arg2];

object.SendMessage("yourfunctionname", arr);
のように配列に値をいれてからメッセージを送ることで複数のデータを送信することが可能なようです。

似た関数に BroadcastMessage() がありますそちらはゲーム中に存在する
(子のオブジェクトを含む)全てのスクリプト中にあるファンクションに対してメッセージを送信します。
使用方法はSendMessageと同じです。






◯ゲームオブジェクトへのアクセス
話が前後しますが他のゲームオブジェクトにアクセスする方法についてです。
リファレンスの内容説明をします。

1. インスペクタにアサインする方法
var target : Transform;
function Update () {
target.Translate(0, 1, 0);
}

fig4


2. ツリー構造内のオブジェクトを参照する

transform.Find()でツリー構造内を移動できます。
これもやはり
transform.root  =ツリーのトップ
transform.parent =一つ上のオブジェクト
という指定することができます。

transform.Find("Hand").GetComponent(OtherScript).DoSomething();
オブジェクト”Hand”を検索して OtherScriptというコンポーネントに作用するスクリプトです。

for (var child : Transform in transform) {
child.Translate(0, 10, 0);
}
子に連なる複数オブジェクトのトランスフォームをとって制御します。

3. Tagで探す場合。
この場合GameObject.FindWithTagを使用してPlayerのタグを検索しy方向に1.0の
移動をすることになります。
var player = GameObject.FindWithTag("Player");
player.transform.Translate(0, 1, 0);

よくある例

for(go in gameObject.FindGameObjectsWithTag("Enemy"))
{
var difference = (go.transform.position-myTrans.position);
 distance = difference.magnitude;
   }
などのようにEnemyタグの複数オブジェクトと自機の距離を判定するような使い方をします。


4. パラメータから取得する場合
OnTriggerStay()はコリジョン内に存在するゲームオブジェクトの判定をします
other:Collider でコリジョン衝突しているオブジェクトの情報を返してきます。
よくでてくる書き方ですが、エネミーの弾丸のスクリプトでこの場合はotherObjectにコリジョン情報が帰ってきます
if(otherObject.gameObject.tag == "Player")
Playerタグが検出されたら 処理を起こすスクリプトです。

function OnTriggerEnter(otherObject: Collider){
if(otherObject.gameObject.tag == "Player"){
var tempExplosion: Transform;

tempExplosion = Instantiate(explosion, transform.position,    transform.rotation);

GameController.playerLives --;
Destroy(gameObject);
    }



5. オブジェクトのタイプで探す場合
FindObjectOfType()を使用して as HingeJoint(=HingeJoint型にキャスト(変換))
forループの中で HingeJoint in hingesを使用して hingesの中からデータをhingeに
移しながらループ実行します。 hingesはHingeJoint型の配列です。

function OnMouseDown () {
var hinges : HingeJoint[] = FindObjectsOfType(HingeJoint) as HingeJoint[];
for (var hinge : HingeJoint in hinges) {
hinge.useSpring = false;
}
}





すこし情報量が多いですがUnityエンジンのゲーム作成に非常に重要なものですので
いまわからなくても 作成の途中で見返して理解してみてください。
一回目は少し難解だったと思いますが、次回からは簡単になります スローペースにいきましょう
最初に高い山にのぼるのが ぶぎょう流ですw

次回からサンプルのプロジェクトの解説をします。 それではー


↑このページのトップヘ