//tips
//System.Linqについて
using System.Linqについて、もう少し調べてみる。
LINQとは、配列やListなどの要素を処理するメソッドを集めたライブラリで、for文やforeach文を使ったループ処理を簡潔に置き換えるのによく使う。
ループ処理の置き換え型として、ラムダ式が使われ、左辺 => 右辺の形で示される。
() => 右辺 // 変数なしの場合
v => 右辺 // 変数が1つの場合
(v1, v2) => 右辺 // 変数が2つの場合
下記で使われた、Selectメソッドは、要素全てを処理して別のオブジェクトに渡すときに使用する。この場合はhitsにgameObject型として処理した要素内容を格納するのに使用している。ラムダ式の右辺に要素の変換内容を記述する。
var hits = Physics.SphereCastAll(transform.position,search_radius,transform.forward, 0.01f).Select(h => h.transform.gameObject).ToList();
もう少し簡単な例で確認すると、
int[] src = {0, 1, 2, 3, 4, 5};
という配列があり、各要素を3で割った余りを算出したいとする。
foreach文を使って要素を1つずつ処理するのは面倒なので、下記のように配列にselectをつけ、各要素に対して一気に処理を行うことができる。
var query = src.Select(x => x % 3);
先の記述をもう少し細かくみていくと、下記のように分けることもできる。
//RaycastHit型からGameObject型への変更を段階に分けて記述する場合
List<RaycastHit> hits1 = Physics.SphereCastAll(transform.position,search_radius,transform.forward,0.01f).;
List<RaycastHit> hits1_RH_list = new List<RaycastHit>(hits1);
List<GameObject> hits1_GO_list = hits1_RH_list.Select(h => h.transform.gameObject).ToList();
//RaycastHit型からGameObject型への変更を一気に記述する場合
var hits2 = Physics.SphereCastAll(transform.position,search_radius,transform.forward,0.01f)
.Select(h => h.transform.gameObject).ToList();
配列の各要素に戻り値のある形または各要素を変換したい場合はselectが便利である。
また、条件指定して要素を抽出するWhere(value => value % 3 == 0)なども使いこなせればさらにラムダ式でシンプルなコードを描けるようになる。
//一度フリーズしたオブジェクトを再起動する
simpleCubeMove.enabled = false;でスクリプトを外したので一定時間経過後に再度スクリプトがアタッチされるように設定する。StartCoroutine("Restartmove”);をsimpleCubeMove.enabled = false;の下に入れておき、下記コードを時間差で発動するようにしておく。
public IEnumerator Restartmove()
{
yield return new WaitForSeconds(2);
simpleCubeMove.enabled = true;
}
これを実行するとなぜか2つのAiオブジェクトのうち一つしか再起動しないことがわかった。
最初の処理が終わったタイミングでコルーチンは終了してしまうのでwhileを用いてループ処理を記述する必要があったよう。
コルーチンのwhileを試しても良かったが、先にAIをrigidbodyを使用して目的地設定を繰り返しながら進むものに切り替えて、スクリプトのオンオフではなく、publicにしたAIのspeedなどの数値調整で停止と再稼働を確かめていきたい。
まずはNavmeshで操作するAIスクリプトを簡素化した。内容はランダムで目的地を設定し、目的地についたら次の目的地を設定し、移動するというもの。プレイヤーの追跡などは削除した。
using UnityEngine;
using System.Collections;
using UnityEngine.AI;
public class MoveAIEnemy2 : MonoBehaviour
{
private SetPosition setPosition;
// エージェント
private NavMeshAgent navMeshAgent;
void Start()
{
setPosition = GetComponent<SetPosition>();
setPosition.CreateRandomPosition(); //目的地設定
navMeshAgent = GetComponent<NavMeshAgent>();
}
void Update()
{
navMeshAgent.SetDestination(setPosition.GetDestination());//NavMeshAgentの「SetDestination」を使用して移動を開始
if (navMeshAgent.remainingDistance < 0.5f)
{
setPosition.CreateRandomPosition();
}
}
}
using UnityEngine;
using System.Collections;
public class SetPosition : MonoBehaviour
{
//初期位置
private Vector3 startPosition;
//目的地
private Vector3 destination;
void Start()
{
// 初期位置を設定
startPosition = transform.position;
}
// ランダムな位置の作成
public void CreateRandomPosition()
{
var randDestination = Random.insideUnitCircle * 4;
// 現在地にランダムな位置を足して目的地にする
SetDestination(startPosition + new Vector3(randDestination.x, 0, randDestination.y));
}
// 目的地設定
public void SetDestination(Vector3 position)
{
destination = position;
}
// 目的地取得
public Vector3 GetDestination()
{
return destination;
}
}
その上で、AIのnaviを一時停止するNavStop()メソッドを作成し、追加。その追加メソッドをFieldonEnterスクリプトのレイの接触で実行させるスクリプトを作成した。するNavStop()メソッドの中にはコルーチンの起動コードも埋め込み時間差で停止が治るようにしている。
using UnityEngine;
using System.Collections;
using UnityEngine.AI;
public class MoveAIEnemy2 : MonoBehaviour
{
private SetPosition setPosition;
// エージェント
private NavMeshAgent navMeshAgent;
void Start()
{
setPosition = GetComponent<SetPosition>();
setPosition.CreateRandomPosition(); //目的地設定
navMeshAgent = GetComponent<NavMeshAgent>();
}
void Update()
{
navMeshAgent.SetDestination(setPosition.GetDestination());//NavMeshAgentの「SetDestination」を使用して移動を開始
if (navMeshAgent.remainingDistance < 0.5f)
{
setPosition.CreateRandomPosition();
}
}
public void NavStop()
{
navMeshAgent.isStopped = true;
StartCoroutine("Restartmove");
}
private IEnumerator Restartmove()
{
yield return new WaitForSeconds(2);
navMeshAgent.isStopped = false;
}
}
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.AI;
public class FieldonEnter : MonoBehaviour
{
Rigidbody rb;
MoveAIEnemy2 CubeMove;
private void Start()
{
//GetComponent<SphereCollider>().enabled = false;
}
public void GetTargetfreeze()
{
float search_radius = 5f;
var hits = Physics.SphereCastAll(transform.position,search_radius,transform.forward, 0.01f).Select(h => h.transform.gameObject).ToList();
if (0 < hits.Count())
{
foreach (var hit in hits)
{
Debug.Log(hit);
if (hit.tag == "Enemy")
{
CubeMove = hit.GetComponent<MoveAIEnemy2>();
CubeMove.NavStop();
//rb = hit.GetComponent<Rigidbody>();
//rb.constraints = RigidbodyConstraints.FreezeAll;
}
}
}
}
/*
private void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.tag == "Enemy")
{
rb = GetComponent<Rigidbody>();
rb.constraints = RigidbodyConstraints.FreezeAll;
}
}
public void OnField()
{
GetComponent<SphereCollider>().enabled = true;
}
*/
}
AIの速度調整は下記のようにnavMeshAgentから変更することもできた。
public void NavStop()
{
//navMeshAgent.isStopped = true;//停止
navMeshAgent.acceleration = 0;
navMeshAgent.speed = 0.5f;
navMeshAgent.angularSpeed = 0;
StartCoroutine("Restartmove");
}
private IEnumerator Restartmove()
{
yield return new WaitForSeconds(2);
//navMeshAgent.isStopped = false;
navMeshAgent.acceleration = 8;
navMeshAgent.speed = 3;
navMeshAgent.angularSpeed = 120;
次はパーティクルシステムで光が湧き上がるエフェクトを作成していく。
下記のサイトを参考に作成した。
エフェクトをy軸方向に伸ばすためにstart life timeの時間を伸ばす。これを最初から各プレイヤーの子要素に格納しておき、条件によって発動するように調整していく。