Skip to content

Instantly share code, notes, and snippets.

@urahimono
Created October 15, 2017 13:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save urahimono/5c8ffa4c5e1bd819c196e43195acd803 to your computer and use it in GitHub Desktop.
Save urahimono/5c8ffa4c5e1bd819c196e43195acd803 to your computer and use it in GitHub Desktop.
【Unity】NavMeshを学ぶ
//------------------------------------------------------------------------
//
// (C) Copyright 2017 Urahimono Project Inc.
//
//------------------------------------------------------------------------
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
[RequireComponent( typeof( NavMeshAgent ) )]
public class ObjectController : MonoBehaviour
{
[SerializeField]
private Transform[] m_targets = null;
[SerializeField]
private float m_destinationThreshold = 0.0f;
protected NavMeshAgent m_navAgent = null;
private int m_targetIndex = 0;
private Vector3 CurretTargetPosition
{
get
{
if( m_targets == null || m_targets.Length <= m_targetIndex )
{
return Vector3.zero;
}
return m_targets[ m_targetIndex ].position;
}
}
protected virtual void Start()
{
m_navAgent = GetComponent<NavMeshAgent>();
m_navAgent.destination = CurretTargetPosition;
StartCoroutine( UpdateOffMeshLink() );
}
private void Update()
{
if( m_navAgent.remainingDistance <= m_destinationThreshold )
{
m_targetIndex = ( m_targetIndex + 1 ) % m_targets.Length;
m_navAgent.destination = CurretTargetPosition;
}
}
private IEnumerator UpdateOffMeshLink()
{
// オフメッシュリンクの挙動が自動モードの場合は、この処理は行わない。
if( m_navAgent.autoTraverseOffMeshLink )
{
yield break;
}
while( true )
{
// オフメッシュリンクに乗るまで待機。
while( !m_navAgent.isOnOffMeshLink )
{
yield return null;
}
// NavMeshの挙動を止めます。
// Stop()はObsoleteになったようなので使わないよ。
m_navAgent.isStopped = true;
// オフメッシュリンクと高さに差があるので、ちょっと微調整。
OffMeshLinkData offMeshLinkData = m_navAgent.currentOffMeshLinkData;
Vector3 targetPos = offMeshLinkData.endPos;
targetPos.y += transform.position.y - offMeshLinkData.startPos.y;
yield return OffMeshLinkProcess( targetPos );
// オフメッシュリンクの計算を完了する。
m_navAgent.CompleteOffMeshLink();
// NavMeshの挙動を再開する。
// Resume()はObsoleteになったようなので使わないよ。
m_navAgent.isStopped = false;
}
}
protected virtual IEnumerator OffMeshLinkProcess( Vector3 i_targetPos )
{
yield return null;
}
} // class ObjectController
//------------------------------------------------------------------------
//
// (C) Copyright 2017 Urahimono Project Inc.
//
//------------------------------------------------------------------------
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
[RequireComponent( typeof( NavMeshAgent ) )]
[RequireComponent( typeof( Rigidbody ) )]
public class ObjectJumpController : ObjectController
{
[SerializeField]
private float m_jumpTime = 0.0f;
private Rigidbody m_rigidbody = null;
protected override void Start()
{
base.Start();
// 放物線の挙動のためにRigidbodyを使う。
// NavMeshAgentを使っているときは使いたくないので、
// isKinematicを有効にしてRigidbodyの無効化する。
m_rigidbody = GetComponent<Rigidbody>();
m_rigidbody.isKinematic = true;
}
protected override IEnumerator OffMeshLinkProcess( Vector3 i_targetPos )
{
m_rigidbody.isKinematic = false;
m_rigidbody.velocity = Vector3.zero;
ShootFixedTime( i_targetPos, m_jumpTime );
yield return new WaitForSeconds( m_jumpTime );
transform.position = i_targetPos;
m_rigidbody.isKinematic = true;
m_rigidbody.velocity = Vector3.zero;
}
private void ShootFixedTime( Vector3 i_targetPosition, float i_time )
{
float speedVec = ComputeVectorFromTime( i_targetPosition, i_time );
float angle = ComputeAngleFromTime( i_targetPosition, i_time );
if( speedVec <= 0.0f )
{
// その位置に着地させることは不可能のようだ!
Debug.LogWarning( "!!" );
return;
}
Vector3 vec = ConvertVectorToVector3( speedVec, angle, i_targetPosition );
SetRigidbody( vec );
}
private Vector3 ConvertVectorToVector3( float i_v0, float i_angle, Vector3 i_targetPosition )
{
Vector3 startPos = transform.position;
Vector3 targetPos = i_targetPosition;
startPos.y = 0.0f;
targetPos.y = 0.0f;
Vector3 dir = ( targetPos - startPos ).normalized;
Quaternion yawRot = Quaternion.FromToRotation( Vector3.right, dir );
Vector3 vec = i_v0 * Vector3.right;
vec = yawRot * Quaternion.AngleAxis( i_angle, Vector3.forward ) * vec;
return vec;
}
private float ComputeVectorFromTime( Vector3 i_targetPosition, float i_time )
{
Vector2 vec = ComputeVectorXYFromTime( i_targetPosition, i_time );
float v_x = vec.x;
float v_y = vec.y;
float v0Square = v_x * v_x + v_y * v_y;
// 負数を平方根計算すると虚数になってしまう。
// 虚数はfloatでは表現できない。
// こういう場合はこれ以上の計算は打ち切ろう。
if( v0Square <= 0.0f )
{
return 0.0f;
}
float v0 = Mathf.Sqrt( v0Square );
return v0;
}
private float ComputeAngleFromTime( Vector3 i_targetPosition, float i_time )
{
Vector2 vec = ComputeVectorXYFromTime( i_targetPosition, i_time );
float v_x = vec.x;
float v_y = vec.y;
float rad = Mathf.Atan2( v_y, v_x );
float angle = rad * Mathf.Rad2Deg;
return angle;
}
private Vector2 ComputeVectorXYFromTime( Vector3 i_targetPosition, float i_time )
{
// 瞬間移動はちょっと……。
if( i_time <= 0.0f )
{
return Vector2.zero;
}
// xz平面の距離を計算。
Vector2 startPos = new Vector2( transform.position.x, transform.position.z );
Vector2 targetPos = new Vector2( i_targetPosition.x, i_targetPosition.z );
float distance = Vector2.Distance( targetPos, startPos );
float x = distance;
// な、なぜ重力を反転せねばならないのだ...
float g = -Physics.gravity.y;
float y0 = transform.position.y;
float y = i_targetPosition.y;
float t = i_time;
float v_x = x / t;
float v_y = ( y - y0 ) / t + ( g * t ) / 2;
return new Vector2( v_x, v_y );
}
private void SetRigidbody( Vector3 i_shootVector )
{
// 速さベクトルのままAddForce()を渡してはいけないぞ。力(速さ×重さ)に変換するんだ
Vector3 force = i_shootVector * m_rigidbody.mass;
m_rigidbody.AddForce( force, ForceMode.Impulse );
}
} // class ObjectJumpController
//------------------------------------------------------------------------
//
// (C) Copyright 2017 Urahimono Project Inc.
//
//------------------------------------------------------------------------
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
[RequireComponent( typeof( NavMeshAgent ) )]
public class ObjectWarpController : ObjectController
{
[SerializeField]
private float m_scaleTime = 0.0f;
[SerializeField]
private float m_waitTime = 0.0f;
protected override IEnumerator OffMeshLinkProcess( Vector3 i_targetPos )
{
Vector3 defaultScale = transform.localScale;
yield return new WaitForSeconds( m_waitTime );
yield return ScaleProcess( Vector3.zero, m_scaleTime );
yield return new WaitForSeconds( m_waitTime );
transform.position = i_targetPos;
yield return ScaleProcess( defaultScale, m_scaleTime );
yield return new WaitForSeconds( m_waitTime );
}
private IEnumerator ScaleProcess( Vector3 i_toScale, float i_time )
{
Vector3 fromScale = transform.localScale;
float startTime = Time.time;
float endTime = startTime + i_time;
while( Time.time < endTime )
{
float elapsedTime = Time.time - startTime;
float elapsedRate = elapsedTime / i_time;
Vector3 scale = Vector3.Lerp( fromScale, i_toScale, elapsedRate );
transform.localScale = scale;
yield return null;
}
transform.localScale = i_toScale;
}
} // class ObjectWarpController
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment