Compare gameObject's transforms and remove repetitions - c#

I have a List<GameObject> and I need to remove doubles that have the same position.y and position.x (position.z is irrelevant).
I'm thinking that I need to compare all elements in a list and go through the previous elements of the same list and compare if they have the same positions and then, if not, populate the new List<GameObject>().
I don't really know how to do this as I'm not an experienced programmer.

If you wish to just remove the duplicates from an existing list, you don't necessarily have to create a new one. You can iterate over the list and use the RemoveAt method to get rid of the undesired elements.
Example
using System.Collections.Generic;
using UnityEngine;
public class TestScript : MonoBehaviour
{
public List<GameObject> collection = new List<GameObject>();
private void Start()
{
RemoveDuplicatesFrom(collection);
}
private void RemoveDuplicatesFrom(List<GameObject> collection)
{
for (int i = 0; i < collection.Count; i++)
{
for (int j = i + 1; j < collection.Count; j++)
{
if (!Match(collection[i], collection[j]))
continue;
collection.RemoveAt(j);
j--;
}
}
}
private bool Match(GameObject object1, GameObject object2)
{
return
object1.transform.position.x == object2.transform.position.x &&
object1.transform.position.y == object2.transform.position.y;
}
}

The function below should work just fine
public static List<GameObject> Delete_Doubles(List<GameObject> gameobjects){
List<Vector2> occupied_coord = new List<Vector2>();
List<GameObject> gameobjects_after = new List<GameObjects>();
Vector2 t_vector = new Vector2();
Vector3 pos = new Vector3();
pos = gameobjects[0].Transform.position;
t_vector.x = pos.x;
t_vector.y = pos.y;
gameobjects_after.Add(gameobjects[0]);
occupied_coord.Add(t_vector);
for(int i = 1; i < gameobjects.Count; i++)
{
pos = gameobjects[i].Transform.position;
t_vector.x = pos.x;
t_vector.y = pos.y;
if (!occupied_coord.Contains(t_vector))
{
gameobjects_after.Add(gameobjects[i]);
occupied_coord.Add(t_vector);
}
}
return gameobjects_after;
}

Related

C# - Loop executes code on all members of array at once instead of iterating through one at a time

I'm making a little boxing game in Unity. I designed a function (attackOverdrive) to accept a whole slew of arguments and then cause the enemy boxer to make attacks based on the data fed into the function. I have another class which sets a bunch of variables and then I read those values, assign them to local variables, and then run my function. Thing is that the code in my loop is executing on all members of the array instead of iterating over them one at a time, resulting in the enemy throwing many attacks at once that were intended to be thrown in sequence. The punchDelayTimer is supposed to count up to a limit, then send a message to instantiate an attack, then loop back and do it again.
I'm going nuts, tried all the different types of iterators and got nowhere. I'm new to C#, no idea what I've done wrong here. The function that sets the variables in the other script is at the bottom of the code block.
using UnityEngine;
using System.Text.RegularExpressions;
public class overdrivePiano : MonoBehaviour
{
private GameObject enemyBoxer;
private pianoRoll theRoll;
private string theCoordMod;
private string[] thePatterns;
private int howMany;
private float thePunchDelay, thePatternDelay, theCoordModAmount, punchDelayTimer, patternDelayTimer;
private Vector2[] theCoords;
void Start()
{
enemyBoxer= GameObject.Find("enemyBoxer");
theRoll = gameObject.GetComponent<pianoRoll>();
punchDelayTimer = 0;
}
private void readRoll()
{
thePatterns = theRoll.patterns;
thePunchDelay = theRoll.punchDelay;
thePatternDelay = theRoll.patternDelay;
theCoords = theRoll.coords;
theCoordMod = theRoll.coordMod;
theCoordModAmount = theRoll.modAmount;
}
public void onSwitch()
{
theRoll.SendMessage("firstVerse");
readRoll();
attackOverdrive(thePatterns, thePunchDelay, thePatternDelay, theCoords);
}
public void attackOverdrive(string[] patterns, float punchDelay, float patternDelay, Vector2[] coords, string coordMod = "none", float modAmount = 0)
{
for(int i = 0; i < patterns.Length; i++)
{
if (patterns[i] == "triangle")
{
int j = 0;
Vector2[] triangleVectors = new Vector2[] {new Vector2(coords[i].x, coords[i].y + 0.75f), new Vector2(coords[i].x - 0.75f, coords[i].y - 0.75f), new Vector2(coords[i].x + 0.75f, coords[i].y - 0.75f)};
do
{
if (punchDelayTimer <= punchDelay)
{
punchDelayTimer += Time.deltaTime; //Debug statement here for i and j both print 0
}
else
{
enemyBoxer.SendMessage("createAttack", triangleVectors[j]);
punchDelayTimer = 0; //Debug statement here outputs 9 statements for the value of i: 0,0,0,1,1,1,2,2,2
j += 1; //Debug statement here outputs 9 statements for the value of j: 0,1,2,0,1,2,0,1,2
}
} while (j < 3);
}
else if (patterns[i] == "square")
{
}
else if (patterns[i] == "circle")
{
}
else if ("verticalLine".CompareTo(patterns[i]) == -1)
{
var result = Regex.Match(patterns[i], #"\d+$", RegexOptions.RightToLeft);
if (result.Success)
{
//Debug.Log(result.Value);
}
}
}
}
}
private void firstVerse()
{
patterns = new string[] {"triangle", "triangle", "triangle", "singleRandom", "singleRandom", "verticalLine5"};
coords = new Vector2[] {new Vector2(-1.3f, 2.5f), new Vector2(0f, -2.5f), new Vector2(1.3f, 2.5f), new Vector2(0,0), new Vector2(0,0), new Vector2(0, 4f)};
punchDelay = 0.5f;
patternDelay = 0.5f;
}
Turns out the float timer was the problem. It was iterating through the array properly, but it was all happening on the same frame. I was able to solve the problem by changing attackOverdrive's return type to IEnumerator and then calling it as a coroutine from OnSwitch. I removed the if statement inside the triangle part of the loop and replaced the float timer with Unity's WaitForSeconds class. Here are the functions that changed in case anyone is interested:
public void onSwitch()
{
theRoll.SendMessage("firstVerse");
readRoll();
StartCoroutine(attackOverdrive(thePatterns, thePunchDelay, thePatternDelay, theCoords));
}
public IEnumerator attackOverdrive(string[] patterns, float punchDelay, float patternDelay, Vector2[] coords, string coordMod = "none", float modAmount = 0)
{
for(int i = 0; i < patterns.Length; i++)
{
if (patterns[i] == "triangle")
{
int j = 0;
Vector2[] triangleVectors = new Vector2[] {new Vector2(coords[i].x, coords[i].y + 0.5f), new Vector2(coords[i].x - 0.5f, coords[i].y - 0.5f), new Vector2(coords[i].x + 0.5f, coords[i].y - 0.5f)};
do
{
enemyBoxer.SendMessage("createAttack", triangleVectors[j]);
yield return new WaitForSeconds(punchDelay);
punchDelayTimer = 0f;
j += 1;
} while (j < 3);
}
else if (patterns[i] == "square")
{
}
else if (patterns[i] == "circle")
{
}
else if ("verticalLine".CompareTo(patterns[i]) == -1)
{
var result = Regex.Match(patterns[i], #"\d+$", RegexOptions.RightToLeft);
if (result.Success)
{
Debug.Log(result.Value);
}
}
else if ("single".CompareTo(patterns[i]) == -1)
{
var result = Regex.Match(patterns[i], #"\d+$", RegexOptions.RightToLeft);
if (result.Success)
{
Debug.Log(result.Value);
}
}
}
}
Thanks to rage_co on the Unity forums, who showed me this fix =]

Leaving one gameObject, deactivate others from an array

I have an array of gameObjects which I can deactivate easily with a loop. However I do not want to deactivate one gameObject from the lot. So I am providing an int value which is the position of the gameObject in that array. How do I deactivate others except for this particular object?
public GameObject[] myObjs;
int exceptionObj;
void Start()
{
exceptionObj = 2; //Position of object in the array
for(int i = 0; i<myObjs.Length; i++)
{
myObjs[i].SetActive(false);
}
}
public GameObject[] myObjs;
int exceptionObj;
void Start()
{
exceptionObj = 2; //Position of object in the array
for(int i = 0; i<myObjs.Length; i++)
{
if(exceptionObj != i)
myObjs[i].SetActive(false);
}
}
Or;
public GameObject[] myObjs;
int exceptionObj;
void Start()
{
exceptionObj = 2; //Position of object in the array
for(int i = 0; i<myObjs.Length; i++)
{
if(exceptionObj == i)
continue;
myObjs[i].SetActive(false);
}
}
Or perhaps even by simply reactivating at the end;
public GameObject[] myObjs;
int exceptionObj;
void Start()
{
exceptionObj = 2; //Position of object in the array
for(int i = 0; i<myObjs.Length; i++)
{
myObjs[i].SetActive(false);
}
myObjs[exceptionObj].SetActive(true);
}
Two ways:
With an if:
exceptionObj = 2; //Position of object in the array
for(int i = 0; i<myObjs.Length; i++)
{
if (i != exceptionObj) // If not the exception ID
myObjs[i].SetActive(false);
}
Or with two loops:
exceptionObj = 2; //Position of object in the array
for(int i = 0; i<exceptionObj; i++)
{
myObjs[i].SetActive(false);
}
for(int i = exceptionObj+1; i<myObjs.Length; i++)
{
myObjs[i].SetActive(false);
}

Unity freezes on Play

I've been making this enemy generator, which is called on Start() and it freezes most of the time. Sometimes it let's to play once but as soon as I hit play second time, it freezes, unity needs to be restarted. Standalone version doesn't fix the problem. Logs last lines:
UnityEngine.DebugLogHandler:Internal_Log(LogType, LogOption, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
EnemyGenerator:Start() (at Assets\Scripts\Utils\EnemyGenerator.cs:23)
(Filename: Assets/Scripts/Utils/EnemyGenerator.cs Line: 23)
Here is my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyGenerator : MonoBehaviour
{
public List<GameObject> camps;
public List<GameObject> enemies;
int threshold;
public List<GameObject> campAreas;
public List<GameObject> enemyAreas;
public int campsToSpawn;
public int enemiesPerAreaMin;
public int enemiesPerAreaMax;
void Start()
{
for (int i = 0; i < campsToSpawn; i++)
{
threshold = Random.Range(0, camps.Count - 1);
Debug.Log(threshold);
var campPlace = Random.Range(0, campAreas.Count - 1);
var camp = Instantiate(camps[threshold], campAreas[campPlace].transform.position, Quaternion.identity);
if (camp != null)
{
campAreas.RemoveAt(campPlace);
}
}
Debug.Break();
bool canSpawn = true;
for (int i = 0; i < enemyAreas.Count; i++)
{
threshold = Random.Range(0, enemies.Count - 1);
List<GameObject> enemiesSpawned = new List<GameObject>();
var enemyCount = Random.Range(enemiesPerAreaMin, enemiesPerAreaMax);
for (int j = 0; j < enemyCount; j++)
{
var pos = new Vector3(Random.Range(enemyAreas[i].GetComponent<BoxCollider>().bounds.min.x, enemyAreas[i].GetComponent<BoxCollider>().bounds.max.x), enemyAreas[i].transform.position.y,
Random.Range(enemyAreas[i].GetComponent<BoxCollider>().bounds.min.z, enemyAreas[i].GetComponent<BoxCollider>().bounds.max.z));
if (enemiesSpawned.Count == 0)
{
GameObject en = null;
enemiesSpawned.Add(en = Instantiate(enemies[threshold], pos, Quaternion.identity));
}
else
{
for (int x = 0; x < enemiesSpawned.Count; x++)
{
if (Vector3.Distance(enemiesSpawned[x].transform.position, pos) < 3f)
{
canSpawn = false;
}
}
if (!canSpawn)
{
j--;
}
else
{
enemiesSpawned.Add(Instantiate(enemies[threshold], pos, Quaternion.identity));
}
}
}
Debug.Break();
}
}
}
I tried to find something that would actually loop forever, but I don't think that's the problem. Wondering if it could be performance leak?
Thank you in advance.
Sry I though it was an issue with Debug.Break();
this is probably the problem
{
for (int x = 0; x < enemiesSpawned.Count; x++)
{
if (Vector3.Distance(enemiesSpawned[x].transform.position, pos) < 3f)
{
canSpawn = false;
}
}
if (!canSpawn)
{
j--;
}
else
{
enemiesSpawned.Add(Instantiate(enemies[threshold], pos, Quaternion.identity));
}
}
You getting a random position inside a certain bounds. If the position is too close u don't spawn and --j; to retry a new position. So if for some reason the bounds are wrong or barely engouth you might be running that function for a very long time.
A quick test is to add a counter before your j for loop.
int debugCounter = 0;
for (int j = 0; j < enemyCount; j++)
{
//at the very top
++debugCounter;
if(debugCounter > 100)//or a bigger number
{
Debug.Log("Too many attemps");
break;
}
...

How can I generate stairs?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateStairs : MonoBehaviour
{
public GameObject stairsPrefab;
public int delay = 3;
public int stairsNumber = 5;
public int stairsHeight = 0;
public Vector3 stairsPosition;
public Vector2 stairsSize;
// Use this for initialization
void Start ()
{
StartCoroutine(BuildStairs());
}
// Update is called once per frame
void Update ()
{
}
private IEnumerator BuildStairs()
{
for (float i = 0; i <= stairsSize.x; i++)
{
for (float k = 0; k <= stairsSize.y; k++)
{
stairsPosition = new Vector3(i, stairsHeight, k);
GameObject stairs = Instantiate(stairsPrefab, stairsPosition, Quaternion.identity);
stairs.transform.localScale = new Vector3(stairsSize.x, 1 , stairsSize.y);
stairsHeight += 1;
yield return new WaitForSeconds(delay);
}
}
}
private void CalculateNextStair()
{
}
}
I messed it up. For example I want to build 5 stairs but the loops are over the stairs size and not number of stairs.
Second it's creating 10 sets of stairs not 5 stairs:
Another problem is how can I make that each stair will be build slowly ? Now it's just Instantiate slowly with delay but how can I generate each stair with delay?
Screenshot of the script inspector:
My current code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateStairs : MonoBehaviour
{
public GameObject stairsPrefab;
public float delay = 0.3f;
public int stairsNumber = 5;
public int stairsPositions = 0;
public int stairsStartPositionHeight = 0;
public float stairsScalingHaight = 1;
public Vector2 stairsPosition;
public Vector2 stairsSize;
// Use this for initialization
void Start()
{
StartCoroutine(BuildStairs());
}
// Update is called once per frame
void Update()
{
}
private IEnumerator BuildStairs()
{
for (float i = 0; i <= stairsNumber; i++)
{
// x=0f, y=z=stairsHeight
stairsPosition = new Vector3(0f, stairsPositions, stairsPositions);
GameObject stairs = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stairs.transform.localScale = new Vector3(
stairsSize.x,
stairsScalingHaight,
stairsSize.y);
stairsStartPositionHeight += 1;
yield return new WaitForSeconds(delay);
}
}
private void CalculateNextStair()
{
}
}
There's no reason to loop over the size of the stairs at all; you want to loop over stairsNumber, which is yet unused in your code.
Also, you don't need to change the x component of your stairs' positions. Keep it at 0f (or whatever you need).
The y and z components of your stairs positions (relative to the starting point) should both be factors of stairHeight. In this particular case, you want them to be equal to stairHeight, so that you get "square" step shapes.
private IEnumerator BuildStairs()
{
for (int i = 0; i <= stairsNumber ; i++) {
// x=0f, y=z=stairsHeight
stairsPosition = new Vector3(0f, stairsHeight, stairsHeight);
GameObject stairs = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stairs.transform.localScale = new Vector3(
stairsSize.x,
1f ,
stairsSize.y);
stairsHeight += 1f;
yield return new WaitForSeconds(delay);
}
}
If you change stairSize to be a Vector3, then you can just use stairSize directly as the localScale, and increment stairsHeight by stairsSize.y instead of just 1f.
If you want to offset the starting position of your stairs, you need to include an offset. I recommend keeping it separate from the height counter until you need to add them.
Also, if you want to have rectangular sized steps, keep a widthFactor to multiply by the height to find how far each step moves horizontally.
Combining these changes might look like this:
Vector3 stairSize;
float stepWidthFactor=1f;
Vector3 stairsStartPosition;
private IEnumerator BuildStairs() {
for (int i = 0; i <= stairsNumber ; i++) {
stairsPosition = new Vector3(
stairsStartPosition.x,
stairsStartPosition.y + stairsHeight,
stairsStartPosition.z + stairsHeight*stepWidthFactor);
GameObject stairs = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stairsHeight += stairsSize.y;
stairs.transform.localScale = stairSize;
yield return new WaitForSeconds(delay);
}
}

How can I add/destroy new objects to existing formation?

In the manager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FormationsManager : MonoBehaviour
{
public Transform squadMemeberPrefab;
public int numberOfSquadMembers = 20;
public int columns = 4;
public int gaps = 10;
public Formations formations;
private int numofmembers;
// Use this for initialization
void Start()
{
numofmembers = numberOfSquadMembers;
formations.Init(numberOfSquadMembers, columns, gaps);
GenerateSquad();
}
// Update is called once per frame
void Update()
{
if (numofmembers != numberOfSquadMembers)
{
GenerateSquad();
}
}
private void GenerateSquad()
{
Transform go = squadMemeberPrefab;
for (int i = 0; i < formations.newpositions.Count; i++)
{
go = Instantiate(squadMemeberPrefab);
go.position = formations.newpositions[i];
go.tag = "Squad Member";
go.transform.parent = gameObject.transform;
}
}
}
And the Formations script:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Formations : MonoBehaviour
{
public List<Vector3> newpositions;
private int numberOfSquadMembers;
private int columns;
private int gaps;
private List<Quaternion> quaternions;
private Vector3 FormationSquarePositionCalculation(int index)
{
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 < numberOfSquadMembers; i++)
{
Vector3 pos = FormationSquarePositionCalculation(i);
Vector3 position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
newpositions.Add(position);
}
}
public void Init(int numberOfSquadMembers, int columns, int gaps)
{
this.numberOfSquadMembers = numberOfSquadMembers;
this.columns = columns;
this.gaps = gaps;
FormationSquare();
}
}
What I want to do is in the FormationsManager in the Update not only just calling GenerateSquad but to add the new once to the last/next position of the existing already formation.
void Update()
{
if (numofmembers != numberOfSquadMembers)
{
GenerateSquad();
}
}
If the value of numberOfSquadMembers is 20 first time and then I changed it to 21 add new object to the end of the formation and same if I change the value of numberOfSquadMembers for example from 20 to 19 or from 21 to 5 destroy the amount of objects from the end and keep the formation shape.
The soldiers the last line is on the right side.
So if I change the value to add more then add it to the right and if I change to less destroy from the right side. The most left line of soldiers is the first.
It is possible if you keep GameObject instances inside FormationsManager class, and then reuse them in GenerateSquad method.
In FormationsManager class, add and modify code as follows.
public GameObject squadMemeberPrefab;
List<GameObject> SquadMembers = new List<GameObject>();
void Update()
{
if (numofmembers != numberOfSquadMembers)
{
numofmembers = numberOfSquadMembers;
formations.Init(numberOfSquadMembers, columns, gaps);
GenerateSquad();
}
}
private void GenerateSquad()
{
Transform go = squadMemeberPrefab;
List<GameObject> newSquadMembers = new List<GameObject>();
int i = 0;
for (; i < formations.newpositions.Count; i++)
{
if (i < SquadMembers.Count)
go = SquadMembers[i];
else
{
go = Instantiate(squadMemeberPrefab);
newSquadMembers.Add(go);
}
go.position = formations.newpositions[i];
go.tag = "Squad Member";
go.transform.parent = gameObject.transform;
}
for (; i < SquadMembers.Count; i++)
Destroy(SquadMembers[i]);
SquadMembers = newSquadMembers;
}
However, I recommend you to consider GameObject Pool (Object Pool), which can thoroughly resolve such object recycle problem. For this purpose, you can use ClientScene.RegisterSpawnHandler. Go to this Unity Documentation page and search text "GameObject pool". You can see an example code there.

Categories

Resources