Unity warlock incinerate

Simulate warlock's spell - Incinerate in world of warcraft:

Construct the mesh and update it each frame

Initialize vertices of mesh
var vertices = new Vector3[(SEGMENTS_COUNT + 1) * 2];

for (var i = 0; i < vertices.Length; i++) {
	vertices [i] = transform.position;
}
1. Initialize uv coordinates of mesh
// same length with vertices array
var uvs = new Vector2[vertices.Length];

for (var i = 0; i < uvs.Length; i++) {
	
	float x = 0, y = i / (float)SEGMENTS_COUNT;
	
	if (i % 2 == 0) {
		x = 0; // left vertex
	} else {
		x = 1; // right vertex
	}
	uvs [i] = new Vector2 (x, y);
}

2. Initialize indices of mesh
// each segment has two trangles and each trangle has three vertices
var indices = new int[SEGMENTS_COUNT * 2 * 3];

for (int i = 0; i < SEGMENTS_COUNT; i++) {
	indices [6 * i + 0] = 0 + i * 2;
	indices [6 * i + 1] = 1 + i * 2;
	indices [6 * i + 2] = 2 + i * 2;


	indices [6 * i + 3 + 0] = 2 + i * 2;
	indices [6 * i + 3 + 1] = 1 + i * 2;
	indices [6 * i + 3 + 2] = 3 + i * 2;

}

3. Fill into mesh
mMesh = meshFilter.mesh = new Mesh ();
mMesh.vertices = vertices;
mMesh.uv = uvs;
mMesh.SetIndices (indices, MeshTopology.Triangles, 0);
mMesh.RecalculateBounds ();

Update mesh each frame

private void UpdateVertices ()
{
	var vertices = mMesh.vertices;

	for (var i = 0; i < vertices.Length; i++) {
		var v = transform.position;
		var segInx = i / 2;
		var ratio = (SEGMENTS_COUNT - segInx * 1f) / SEGMENTS_COUNT;
		
		var seg = mMoveDir.normalized * segInx * WIDTH / 6f;

		var spine = Mathf.Sin (ratio * Mathf.PI * 2.5f + Time.time * 6) * Vector3.left * WIDTH * 1.5f;
		
		var wave = Vector3.left * WIDTH * Mathf.Min (1f, ratio + 0.5f);
		
		if (i % 2 == 0) {
			v = transform.position - seg - wave + spine;
		} else {
			v = transform.position - seg + wave + spine;
		}

		vertices [i] = v;
	}
	mMesh.vertices = vertices;
}

Render the mesh with shader

Properties
{
	_MainTex ("Texture", 2D) = "white" {}
	_MaskTex("Mask",2D) = "white"{}
	_MaskTex2("Mask2",2D) = "white"{}
}

_MaskTex flowing texture for simulating dynamic fires
_MaskTex2 static texture for masking the incinerate overall shape

Full version of source code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class IncinerateScript : MonoBehaviour
{
	public Transform Target;

	private static int SEGMENTS_COUNT = 21;
	private static int WIDTH = 50;
	private static int SPEED = 10;

	private MeshFilter mMeshFilter;
	private Mesh mMesh;
	private Vector3 mMoveDir;


	private bool mIsInit;

	void Start ()
	{
		mMeshFilter = GetComponent<MeshFilter> ();

		if (mMeshFilter != null) {

			InitVertices (mMeshFilter);

			mIsInit = true;
		}
	}

	void Update ()
	{
		if (Target)
			mMoveDir = Target.position - transform.position;

		if (mIsInit)
			UpdateVertices ();
	}

	private void InitVertices (MeshFilter meshFilter)
	{
		var vertices = new Vector3[(SEGMENTS_COUNT + 1) * 2];
		var uvs = new Vector2[vertices.Length];
		var indices = new int[SEGMENTS_COUNT * 2 * 3];


		for (var i = 0; i < vertices.Length; i++) {
			vertices [i] = transform.position;
		}

		for (var i = 0; i < uvs.Length; i++) {
			float x = 0, y = i / (float)SEGMENTS_COUNT;
			if (i % 2 == 0) {
				x = 0;
			} else {
				x = 1;
			}
			uvs [i] = new Vector2 (x, y);
		}

		for (int i = 0; i < SEGMENTS_COUNT; i++) {
			indices [6 * i + 0] = 0 + i * 2;
			indices [6 * i + 1] = 1 + i * 2;
			indices [6 * i + 2] = 2 + i * 2;


			indices [6 * i + 3 + 0] = 2 + i * 2;
			indices [6 * i + 3 + 1] = 1 + i * 2;
			indices [6 * i + 3 + 2] = 3 + i * 2;

		}


		mMesh = meshFilter.mesh = new Mesh ();
		mMesh.vertices = vertices;
		mMesh.uv = uvs;
		mMesh.SetIndices (indices, MeshTopology.Triangles, 0);
		mMesh.RecalculateBounds ();

	}

	private void UpdateVertices ()
	{
		var vertices = mMesh.vertices;

		for (var i = 0; i < vertices.Length; i++) {
			var v = transform.position;
			var segInx = i / 2;
			var ratio = (SEGMENTS_COUNT - segInx * 1f) / SEGMENTS_COUNT;
			
			var seg = mMoveDir.normalized * segInx * WIDTH / 6f;

			var spine = Mathf.Sin (ratio * Mathf.PI * 2.5f + Time.time * 6) * Vector3.left * WIDTH * 1.5f;
			var wave = Vector3.left * WIDTH * Mathf.Min (1f, ratio + 0.5f);
			if (i % 2 == 0) {
				v = transform.position - seg - wave + spine;
			} else {
				v = transform.position - seg + wave + spine;
			}
 
			vertices [i] = v;
		}
		mMesh.vertices = vertices;
	}
}


and the shader:

Shader "Suntabu/Incinerate"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_MaskTex("Mask",2D) = "white"{}
		_MaskTex2("Mask2",2D) = "white"{}
	}
	SubShader
	{
		Tags { "RenderType"="Transparent" }
		LOD 100 Cull off
		Blend SrcAlpha OneMinusSrcAlpha
		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float2 uv2 : TEXCOORD1;
				float2 uv3 : TEXCOORD2;
				UNITY_FOG_COORDS(1)
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _MaskTex;
			float4 _MaskTex_ST;
			sampler2D _MaskTex2;
			float4 _MaskTex2_ST;
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex) + _Time.xy;
				o.uv2 = TRANSFORM_TEX(v.uv, _MaskTex) - _Time.xy;
				o.uv3 = TRANSFORM_TEX(v.uv, _MaskTex2);
				UNITY_TRANSFER_FOG(o,o.vertex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				// sample the texture
				fixed4 col = tex2D(_MainTex, i.uv);
				fixed4 mask = tex2D(_MaskTex,i.uv2);
				fixed4 mask2 = tex2D(_MaskTex2,i.uv3);
				return fixed4(col.rgb,mask.a) *mask2.a * 1.3;
			}
			ENDCG
		}
	}
}

WRITTEN BY:    Suntabu