Projectile Prediction System using a Mesh

A couple of days ago I posted a video tutorial about mesh creation. I’ve had a request to make the code available so I show it off and put the code up here.

Basically it’s a mesh of the bounding volume of a projectile released with a small amount of uncertainty in the initial velocity (both magnitude and direction).

For my application of this I don’t actually render the mesh; instead I use a shader I found here to highlight the intersection between two meshes, so that the user can ‘see’ where a projectile would land. Useful for pulling off grenade tricks.

Video here (feel free to check out my other videos :smile:)

Code here (watch the video for an explanation of getting this up and running. Shouldn’t take any time at all)

Hope you like it, let me know if you have any problems

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

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class MeshTesting : MonoBehaviour {
	
	private float distance;
	private float height = 1.83f;
	private int partNo = 30;
	private int groundRes = 25;
	private Vector2[] groundPos;
	private float newVel;
	private float[] ratio;
	private float g = 9.81f;
	private float initSpeed = 20f;
	private float angle = 0f;
	private float radius = 1f;
	private int particleCounter = 0;

	private Mesh mesh;
	private List<Vector3> vertices;
	private int[] triangles;	
	public Transform angleMeasurer;
	private Transform _transform;
	private MeshRenderer meshRenderer;
	
	//private bool isActive = true;
	
	public Transform cube;
	
	private Transform[,] cubeList;
	
	// Use this for initialization
	void Start () {	
		
		meshRenderer = GetComponent<MeshRenderer>();
		
		_transform = transform;
		vertices = new List<Vector3>();
		triangles = new int[groundRes*3*partNo*6];
		
		cubeList = new Transform[groundRes,3*partNo];
		
		
		GetComponent<MeshFilter>().mesh = mesh = new Mesh();
		mesh.Clear();
		mesh.name = "TrajectoryMesh";

		float temp = 2* Mathf.PI / groundRes;
		groundPos = new Vector2[groundRes];
		ratio = new float[groundRes];
			
		angle = Mathf.Deg2Rad*45f; //angle = Mathf.Deg2Rad*AngleConvert(cam.eulerAngles.x);
		distance = (Mathf.Tan (angle)+Mathf.Sqrt(Mathf.Pow(Mathf.Tan (angle),2f)+height*2f*(g / Mathf.Pow (Mathf.Cos(angle)*initSpeed,2f))))/(g/ Mathf.Pow (Mathf.Cos(angle)*initSpeed,2)); //distance = MidPoint (angle);
						
		for(int i = 0; i < groundRes; i++)
		{
			float ang = i * temp;
			groundPos[i] = new Vector2(radius*Mathf.Cos (ang),radius*Mathf.Sin (ang));
			float addon = distance + groundPos[i].y;

			newVel = height+addon*Mathf.Tan (angle) < 0f ? initSpeed : Mathf.Sqrt(g*Mathf.Pow(addon/Mathf.Cos (angle),2f)/(2f*(height + addon*Mathf.Tan (angle))));

			ratio[i] = groundPos[i].x/addon;
			
			for(int j = 0; j < 3*partNo; j++)
			{				
				float zed = (addon*(j))/partNo;
				float why = -g*Mathf.Pow (zed,2f)/(2f*Mathf.Pow(Mathf.Cos(angle)*newVel,2f)) + Mathf.Tan (angle)*zed; //float why = TrajectoryHeight(angle, zed, initSpeed);
				float ecks = ratio[i]*zed;
				//Debug.Log (instPos + " " + particleCounter);
				vertices.Add(new Vector3(ecks, why, zed));
				cubeList[i,j] = Instantiate(cube,  transform.TransformPoint(new Vector3(ecks,why,zed)),Quaternion.identity) as Transform;
				particleCounter++;
			}			
		}		
		
		mesh.vertices = vertices.ToArray();
		
		for(int i = 0; i < groundRes; i++)
		{
			triangles[3*i + 1] = i*90 + 1;
			triangles[3*i + 2] = (i+1)*90 + 1;			
			if(3*i+2 == 74) triangles[3*i+2] = 1;
		}
		
		for(int j = 0, k = 0; j < 40; j++)
		{
			for(int i = 0; i < groundRes - 1; i++)
			{
				triangles[74+ 6*k + 1] = 90*i + 1 + j;
				triangles[74+ 6*k + 2] = 90*i + 2 + j;
				triangles[74+ 6*k + 3] = 90*(i+1) + 2 + j;
				triangles[74+ 6*k + 4] = 90*(i+1) + 2 + j;
				triangles[74+ 6*k + 5] = 90*(i+1) + 1 + j;
				triangles[74+ 6*k + 6] = 90*i + 1 + j;
				
				k++;
			}			
			
			triangles[74+ 6*k + 1] = 90*24 + 1 + j;
			triangles[74+ 6*k + 2] = 90*24 + 2 + j;
			triangles[74+ 6*k + 3] = 2 + j;
			triangles[74+ 6*k + 4] = 2 + j;
			triangles[74+ 6*k + 5] = 1 + j;
			triangles[74+ 6*k + 6] = 90*24 + 1 + j;	
			k++;
		}
		
		mesh.triangles = triangles;
		}	
	
	void Update()
	{
		if(meshRenderer.enabled)
		{
			float angle = Mathf.Atan((angleMeasurer.position.y - _transform.position.y)/(angleMeasurer.position.x - _transform.position.x));
			vertices.Clear();
			
			int count = 0;
			
			distance = (Mathf.Tan (angle)+Mathf.Sqrt(Mathf.Pow(Mathf.Tan (angle),2f)+height*2f*(g / Mathf.Pow (Mathf.Cos(angle)*initSpeed,2f))))/(g/ Mathf.Pow (Mathf.Cos(angle)*initSpeed,2)); //distance = MidPoint (angle);
	
			for(int j = 0; j < groundRes; j++)
			{
				float addon = distance + groundPos[j].y < 0 ? -(distance + groundPos[j].y) : distance + groundPos[j].y;
				float newVel = height+addon*Mathf.Tan (angle) < 0 ? initSpeed : Mathf.Sqrt(g*Mathf.Pow(addon/Mathf.Cos (angle),2f)/(2f*(height + addon*Mathf.Tan (angle))));
				ratio[j] = groundPos[j].x/addon;
				
				for(int i = 0; i < 3*partNo; i++)
				{
					float zed = (addon*i)/partNo;
					float why = -g*Mathf.Pow (zed,2f)/(2f*Mathf.Pow(Mathf.Cos(angle)*newVel,2f)) + Mathf.Tan (angle)*zed; //float why = TrajectoryHeight(angle, zed, initSpeed);
					float ecks = ratio[j]*zed;
					
					cubeList[j,i].position = transform.TransformPoint(new Vector3(ecks,why,zed));
					vertices.Add(new Vector3(ecks,why,zed));
					count++;
				}			
			}		
			
			mesh.vertices = vertices.ToArray();
		}

This is great! Thanks for supplying the code too.

I must add, after you have you vertices variable add the uvs

private List<Vector3> vertices;
private List<Vector2> uvs;

then in Start method instantiate it after vertices, just for order

vertices = new List<Vector3>();
        uvs = new List<Vector2>();

after filling your vertices list, you should be filling your uvs list

vertices.Add(new Vector3(ecks, why, zed));
                uvs.Add(new Vector2(ecks, zed));

then when you have you full list of vertices and uvs, you should let the mesh know them

mesh.vertices = vertices.ToArray();
        mesh.uv = uvs.ToArray();

then let the mesh work a little with the info it got after the triangles are set

mesh.triangles = triangles;
        mesh.RecalculateNormals();
        mesh.RecalculateBounds();
        mesh.Optimize();

and we are done in code.

you will need a uv_checker to know what part of the texture you are using
http://metaverse.mitsi.com/Secondlife/posts/uvmaps/images/uv_checker large.png

you set the texture to a material and that material to your meshrenderer of the trajectory mesh

and the result is this

inside view

outside view

also, if you want the mesh to be showing from the outside, the triangle’s vertices need to be in other order

just change the value of the 2 by 3 and 5 by 6 in both cycles and thats it

old code

triangles[74+ 6*k + 1] = 90*i + 1 + j;
triangles[74+ 6*k + 2] = 90*i + 2 + j;
triangles[74+ 6*k + 3] = 90*(i+1) + 2 + j;
triangles[74+ 6*k + 4] = 90*(i+1) + 2 + j;
triangles[74+ 6*k + 5] = 90*(i+1) + 1 + j;
triangles[74+ 6*k + 6] = 90*i + 1 + j;

new code

triangles[74+ 6*k + 1] = 90*i + 1 + j;
triangles[74+ 6*k + 2] = 90*(i+1) + 2 + j;
triangles[74+ 6*k + 3] = 90*i + 2 + j;
triangles[74+ 6*k + 4] = 90*(i+1) + 2 + j;
triangles[74+ 6*k + 5] = 90*i + 1 + j;
triangles[74+ 6*k + 6] = 90*(i+1) + 1 + j;

this is the result outside view when normals to the outside

hope this helps anyone, and again, GREAT POST Tom!

I want the user to add points to the screen and the application will automatic connect them together to create one mesh from these points on runtime is this possible especially that the user can add up to 100 point .