Unity3D Building Damage Tutorial- Part X - Health, damage spreading and open edges

Well, in this part I'll talk about two small enhancements and one more complex issue.

Health
As you may remember, for now the health of Alias is stored as float, ranging from 0 to 1. When state of Alias is



VerticleState.Burning, after every update the health is reduced, until health is smalled than 0 and Alias is destroyed. This implementation has some undeniable disadvantages. For example, on right picture Aliases taking part in big triangles in walls has as much health, and as a result will burn as fast as the small traingles in wooden poles. I decided to modify that and calculate health of each Alias so that it will depend on how "big" the Alias is => how long are the links to other aliases.

CalculateHealth method
private float CalculateHealth{ float sum = 0f; foreach(int i in LinkedAliases){ sum += Vector3.Distance(positionRelative, OwnerManager.Aliases[i].positionRelative); } 			return sum/LinkedAliases.Count*OwnerManager.AliasHealthParameter; } Returned value is average lenght of links, multiplied by. I decided to start moving parameters to MeshManager, as later configuring it would be much easier.

Health parameter
private void Initialise{ Triangles = new List ; LinkedAliases = new List ; Aliases = new List ; state = VerticleState.Standard; positionAbsolute = Vector3.zero; health = 9999f; LinksToOtherBranches = new List ; } We initialise it with so strange number, becouse in first update we check if it was still not changed, and if it was not, we calculate it. public void Update{ if(health == 9999f){//it is still unassigned health = CalculateHealth; } We can't calculate health before, lets say in initialise as LinkedAliases are not set there yet.

Fire Spreading
Before, when aliases state was VerticleState.Burning, during the Update a TryToFireLinkedAliases method was called, that called the StartFire method on every Linked Alias. I think that this was not good enough. New fire spreading implementation should So, I've modified the TryToFireLikedAliases method a bit
 * 1) Spreading should be bit more random
 * 2) Not in every "Update" fire should be spreaded - it may take more time
 * 3) A Alias that is linked to 5 burning Aliases should "get fire" faster that is linked burning Alias.

TryToFireLinkedAliases method
public void TryToFireLinkedAliases{ foreach(int k in LinkedAliases){ OwnerManager.Aliases[k].TryToStartFire; } 		} Just calling an other method

TryToStartFire method
public void TryToStartFire{ if(TryingToBeFired <= 1f){ TryingToBeFired += Random.value * OwnerManager.TryingToBeFiredParameter; }			 		} There is conversion. From being a bool, TryingToBeFired is a float! private float TryingToBeFired = 0f; ...  ...   ...   ...   ... 		public void Update{ ... ... ... ... ... 			if((TryingToBeFired>=1f)&&state != VerticleState.Burning){ state = VerticleState.Burning; } TryingToBeFired will be increased by some random value multiplied by a parameter. When it reaches 1, VerticleState will be set to Burning.

Open Meshes, open Edges




When i was trying to burn this Arabian House, I realized that there was an issue with the mesh. Mesh was made from several, "open" objects, as seen at the left photo. When we add twins (right photo), there is a void between Aliases (red line) and twins (green line), and we can see inside of mesh. Of course, we have to repair it.

Plan

 * 1) Find "Hole" => Edges that participate in only one triangle!
 * 2) Fill the void between this edge and its twin edge

Finding these edges
Unity stores no raw data about edges. We will have to use mesh.traingles. More efficient packed data about traingles are stored in the custom Traingles List in MeshManager class.

CheckUnClosedMeshes
public MeshManager (ref MeshFilter meshFilter){//constructor ...  ...   ...   ...   ...   ... 		CheckUnClosedMeshes; MakeMeshThick; ProducePlannedTrianglesBetweenAliasAndTwin; And then the method itself. private void CheckUnClosedMeshes{//tutorial, part X //Okay, what do we need? We need a list of edges that belond to only one triangle! Dictionary  EdgesList = new Dictionary; //This is intresting. When we will add new edges, we will duplicate some data //So how to find duplicates? I decided to use Dictionary. We will be given a "edge data" consisting of two Alias numbers. They will form Vector3.x and Vector3.y //Using them we are going to form a (float) key that will be diffrent for every two diffent numbers (Alias numbers). Vector3.z will be the number of times this edge will appear //than by checking the Vector3.z data we will find duplicates. foreach(Vector3 tri in Triangles){ // as we know, in each traingle there are three edges. Moreover data in Traingles is stored as verticles, not triangles data TryToAddEdgeToEdgesDictionary( VerticleToAliasArray[ (int) tri.x ], VerticleToAliasArray[ (int) tri.y ], EdgesList); TryToAddEdgeToEdgesDictionary( VerticleToAliasArray[ (int) tri.y ], VerticleToAliasArray[ (int) tri.z ], EdgesList); TryToAddEdgeToEdgesDictionary( VerticleToAliasArray[ (int) tri.x ], VerticleToAliasArray[ (int) tri.z ], EdgesList); } 		//Okay, lets find the edges which are in only one triangle foreach( KeyValuePair pair in EdgesList){ if(pair.Value.z == 0){ AddToPlannedTrianglesBetweenAliasAndTwin( (int)	pair.Value.x, (int) pair.Value.y); } 		} 	} Note that it is called before "thicking" as I dont want to work on Twins as well. Function is explained in comments.

TryToAddEdgeToEdgesDictionary Method
private void TryToAddEdgeToEdgesDictionary(int x, int y, Dictionary EdgesList){ //given numbers are alias numbers if( x == y ){//Sometimes happen, but should not. Anyway, calculating anything then has no point return; } 		KeyValuePair Temp; Temp = MakeOneEdgeToDictionary(x, y) ; if(EdgesList.ContainsKey(Temp.Key)){ //check is this pair was arleady added. If so, increment Vector3.z Else, add this edge Vector3 Current = EdgesList[Temp.Key]; Current.z = Current.z +1; EdgesList[Temp.Key] = Current; }else{ EdgesList.Add(Temp.Key, Temp.Value); } 	}

MakeOneEddgeToDictionary method
private KeyValuePair MakeOneEdgeToDictionary(int x, int y){//does what name says, used in CheckUnClosedMeshes method return new KeyValuePair( ProduceSeed(x,y), new Vector3(x, y, 0)); //As number of times the edge was found (Vector3.z) zero is now given. Has to be modified in caller method! }

ProduceSeed
private float ProduceSeed(int x, int y){// unique seed of two ints int a, b; 		if(x>y){ a = x; b = y;} else { a = y; b = x ;}//these manevuers are made to produce seed identical no mater in what order the parameters were send. When we give alias numbers 12 and 15 or 15 and 12 the same effect will happen return (a * Aliases.Count + b );// Not sure how random it is, hope that enough random }

AddToPlannedTrianglesBetweenAliasAndTwin method
Intrasting method. Called from CheckUnClosedMeshes function when an edge is used in only one triangle. private void AddToPlannedTrianglesBetweenAliasAndTwin(int x, int y){//we want to store data about which new edges has to produce walls. //this is becouse calculating this edges is done BEFORE making Mesh Thick. As in making walls we use twins, it has to be done later, //after meshes twins are placed. This wall making is done in ProducePlannedTrianglesBetweenWalls PlannedTrianglesToBeMadeBetweenAliasAndTwin.Add( new Vector2( x, y ) ); } and on top of page private List  PlannedTrianglesToBeMadeBetweenAliasAndTwin; ...  ...   ...   ...   ...   ... 	private void Initialise  { ...  ...   ...   ...   ...   	PlannedTrianglesToBeMadeBetweenAliasAndTwin = new List; We cannot make walls between Aliases and Twins as this is calculated before "mesh-thinking". The data of which edges are "unclosed" is stored in a list, which is that used in  method just after   is done.

ProducePlannedTrainglesBetweenAliasAndTwin method
private void ProducePlannedTrianglesBetweenAliasAndTwin{ if(IsMeshThick == false){ Debug.Log("Mesh is not thick. Becouse of that I cannot make triangles as there is not twins! "); return; }else{ foreach(Vector2 vec in PlannedTrianglesToBeMadeBetweenAliasAndTwin){//Procedures similar to ones used in Verticle class, during wall-making after a destruction of Alias AddWallToWallsList( (int) vec.x					, (int) vec.y					, (int) vec.x + Aliases.Count/2); AddWallToWallsList( (int) vec.y					, (int) vec.x + Aliases.Count/2	, (int) vec.y + Aliases.Count/2); AddWallToWallsList( (int) vec.x + Aliases.Count/2, (int) vec.y					, (int) vec.x ); AddWallToWallsList( (int) vec.y + Aliases.Count/2, (int) vec.x + Aliases.Count/2	, (int) vec.y ); } 			MakeWallsFromList; UpdateTrianglesList; TranslateFromMeshToManager; } 	} Okay, quite simple. As you see these additional walls will be "two-sided" unfortunately, but as these calculations are done only once, ant the start of map, this is no big deal.

Ending
Files are on Github Repo. Version 8