2011年7月28日 星期四

UNITY Motion Trail Script!!

Dear All:


原理:
遊戲用到的Motion Trail原理其實很簡單,就是利用一個Queue,將兩參考點於每個Frame的位置記錄下來,這記錄通常是有條件的,也許是兩參考點與上一個Frame的紀錄比較距離,若高過某一threadhould就push in,此外在每個Frame裡也檢查已經在Queue中的其它點,生命周期是不是已經到了,如果是就將這次的紀錄pop out。Pseudo Code如下所示:
 Update() { float now = Time.time;
    Vector3 P1 = m_CurPointA;
    Vector3 P2 = m_CurPointB;
    Vector3 preP1 = m_Queue[0].PointA;
    Vector3 preP2 = m_Queue[0].PointB;

    if( (P1 - preP1).length > 0.5f ||
         (P2 - preP2).length > 0.5f ) {
         m_Queue.push( p1, p2, now );
    }

    while( m_Queue.count > 0 &&
                now - m_Queue[end].time > 1.0f ) {
         m_Queue.pop( end );
    }
}
接下來的問題就是,將這Queue裡的“點”加上建構三角面的Index及計算出貼圖UV,進行Rendering,即可得到兩點間的移動軌跡,加上貼圖就如同上圖所示了。Pseudo Code如下所示:
//Generate triangles indices:
trailMesh.triangles = new int[(m_Queue.count-1)*2*3];
for( int i=0; i trailMesh.triangles[i * 6 + 0] = i * 2; i++ ) {
    trailMesh.triangles[i * 6 + 0] = i * 2;
    trailMesh.triangles[i * 6 + 1] = i * 2 + 1;
    trailMesh.triangles[i * 6 + 2] = i * 2 + 2;

    trailMesh.triangles[i * 6 + 3] = i * 2 + 2;
    trailMesh.triangles[i * 6 + 4] = i * 2 + 1;
    trailMesh.triangles[i * 6 + 5] = i * 2 + 3;
}

進階修飾:
如果只單純的這樣產生Motion Trail,你因該會發現,畫出來的弧線會菱菱角角的不是很好看。至於要怎麼加強這個部分呢?很簡單,就拿曲線演算來使用吧!在這裡使用的是Catmull-Rom Spline,它有幾個特點:
1. Catmull-Rom保證,曲線一定通過控制點
2. Spline C1 contunuous,這表示在tangent的方向與長度上,不會有區別
3. 計算簡單
數學表示式如下:
q(t) = 0.5 *( (2 * P1) + (-P0 + P2) * t + (2*P0 - 5*P1 + 4*P2 - P3) * t2 + (-P0 + 3*P1- 3*P2 + P3) * t3)

Pseudo Code如下所示:
public static TrailSection Catmull_Rom(
TrailSection p0, TrailSection p1,
TrailSection p2, TrailSection p3,
float t )
{
     TrailSection section = new TrailSection();

     float t2 = t * t;
     float t3 = t2 * t;
     float a0 = -t3 + 2*t2 - t;
     float a1 = 3*t3 - 5*t2 + 2;
     float a2 = -3*t3 + 4*t2 + t;
     float a3 = t3 - t2;

     section.pointS = (a0*p0.pointS + a1*p1.pointS + a2*p2.pointS + a3*p3.pointS) * 0.5f;
     section.pointE = (a0*p0.pointE + a1*p1.pointE + a2*p2.pointE + a3*p3.pointE) * 0.5f;
     section.time = (a0*p0.time + a1*p1.time + a2*p2.time + a3*p3.time) * 0.5f;

     return section;
}


完整Script:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
class TrailSection
{
 public static TrailSection Catmull_Rom( TrailSection p0, TrailSection p1,
                                         TrailSection p2, TrailSection p3,
                                         float t )
 {
  TrailSection section = new TrailSection();

  float t2 = t * t;
  float t3 = t2 * t;
  float a0 = -t3 + 2*t2 - t;
  float a1 = 3*t3 - 5*t2 + 2;
  float a2 = -3*t3 + 4*t2 + t;
  float a3 = t3 - t2;

  section.pointS = (a0*p0.pointS + a1*p1.pointS + a2*p2.pointS + a3*p3.pointS) * 0.5f;
  section.pointE = (a0*p0.pointE + a1*p1.pointE + a2*p2.pointE + a3*p3.pointE) * 0.5f;
  section.time = (a0*p0.time + a1*p1.time + a2*p2.time + a3*p3.time) * 0.5f;

  return section;
 }

 public Vector3 pointS;
 public Vector3 pointE;
 public float time;
}
class TrailMesh
{
 public Vector3[] vertices = null;
 public Color[] colors = null;
 public Vector2[] uv = null;
 public int[] triangles = null;
}
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class MotionTrail : MonoBehaviour {
 public Vector3 m_refStartPoint = Vector3.zero;
 public Vector3 m_refEndPoint = Vector3.zero;

 public float m_time = 2.0f;
 public float m_minDistance = 0.1f;
 public Color m_startColor = Color.white;
 public Color m_endColor = new Color(1, 1, 1, 0);
 public float m_trailScale = 1.0f;
 public int m_MaxSections = 10;
 public int m_MaxSegments = 3;

 private List m_sections = new List ();
 private List m_sectionsT = new List ();
 private Mesh m_mesh;

 protected void Start () {
  m_mesh = GetComponent ().mesh;
 }

 protected void LateUpdate () {
  Matrix4x4 mat = transform.localToWorldMatrix;
  Vector3 S = mat.MultiplyPoint( m_refStartPoint );
  Vector3 E = mat.MultiplyPoint( m_refEndPoint );
  float now = Time.time;

  //Add a new trail section:
  if( m_sections.Count == 0 ||
   (m_sections[0].pointS - S).sqrMagnitude > m_minDistance * m_minDistance ||
   (m_sections[0].pointE - E).sqrMagnitude > m_minDistance * m_minDistance)
  {
   TrailSection section = new TrailSection();
   section.pointS = S;
   section.pointE = E;
   section.time = now;
   m_sections.Insert( 0, section );
  }

  //Remove old sections:
  while( m_sections.Count > 0 &&
         (now > m_sections[m_sections.Count - 1].time + m_time ||
          m_sections.Count > m_MaxSections))
  {
   m_sections.RemoveAt( m_sections.Count-1 );
  }

  //Rebuild mesh:
  m_mesh.Clear();

  if( m_sections.Count < 4 )
   return;

  //Generate mesh:
  TrailMesh trailMesh = GenerateTrailMesh();

  //Assign to mesh:
  if( trailMesh != null ) {
   m_mesh.vertices = trailMesh.vertices;
   m_mesh.colors = trailMesh.colors;
   m_mesh.uv = trailMesh.uv;
   m_mesh.triangles = trailMesh.triangles;
  }
 }

    protected void OnDrawGizmosSelected() {
  Matrix4x4 mat = transform.localToWorldMatrix;
  mat.MultiplyPoint( m_refStartPoint );

  Vector3 S = mat.MultiplyPoint( m_refStartPoint );
  Vector3 E = mat.MultiplyPoint( m_refEndPoint );

  Gizmos.DrawWireSphere( S, 0.02f );
  Gizmos.DrawWireSphere( E, 0.02f );
  Gizmos.DrawLine( S, E );
    }

 private TrailMesh GenerateTrailMesh () {
  List sections = InterpolateTrailMesh();

  TrailMesh trailMesh = new TrailMesh();
  trailMesh.vertices = new Vector3[sections.Count*2];
  trailMesh.colors = new Color[sections.Count*2];
  trailMesh.uv = new Vector2[sections.Count*2];

  TrailSection curSection = sections[0];

  //Use matrix instead of transform.TransformPoint for performance reasons
  Matrix4x4 localSpaceTransform = transform.worldToLocalMatrix;

  //Generate vertex, uv and colors:
  Vector2 uv1 = Vector2.zero;
  Vector2 uv2 = Vector2.zero;

  for( int i=0; i    curSection = sections[i];

   //Calculate u for texture uv and color interpolation:
   float u = 0.0f;
   //if (i != 0)
   // u = Mathf.Clamp01( (Time.time - curSection.time) / m_time );
   u = (1.0f/(float)(sections.Count-1)) * i ;

   //Generate vertices:
   float scale = (1.0f/(float)(sections.Count-1)) * i * 0.5f * m_trailScale;
   Vector3 dirToS = curSection.pointS - curSection.pointE;
   float l = dirToS.magnitude;
   dirToS.Normalize();

   curSection.pointS += ( l * scale * -dirToS );
   curSection.pointE += ( l * scale * dirToS );
   trailMesh.vertices[i * 2 + 0] = localSpaceTransform.MultiplyPoint( curSection.pointS );
   trailMesh.vertices[i * 2 + 1] = localSpaceTransform.MultiplyPoint( curSection.pointE );

   uv1.x = u; uv1.y = 0;
   uv2.x = u; uv2.y = 1;
   trailMesh.uv[i * 2 + 0] = uv1;
   trailMesh.uv[i * 2 + 1] = uv2;

   //fade colors out over time:
   Color interpolatedColor = Color.Lerp(m_startColor, m_endColor, u);
   trailMesh.colors[i * 2 + 0] = interpolatedColor;
   trailMesh.colors[i * 2 + 1] = interpolatedColor;
  }

  //Generate triangles indices:
  trailMesh.triangles = new int[(sections.Count-1)*2*3];
  for( int i=0; i    trailMesh.triangles[i * 6 + 0] = i * 2;
   trailMesh.triangles[i * 6 + 1] = i * 2 + 1;
   trailMesh.triangles[i * 6 + 2] = i * 2 + 2;

   trailMesh.triangles[i * 6 + 3] = i * 2 + 2;
   trailMesh.triangles[i * 6 + 4] = i * 2 + 1;
   trailMesh.triangles[i * 6 + 5] = i * 2 + 3;
  }
  return trailMesh;
 }

 private List InterpolateTrailMesh () {
  if( m_MaxSegments <= 0 )
   m_MaxSegments = 1;

  int total = (m_sections.Count-1)*m_MaxSegments;
  while( m_sectionsT.Count > total )
   m_sectionsT.RemoveAt(0);
  for( int i=0; i    TrailSection p0, p1, p2, p3;
   if( i == 0 )
    p0 = m_sections[i];
   else
    p0 = m_sections[i-1];

   p1 = m_sections[i];
   p2 = m_sections[i+1];

   if( i+2 >= m_sections.Count-1 )
    p3 = m_sections[i+1];
   else
    p3 = m_sections[i+2];

   for( int delta=0; delta     float t = (float)delta/(float)m_MaxSegments;
    int index = (i*m_MaxSegments) + delta;
    if( index < m_sectionsT.Count )
     m_sectionsT[index] = TrailSection.Catmull_Rom( p0, p1, p2, p3, t );
    else
     m_sectionsT.Add( TrailSection.Catmull_Rom( p0, p1, p2, p3, t ) );
   }
  }
  return m_sectionsT;
 }
}

沒有留言:

張貼留言

LinkWithin

Related Posts Plugin for WordPress, Blogger...