Skip to content

Instantly share code, notes, and snippets.

@kurtdekker
Created July 1, 2021 16:44
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 kurtdekker/e3117bef40fbf329d87c2bfd3c0121d2 to your computer and use it in GitHub Desktop.
Save kurtdekker/e3117bef40fbf329d87c2bfd3c0121d2 to your computer and use it in GitHub Desktop.
Mote System (particle system) for Jetpack Kurt
using UnityEngine;
using System.Collections;
public class MoteSystem : MonoBehaviour
{
public Transform target;
System.Func<Vector3> GetPlayerVelocity;
const float limitDistance = 15.0f;
const int BASE_NUM_MOTES = 300;
float[] MoteXs;
float[] MoteYs;
float[] MoteZs;
float[] MoteAngles;
int numMotes;
GameObject CommonMoteGameObject;
float MasterScale;
// only make these with this factory:
public static MoteSystem Create(
Transform target,
System.Func<Vector3> GetPlayerVelocity,
float MasterSizeScale = 1.0f,
float MasterCountScale = 1.0f)
{
MoteSystem ms = new GameObject ("MoteSystem.Create();").AddComponent<MoteSystem> ();
ms.target = target;
ms.GetPlayerVelocity = GetPlayerVelocity;
ms.MasterScale = MasterSizeScale;
ms.mote_mtl = new Material( Resources.Load<Material> ( "Materials/mote1"));
ms.mote_mtl = new Material( Resources.Load<Material>(
"Textures/particles/dust_mote_sheet_mtl"));
GameObject go = new GameObject ("MoteSystem.CommonMoteGameObject");
go.transform.SetParent (ms.transform);
MeshFilter mf = go.AddComponent<MeshFilter> ();
ms.mesh = mf.mesh;
ms.numMotes = (int)(BASE_NUM_MOTES * MasterCountScale);
ms.verts = new Vector3[ ms.numMotes * 4];
ms.tris = new int[ ms.numMotes * 6];
ms.uvs = new Vector2[ ms.verts.Length];
MeshRenderer mr = go.AddComponent<MeshRenderer>();
mr.material = ms.mote_mtl;
ms.CommonMoteGameObject = go;
var MoteXs = new float[ms.numMotes];
var MoteYs = new float[ms.numMotes];
var MoteZs = new float[ms.numMotes];
var MoteAngles = new float[ms.numMotes];
for (int i = 0; i < ms.numMotes; i++)
{
MoteXs[i] = Random.Range (-limitDistance, limitDistance);
MoteYs[i] = Random.Range (-limitDistance, limitDistance);
MoteZs[i] = Random.Range (-limitDistance, limitDistance);
MoteAngles[i] = Random.Range ( 0, Mathf.PI * 2);
}
ms.MoteXs = MoteXs;
ms.MoteYs = MoteYs;
ms.MoteZs = MoteZs;
ms.MoteAngles = MoteAngles;
ms.TargetPosition = target.position;
ms.RenderMotesToMesh ( true);
return ms;
}
Material mote_mtl;
Mesh mesh;
Vector3[] verts;
int[] tris;
Vector2[] uvs;
void RenderMotesToMesh ( bool firstUpdate)
{
CommonMoteGameObject.transform.position = TargetPosition;
Quaternion Q = target.rotation;
Vector3 WindStretchStreaking = Vector3.zero;
if (DSM.Settings.MoteSystemEnableStreaking.bValue)
{
WindStretchStreaking = wind;
WindStretchStreaking -= GetPlayerVelocity() * Time.deltaTime;
}
// now onto making the geomtry based on where things are now
int nv = 0;
int nt = 0;
for (int i = 0; i < numMotes; i++)
{
Vector3 worldPosition = new Vector3( MoteXs[i], MoteYs[i], MoteZs[i]);
Vector3 pos = worldPosition - TargetPosition;
float sz = 0.05f + (0.10f * i) / numMotes;
sz *= MasterScale;
float angle = MoteAngles[i];
float S = Mathf.Sin (angle) * sz;
float C = Mathf.Cos (angle) * sz;
verts [nv + 0] = pos + Q * new Vector3 (-S, -C) + WindStretchStreaking;
verts [nv + 1] = pos + Q * new Vector3 (-C, S);
verts [nv + 2] = pos + Q * new Vector3 ( S, C) - WindStretchStreaking;
verts [nv + 3] = pos + Q * new Vector3 ( C, -S);
if (firstUpdate)
{
// this was for the single-particle material
// uvs [nv + 0] = new Vector2 (0, 0);
// uvs [nv + 1] = new Vector2 (1, 0);
// uvs [nv + 2] = new Vector2 (1, 1);
// uvs [nv + 3] = new Vector2 (0, 1);
// this is for the tilesheet of particles
int n = i % 12;
float f = 0.25f;
float x = (n & 3) * f;
float y = (n / 4) * f;
y = 0.75f - y;
uvs [nv + 0] = new Vector2 (x, y);
uvs [nv + 1] = new Vector2 (x + f, y);
uvs [nv + 2] = new Vector2 (x + f, y + f);
uvs [nv + 3] = new Vector2 (x, y + f);
}
tris [nt + 0] = nv + 0;
tris [nt + 1] = nv + 1;
tris [nt + 2] = nv + 2;
tris [nt + 3] = nv + 0;
tris [nt + 4] = nv + 2;
tris [nt + 5] = nv + 3;
nv += 4;
nt += 6;
}
mesh.vertices = verts;
mesh.uv = uvs;
mesh.triangles = tris;
if (firstUpdate)
{
// mesh.RecalculateBounds();
mesh.bounds = new Bounds(Vector3.zero, Vector3.one * limitDistance * 2);
}
}
Vector3 wind;
void GetWind()
{
wind = WeatherController.CurrentWind / WeatherController.WindToPlayerVelocityAdjustmentFactor;
wind *= Time.deltaTime;
}
Vector3 TargetPosition; // snapshot
void FixedUpdate()
{
GetWind();
{
float windX = wind.x;
for (int i = 0; i < numMotes; i++)
{
MoteXs[i] += windX;
}
}
{
float windY = wind.y;
for (int i = 0; i < numMotes; i++)
{
MoteYs[i] += windY;
}
}
{
float windZ = wind.z;
for (int i = 0; i < numMotes; i++)
{
MoteZs[i] += windZ;
}
}
}
void ClipAndWrapMotes()
{
// this clip/wrap used to be in FixedUpdate() right after
// every single move; that was wasteful!
// - unrolled it from the main render loop
// - interleaved it for each array
{
float coordinate = TargetPosition.x;
for (int i = 0; i < numMotes; i++)
{
if (MoteXs[i] < coordinate - limitDistance)
{
MoteXs[i] += limitDistance * 2;
}
if (MoteXs[i] > coordinate + limitDistance)
{
MoteXs[i] -= limitDistance * 2;
}
}
}
{
float coordinate = TargetPosition.y;
for (int i = 0; i < numMotes; i++)
{
if (MoteYs[i] < coordinate - limitDistance)
{
MoteYs[i] += limitDistance * 2;
}
if (MoteYs[i] > coordinate + limitDistance)
{
MoteYs[i] -= limitDistance * 2;
}
}
}
{
float coordinate = TargetPosition.z;
for (int i = 0; i < numMotes; i++)
{
if (MoteZs[i] < coordinate - limitDistance)
{
MoteZs[i] += limitDistance * 2;
}
if (MoteZs[i] > coordinate + limitDistance)
{
MoteZs[i] -= limitDistance * 2;
}
}
}
}
void Update()
{
TargetPosition = target.position;
ClipAndWrapMotes();
RenderMotesToMesh (false);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment