Hi everyone I have been making and experimenting on an 3d endless runner game and came with an issue. Here is what I meant to achieve. I have two list of gameobjects called activeTiles and deactivatedTiles. According to my idea I want to first add all the prefab tiles to the deactivatedtTiles list. Next I have a float amtTileOnScreen variable which controls the amount of tiles put in front of the player to run. Then I take a random Tile from the list of deactivated tiles to activated tiles by considering the amount of tiles o screen and put in-front of the player. Used tiles are put back to deactivated tile list and the whole cycle begins.
The question is how do I achieve this? Help would be appreciated.
Here's what I have tried.
public class TileManager : MonoBehaviour
{
public GameObject[] tilePrefabs;
private Transform playerTransform;
private float spawnZ = -12f;
private float tileLength = 24.0f;
private int amtOfTilesOnScreen = 5;
private float safeZone = 56.0f;
private GameObject spawnedTile;
public static List<GameObject> activeTiles;
public static List<GameObject> deactivatedTiles;
private int lastPrefabIndex = 0;
private Vector3 transformTiles;
// Use this for initialization
void Start ()
{
activeTiles = new List<GameObject>();
deactivatedTiles = new List<GameObject>();
playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
for (int i = 0; i < amtOfTilesOnScreen; i++)
{
if (i < 1)
{
activeTiles.Add(SpawnTileAtFront(0));
}
else
{
activeTiles.Add(SpawnTileAtFront());
}
}
}
void Update ()
{
if (playerTransform.position.z - safeZone > (spawnZ - amtOfTilesOnScreen * tileLength))
{
for (int i = 0; i < tilePrefabs.Length; i++)
{
spawnedTile = SpawnTileAtFront();
deactivatedTiles.Add(spawnedTile);
Debug.Log(deactivatedTiles.Count);
}
if (activeTiles.Count < (amtOfTilesOnScreen + 1))
{
activeTiles.Add(GetRandomDeactivatedTile());
MoveTileToTheFront(GetRandomDeactivatedTile());
}
else
{
var disposeTile = activeTiles[0];
deactivatedTiles.Add(disposeTile);
DisposeActiveTiles(0);
}
}
}
private void MoveTileToTheFront(GameObject tile)
{
tile.transform.position = Vector3.forward * spawnZ;
spawnZ += tileLength;
}
private GameObject SpawnTileAtFront(int prefabIndex = -1)
{
GameObject go;
if (prefabIndex == -1)
{
go = Instantiate(tilePrefabs[RandomPrefabIndex()]) as GameObject;
}
else
{
go = Instantiate(tilePrefabs[prefabIndex]) as GameObject;
}
go.transform.SetParent(transform);
MoveTileToTheFront(go);
return go;
}
private void DisposeActiveTiles(int index)
{
GameObject unusedTile = activeTiles[index];
activeTiles.RemoveAt(index);
deactivatedTiles.Add(unusedTile);
}
private GameObject GetRandomDeactivatedTile()
{
if (deactivatedTiles.Count == 0)
return null;
int randomIndex = Random.Range(0, deactivatedTiles.Count);
GameObject unusedTile = deactivatedTiles[randomIndex];
deactivatedTiles.RemoveAt(randomIndex);
return unusedTile;
}
private int RandomPrefabIndex()
{
if (tilePrefabs.Length <= 0)
{
return 0;
}
int randomIndex = lastPrefabIndex;
while (randomIndex == lastPrefabIndex)
{
randomIndex = Random.Range(0, tilePrefabs.Length);
}
lastPrefabIndex = randomIndex;
return lastPrefabIndex;
}
}
void Update ()
{
while (playerTransform.position.z - safeZone > (spawnZ - amtOfTilesOnScreen * tileLength))
{
// we need to add a new tile in front of the player
GameObject t;
if (deactivatedTiles.Count == 0) {
// no deactivated tiles so we need to instantiate a new tile
t = SpawnTileAtFront ();
} else {
// otherwise take deactivated tile into use
t = GetRandomDeactivatedTile ();
MoveTileToTheFront (t);
}
// new tile is now active tile
activeTiles.Add (t);
// take oldest active tile and move it to deactivated list
DisposeActiveTiles(0);
}
}
Related
This question already has answers here:
How to make the script wait/sleep in a simple way in unity
(7 answers)
Closed 1 year ago.
This is the waypoints manager script attached to empty GameObject :
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class WaypointsManager : MonoBehaviour
{
public GameObject npcPrefab;
public int numberOfNpcs;
public GameObject waypointsPrefab;
public List<GameObject> waypoints = new List<GameObject>();
public int numberOfWaypoints;
public bool useWaypointsPrefab = false;
private GameObject waypointObject;
// Start is called before the first frame update
void Awake()
{
for (int i = 0; i < numberOfWaypoints; i++)
{
if (useWaypointsPrefab)
{
waypointObject = Instantiate(npcPrefab, Vector3.zero, Quaternion.identity);
}
else
{
waypointObject = new GameObject();
}
waypointObject.tag = "Waypoint";
waypointObject.name = "Waypoint";
waypointObject.transform.position = new Vector3(Random.Range(0, 10), Random.Range(0, 10), Random.Range(0, 10));
waypoints.Add(waypointObject);
}
for (int i = 0; i < numberOfNpcs; i++)
{
if (npcPrefab != null)
{
GameObject npc = Instantiate(npcPrefab, Vector3.zero, Quaternion.identity);
}
}
}
// Update is called once per frame
void Update()
{
}
}
And this script is attached to each npc :
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Waypoints : MonoBehaviour
{
public List<GameObject> waypoints = new List<GameObject>();
public float movementSpeed;
public float rotationSpeed;
public bool reverse = false;
public bool go = false;
public int numberOfWaypoints;
public int nextWaypointNumber;
private int waypointIndex = 0;
private GameObject nextWayPoint;
// Start is called before the first frame update
void Start()
{
waypoints = GameObject.FindGameObjectsWithTag("Waypoint").ToList();
numberOfWaypoints = waypoints.Count;
if (reverse)
{
waypointIndex = waypoints.Count - 1;
}
else
{
waypointIndex = 0;
}
StartCoroutine(MoveNpc());
}
// Update is called once per frame
void Update()
{
if (go)
{
if (reverse && waypointIndex == 0)
{
waypointIndex = waypoints.Count - 1;
}
if (reverse == false && waypointIndex == waypoints.Count)
{
waypointIndex = 0;
}
nextWayPoint = waypoints[waypointIndex];
nextWaypointNumber = waypointIndex;
transform.position = Vector3.MoveTowards(transform.position,
waypoints[waypointIndex].transform.position, Time.deltaTime * movementSpeed);
float distance = Vector3.Distance(transform.position, waypoints[waypointIndex].transform.position);
if (distance > 0f)
{
// Try to rotate to face the waypoint only if we're not on top of it.
var rotation = Quaternion.LookRotation(nextWayPoint.transform.position - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * rotationSpeed);
}
else
{
numberOfWaypoints--;
if (reverse)
{
waypointIndex--;
}
else
{
waypointIndex++;
}
}
}
}
private IEnumerator MoveNpc()
{
yield return new WaitForSeconds(3f);
go = true;
}
private void OnDrawGizmos()
{
if (waypoints != null)
{
for (int i = 0; i < waypoints.Count; i++)
{
Gizmos.color = Color.green;
Gizmos.DrawSphere(waypoints[i].transform.position, 0.1f);
}
}
if (nextWayPoint != null)
{
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, nextWayPoint.transform.position);
}
}
}
In the Waypoints script I'm starting a coroutine with a 3 seconds delay but still all the npcs are moving at the same time like one npc. I want it to wait 3 seconds send npc wait 3 seconds send npc until all npcs are moving along the waypoints.
StartCoroutine(MoveNpc());
ISSUE
All npcs are being spawned at the same time, each waits 3 seconds (all at the same time), and then start moving.
SOLUTION
You could create a function that takes the amount of time they need to wait, and use that delay time in your coroutine.
NPC
public void StartMovingAfterSeconds(float seconds)
{
StartCoroutine(MoveNPC(seconds));
}
private IEnumerator MoveNpc(float delayTimeSeconds)
{
yield return new WaitForSeconds(delayTimeSeconds);
go = true;
}
From your manager you will need to track the delay time as you spawn the npcs.
MANAGER
// Amount to delay movement by (can be exposed in the inspector)
//
var delayTime = 3f;
// Accumulated delay
//
var currentDelay = 0f;
if (npcPrefab != null)
{
for (int i = 0; i < numberOfNpcs; i++)
{
var npc = Instantiate(npcPrefab, Vector3.zero, Quaternion.identity);
npc.StartMovingAfterSeconds(currentDelay);
currentDelay += delayTime;
}
}
We start with a delay of 0 (currentDelay) and add the amount we want to delay by (delayTime) in each iteration. The first delay is 0, next is 3, then 6, etc..
I have a 3D board game in Unity. I would like to move my character without having to press a key, but most importantly I would like to show a dynamic panel in canvas for whatever square the character lands on. So far I have the dice rolling and the character moving (after pressing a key) the correct amount of squares, but I am unable to figure out how to activate the panel based on the square color. Any help would be appreciated.
Here is my CharacterScript:
public class CharacterScript : MonoBehaviour
{
public Path currentPath;
public int squarePosition;
public int squares;
bool isMoving;
public GameObject PinkSquarePanel = GameObject.FindGameObjectWithTag("PinkSquare");
public GameObject CyanSquarePanel = GameObject.FindGameObjectWithTag("CyanSquare");
public GameObject WhiteSquarePanel = GameObject.FindGameObjectWithTag("WhiteSquare");
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape) && !isMoving)
{
squares = DiceNumberTextScript.diceNumber;
}
StartCoroutine(Move());
if (squares == 0)
{
ActivateSquarePanel();
}
}
IEnumerator Move()
{
if (isMoving)
{
yield break;
}
isMoving = true;
while (squares > 0)
{
Vector3 nextPosition = currentPath.squareList[squarePosition + 1].position;
while (MoveToNextSquare(nextPosition))
{
yield return null;
}
yield return new WaitForSeconds(0.1f);
squares--;
squarePosition++;
}
isMoving = false;
}
bool MoveToNextSquare(Vector3 goal)
{
return goal != (transform.position = Vector3.MoveTowards(transform.position, goal, 4f * Time.deltaTime));
}
void ActivateSquarePanel()
{
if (squarePosition.Equals(PinkSquarePanel))
{
PinkSquarePanel.GetComponent<CanvasGroup>().alpha = 1;
}
else if (squarePosition.Equals(CyanSquarePanel))
{
CyanSquarePanel.GetComponent<CanvasGroup>().alpha = 1;
}
else if (squarePosition.Equals(WhiteSquarePanel))
{
WhiteSquarePanel.GetComponent<CanvasGroup>().alpha = 1;
}
}
}
And here is my PathScript:
public class Path : MonoBehaviour
{
Transform[] squareObjects;
public List<Transform> squareList = new List<Transform>();
GameObject[] PinkSquares = GameObject.FindGameObjectsWithTag("PinkSquare");
PinkSquare[] pinkList = new PinkSquare[1];
GameObject[] CyanSquares = GameObject.FindGameObjectsWithTag("CyanSquare");
CyanSquare[] cyanList = new CyanSquare[1];
GameObject[] WhiteSquares = GameObject.FindGameObjectsWithTag("WhiteSquare");
WhiteSquare[] whiteList = new WhiteSquare[1];
private void OnDrawGizmos()
{
Gizmos.color = Color.black;
FillSquares();
for (int i = 0; i < squareList.Count; i++)
{
Vector3 currentPosition = squareList[i].position;
if (i > 0)
{
Vector3 previousPosition = squareList[i - 1].position;
Gizmos.DrawLine(previousPosition, currentPosition);
if(currentPosition.Equals(PinkSquares))
{
pinkList[i] = new PinkSquare();
}
else if (currentPosition.Equals(CyanSquares))
{
cyanList[i] = new CyanSquare();
}
else if (currentPosition.Equals(WhiteSquares))
{
whiteList[i] = new WhiteSquare();
}
}
}
}
void FillSquares()
{
squareList.Clear();
squareObjects = GetComponentsInChildren<Transform>();
foreach (Transform square in squareObjects)
{
if (square != this.transform)
{
squareList.Add(square);
}
}
}
}
I believe your issue is in your comparisons, you are trying to use an Equals to compare your currentPosition which is a Vector3 type to a GameObject[] which is an array of gameObjects. As I mentioned in my comments, this comparison will always fail as an array of gameObjects can not be equal to a vector.
Instead of using these lines, try this line:
if(squareList[i].gameObject.tag.CompareTag("PinkSquare")
The full snippet of if else would look like
if(squareList[i].gameObject.tag.CompareTag("PinkSquare")
{
pinkList[i] = new PinkSquare();
}
else if(squareList[i].gameObject.tag.CompareTag("CyanSquare")
{
cyanList[i] = new CyanSquare();
}
else if(squareList[i].gameObject.tag.CompareTag("WhiteSquare")
{
whiteList[i] = new WhiteSquare();
}
Your CharacterScript is going to need to get the gameObject or Transform from the Path script as it is only keeping track of indexes. Your issue in this script is you are comparing an integer to a GameObject which would never be true. I would also recommend not using OnDrawGizmos() as it is an editor only script and should only be used to render editor debugging tools. The only reference to a Gizmo I see in the function is Gizmos.color = Color.black; which does nothing as you are not rendering a gizmo anywhere. I would move this code to a different function and call it from your CharacterScript. Have the return type be GameObject or Transform of the square you are on, so the CharacterSCript can check which color it lands on. Using an Integer nor Vector3 to compare to a GameObject[] will never work.
I am not sure if there are issues elsewhere in the code, but as this comparison would always fail, none of these statements would get broken into. What this means is your panels would never have the chance to get their alpha set nor get created.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class MoveOnCurvedLines : MonoBehaviour
{
public LineRenderer lineRenderer;
public List<GameObject> objectsToMove = new List<GameObject>();
public float speed;
public bool go = false;
public bool moveToFirstPositionOnStart = false;
private Vector3[] positions;
private Vector3[] pos;
private int index = 0;
private bool goForward = true;
private List<GameObject> objectsToMoveCopy = new List<GameObject>();
// Start is called before the first frame update
void Start()
{
objectsToMove = GameObject.FindGameObjectsWithTag("New Prefab").ToList();
pos = GetLinePointsInWorldSpace();
if (moveToFirstPositionOnStart == true)
{
for (int i = 0; i < objectsToMove.Count; i++)
{
objectsToMove[i].transform.position = pos[index];
}
}
StartCoroutine(AddNew());
}
Vector3[] GetLinePointsInWorldSpace()
{
positions = new Vector3[lineRenderer.positionCount];
//Get the positions which are shown in the inspector
lineRenderer.GetPositions(positions);
//the points returned are in world space
return positions;
}
// Update is called once per frame
void Update()
{
if (go == true)
{
Move();
}
}
void Move()
{
for (int i = 0; i < objectsToMoveCopy.Count; i++)
{
Vector3 newPos = objectsToMoveCopy[i].transform.position;
float distanceToTravel = speed * Time.deltaTime;
bool stillTraveling = true;
while (stillTraveling)
{
Vector3 oldPos = newPos;
newPos = Vector3.MoveTowards(oldPos, pos[index], distanceToTravel);
distanceToTravel -= Vector3.Distance(newPos, oldPos);
if (newPos == pos[index]) // Vector3 comparison is approximate so this is ok
{
// when you hit a waypoint:
if (goForward)
{
bool atLastOne = index >= pos.Length - 1;
if (!atLastOne) index++;
else { index--; goForward = false; }
}
else
{ // going backwards:
bool atFirstOne = index <= 0;
if (!atFirstOne) index--;
else { index++; goForward = true; }
}
}
else
{
stillTraveling = false;
}
}
objectsToMoveCopy[i].transform.position = newPos;
}
}
IEnumerator AddNew()
{
WaitForSeconds waitThreeSeconds = new WaitForSeconds(3);
foreach (var objToMove in objectsToMove)
{
yield return waitThreeSeconds;
objectsToMoveCopy.Add(objToMove);
}
}
}
I'm using StartCoroutine and the method AddNew to move each object between the waypoints every 3 seconds.
The logic :
First object to move from the List is start moving from the first position.
After 3 seconds the second object to move from the List is start moving from the first position.
The goal is to make that each object will start moving from the first position after 3 seconds following the first moving object before him so in the end I will have the objects moving with spoaces of 3 seconds between them.
The problem :
The first object is start moving after 3 seconds from the first position then the second and third and the resto f objects are start moving but from the last moved object and the other objects that already move are get merged with the other objects in the end I have a group of all the objects to move are moving together.
The same behave I want to be if they are moving in reverse.
Your objects merge, since you only have one index for the objects' target position, meaning all objects move towards the same point, not each object towards it's respective next point on the path. This results in a merge as soon as the first object turns around and runs backwards.
It would be best to split your logic into two classes, since otherwise you'd have to keep track of every object's path separately, meaning you need an int[] indices for the current target position of each object, another array for the goForward bools and so on for every new property you introduce.
Controller:
public class MovementController : MonoBehaviour
{
[SerializeField]
private LineRenderer lineRenderer;
[SerializeField]
private float speed;
[SerializeField]
private bool moveToFirstPositionOnStart;
public List<MoveOnCurvedLines> movingObjects = new List<MoveOnCurvedLines>();
void Start()
{
Vector3[] positions = GetPositions();
movingObjects = GameObject.FindGameObjectsWithTag("New Prefab").Select(go => go.GetComponent<MoveOnCurvedLines>().ToList();
foreach (MoveOnCurvedLines obj in movingObjects)
{
obj.Init(positions, speed, moveToFirstPositionOnStart);
}
StartCoroutine(TriggerObjects(false));
}
Vector3[] GetPositions()
{
Vector3[] positions = new Vector3[lineRenderer.positionCount];
//Get the positions which are shown in the inspector
lineRenderer.GetPositions(positions);
return positions;
}
IEnumerator TriggerObjects(bool delayFirstObject)
{
WaitForSeconds waitThreeSeconds = new WaitForSeconds(3);
if (delayFirstObject)
yield return waitThreeSeconds;
foreach (MoveOnCurvedLines obj in movingObjects)
{
obj.StartMoving();
yield return waitThreeSeconds;
}
}
}
Movement logic:
public class MoveOnCurvedLines : MonoBehaviour
{
private Transform myTransform;
private bool initialized;
private Vector3[] pos;
private int posIndex = 0;
private float speed;
private bool goForward = true;
private Coroutine moving;
public void Init(Vector3[] positions, float speed, bool instantlyMoveToFirstPosition)
{
myTransform = transform;
pos = positions;
this.speed = speed;
if (instantlyMoveToFirstPosition)
myTransform.position = positions[0];
initialized = true;
}
public void StartMoving()
{
if (initialized && moving == null)
moving = StartCoroutine(Move());
}
public void StopMoving()
{
if (moving != null)
{
StopCoroutine(moving);
moving = null;
}
}
private IEnumerator Move()
{
while (true)
{
Vector3 newPos = myTransform.position;
float distanceToTravel = speed * Time.deltaTime;
bool stillTraveling = true;
while (stillTraveling)
{
Vector3 oldPos = newPos;
newPos = Vector3.MoveTowards(oldPos, pos[posIndex], distanceToTravel);
distanceToTravel -= Vector3.Distance(newPos, oldPos);
if (newPos == pos[posIndex]) // Vector3 comparison is approximate so this is ok
{
// when you hit a waypoint:
if (goForward)
{
bool atLastOne = posIndex >= pos.Length - 1;
if (!atLastOne)
{
posIndex++;
}
else
{
posIndex--;
goForward = false;
}
}
else
{ // going backwards:
bool atFirstOne = posIndex <= 0;
if (!atFirstOne)
{
posIndex--;
}
else
{
posIndex++;
goForward = true;
}
}
}
else
{
stillTraveling = false;
}
}
myTransform.position = newPos;
}
}
}
MovementController only provides the necessary data, which all your objects share (e.g. the path), but every MoveOnCurvedLines object keeps track of it's progress independently.
Optimizations:
I cached transform in myTransform, since Unity's transform calls GetComponent<Transform>() every time producing unnecessary overhead.
Moving is done in a coroutine, not in Update, since checking n go bools every frame n objects do not move is unnecessary.
I changed your public fields to private ones getting serialized, since it's best practice to restrict access as much as possible. If you need to access them from another script in your project just make them public again.
I'm currently working on a project in Unity. It generates random pillars as platforms for the player to jump on. I want it to only spawn a pillar if Vector3.Distance(itsRandomPosition, eachOtherPillarsPosition) is greater or equal to minDisBetweenPillars.
After a lot of trying, it still doesn't work and spawn inside of each other. Here's my code:
using UnityEngine;
public class CreateRandPlatform : MonoBehaviour
{
public GameObject platformPrefab;
public Transform map;
public float minDisBetweenPillars;
public float spawnRange;
public float spawnRangeY;
public int totPillars;
private void Start()
{
SpawnPillars();
}
private bool CanSpawn(float min, Vector3 a, Vector3 b)
{
bool res = Vector3.Distance(a, b) >= min;
return res == true;
}
private void SpawnPillars()
{
for (int i = 0; i < totPillars; i++)
{
Vector3 spawnPos = new Vector3(Random.Range(-spawnRange, spawnRange), -30, Random.Range(-spawnRange, spawnRange));
GameObject[] pillars = GameObject.FindGameObjectsWithTag("Pillar");
bool canSpawn = false;
foreach (GameObject pillar in pillars)
{
Vector3 pillarPos = pillar.transform.position;
if (CanSpawn(minDisBetweenPillars, spawnPos, pillarPos) == true)
{
canSpawn = true;
break;
}
}
if (canSpawn == true)
{
spawnPos.y = -30 + Random.Range(-spawnRangeY, spawnRangeY);
Instantiate(platformPrefab, spawnPos, Quaternion.identity, map);
}
else
{
i--;
}
}
}
}
you're currently checking if the new pillar will be outside the range of ANY one of the existing ones.
What you want is a check, if it is outside the range of ALL of the existing ones:
bool canSpawn = true;
foreach (GameObject pillar in pillars)
{
Vector3 pillarPos = pillar.transform.position;
if (!CanSpawn(minDisBetweenPillars, spawnPos, pillarPos))
{
canSpawn = false;
break;
}
}
Also you might want to think about only checking x-z-distance. The calculation is cheaper and i suppose your pillars are y alligned, meaning the y distance will only falsify your result.
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.