Marching cubes not creating expected meshes - c#

I have been trying to implement marching cubes in unity with c# but the meshes that are being created all have a rotation that is off by 90 degrees.
in this example I am trying to create a cube.
this is my current code that I have if any one can figure out what the problem is that would be great thanks.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
public class train : MonoBehaviour
{
//setup e.g varables and lists
//----------------------------------------------------------------------------------------------------
//size of the chunk
public int size = 10;
//binary stuff
public List<int> binary = new List<int>();
int bitewisenum = 0;
//posithions
public static Vector3 xyz = new Vector3();
public Vector3 list_edges = new Vector3();
//how much air the cut off point bettween air and solid 0.45 - 0.55 works best
public float dencity = 0.4f;
//mesh
public Vector3[] vertices;
public List<Vector3> vertices_list = new List<Vector3>();
public List<Vector3> vertices_list_store = new List<Vector3>();
public List<Vector3> inverlist = new List<Vector3>();
//we can convert it back to an array later
public List<int> tri = new List<int>();
Mesh mesh;
private int[] num;
private float airsolid_test;
public float cutoff; // 11 works best
//perlin noise
//---------------------------------------------------------------------------------------------------
public static float Perlin3D(float x, float y, float z, float cut)
{
//float AB = Mathf.PerlinNoise(x, y);
//float BC = Mathf.PerlinNoise(y, z);
//float AC = Mathf.PerlinNoise(x, z);
//
//
//float BA = Mathf.PerlinNoise(y, x);
//float CB = Mathf.PerlinNoise(z, y);
//float CA = Mathf.PerlinNoise(z, x);
//
////get the avarge
//float ABC = AB + BC + AC + BA + CB + CA;
//float per = ABC / 6f;
//if (y > cut)
//{
// per = Mathf.Pow(per * (y / 8.5f), 2.718f) + 0.3f;
//}
//testing
float per;
if (x >= cut)
{
per = 0f;
} else if (y >= cut)
{
per = 0f;
}
else if (z <= 0)
{
per = 0f;
}
else if (x <= 0)
{
per = 0f;
}
else if (z >= cut)
{
per = 0f;
}
else if (y <= 0)
{
per = 0f;
}else
{
per = 10f;
}
//testing ends
return per;
}
//----------------------------------------------------------------------------------------
private void Start()
{
//set up binary
binary.Add(1);
binary.Add(2);
binary.Add(4);
binary.Add(8);
binary.Add(16);
binary.Add(32);
binary.Add(64);
binary.Add(128);
//setup mesh
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
tri.Clear();
//testing --------------------------------------
airsolid_test = dencity;
//0.1 is because of the perlin noise
for (float x = 0.1f; x < 16; x++)
{
for (int y = 0; y < 15; y++)
{
for (int z = 0; z < 15; z++)
{
//airsolid_test = dencity;
//if (y >= cutoff)
//{//mountans
// airsolid_test += 0.0f;//0.2f
//}
//else
//{//caves
// airsolid_test -= 0.005f;//0.005
//}
xyz.x = x;
xyz.y = y;
xyz.z = z;
createshape(x, y, z);
upmesh();
}
}
}
}
//xyz.x = x;
// xyz.y = y;
// xyz.z = z;
// createshape(x, y, z);
//upmesh();
//upmesh stands for update mesh
void upmesh()
{
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = tri.ToArray();
mesh.RecalculateNormals();
}
//-------------------------------------------------------------------------------------
// this is the main function and you give it an xyz cordanit and it sets the mesh's triangles and veticeys
void createshape(float x, float y, float z)
{
//list for the binary
List<int> bite = new List<int>();
bite.Clear();
bitewisenum = 0;
//this is getting the binay representation for each point on the cube if the point is > the density value store it as a 1 else as a 0
// so we end up with a binary number like 10011101
//position 7
if (Perlin3D(x + 1, y, z + 1, cutoff) > airsolid_test)
{
bite.Add(1);
//these store the value for intertopilation later but im not using it yet
float Pose_7 = Perlin3D(x + 1, y, z + 1,cutoff);
}
else
{
bite.Add(0);
}
//position 6
if (Perlin3D(x + 1, y + 1, z + 1, cutoff) > airsolid_test)
{
bite.Add(1);
float Pose_6 = Perlin3D(x + 1, y + 1, z + 1, cutoff);
}
else
{
bite.Add(0);
}
//position 5
if (Perlin3D(x, y + 1, z + 1, cutoff) > airsolid_test)
{
bite.Add(1);
float Pose_5 = Perlin3D(x, y + 1, z + 1, cutoff);
}
else
{
bite.Add(0);
}
//position 4
if (Perlin3D(x, y, z + 1, cutoff) > airsolid_test)
{
bite.Add(1);
float Pose_4 = Perlin3D(x, y, z + 1, cutoff);
}
else
{
bite.Add(0);
}
//position 3
if (Perlin3D(x + 1, y, z, cutoff) > airsolid_test)
{
bite.Add(1);
float Pose_3 = Perlin3D(x + 1, y, z, cutoff);
}
else
{
bite.Add(0);
}
//position 2
if (Perlin3D(x + 1, y + 1, z, cutoff) > airsolid_test)
{
bite.Add(1);
float Pose_2 = Perlin3D(x + 1, y + 1, z, cutoff);
}
else
{
bite.Add(0);
}
//position 1
if (Perlin3D(x, y + 1, z, cutoff) > airsolid_test)
{
bite.Add(1);
float Pose_1 = Perlin3D(x, y + 1, z, cutoff);
}
else
{
bite.Add(0);
}
//position 0
if (Perlin3D(x, y, z, cutoff) > airsolid_test)
{
bite.Add(1);
float Pose_0 = Perlin3D(x, y, z, cutoff);
}
else
{
bite.Add(0);
}
//convert binary in to decimal because i dont know an easy way to do this but
//loop throught all items in the binary list and change them
for (int i = 0; i <= 7; i++)
{
if (bite[i] == 1)
{
bitewisenum += binary[i];
}
}
//print(bitewisenum);
//check if its not all air or inside an object
//this means that we check if its 0 or 255
//now we need to make the mesh and show it
if (bitewisenum != 0 && bitewisenum != 255)
{
//Debug.Log("made a mesh");
//store the int array for easyer use
int[] trian = table.TriangleTable[bitewisenum];
/*
* at this point the int array describes edges not postions
* and we need to change that so what we do is loop through ever item in the list and
* parce it through a function(table.transform_to_postion) this returns a Vector 3
* which we then add to a list
*/
vertices_list.Clear();
for (int num = 0; num < trian.Length; num++)
{
//get the actule postions
//we give the function a edge index as an int which represents a edge index and it gives you the actule postion back as a vector 3
list_edges = transform_to_postion(trian[num]);
//add them to the list
vertices_list.Add(list_edges);
vertices_list_store.Add(list_edges);
//end of the for loop
}
// set vetaceys
vertices = vertices_list_store.ToArray();
//
//setting the triangles based off how many items there are on the verticeys list
if (vertices_list.Count / 3 == 1)
{
for (int i = 1; i < 4; i++)
{
tri.Add(tri.Count);
}
}
else if (vertices_list.Count / 3 == 2)
{
for (int i = 1; i < 7; i++)
{
tri.Add(tri.Count);
}
}
else if (vertices_list.Count / 3 == 3)
{
for (int i = 1; i < 10; i++)
{
tri.Add(tri.Count);
}
}
else if (vertices_list.Count / 3 == 4)
{
for (int i = 1; i < 13; i++)
{
tri.Add(tri.Count);
}
}
else if (vertices_list.Count / 3 == 5)
{
for (int i = 1; i < 16; i++)
{
tri.Add(tri.Count);
}
}
/*
Debug.Log("the legnth of verticys: "+vertices.Length);
Debug.Log("the legnth of tri: "+tri.Length);
Debug.Log("tri legnth " + tri.Length);
end of the if loop
*/
}
//end of the function
}
public static Vector3 transform_to_postion(int number_edge)
{
Vector3 xyz_of_edge = new Vector3();
//check which one it is and then set the xyz
if (number_edge == 0)
{
xyz_of_edge.x = xyz.x + 0.5f;
xyz_of_edge.y = xyz.y;
xyz_of_edge.z = xyz.z + 1f;
}
else if (number_edge == 1)
{
xyz_of_edge.x = xyz.x + 1f;
xyz_of_edge.y = xyz.y;
xyz_of_edge.z = xyz.z + 0.5f;
}
else if (number_edge == 2)
{
xyz_of_edge.x = xyz.x + 0.5f;
xyz_of_edge.y = xyz.y;
xyz_of_edge.z = xyz.z;
}
else if (number_edge == 3)
{
xyz_of_edge.x = xyz.x;
xyz_of_edge.y = xyz.y;
xyz_of_edge.z = xyz.z + 0.5f;
}
else if (number_edge == 4)
{
xyz_of_edge.x = xyz.x + 0.5f;
xyz_of_edge.y = xyz.y + 1f;
xyz_of_edge.z = xyz.z + 1f;
}
else if (number_edge == 5)
{
xyz_of_edge.x = xyz.x + 1f;
xyz_of_edge.y = xyz.y + 1f;
xyz_of_edge.z = xyz.z + 0.5f;
}
else if (number_edge == 6)
{
xyz_of_edge.x = xyz.x + 0.5f;
xyz_of_edge.y = xyz.y + 1f;
xyz_of_edge.z = xyz.z;
}
else if (number_edge == 7)
{
xyz_of_edge.x = xyz.x;
xyz_of_edge.y = xyz.y + 1;
xyz_of_edge.z = xyz.z + 0.5f;
}
else if (number_edge == 8)
{
xyz_of_edge.x = xyz.x;
xyz_of_edge.y = xyz.y + 0.5f;
xyz_of_edge.z = xyz.z + 1f;
}
else if (number_edge == 9)
{
xyz_of_edge.x = xyz.x + 1f;
xyz_of_edge.y = xyz.y + 0.5f;
xyz_of_edge.z = xyz.z + 1f;
}
else if (number_edge == 10)
{
xyz_of_edge.x = xyz.x + 1f;
xyz_of_edge.y = xyz.y + 0.5f;
xyz_of_edge.z = xyz.z;
}
else if (number_edge == 11)
{
xyz_of_edge.x = xyz.x;
xyz_of_edge.y = xyz.y + 0.5f;
xyz_of_edge.z = xyz.z;
}
//Debug.Log("edge index " + number_edge);
//Debug.Log("edges "+xyz_of_edge);
//Debug.Log(xyz_of_edge);
return xyz_of_edge;
}
}

I figured out that the problem was the way that I was labeling the points of the cube. I had had them wrong for the triangulation table this was causing the orientation error.
to fix it all I did was in my code I redefined the points to there actual value.

Related

Unity Map generation and colors

I am currently following a Brackeys tutorial on procedural terrain generation colors. I got to a point where it gives me this error:
IndexOutOfRangeException: Index was outside the bounds of the array. mapgeneration.CreateShape () (at Assets/mapgeneration.cs:108
mapgeneration.Update () (at Assets/mapgeneration.cs:131)
I am using gradients to display color on line 108 in CreateShape. This is the line:
colors[iloopedforvertecy] = gradient.Evaluate(height);
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
public class mapgeneration : MonoBehaviour
{
Mesh mesh;
Color[] colors;
Vector3[] vertices;
int[] triangles;
public int xsize = 20;
public int zsize = 20;
[Range(1, 100)]
public float smooth = 1.0f;
public MeshCollider _mesh;
public Transform water;
public float scale;
public float smoothfactor;
public float xoffset = 0.0f;
public float zoffset = 0.0f;
public float minwaterheight;
public float maxwaterheight;
public float minterainheight;
public float maxterainheight;
public Gradient gradient;
// Start is called before the first frame update
void Start()
{
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
CreateShape();
_mesh = GetComponent<MeshCollider>();
water.transform.position = new Vector3(0, Random.Range(minwaterheight, maxwaterheight), 0);
}
void CreateShape()
{
vertices = new Vector3[(xsize + 1) * (zsize + 1)];
water.transform.localScale = new Vector3(xsize, 0, zsize);
int iloopedforvertecy = 0;
triangles = new int[xsize * zsize * 6];
int vert = 0;
int tris = 0;
for(int z = 0; z < zsize; z++)
{
for(int x = 0; x < xsize; x++)
{
triangles[tris + 0] = vert + 0;
triangles[tris + 1] = vert + xsize + 1;
triangles[tris + 2] = vert + 1;
triangles[tris + 3] = vert + 1;
triangles[tris + 4] = vert + xsize + 1;
triangles[tris + 5] = vert + xsize + 2;
vert++;
tris += 6;
}
vert++;
}
colors = new Color[vertices.Length];
for (int z = 0; z <= zsize; z++)
{
for(int x = 0; x <= xsize; x++)
{
float xCoord = (float)x / xsize * scale + xoffset;
float zCoord = (float)z / zsize * scale + zoffset;
float y = Mathf.PerlinNoise(xCoord * smooth, zCoord * smooth) * smoothfactor;
vertices[iloopedforvertecy] = new Vector3(x, y, z);
iloopedforvertecy += 1;
if(y > maxterainheight)
{
maxterainheight = y;
}
if(y < minterainheight)
{
minterainheight = y;
}
float height = Mathf.InverseLerp(minterainheight, maxterainheight, 0.5f); //vertices[iloopedforvertecy].z
Debug.LogWarning(height);
colors[iloopedforvertecy] = gradient.Evaluate(height);
}
}
}
void UpdateMsh()
{
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();
mesh.colors = colors;
}
void Update()
{
CreateShape();
UpdateMsh();
}
}
I know that my code is messy. Still new to coding and unity in general.
Oh PS. Can somebody please help me add a collider to code generated object as you can see in the code above?
In your for-loop you increase the value of iloopedforvertecy before you evaluate your gradient value.
Because of this the last value of iloopedforvertecy will be greater than the array length of your colors array.
Try to move the line which increases the value at the end of the loop
for(int x = 0; x <= xsize; x++)
{
float xCoord = (float)x / xsize * scale + xoffset;
float zCoord = (float)z / zsize * scale + zoffset;
float y = Mathf.PerlinNoise(xCoord * smooth, zCoord * smooth) * smoothfactor;
vertices[iloopedforvertecy] = new Vector3(x, y, z);
// iloopedforvertecy += 1; // HERE THE VALUE WAS INCREASED
if(y > maxterainheight)
{
maxterainheight = y;
}
if(y < minterainheight)
{
minterainheight = y;
}
float height = Mathf.InverseLerp(minterainheight, maxterainheight, 0.5f); //vertices[iloopedforvertecy].z
Debug.LogWarning(height);
colors[iloopedforvertecy] = gradient.Evaluate(height);
iloopedforvertecy += 1; // HERE SHOULD THE VALUE BE INCREASED
}
In the example here I commented the two lines.

How can I add remove more number of objects at runtime?

The script is a bit long I'm not sure what I can reduce I will try.
When I'm running the program it's starting by making the square formation.
What I'm trying to archive is that at each time in runtime if I change the numberOfSquadMembers value up or down add/remove to each squad the more/less value of squad members.
Same for the numberOfSquads value.
The problem is I'm not sure how to do it in the Update() how to add more squads/squadmemebers or destroy when the values are less then the current ?
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Formations : MonoBehaviour
{
enum Formation
{
Square, Circle, Triangle
}
[Header("Main Settings")]
[Space(5)]
[Range(4, 100)]
public int numberOfSquadMembers = 20;
[Range(1, 20)]
public int numberOfSquads = 1;
[Range(0, 4)]
public int columns = 4;
public int gaps = 10;
public int circleRadius = 10;
public float yOffset = 0;
[Range(3, 50)]
public float moveSpeed = 3;
[Range(3, 50)]
public float rotateSpeed = 1;
public float threshold = 0.1f;
public bool randomSpeed = false;
[Range(1, 100)]
public int randSpeedMin = 1;
[Range(1, 100)]
public int randSpeedMax = 1;
public bool startRandomFormation = false;
public string currentFormation;
private Formation formation;
private List<Quaternion> quaternions = new List<Quaternion>();
private List<Vector3> newpositions = new List<Vector3>();
private bool move = false;
private bool squareFormation = false;
private List<GameObject> squadMembers = new List<GameObject>();
private float[] step;
private int[] randomSpeeds;
private int index = 0;
private bool ready = false;
private GameObject[] squads;
private Vector3 startVector;
private Vector3 total;
private List<Vector3> endpositions = new List<Vector3>();
public void InitFormations()
{
if (startRandomFormation)
{
formation = (Formation)UnityEngine.Random.Range(0, Enum.GetNames(typeof(Formation)).Length);
}
else
{
formation = Formation.Square;
}
squads = GameObject.FindGameObjectsWithTag("Squad");
for (int i = 0; i < squads.Length; i++)
{
foreach (Transform squadMember in squads[i].transform)
{
squadMembers.Add(squadMember.gameObject);
}
}
foreach (GameObject unit in squadMembers)
{
if (unit != null)
{
total += unit.transform.position;
}
}
Vector3 center = total / squadMembers.Count;
//Vector3 endPos = GameObject.Find("Cube").transform.position;
foreach (GameObject unit in squadMembers)
{
startVector = unit.transform.position - center;
endpositions.Add(/*endPos + */startVector);
}
currentFormation = formation.ToString();
ChangeFormation();
randomSpeeds = RandomSpeeds(randSpeedMin, randSpeedMax, squadMembers.Count);
step = new float[squadMembers.Count];
ready = true;
}
private void Start()
{
InitFormations();
}
// Update is called once per frame
void Update()
{
if (ready == true)
{
if (Input.GetKeyDown(KeyCode.C))
{
randomSpeeds = RandomSpeeds(randSpeedMin, randSpeedMax, squadMembers.Count);
foreach (int speedV in randomSpeeds)
{
if (index == randomSpeeds.Length)
index = 0;
step[index] = speedV * Time.deltaTime;
index++;
}
ChangeFormation();
}
if (move == true)
{
MoveToNextFormation();
}
}
}
private void ChangeFormation()
{
switch (formation)
{
case Formation.Square:
FormationSquare();
break;
case Formation.Circle:
FormationCircle();
break;
case Formation.Triangle:
FormationTriangle();
break;
}
}
private void FormationTriangle()
{
newpositions = new List<Vector3>();
int height = Mathf.CeilToInt((Mathf.Sqrt(8 * squadMembers.Count + 1f) - 1f) / 2);
int slots = (int)(height * (height + 1f) / 2f);
float verticalModifier = 1.25f; // * 1.25f to increase horizontal space
float horizontalModifier = 0.8f; // * 0.8f to decrease "vertical" space
float width = 0.5f * (height - 1f);
Vector3 startPos = new Vector3(width * horizontalModifier, 0f, (float)(height - 1f) * verticalModifier);
int finalRowCount = height - slots + squadMembers.Count;
for (int rowNum = 0; rowNum < height && newpositions.Count < squadMembers.Count; rowNum++)
{
for (int i = 0; i < rowNum + 1 && newpositions.Count < squadMembers.Count; i++)
{
float xOffset = 0f;
if (rowNum + 1 == height)
{
// If we're in the last row, stretch it ...
if (finalRowCount != 1)
{
// Unless there's only one item in the last row.
// If that's the case, leave it centered.
xOffset = Mathf.Lerp(
rowNum / 2f,
-rowNum / 2f,
i / (finalRowCount - 1f)
) * horizontalModifier;
}
}
else
{
xOffset = (i - rowNum / 2f) * horizontalModifier;
}
float yOffset = (float)rowNum * verticalModifier;
Vector3 position = new Vector3(
startPos.x + xOffset, 0f, startPos.y - yOffset);
newpositions.Add(position);
}
}
move = true;
formation = Formation.Square;
}
private Vector3 FormationSquarePositionCalculation(int index) // call this func for all your objects
{
float posX = (index % columns) * gaps;
float posY = (index / columns) * gaps;
return new Vector3(posX, posY);
}
private void FormationSquare()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
for (int i = 0; i < squadMembers.Count; i++)
{
Vector3 pos = FormationSquarePositionCalculation(i);
//squadMembers[i].transform.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
squadMembers[i].transform.Rotate(new Vector3(0, -90, 0));
//newpositions.Add(new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y));
newpositions.Add(new Vector3(endpositions[i].x + pos.x, 0, endpositions[i].y + pos.y));
}
move = true;
squareFormation = true;
formation = Formation.Circle;
}
private Vector3 FormationCirclePositionCalculation(Vector3 center, float radius, int index, float angleIncrement)
{
float ang = index * angleIncrement;
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.z = center.z + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
pos.y = center.y;
return pos;
}
private void FormationCircle()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
Vector3 center = transform.position;
float radius = (float)circleRadius / 2;
float angleIncrement = 360 / squadMembers.Count;//(float)numberOfSquadMembers;
for (int i = 0; i < squadMembers.Count; i++)//numberOfSquadMembers; i++)
{
Vector3 pos = FormationCirclePositionCalculation(center, radius, i, angleIncrement);
var rot = Quaternion.LookRotation(center - pos);
if (Terrain.activeTerrain == true)
pos.y = Terrain.activeTerrain.SampleHeight(pos);
pos.y = pos.y + yOffset;
newpositions.Add(pos);
quaternions.Add(rot);
}
move = true;
squareFormation = false;
formation = Formation.Triangle;
}
private void MoveToNextFormation()
{
if (randomSpeed == false)
{
if (step.Length > 0)
step[0] = moveSpeed * Time.deltaTime;
}
for (int i = 0; i < squadMembers.Count; i++)
{
squadMembers[i].transform.LookAt(newpositions[i]);
if (randomSpeed == true)
{
squadMembers[i].transform.position =
Vector3.MoveTowards(squadMembers[i].transform.position, newpositions[i], step[i]);
}
else
{
squadMembers[i].transform.position =
Vector3.MoveTowards(squadMembers[i].transform.position, newpositions[i], step[0]);
}
if (Vector3.Distance(squadMembers[i].transform.position, newpositions[i]) < threshold)
{
if (squareFormation == true)
{
Vector3 degrees = new Vector3(0, 0, 0);
Quaternion quaternion = Quaternion.Euler(degrees);
squadMembers[i].transform.rotation = Quaternion.Slerp(squadMembers[i].transform.rotation, quaternion, rotateSpeed * Time.deltaTime);
}
else
{
squadMembers[i].transform.rotation = Quaternion.Slerp(squadMembers[i].transform.rotation, quaternions[i], rotateSpeed * Time.deltaTime);
}
}
}
}
private static int[] RandomSpeeds(int min, int max, int howMany)
{
int[] myNumbers = new int[howMany];
for (int i = 0; i < howMany; i++)
{
myNumbers[i] = UnityEngine.Random.Range(min, max);
}
return myNumbers;
}
}
This is the script that generate the squads and number of squads members first time :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SquadsGenerator : MonoBehaviour
{
public GameObject squadPrefab;
public int numberOfSquads;
public int numberOfMembersInsquad;
private GameObject squadsParent;
private void Start()
{
squadsParent = GameObject.Find("Squads");
GenerateSquads(numberOfSquads, numberOfMembersInsquad, squadPrefab);
}
// Update is called once per frame
void Update()
{
}
public void GenerateSquads(int squadsCount,
int numberOfMembers,
GameObject squadMemberPrefab)
{
for (int i = 0; i < squadsCount; i++)
{
GameObject newSquad = new GameObject();
newSquad.name = "Squad " + i;
newSquad.tag = "Squad";
newSquad.transform.parent = squadsParent.transform;
for (int x = 0; x < numberOfMembers; x++)
{
var go = Instantiate(squadMemberPrefab);
go.name = "Member " + x;
go.tag = "Squad Member";
go.transform.parent = newSquad.transform;
switch (i % 6)
{
case 0: ColorSquad(go, Color.green); break;
case 1: ColorSquad(go, Color.red); break;
case 2: ColorSquad(go, Color.blue); break;
case 3: ColorSquad(go, Color.yellow); break;
case 4: ColorSquad(go, Color.cyan); break;
case 5: ColorSquad(go, Color.magenta); break;
}
}
}
}
private void ColorSquad(GameObject squad, Color color)
{
Renderer rend = squad.GetComponent<Renderer>();
rend.material.SetColor("_Color", color);
}
}
Update :
What I have tried so far :
I added this part to the Update()
if(oldNumOfSquadMemebers != numberOfSquadMembers)
{
var tt = numberOfSquadMembers - oldNumOfSquadMemebers;
for (int i = 0; i < tt; i++)
{
var go = Instantiate(squadMemberPrefab);
squadMembers.Add(go);
}
oldNumOfSquadMemebers = numberOfSquadMembers;
FormationSquare();
}
The problem now is in the FormationSquare method :
private void FormationSquare()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
for (int i = 0; i < squadMembers.Count; i++)
{
Vector3 pos = FormationSquarePositionCalculation(i);
squadMembers[i].transform.Rotate(new Vector3(0, -90, 0));
newpositions.Add(new Vector3(endpositions[i].x + pos.x, 0, endpositions[i].y + pos.y));
}
move = true;
squareFormation = true;
formation = Formation.Circle;
}
Now I'm making a new instance for the newpositions List so each time I change the number of squad members there are more members but less newpositions so at some time I'm getting out of index exception.
If I don't make a new instance for the List then it will work but then after some time it will throw the out of index exception this time because it keep adding more and more new positions to the list.
So this newpositions List I'm stuck with it. Not sure how to solve it.

How can I set the newPositions distance to be shorter closer to the original squadMembers positions?

I have 3 formations:
In each formation I'm calculating new positions to move to and add them to the List newPositions.
private void FormationTriangle()
{
newpositions = new List<Vector3>();
int height = Mathf.CeilToInt((Mathf.Sqrt(8 * squadMembers.Count + 1f) - 1f) / 2);
int slots = (int)(height * (height + 1f) / 2f);
float verticalModifier = 1.25f; // * 1.25f to increase horizontal space
float horizontalModifier = 0.8f; // * 0.8f to decrease "vertical" space
float width = 0.5f * (height - 1f);
Vector3 startPos = new Vector3(width * horizontalModifier, 0f, (float)(height - 1f) * verticalModifier);
int finalRowCount = height - slots + squadMembers.Count;
for (int rowNum = 0; rowNum < height && newpositions.Count < squadMembers.Count; rowNum++)
{
for (int i = 0; i < rowNum + 1 && newpositions.Count < squadMembers.Count; i++)
{
float xOffset = 0f;
if (rowNum + 1 == height)
{
// If we're in the last row, stretch it ...
if (finalRowCount != 1)
{
// Unless there's only one item in the last row.
// If that's the case, leave it centered.
xOffset = Mathf.Lerp(
rowNum / 2f,
-rowNum / 2f,
i / (finalRowCount - 1f)
) * horizontalModifier;
}
}
else
{
xOffset = (i - rowNum / 2f) * horizontalModifier;
}
float yOffset = (float)rowNum * verticalModifier;
Vector3 position = new Vector3(
startPos.x + xOffset, 0f, startPos.y - yOffset);
newpositions.Add(position);
}
}
move = true;
formation = Formation.Square;
}
private Vector3 FormationSquarePositionCalculation(int index) // call this func for all your objects
{
float posX = (index % columns) * gaps;
float posY = (index / columns) * gaps;
return new Vector3(posX, posY);
}
private void FormationSquare()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
for (int i = 0; i < squadMembers.Count; i++)
{
Vector3 pos = FormationSquarePositionCalculation(i);
//squadMembers[i].transform.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
squadMembers[i].transform.Rotate(new Vector3(0, -90, 0));
newpositions.Add(new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y));
}
move = true;
squareFormation = true;
formation = Formation.Circle;
}
private Vector3 FormationCirclePositionCalculation(Vector3 center, float radius, int index, float angleIncrement)
{
float ang = index * angleIncrement;
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.z = center.z + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
pos.y = center.y;
return pos;
}
private void FormationCircle()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
Vector3 center = transform.position;
float radius = (float)circleRadius / 2;
float angleIncrement = 360 / (float)numberOfSquadMembers;
for (int i = 0; i < numberOfSquadMembers; i++)
{
Vector3 pos = FormationCirclePositionCalculation(center, radius, i, angleIncrement);
var rot = Quaternion.LookRotation(center - pos);
if (Terrain.activeTerrain == true)
pos.y = Terrain.activeTerrain.SampleHeight(pos);
pos.y = pos.y + yOffset;
newpositions.Add(pos);
quaternions.Add(rot);
}
move = true;
squareFormation = false;
formation = Formation.Triangle;
}
Then I'm moving the squadMembers to the newPositions:
private void MoveToNextFormation()
{
if (randomSpeed == false)
{
if (step.Length > 0)
step[0] = moveSpeed * Time.deltaTime;
}
for (int i = 0; i < squadMembers.Count; i++)
{
squadMembers[i].transform.LookAt(newpositions[i]);
if (Vector3.Distance(squadMembers[i].transform.position, newpositions[i]) > 30f)
{
if (randomSpeed == true)
{
squadMembers[i].transform.position =
Vector3.MoveTowards(squadMembers[i].transform.position, newpositions[i], step[i]);
}
else
{
squadMembers[i].transform.position =
Vector3.MoveTowards(squadMembers[i].transform.position, newpositions[i], step[0]);
}
}
if (Vector3.Distance(squadMembers[i].transform.position, newpositions[i]) < threshold)
{
if (squareFormation == true)
{
Vector3 degrees = new Vector3(0, 0, 0);
Quaternion quaternion = Quaternion.Euler(degrees);
squadMembers[i].transform.rotation = Quaternion.Slerp(squadMembers[i].transform.rotation, quaternion, rotateSpeed * Time.deltaTime);
}
else
{
squadMembers[i].transform.rotation = Quaternion.Slerp(squadMembers[i].transform.rotation, quaternions[i], rotateSpeed * Time.deltaTime);
}
}
}
}
But if I want that the squadMembers will not move at all but will change to the next formation on the same position they are ? Or maybe to move just a little bit for example to distance 20 or 5 or 30 or 300 ?
I tried to add a distance check for the move:
if (Vector3.Distance(squadMembers[i].transform.position, newpositions[i]) > 30f)
So they are changing to the next formation when the distance more then 30.
But then the formation is not looking good. If for example it's the square formation the formation after they finished moving looks like a wave.
Screenshot example:
Wave formation

Unity RTS Total War Style Click and Drag Unit Formation

I've been working in unity trying to implement the total war style of unit formation width/depth creation with click and drag like seen in this video at about 15 seconds:
https://www.youtube.com/watch?v=3aGRzy_PzJQ
I have written some code, but I am getting out of index errors. Here it is so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Formation : MonoBehaviour {
// Use this for initialization
void Start()
{
formationMaker();
}
private void FixedUpdate()
{
formationShape();
}
public GameObject unit;//this is a cube prefab with scale dimensions .45,1.8,.3
public Transform parent;//this is the parent object of the cubes acting as anchor point
private Vector3 startClick;
private Vector3 endClick;
private float width;
private List<GameObject> unitsList = new List<GameObject>();
private int numberOfMen;
private int rows;
private int leftOverMen;//was going to use these two to calculate the number of men in the last row and...
private int lastRowSpace;//...the amount of space needed to center them
private int count;
private float manWidth = (float)4.5;
private void formationMaker()//this makes the formation upon start
{
for(int x = -10; x< 10; x++)
{
for(int z = -4; z< 4; z++)
{
Instantiate(unit, new Vector3(x* .7F, .9f, z*1.0f), Quaternion.identity, parent);
unitsList.Add(unit);
}
}
}
private void formationShape()
{
if (Input.GetMouseButtonDown(1))
{
startClick = Input.mousePosition;
}
else if (Input.GetMouseButtonUp(1))
{
endClick = Input.mousePosition;
}
//ERROR HERE?
width = Vector3.Distance(startClick, endClick)/manWidth;//width has to be the width in terms of number of men, but i'm not sure where to get this width
//ERROR HERE
numberOfMen = unitsList.Count;
rows = (int)Mathf.Floor(numberOfMen / width);
leftOverMen = (int)Mathf.Ceil(numberOfMen % width);
lastRowSpace = ((int)width - leftOverMen) / 2;
Debug.Log(rows);
Debug.Log(width);
Debug.Log(numberOfMen);
if (width != 0)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < width; j++)
{
unitsList[count].transform.position = new Vector3(i * .7F, .9f, j * 1.0f) + startClick;
count++;
}
}
}
width = 0;
}
void Update () {
}
}
Thanks for any help!
Your count not reset to zero,and you should convert width to int to eliminate the error.
PS.Input.mousePosition is pixel coordinates,not world position!
You can't get world position by use Input.mousePosition ( use Raycast instead)
Your width can be zero => (int)Mathf.Floor(numberOfMen / width); can lead to errors
If you still look for solution, you can refer to this code (I edited from your code)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Formation : MonoBehaviour
{
public int numberSpawn;
// Use this for initialization
private void Awake()
{
m_BoxSelected = new BoxSelected();
}
void Start()
{
//count = 0;
//formationMaker();
formationMaker(new Vector3(1, 1, 1), new Vector3(1, 1, 1), 30, numberSpawn);
//formationMaker3(new Vector3(), new Vector3(-20, 0, -20), 2f, ref unitsList);
}
private void FixedUpdate()
{
}
public BoxSelected m_BoxSelected;
public Camera m_MainCamera;
private Ray m_ray;
public GameObject unit;//this is a cube prefab with scale dimensions .45,1.8,.3
public Transform parent;//this is the parent object of the cubes acting as anchor point
private Vector3 startClick;
private Vector3 endClick;
private float width;
[SerializeField]
private List<GameObject> unitsList = new List<GameObject>();
private int numberOfMen;
[SerializeField]
private float manWidth = (float)4.5;
private void formationMaker()//this makes the formation upon start
{
for (int x = 0; x < 4; x++)
{
for (int z = 0; z < 4; z++)
{
Instantiate(unit, new Vector3(x * .7F, 0f, z * 1.0f), Quaternion.identity, parent);
unitsList.Add(unit);
}
}
}
private void formationMaker(Vector3 _StartPos, Vector3 _EndPos, float SizeObj, int _NumberObj)//this makes the formation upon start
{
float _width = Mathf.Abs(_StartPos.x - _EndPos.x);
float _height = Mathf.Abs(_StartPos.z - _EndPos.z);
int row = 0, col = 0;
if (_width > SizeObj && _height > SizeObj)
{
float dt = _width * _height;
float _SumCanContain = dt / SizeObj;
if (_SumCanContain >= _NumberObj)
{
// OK
}
else
{
// kiểm tra xem mảng có đựng vừa số Obj không
float Temp = Mathf.Sqrt(_NumberObj);
if ((int)(Temp + 0.5) != (int)Temp) // Phần thập phân lớn hơn 0.5
{
row = (int)(Temp + 1);
col = (int)(Temp + 1);
_width = (int)(Temp + 1) * SizeObj;
_height = (int)(Temp + 1) * SizeObj;
}
else
{
row = ((int)(Temp) + 1);
col = (int)(Temp);
_width = ((int)(Temp) + 1) * SizeObj;
_height = (int)(Temp) * SizeObj;
}
}
}
else
{
// kiểm tra xem mảng có đựng vừa số Obj không
float Temp = Mathf.Sqrt(_NumberObj);
if ((int)(Temp + 0.5) != (int)Temp) // Phần thập phân lớn hơn 0.5
{
row = (int)(Temp + 1);
col = (int)(Temp + 1);
_width = (int)(Temp + 1);
_height = (int)(Temp + 1);
}
else
{
row = ((int)(Temp) + 1);
col = (int)(Temp);
_width = ((int)(Temp) + 1);
_height = (int)(Temp);
}
}
int cout = 0;
for (int x = 0; x < _width; x++)
{
for (int z = 0; z < _height; z++)
{
if (cout < _NumberObj)
{
//Instantiate(unit, new Vector3(x * .7F, .9f, z * 1.0f), Quaternion.identity, parent);
unitsList.Add(Instantiate(unit, new Vector3(x * .7F, .9f, z * 1.0f), Quaternion.identity, parent));
cout++;
}
}
}
}
#region Add Funtion
public int ReverseToOne(int value)
{
if (value > 0)
{
return -1;
}
else
{
return 1;
}
}
private void FormationShapeSquad(Vector3 _StartPos, Vector3 _EndPos, float SizeObj, ref List<GameObject> ListObj)//this makes the formation upon start
{
float _NumberObj = ListObj.Count;
float _width = Mathf.Abs(_StartPos.x - _EndPos.x);
float _height = Mathf.Abs(_StartPos.z - _EndPos.z);
int Cols = 0, Rows = 0;
if (_width > SizeObj && _height > SizeObj)
{
float Acreage = _width * _height;
float _SumCanContain = Acreage / SizeObj;
if (_SumCanContain >= _NumberObj)
{
// OK
Debug.Log("OK");
Cols = (int)(_width / SizeObj);
Rows = (int)(_height / SizeObj);
}
else
{
// kiểm tra xem mảng có đựng vừa số Obj không
float Temp = Mathf.Sqrt(_NumberObj);
if ((int)(Temp + 0.5) != (int)Temp) // Phần thập phân lớn hơn 0.5
{
Cols = (int)(Temp + 1);
Rows = (int)(Temp + 1);
_width = Cols * SizeObj;
_height = Rows * SizeObj;
}
else
{
Cols = ((int)(Temp) + 1);
Rows = (int)(Temp);
_width = Cols * SizeObj;
_height = Rows * SizeObj;
}
}
}
else
{
// kiểm tra xem mảng có đựng vừa số Obj không
float Temp = Mathf.Sqrt(_NumberObj);
if ((int)(Temp + 0.5) != (int)Temp) // Phần thập phân lớn hơn 0.5
{
Cols = (int)(Temp + 1);
Rows = (int)(Temp + 1);
_width = (int)(Temp + 1) * SizeObj;
_height = (int)(Temp + 1) * SizeObj;
}
else
{
Cols = ((int)(Temp) + 1);
Rows = (int)(Temp);
_width = ((int)(Temp) + 1) * SizeObj;
_height = (int)(Temp) * SizeObj;
}
}
int cout = 0;
int _dirZ = ReverseToOne((int)(_StartPos.z - _EndPos.z));
int _dirX = ReverseToOne((int)(_StartPos.x - _EndPos.x));
float TempWeight = _StartPos.x;
float TempHeight = _StartPos.z;
for (int i = 0; i < Cols; i++)
{
for (int j = 0; j < Rows; j++)
{
if (cout < _NumberObj)
{
ListObj[cout].transform.position = new Vector3(TempWeight, 0f, TempHeight);
cout++;
TempHeight += _dirZ * SizeObj;
}
}
TempWeight += _dirX * SizeObj;
TempHeight = _StartPos.z;
}
}
private void FormationLine(Vector3 _StartPos, Vector3 _EndPos, float SizeObj, ref List<GameObject> ListObj)//this makes the formation upon start
{
float _NumberObj = ListObj.Count;
//float _distance = Vector2.Distance(new Vector2 (_StartPos.x, _StartPos.z) , new Vector2( _EndPos.x, _EndPos.z));
float _distance = Vector3.Distance(_StartPos, _EndPos);
int Row = 0, Col = 0;
float _width = 0, _height = 0;
/*if(_distance > SizeObj)
{*/
//Use Default value
float Temp = Mathf.Sqrt(_NumberObj);
if ((int)(Temp + 0.5) != (int)Temp) // Phần thập phân lớn hơn 0.5
{
Row = (int)(Temp + 1);
Col = (int)(Temp + 1);
_width = Row * SizeObj;
_height = Col * SizeObj;
}
else
{
Row = ((int)(Temp));
Col = (int)(Temp) + 1;
_width = Row * SizeObj;
_height = Col * SizeObj;
}
//Debug.Log("_distance " + _distance + " ; witdh = " + _width, gameObject);
if (_distance > _width)
{
//Debug.Log("Out Wrong _distance "+_distance+" ; witdh = " + _width , gameObject);
// Use dyamin setRTS
Row = (int)(Mathf.Ceil(_NumberObj / Row));
Col = (int)(_distance / SizeObj);
}
else
{
//Use default Arange => do not change col, row
// Handle StartPos and EndPpos same Pos (just Click, not drag Mouse)
if (_distance == 0)
{
Vector3 Dir = new Vector3(-1, 0, -1);
_EndPos += Dir;
}
}
int cout = 0;
Vector3 TempEndPos = _EndPos;
Vector3 TempStartPos = _StartPos;
for (int i = 0; i < Row; i++)
{
Vector3 Dir = _EndPos - _StartPos;
Dir = Dir.normalized;
Vector2 VTPT = new Vector2(Dir.z, -Dir.x);
VTPT = VTPT.normalized;
Debug.DrawLine(_StartPos, _EndPos, Color.yellow);
for (int j = 0; j < Col; j++)
{
if (cout >= _NumberObj)
{
break;
}
ListObj[cout].transform.position = TempStartPos;
cout++;
Vector3 _dir = TempEndPos - TempStartPos;
_dir = _dir.normalized;
TempStartPos += _dir * SizeObj;
TempEndPos += _dir * SizeObj;
}
if (cout >= _NumberObj)
{
break;
}
TempStartPos = _StartPos + new Vector3(VTPT.x, 0, VTPT.y) * SizeObj;
TempEndPos = _EndPos + new Vector3(VTPT.x, 0, VTPT.y) * SizeObj;
Debug.DrawLine(_StartPos, TempStartPos, Color.red);
Debug.DrawLine(_EndPos, TempEndPos, Color.green);
_StartPos = TempStartPos;
_EndPos = TempEndPos;
}
}
#endregion
private void formationShape()
{
if (Input.GetMouseButtonDown(0))
{
//Debug.LogWarning("Click Button Down");
//startClick = Input.mousePosition;
RaycastHit hit;
m_ray = m_MainCamera.ScreenPointToRay(Input.mousePosition);
Physics.Raycast(m_ray, out hit, 1000f);
startClick = hit.point;
m_BoxSelected.m_baseMin = startClick;
}
if (Input.GetMouseButton(0))
{
RaycastHit hit;
m_ray = m_MainCamera.ScreenPointToRay(Input.mousePosition);
Physics.Raycast(m_ray, out hit, 1000f);
endClick = hit.point;
m_BoxSelected.m_baseMax = endClick;
//FormationShapeSquad(startClick, endClick, 1.5f, ref unitsList);
FormationLine(startClick, endClick, 1.5f, ref unitsList);
}
if (Input.GetMouseButtonUp(0))
{
//formationMaker3(startClick, endClick, 1.5f, ref unitsList);
}
}
void Update()
{
formationShape();
}
private void OnDrawGizmos()
{
Gizmos.DrawWireCube(m_BoxSelected.m_Center, m_BoxSelected.m_Size);
}
}
public class BoxSelected
{
public BoxSelected() { }
public Vector3 m_baseMin, m_baseMax;
public Vector3 m_Center
{
get
{
Vector3 _center = m_baseMin + (m_baseMax - m_baseMin) * 0.5f;
_center.y = (m_baseMax - m_baseMin).magnitude * 0.5f;
return _center;
}
}
public Vector3 m_Size
{
get
{
return new Vector3(
Mathf.Abs(m_baseMax.x - m_baseMin.x),
(m_baseMax - m_baseMin).magnitude,
Mathf.Abs(m_baseMax.z - m_baseMin.z)
);
}
}
public Vector3 m_Extents
{
get
{
return m_Size * 0.5f;
}
}
}
Solution for FormationShapeSquad and FormationLine
Sorry for my bad English!

Programatically design chess board cubes in unity

I'm new to Unity and am trying to create the chess board background in unity. I need the scene to be full of cubes using the prefab I created. When the counter is even, the cube is set to black else to red. I don't know what is going on really because nothing is showing up when z is 0. This is my code..
public GameObject pre;
public int counter = 0;
public int worldWidth = 20;
public int worldHeight = 20;
private float positionX;
private float positionY;
void Start()
{
chessBoardCube = Resources.Load<GameObject>("Prefabs/pre");
positionX = -11.24f;
positionY = 4.8f;
counter = 0;
}
void Update()
{
for (int x = 0; x < worldWidth; x++)
{
for (int z = 0; z < worldHeight; z++)
{
if (counter % 2 == 0)
{
GameObject block = Instantiate(pre, Vector3.zero, Quaternion.identity) as GameObject;
block.renderer.material.color = Color.black;
block.transform.parent = transform;
float xP = positionX * 3;
float yP = positionY * z;
block.transform.localScale = new Vector3(2, 2, 1);
block.transform.localPosition = new Vector3(xP, 0);
}
else
{
GameObject block = Instantiate(pre, Vector3.zero, Quaternion.identity) as GameObject;
block.renderer.material.color = Color.red;
block.transform.parent = transform;
float xP = positionX * x;
float yP = positionY * z;
block.transform.localScale = new Vector3(2, 2, 1);
block.transform.localPosition = new Vector3(xP, 0);
}
}
counter++;
}
}
The positionX and positionY variables are the position that the prefab is set.
Any help would be appreciated.
Thanks!
I'd suggest something like this:
public GameObject pre;
public int worldWidth = 20;
public int worldHeight = 20;
public float cubeSize = 2;
private float positionX;
private float positionY;
private float positionZ;
void Start()
{
chessBoardCube = Resources.Load<GameObject>("Prefabs/pre");
positionX = -11.24f;
positionY = 4.8f;
positionZ = 0.0f;
}
//Probably unwanted in update
void Update()
{
for (int x = 0; x < worldWidth; x++)
{
for (int y = 0; y < worldHeight; y++)
{
GameObject block = Instantiate(pre, Vector3.zero, Quaternion.identity) as GameObject;
block.transform.parent = transform;
if(x%2 == 0) {
if(y%2 == 0) {
block.renderer.material.color = Color.black;
} else {
block.renderer.material.color = Color.red;
}
} else {
if(y%2 == 0) {
block.renderer.material.color = Color.red;
} else {
block.renderer.material.color = Color.black;
}
}
float xP = positionX + x*cubeSize;
float yP = positionY + y*cubeSize;
block.transform.localScale = new Vector3(cubeSize, cubeSize, 1);
block.transform.localPosition = new Vector3(xP, yP, positionZ);
}
}
}
void generateBoard(){
float tempX = cubePrefab.transform.localScale.x;
float tempY = cubePrefab.transform.localScale.y;
float tempZ = cubePrefab.transform.localScale.z;
for (int i = 0; i < boardLength; i++) {
tempX = cubePrefab.transform.localScale.x;
for (int j = 0; j < boardWidth; j++) {
GameObject block = Instantiate(cubePrefab, parentTransform) as GameObject;
block.transform.localPosition = new Vector3(tempX , tempY , tempZ);
tempX += cubePrefab.transform.localScale.x;
if(i%2 == 0){
if(j%2 == 0){
block.GetComponent<Renderer>().material.color = Color.white;
}else{
block.GetComponent<Renderer>().material.color = Color.black;
}
}else{
if(j%2 == 0){
block.GetComponent<Renderer>().material.color = Color.black;
}else{
block.GetComponent<Renderer>().material.color = Color.white;
}
}
}
tempZ += cubePrefab.transform.localScale.z;
}
}

Categories