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

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

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回目の記事を読んでください。



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

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



それではまた