/// <summary> /// Updates points' positions from time to time. /// </summary> /// <param name="line"></param> /// <param name="deltaTime"></param> private void UpdatePoints(TrailGraphics trail, float deltaTime) { for (int i = 0; i < trail.points.Count; i++) { trail.points[i].Update(deltaTime); } }
protected override void UpdateTrail(TrailGraphics trail, float deltaTime) { if (trail.points.Count <= 0 || !updateNow) { return; } Segment(trail.points.Count - 1, m_transform.position, trail); for (int i = trail.points.Count - 1; i > 0; i--) { Segment(i - 1, trail.points[i].position, trail); } updateNow = false; }
/// <summary> /// Count the points whose life time have not passed yet. /// We do nothing to those whose life time have passed. /// Because we are using RingBuffer as an Object Pool :) /// </summary> /// <param name="trail"></param> /// <returns></returns> private int ActivePointsNumber(TrailGraphics trail) { int count = 0; for (int i = 0; i < trail.points.Count; i++) { if (trail.points[i].timeSoFar < parameter.lifeTime) { count++; } } return(count); }
private void Segment(int i, Vector3 pos, TrailGraphics trail) { Vector3 d = pos - trail.points[i].position; float mag = d.magnitude; if (mag <= 0.001f) { mag = 1f; } Vector3 temp = Vector3.zero; temp.x = pos.x - Vector3.Dot(right, d) * jointDistance * followScale / mag; temp.y = pos.y - Vector3.Dot(up, d) * jointDistance * followScale / mag; temp.z = pos.z - Vector3.Dot(forward, d) * jointDistance * followScale / mag; trail.points[i].position = temp; }
/// <summary> /// Just in case the Emission is suddenly shut-down or activated. /// </summary> private void CheckEmitChange() { if (isEmitting != Emit) { isEmitting = Emit; if (isEmitting) { activeTrail = new TrailGraphics(GetMaxPoints()); activeTrail.activeSelf = true; OnStartEmit(); } else { OnStopEmit(); activeTrail.activeSelf = false; fadingTrails.Add(activeTrail); activeTrail = null; } } }
/// <summary> /// De-constructor. /// </summary> protected virtual void OnDestroy() { totalTrailsCount--; if (totalTrailsCount == 0) { if (generatedMeshes != null && generatedMeshes.Count > 0) { foreach (Mesh m in generatedMeshes) { #if UNITY_EDITOR DestroyImmediate(m, true); #else Destroy(m); #endif } } generatedMeshes = null; mat2Trail.Clear(); mat2Trail = null; } if (activeTrail != null) { activeTrail.Dispose(); activeTrail = null; } if (fadingTrails != null) { foreach (TrailGraphics fadingTrail in fadingTrails) { if (fadingTrail != null) { fadingTrail.Dispose(); } } fadingTrails.Clear(); } }
/// <summary> /// De-constructor. /// </summary> protected virtual void OnDestroy() { totalTrailsCount--; if (activeTrail != null) { activeTrail.Dispose(); activeTrail = null; } if (fadingTrails != null) { foreach (TrailGraphics fadingTrail in fadingTrails) { if (fadingTrail != null) { fadingTrail.Dispose(); } } fadingTrails.Clear(); } }
/// <summary> /// Serves as a variable-initializer. /// </summary> protected virtual void Awake() { totalTrailsCount++; parameter.isCross = parameter.trailType == TrailType.Cross; ///The only Guy :) Last Titan Standing. if (totalTrailsCount == 1) { //mat2Trail = new Dictionary<Material, List<TrailGraphics>>(); //generatedMeshes = new List<Mesh>(); } fadingTrails = new List <TrailGraphics>(); m_transform = transform; isEmitting = Emit; if (isEmitting) { activeTrail = new TrailGraphics(GetMaxPoints(), parameter.isCross); activeTrail.activeSelf = true; OnStartEmit(); } }
/// <summary> /// Serves as a variable-initializer. /// </summary> protected virtual void Awake() { totalTrailsCount++; ///The only Guy :) Last Titan Standing. if (totalTrailsCount == 1) { mat2Trail = new Dictionary <Material, List <TrailGraphics> >(); generatedMeshes = new List <Mesh>(); } fadingTrails = new List <TrailGraphics>(); m_transform = transform; isEmitting = Emit; if (parameter.orientationType == TrailOrientation.LookAt && parameter.lookAt == null) { if (Camera.main != null) { parameter.lookAt = Camera.main.transform; Debug.Log(gameObject.name + ": 未指定面向物体, 因此默认面向主摄像机. "); } else { parameter.orientationType = TrailOrientation.Local; parameter.forwardOverride = Vector3.forward; Debug.Log(gameObject.name + ": 无法指定当前Trail的朝向, 为避免错误转化为Local. "); } } if (isEmitting) { activeTrail = new TrailGraphics(GetMaxPoints()); activeTrail.activeSelf = true; OnStartEmit(); } }
/// <summary> /// Just in case the Emission is suddenly shut-down or activated. /// </summary> private void CheckEmitChange() { if (isEmitting != Emit) { isEmitting = Emit; if (isEmitting) { activeTrail = new TrailGraphics(GetMaxPoints(), parameter.isCross); activeTrail.activeSelf = true; OnStartEmit(); } else { if (activeTrail == null) { return; } OnStopEmit(); activeTrail.activeSelf = false; fadingTrails.Add(activeTrail); activeTrail = null; } } }
protected override void UpdateTrail(TrailGraphics trail, float deltaTime) { if (!trail.activeSelf) { return; } int trailPointIdx = 0; for (int i = 0; i < controlPoints.Count; i++) { trail.points[trailPointIdx].position = controlPoints[i].position; trail.points[trailPointIdx].forwardDirection = controlPoints[i].forward; trailPointIdx++; if (i < controlPoints.Count - 1) { Vector3 cp1, cp2; float distance = Vector3.Distance(controlPoints[i].position, controlPoints[i + 1].position) / 2; if (i == 0) { cp1 = controlPoints[i].position + (controlPoints[i + 1].position - controlPoints[i].position).normalized * distance; } else { cp1 = controlPoints[i].position + (controlPoints[i + 1].position - controlPoints[i - 1].position).normalized * distance; } int nextIdx = i + 1; if (nextIdx == controlPoints.Count - 1) { cp2 = controlPoints[nextIdx].position + (controlPoints[nextIdx - 1].position - controlPoints[nextIdx].position).normalized * distance; } else { cp2 = controlPoints[nextIdx].position + (controlPoints[nextIdx - 1].position - controlPoints[nextIdx + 1].position).normalized * distance; } TrailPoint current = trail.points[trailPointIdx - 1]; TrailPoint next = trail.points[trailPointIdx + pointsInMiddle]; for (int j = 0; j < pointsInMiddle; j++) { float t = (((float)j + 1) / ((float)pointsInMiddle + 1)); trail.points[trailPointIdx].position = Bezier.CalculateCubic(t, controlPoints[i].position, cp1, cp2, controlPoints[i + 1].position); trail.points[trailPointIdx].timeSoFar = Mathf.Lerp(current.timeSoFar, next.timeSoFar, t); trail.points[trailPointIdx].forwardDirection = Vector3.Lerp(current.forwardDirection, next.forwardDirection, t); trailPointIdx++; } } } int lastCPIdx = (pointsInMiddle + 1) * (controlPoints.Count - 1); int prevCPIdx = lastCPIdx - pointsInMiddle - 1; int activeCount = lastCPIdx + 1; float distance2Src = trail.points[prevCPIdx].distance2Src; for (int i = prevCPIdx + 1; i < activeCount; i++) { distance2Src += Vector3.Distance(trail.points[i - 1].position, trail.points[i].position); trail.points[i].distance2Src = distance2Src; } }
/// <summary> /// Help a trail generate its own Mesh. /// </summary> /// <param name="trail"></param> private void GenerateMesh(TrailGraphics trail) { trail.mesh.Clear(false); Vector3 cameraForward = Camera.main.transform.forward; ///Determine the trail forward direction. switch (parameter.orientationType) { case TrailOrientation.LookAt: cameraForward = (parameter.lookAt.position - m_transform.position).normalized; break; case TrailOrientation.Local: cameraForward = m_transform.forward; break; case TrailOrientation.World: cameraForward = parameter.forwardOverride.normalized; break; default: break; } trail.activeCount = ActivePointsNumber(trail); ///No way to draw a Mesh with only 2 vertices or even less. Exit. if (trail.activeCount < 2) { return; } int vertIdx = 0; for (int i = 0; i < trail.points.Count; i++) { TrailPoint tp = trail.points[i]; float timeFraction = tp.timeSoFar / parameter.lifeTime; if (timeFraction > 1) { continue; } if (parameter.orientationType == TrailOrientation.Local) { cameraForward = tp.forwardDirection; } Vector3 cross = Vector3.zero; if (i < trail.points.Count - 1) { cross = Vector3.Cross((trail.points[i + 1].position - tp.position).normalized, cameraForward).normalized; } else { cross = Vector3.Cross((tp.position - trail.points[i - 1].position).normalized, cameraForward).normalized; } Color c = parameter.colorOverLife.Evaluate(1 - (float)vertIdx / (float)trail.activeCount / 2f); float s = parameter.sizeOverLife.Evaluate(timeFraction); trail.vertices[vertIdx] = tp.position + cross * s; trail.uvs[vertIdx] = new Vector2(tp.distance2Src / parameter.quadScaleFactor, 0.0f); trail.normals[vertIdx] = cameraForward; trail.colors[vertIdx] = c; vertIdx++; trail.vertices[vertIdx] = tp.position - cross * s; trail.uvs[vertIdx] = new Vector2(tp.distance2Src / parameter.quadScaleFactor, 1.0f); trail.normals[vertIdx] = cameraForward; trail.colors[vertIdx] = c; vertIdx++; } ///"Stack" all redundant vertices into the termination position. Vector2 termination = trail.vertices[vertIdx - 1]; for (int i = vertIdx; i < trail.vertices.Length; i++) { trail.vertices[i] = termination; } ///Now let's focus on triangle array ... int triIdx = 0; for (int i = 0, imax = 2 * (trail.activeCount - 1); i < imax; i++) { ///Ok, start point. if (i % 2 == 0) { trail.indices[triIdx++] = i; trail.indices[triIdx++] = i + 1; trail.indices[triIdx++] = i + 2; } else ///Reverse the process. { trail.indices[triIdx++] = i + 2; trail.indices[triIdx++] = i + 1; trail.indices[triIdx++] = i; } } ///"Squash" all the redundant vertices and triangle arrays. int termIdx = trail.indices[triIdx - 1]; for (int i = triIdx; i < trail.indices.Length; i++) { trail.indices[i] = termIdx; } ///So now comes the Exciting part. CONG! trail.mesh.vertices = trail.vertices; ///Setting Indices array directly to mesh also works. trail.mesh.SetIndices(trail.indices, MeshTopology.Triangles, 0); trail.mesh.uv = trail.uvs; trail.mesh.normals = trail.normals; trail.mesh.colors = trail.colors; }
protected virtual void UpdateTrail(TrailGraphics trail, float deltaTime) { }
/// <summary> /// Help a trail generate its own Mesh. /// </summary> /// <param name="trail"></param> private void GenerateMesh(TrailGraphics trail) { trail.mesh.Clear(false); Vector3 cameraForward = GetFacing(); trail.activeCount = ActivePointsNumber(trail); /// No way to draw a Mesh with only 2 vertices or even less. Exit. if (trail.activeCount < 2) { return; } int vertIdx = 0; Vector3 lastCross = cameraForward; if (parameter.trailType == TrailType.Vertical || parameter.isCross) { for (int i = 0; i < trail.points.Count; i++) { TrailPoint tp = trail.points[i]; float timeFraction = tp.timeSoFar / parameter.lifeTime; if (timeFraction > 1) { continue; } if (parameter.orientationType == TrailOrientation.Local) { cameraForward = tp.forwardDirection; } Vector3 cross = Vector3.zero; Vector3 moveDir = cameraForward; if (i < trail.points.Count - 1) { moveDir = (trail.points[i + 1].position - tp.position).normalized; } else { moveDir = (tp.position - trail.points[i - 1].position).normalized; } cross = Vector3.Cross(moveDir, cameraForward).normalized; if (cross.magnitude < 0.9f) { cross = lastCross.normalized; } else if (Vector3.Dot(cross, lastCross) < 0f) { cross = -cross; lastCross = -cross; } else { lastCross = cross; } Color c = parameter.colorOverLife.Evaluate(1 - (float)vertIdx / (float)trail.activeCount / 2f); float s = parameter.sizeOverLife.Evaluate(timeFraction); float uvx = parameter.isTile ? tp.distance2Src / parameter.quadScaleFactor : 1 - timeFraction; Vector3 offset = cross * s * parameter.sizeMultiplier; trail.vertices[vertIdx] = tp.position + offset; trail.uvs[vertIdx] = new Vector2(uvx, 0.0f); trail.normals[vertIdx] = cameraForward; trail.colors[vertIdx] = c; vertIdx++; trail.vertices[vertIdx] = tp.position - offset; trail.uvs[vertIdx] = new Vector2(uvx, 1.0f); trail.normals[vertIdx] = cameraForward; trail.colors[vertIdx] = c; vertIdx++; } } int oldVertIdx = vertIdx; lastCross = cameraForward; if (parameter.trailType == TrailType.Horizontal || parameter.isCross) { for (int i = 0; i < trail.points.Count; i++) { TrailPoint tp = trail.points[i]; float timeFraction = tp.timeSoFar / parameter.lifeTime; if (timeFraction > 1) { continue; } if (parameter.orientationType == TrailOrientation.Local) { cameraForward = tp.forwardDirection; } Vector3 cross = cameraForward; Color c = parameter.colorOverLife.Evaluate(1 - ((float)vertIdx - oldVertIdx) / (float)trail.activeCount / 2f); float s = parameter.sizeOverLife.Evaluate(timeFraction); float uvx = parameter.isTile ? tp.distance2Src / parameter.quadScaleFactor : 1 - timeFraction; Vector3 offset = cross * s * parameter.sizeMultiplier; trail.vertices[vertIdx] = tp.position + offset; trail.uvs[vertIdx] = new Vector2(uvx, 0.0f); trail.normals[vertIdx] = cameraForward; trail.colors[vertIdx] = c; vertIdx++; trail.vertices[vertIdx] = tp.position - offset; trail.uvs[vertIdx] = new Vector2(uvx, 1.0f); trail.normals[vertIdx] = cameraForward; trail.colors[vertIdx] = c; vertIdx++; } } /// "Stack" all redundant vertices into the termination position. Vector3 termination = trail.vertices[vertIdx - 1]; for (int i = vertIdx; i < trail.vertices.Length; i++) { trail.vertices[i] = termination; } /// Now let's focus on triangle array ... int triIdx = 0; for (int i = 0, imax = 2 * (trail.activeCount - 1); i < imax; i++) { /// Ok, start point. if (i % 2 == 0) { trail.indices[triIdx++] = i; trail.indices[triIdx++] = i + 1; trail.indices[triIdx++] = i + 2; } else /// Reverse the process. { trail.indices[triIdx++] = i + 2; trail.indices[triIdx++] = i + 1; trail.indices[triIdx++] = i; } } if (parameter.isCross) { for (int i = 2 * trail.activeCount, imax = 4 * trail.activeCount - 2; i < imax; i++) { if (i % 2 == 0) { trail.indices[triIdx++] = i; trail.indices[triIdx++] = i + 1; trail.indices[triIdx++] = i + 2; } else { trail.indices[triIdx++] = i + 2; trail.indices[triIdx++] = i + 1; trail.indices[triIdx++] = i; } } } /// "Squash" all the redundant vertices and triangle arrays. int termIdx = trail.indices[triIdx - 1]; for (int i = triIdx; i < trail.indices.Length; i++) { trail.indices[i] = termIdx; } /// So now comes the Exciting part. CONG! trail.mesh.vertices = trail.vertices; /// Setting Indices array directly to mesh also works. trail.mesh.SetIndices(trail.indices, MeshTopology.Triangles, 0); trail.mesh.uv = trail.uvs; trail.mesh.normals = trail.normals; trail.mesh.colors = trail.colors; }