I am using this code to generate a number of sphere around a center point, with different size and position.
There is a conditional check to prevent sphere from overlapping.
The problem I face is, in the scene some sphere are always overlapping.
=>
[Header("Galaxy")]
public int numberOfStars = 300;
public int maximumRadius = 100;
public float minDistBetweenStars = 2f;
[Header("Star")]
public float minimumSize = 1f;
public float maximumSize = 2f;
void Start()
{
for(int i = 0; i < numberOfStars; i++)
{
float distance = Random.Range(0, maximumRadius);
float angle = Random.Range(0, 2 * Mathf.PI);
float starSize = Random.Range(minimumSize, maximumSize);
Vector3 starPosition = new Vector3(distance * Mathf.Cos(angle), 0, distance * Mathf.Sin(angle));
Collider[] starCollider = Physics.OverlapSphere(starPosition, (starSize * 0.5f) + minDistBetweenStars);
if (starCollider.Length == 0)
{
GameObject star = GameObject.CreatePrimitive(PrimitiveType.Sphere);
star.transform.position = starPosition;
star.transform.localScale = new Vector3(starSize, starSize, starSize);
}
else
{
i--;
}
}
}
I do pass in the remove, so some sphere are being removed,m but I still see a lot of overlapping.
So the problem was we were not disabling the collider on the sphere we was checking :P
public class StarGenSO : MonoBehaviour
{
[Header("Galaxy")]
public int numberOfStars = 300;
public int maximumRadius = 100;
public float minDistBetweenStars = 2f;
[Header("Star")]
public float minimumSize = 1f;
public float maximumSize = 2f;
void Awake()
{
for (int i = 0; i < numberOfStars; i++)
{
GameObject star = GameObject.CreatePrimitive(PrimitiveType.Sphere);
float distance = Random.Range(0, maximumRadius);
float angle = Random.Range(0, 2 * Mathf.PI);
float starSize = Random.Range(minimumSize, maximumSize);
Vector3 starPosition = new Vector3(distance * Mathf.Cos(angle), 0, distance * Mathf.Sin(angle));
var currentCol = star.GetComponent<Collider>();
currentCol.enabled = false;
Collider[] starCollider = Physics.OverlapSphere(starPosition, (starSize * 0.5f) + minDistBetweenStars);
if (starCollider.Length == 0)
{
star.transform.position = starPosition;
star.transform.localScale = new Vector3(starSize, starSize, starSize);
currentCol.enabled = true;
}
else
{
Debug.Log("remove");
Destroy(star);
i--;
}
}
}
}
A preferred solution
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StarGenSo : MonoBehaviour
{
[Header("Galaxy")]
public int numberOfStars = 300;
public int maximumRadius = 100;
public float minDistBetweenStars = 2f;
[Header("Star")]
public float minimumSize = 1f;
public float maximumSize = 2f;
[Header("Star Object")]
public GameObject obj;
IEnumerator Start()
{
for (int i = 0; i < numberOfStars; i++)
{
float distance = Random.Range(0, maximumRadius);
float angle = Random.Range(0, 2 * Mathf.PI);
float starSize = Random.Range(minimumSize, maximumSize);
Vector3 starPosition = new Vector3(distance * Mathf.Cos(angle), 0, distance * Mathf.Sin(angle));
Collider[] starCollider = Physics.OverlapSphere(starPosition, (starSize * 0.5f) + minDistBetweenStars);
if (starCollider.Length == 0)
{
GameObject star = Instantiate(obj);
star.transform.position = starPosition;
star.transform.localScale = new Vector3(starSize, starSize, starSize);
star.AddComponent<SphereCollider>();
}
else
{
i--;
}
}
yield return null;
}
}
Result:
The problem here is your starSize use both for local scale (multiple factor) and sphere collider (flat factor). Eg: your star have default size of 2 and your starSize equal 2, then your actual radius of your sphere is 4, but your collider check for 2*0.5 + 2 = 3.
Related
In relation to the question asked here (How to place spheres in a half circle shape between 2 points) that generates spheres between two points A and B.
How do I create just one sphere that moves from Point A to Point B and then back from Point B to Point A in a loop cycle? How do I use Lerp in this context?
I have tried making the sphere move in the angle (half circle) described in the below code but it always moves in a straight line.
The below code generates spheres between two points.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GetCurves : MonoBehaviour
{
public GameObject A;
public GameObject B;
public int amount;
[ContextMenu("PlaceSpheres()")]
public void Start()
{
PlaceSpheres(A.transform.position, B.transform.position, amount);
}
public void PlaceSpheres(Vector3 posA, Vector3 posB, int numberOfObjects)
{
// get circle center and radius
var radius = Vector3.Distance(posA, posB) / 2f;
var centerPos = (posA + posB) / 2f;
// get a rotation that looks in the direction
// posA -> posB
var centerDirection = Quaternion.LookRotation((posB - posA).normalized);
for (var i = 0; i < numberOfObjects; i++)
{
var angle = Mathf.PI * (i+1) / (numberOfObjects + 1); //180 degrees
var x = Mathf.Sin(angle) * radius;
var z = Mathf.Cos(angle) * radius;
var pos = new Vector3(x, 0, z);
// Rotate the pos vector according to the centerDirection
pos = centerDirection * pos;
var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.position = centerPos + pos;
sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
}
}
}
The below script I had created that makes an object move between two points in a loop but only in a straight line. How do I make it move in a curve (180 degrees)?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RunInLoop : MonoBehaviour
{
public float speed = 0.25f;
public Transform PointA;
public Transform PointB;
private Vector3 origin;
private bool backToOrigin;
void Start()
{
transform.position = PointA.transform.position;
origin = transform.position;
}
void Update()
{
transform.position = Vector3.MoveTowards(transform.position, backToOrigin ? origin : PointB.transform.position, speed * Time.deltaTime);
// if one of the two positions is reached invert the flag
if (transform.position == PointB.transform.position || transform.position == origin)
{
backToOrigin = !backToOrigin;
}
}
}
Solution using your code
As I told you in my last answer that provided your first code you should store them in a list and then make the object move between them:
public class GetCurves : MonoBehaviour
{
public GameObject A;
public GameObject B;
public int amount;
public float moveSpeed;
private List<Vector3> positions = new List<Vector3>();
private Transform sphere;
private int currentIndex = 0;
private bool movingForward = true;
private void Start()
{
sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform;
sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
GeneratePositions(A.transform.position, B.transform.position, amount);
sphere.position = positions[0];
}
private void GeneratePositions(Vector3 posA, Vector3 posB, int numberOfObjects)
{
// get circle center and radius
var radius = Vector3.Distance(posA, posB) / 2f;
var centerPos = (posA + posB) / 2f;
// get a rotation that looks in the direction
// posA -> posB
var centerDirection = Quaternion.LookRotation((posB - posA).normalized);
for (var i = 0; i < numberOfObjects; i++)
{
var angle = Mathf.PI * (i + 1) / (numberOfObjects + 1); //180 degrees
var x = Mathf.Sin(angle) * radius;
var z = Mathf.Cos(angle) * radius;
var pos = new Vector3(x, 0, z);
// Rotate the pos vector according to the centerDirection
pos = centerDirection * pos;
// store them in a list this time
positions.Add(centerPos + pos);
}
}
private void Update()
{
if (positions == null || positions.Count == 0) return;
// == for Vectors works with precision of 0.00001
// if you need a better precision instead use
//if(!Mathf.Approximately(Vector3.Distance(sphere.position, positions[currentIndex]), 0f))
if (sphere.position != positions[currentIndex])
{
sphere.position = Vector3.MoveTowards(sphere.transform.position, positions[currentIndex], moveSpeed * Time.deltaTime);
return;
}
// once the position is reached select the next index
if (movingForward)
{
if (currentIndex + 1 < positions.Count)
{
currentIndex++;
}
else if (currentIndex + 1 >= positions.Count)
{
currentIndex--;
movingForward = false;
}
}
else
{
if (currentIndex - 1 >= 0)
{
currentIndex--;
}
else
{
currentIndex++;
movingForward = true;
}
}
}
}
If you want to stick to Single-Responsibility-Principles you could also seperate the movement from the list generation like e.g.
public class GetCurves : MonoBehaviour
{
public GameObject A;
public GameObject B;
public int amount;
public float moveSpeed;
private void Start()
{
GeneratePositions(A.transform.position, B.transform.position, amount);
}
private void GeneratePositions(Vector3 posA, Vector3 posB, int numberOfObjects)
{
// get circle center and radius
var radius = Vector3.Distance(posA, posB) / 2f;
var centerPos = (posA + posB) / 2f;
// get a rotation that looks in the direction
// posA -> posB
var centerDirection = Quaternion.LookRotation((posB - posA).normalized);
List<Vector3> positions = new List<Vector3>();
for (var i = 0; i < numberOfObjects; i++)
{
var angle = Mathf.PI * (i + 1) / (numberOfObjects + 1); //180 degrees
var x = Mathf.Sin(angle) * radius;
var z = Mathf.Cos(angle) * radius;
var pos = new Vector3(x, 0, z);
// Rotate the pos vector according to the centerDirection
pos = centerDirection * pos;
// store them in a list this time
positions.Add(centerPos + pos);
}
var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
var movement = sphere.AddComponent<MoveBetweenPoints>();
movement.positions = positions;
movement.moveSpeed = moveSpeed;
}
and in a seperate script
public class MoveBetweenPoints : MonoBehaviour
{
public List<Vector3> positions = new List<Vector3>();
public float moveSpeed;
privtae bool movingForward = true;
private int currentIndex = 0;
private void Update()
{
if (positions == null || positions.Count == 0) return;
// == for Vectors works with precision of 0.00001
// if you need a better precision instead use
//if(!Mathf.Approximately(Vector3.Distance(sphere.position, positions[currentIndex]), 0f))
if (sphere.position != positions[currentIndex])
{
transform.position = Vector3.MoveTowards(transform.position, positions[currentIndex], moveSpeed * Time.deltaTime);
return;
}
// once the position is reached select the next index
if (movingForward)
{
if (currentIndex + 1 < positions.Count)
{
currentIndex++;
}
else if (currentIndex + 1 >= positions.Count)
{
currentIndex--;
movingForward = false;
}
}
else
{
if (currentIndex - 1 >= 0)
{
currentIndex--;
}
else
{
currentIndex++;
movingForward = true;
}
}
}
}
Actual Solution
However, if you want a smooth movement on a circle curve ... why even reduce that circle curcve to a certain amount of positions? You could directly moove according to the angle between 0° and 180° like this:
public class GetCurves : MonoBehaviour
{
public GameObject A;
public GameObject B;
// now in Angles per second
public float moveSpeed;
private Transform sphere;
private bool movingForward = true;
private float angle;
private void Start()
{
sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform;
sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
}
private void Update()
{
if (movingForward)
{
angle += moveSpeed * Time.deltaTime;
}
else
{
angle -= moveSpeed * Time.deltaTime;
}
if (angle < 0)
{
angle = 0;
movingForward = true;
}
else if (angle > 180)
{
angle = 180;
movingForward = false;
}
// get circle center and radius
var radius = Vector3.Distance(A.transform.position, B.transform.position) / 2f;
var centerPos = (A.transform.position + B.transform.position) / 2f;
// get a rotation that looks in the direction
// posA -> posB
var centerDirection = Quaternion.LookRotation((B.transform.position - A.transform.position).normalized);
var x = Mathf.Sin(angle * Mathf.Deg2Rad) * radius;
var z = Mathf.Cos(angle * Mathf.Deg2Rad) * radius;
var pos = new Vector3(x, 0, z);
// Rotate the pos vector according to the centerDirection
pos = centerDirection * pos;
sphere.position = centerPos + pos;
}
}
I'm currently working on a game and I ran into a small issue. I'm currently trying to create an object pool for objects that will reappear constantly in my game in order to improve the framerate of my game. However when creating this object pool whenever the game starts and the pool of objects is created and then activated and then deactivated and then reactivated again the game objects always get reactivated in a static state. I've tried going through my code to find out where this problem might be coming from but I have no Idea. All the reactivated game objects have rigidbodys and the proper tags.
Ive tried going trough all the different classes which might be creating this problem but I haven't found anything out of the ordinary myself.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnPoint: MonoBehaviour
{
public Transform SpawnPoints;
public GameObject[] interact;
List<float> StarPositions = new List<float>();
int Interact;
int index = 1;
public int pooledAmount = 40;
List<GameObject> colouredBalls;
public static bool spawnAllowed;
public static int count = 0;
public void Start()
{
colouredBalls = new List<GameObject>();
for (int c = 0; c < pooledAmount; c++)
{
GameObject obj = (GameObject)Instantiate(interact[0]);
obj.SetActive(false);
colouredBalls.Add(obj);
}
if (ScoreScript.scoreValue < 5)
{
Vector2 pos = Camera.main.WorldToViewportPoint(transform.position);
for (int x = 1; x < 5; x++)
{
//Vector3 SpawnPos = spawnPoints[d].position;
int NrSpawnpoints = 4;
int NrSpaces = NrSpawnpoints + 1;
double Xlegnth = 1.0;
double spawnPosX = x * Xlegnth / NrSpaces;
pos.x = (float)spawnPosX;
pos.y = 1.3f;
Vector2 Posi = Camera.main.ViewportToWorldPoint(pos);
Instantiate(SpawnPoints, Posi, Quaternion.identity);
//Debug.Log(Posi);
}
}
spawnAllowed = true;
InvokeRepeating("SpawnAInteract", 0f, 1f);
}
void SpawnAInteract()
{
if (spawnAllowed)
{
int randomSpawnPoint;
Vector2 pos = Camera.main.WorldToViewportPoint(transform.position);
//Vector2 starpos = Camera.main.WorldToViewportPoint(transform.position);
if (index % 10 != 0)
{
for (int d = 1; d < 5; d++)
{
//Vector3 SpawnPos = spawnPoints[d].position;
int NrSpawnpoints = 4;
int NrSpaces = NrSpawnpoints + 1;
double Xlegnth = 1.0;
double spawnPosX = d * Xlegnth / NrSpaces;
pos.x = (float)spawnPosX;
pos.y = 1.3f;
Vector2 Posi = Camera.main.ViewportToWorldPoint(pos);
if (!colouredBalls[d].activeInHierarchy)
{
colouredBalls[d].transform.position = Posi;
colouredBalls[d].transform.rotation = transform.rotation;
colouredBalls[d].SetActive(true);
//Debug.Log("Nr active Balls:" + f + colouredBalls[f].activeInHierarchy);
Debug.Log("Nr active Balls:" + d + colouredBalls[d].activeInHierarchy);
count++;
break;
}
}
index++;
}
else
{
for (int d = 1; d < 5; d++)
{
int NrSpawnpoints = 4;
int NrSpaces = NrSpawnpoints + 1;
double Xlegnth = 1.0;
double spawnPosX = d * Xlegnth / NrSpaces;
pos.x = (float)spawnPosX;
pos.y = 1.3f;
Vector2 Posi = Camera.main.ViewportToWorldPoint(pos);
StarPositions.Add((float)spawnPosX);
//Debug.Log("Starpositions " + StarPositions.ToString());
//edit this
double StarPos = spawnPosX - Xlegnth / NrSpaces / 2;
//Change to a list
//Debug.Log("Star " + d);
StarPositions[d - 1] = (float)StarPos;
}
//edit this to make the star appear at the StarPosition directly to the left or to the right of the WhiteBall
Vector2 Start = new Vector2(0, 0);
Vector2 StartCon = Camera.main.ViewportToWorldPoint(Start);
float whiteBallX = GameObject.FindWithTag("White Ball").transform.position.x;
for (int d = 1; d < 5; d++)
{
if (whiteBallX >= StartCon.x && whiteBallX <= StarPositions[d - 1])
{
int[] potentialStarPositions = { d, d + 2 };
int positionIndex = Random.Range(0, 2);
int randomSpawnPoin = potentialStarPositions[positionIndex];
pos.x = StarPositions[randomSpawnPoin];
pos.y = 1.3f;
Vector2 StarPosi = Camera.main.ViewportToWorldPoint(pos);
Interact = 1;
Instantiate(interact[Interact], StarPosi, Quaternion.identity);
break;
}
}
index++;
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectDestroy: MonoBehaviour
{
public Transform CameraCollider;
int NonActive = 0;
void Start()
{
Vector3 ScreenSize = new Vector3(1.5f, 1.5f, 1.5f);
Vector3 ScreenSizeAdj = Camera.main.ViewportToWorldPoint(ScreenSize);
CameraCollider.localScale = ScreenSizeAdj;
Vector3 ScreenPos = new Vector3(0.5f, 0.5f, 0);
Vector3 ScreenPosAdj = Camera.main.ViewportToWorldPoint(ScreenPos);
Instantiate(CameraCollider, ScreenPosAdj, Quaternion.identity);
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.CompareTag("ColouredBall Highress") || other.gameObject.CompareTag("Star"))
{
other.gameObject.SetActive(false);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InteractControl : MonoBehaviour
{
Rigidbody2D rb;
GameObject target;
float moveSpeed;
Vector3 directionToTarget;
Renderer m_Renderer;
void Start()
{
target = GameObject.Find("White Ball");
rb = GetComponent<Rigidbody2D>();
moveSpeed = 3f; //Movement speed of all the obstacles and powerups
MoveInteract(); //Method responsable for the movement of the obstacles and powerups, gets called at start
}
void MoveInteract() //Method responsable for the movement of the obstacles and stars
{
if (target != null)
{
if(ScoreScript.scoreValue > 5) //Determine when RedBall goes from going down in a straigh line to following white ball
{
directionToTarget = (target.transform.position - transform.position).normalized;
rb.velocity = new Vector2(directionToTarget.x * moveSpeed,
directionToTarget.y * moveSpeed);
}
else //Red Ball goes down in a straigh line
{
directionToTarget = new Vector3(0, -1, 0);
rb.velocity = new Vector2(0, directionToTarget.y * moveSpeed);
}
}
else
rb.velocity = Vector3.zero;
}
}
This is normal, when you deactivate an object the velocity resets. You must save their velocity before deactivating then set it again when reactivated.
I would look for other kind of optimization, this makes code more complex and probably there are better ways of optimizing it.
Edit: A quick solution would be to deactivate the sprite renderer, instead of the whole game object. This would optimize gpu load which I guess is the problem.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class SquadFormation : MonoBehaviour
{
enum Formation
{
Square, Circle, Triangle
}
[Header("Main Settings")]
[Space(5)]
public Transform squadMemeberPrefab;
[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 int numofobjects = 0;
// Use this for initialization
void Start()
{
numofobjects = numberOfSquadMembers;
if (startRandomFormation)
{
formation = (Formation)UnityEngine.Random.Range(0, Enum.GetNames(typeof(Formation)).Length);
}
else
{
formation = Formation.Square;
}
currentFormation = formation.ToString();
ChangeFormation();
foreach (Transform child in gameObject.transform)
{
if (child.tag == "Squad Member")
squadMembers.Add(child.gameObject);
}
randomSpeeds = RandomNumbers(randSpeedMin, randSpeedMax, squadMembers.Count);
step = new float[squadMembers.Count];
}
// Update is called once per frame
void Update()
{
if (numofobjects != numberOfSquadMembers)
{
numofobjects = 0;
numofobjects = numberOfSquadMembers;
squadMembers = new List<GameObject>();
FormationSquare();
}
if (Input.GetKeyDown(KeyCode.F))
{
randomSpeeds = RandomNumbers(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;
}
}
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>();
Transform go = squadMemeberPrefab;
for (int i = 0; i < numofobjects; i++)
{
if (squadMembers.Count == 0)
go = Instantiate(squadMemeberPrefab);
Vector3 pos = FormationSquarePositionCalculation(i);
go.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
go.Rotate(new Vector3(0, -90, 0));
go.tag = "Squad Member";
go.transform.parent = gameObject.transform;
newpositions.Add(go.transform.position);
}
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);
pos.y = Terrain.activeTerrain.SampleHeight(pos);
pos.y = pos.y + yOffset;
newpositions.Add(pos);
quaternions.Add(rot);
}
move = true;
squareFormation = false;
formation = Formation.Square;
}
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[] RandomNumbers(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;
}
}
In the constructor I'm searching for childs with the tag Squad Member.
But the List squadMembers will be empty since the script is attached to a new empty GameObject without any childs.
Then also the variable step will be empty.
Then inside the method MoveToNextFormation I'm checking if step is empty or not:
if (step.Length > 0)
step[0] = moveSpeed * Time.deltaTime;
If not checking the it will throw exception since there is nothing at index 0 it's null. But then if step is empty there will be no speed/s at all for the objects movements.
That's one problem.
I'm not sure even why in the constructor I did the part with the children and the "Squad Member" tag. I'm not creating yet any children with this tag so I'm confused about what I tried to do in the constructor.
The second problem is in this lines in the FormationSquare method:
if (squadMembers.Count == 0)
go = Instantiate(squadMemeberPrefab);
But if squadMembers is empty then it will throw exception somewhere else in other places in the code. And I'm creating new objects inside the FormationSquare method that's since I'm starting by default with the FormationSquare but what if I want to start by default with the FormationCircle method ?
The idea is to start with minimum (1) number of squads and with minimum (4) number of members in the squad when starting the program. Or to start with any range between min and max. But it's all messed up.
In your case, I would separate the squad member prefab instantiation from the squad shape formatting, doing this will help you identify your bug.
For example add the following methods and use them during 'Start':
void AddSquadMember()
{
// Use this to instantiate/spawn a new game object prefab.
}
void AddSquadMember(GameObject object)
{
// Use this for game object already in the scene. (.eg the children with your tag)
}
Then on the formation methods remove the intantiate calls and just use whatever game object you have in the list.
Finally, I would toss the 'numofobjects' variable. Then use 'squadMembers.Count' instead of both 'numofobjects' and 'numberOfSquadMembers' assuming that during 'Start' you have taken care of instantiating all game objects in order to 'numberOfSquadMembers == squadMembers.Count'. That is because you might need to raise the squad with a few more members during gameplay.
private void MoveToNewFormation()
{
squadMembers = GameObject.FindGameObjectsWithTag("Squad Member");
float step = speed * Time.deltaTime;
for (int i = 0; i < squadMembers.Length; i++)
{
squadMembers[i].transform.LookAt(newpos[i]);
squadMembers[i].transform.position = Vector3.MoveTowards(squadMembers[i].transform.position, newpos[i], step);
//squadMembers[i].transform.rotation = qua[i];
}
}
And calling it in the Update:
void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
ChangeFormation();
}
if (move == true)
MoveToNewFormation();
}
Once when one of the squadMembers reached to the newpos then i want to make
squadMembers[i].transform.rotation = qua[i];
qua is a List and i want to rotate the squad member once he reached the newpos.
Inside MoveToNewFormation i thought to add after the loop the line:
if (squadMembers[i].transform.position == newpos[i])
{
squadMembers[i].transform.rotation = qua[i];
}
But it's after the loop so 'i' not exist.
This is the complete script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SquadFormation : MonoBehaviour
{
enum Formation
{
Square, Circle, Triangle
}
public Transform squadMemeber;
public int columns = 4;
public int squareSpace = 10;
public int circleSpace = 40;
public int numberOfObjects = 20;
public float yOffset = 0;
public float speed = 3;
private Formation formation;
private GameObject[] squadMembers;
private List<Quaternion> qua = new List<Quaternion>();
private List<Vector3> newpos = new List<Vector3>();
private bool move = false;
// Use this for initialization
void Start()
{
formation = Formation.Square;
ChangeFormation();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
ChangeFormation();
}
if (move == true)
MoveToNewFormation();
}
private void ChangeFormation()
{
switch (formation)
{
case Formation.Square:
FormationSquare();
break;
case Formation.Circle:
FormationCircle();
break;
}
}
private Vector3 FormationSquarePositionCalculation(int index) // call this func for all your objects
{
float posX = (index % columns) * squareSpace;
float posY = (index / columns) * squareSpace;
return new Vector3(posX, posY);
}
private void FormationSquare()
{
for (int i = 0; i < numberOfObjects; i++)
{
Transform go = Instantiate(squadMemeber);
Vector3 pos = FormationSquarePositionCalculation(i);
go.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
go.Rotate(new Vector3(0, -90, 0));
go.tag = "Squad Member";
}
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()
{
Vector3 center = transform.position;
float radius = (float)circleSpace / 2;
float angleIncrement = 360 / (float)numberOfObjects;
for (int i = 0; i < numberOfObjects; i++)
{
Vector3 pos = FormationCirclePositionCalculation(center, radius, i, angleIncrement);
var rot = Quaternion.LookRotation(center - pos);
pos.y = Terrain.activeTerrain.SampleHeight(pos);
pos.y = pos.y + yOffset;
newpos.Add(pos);
qua.Add(rot);
}
move = true;
formation = Formation.Square;
}
private void MoveToNewFormation()
{
squadMembers = GameObject.FindGameObjectsWithTag("Squad Member");
float step = speed * Time.deltaTime;
for (int i = 0; i < squadMembers.Length; i++)
{
squadMembers[i].transform.LookAt(newpos[i]);
squadMembers[i].transform.position = Vector3.MoveTowards(squadMembers[i].transform.position, newpos[i], step);
//squadMembers[i].transform.rotation = qua[i];
}
//if (squadMembers[i].transform.position == newpos[i])
}
}
You can use a threshold and check the distance using that threshold.
Define threshold like this at start of script.
public float threshold = 0.1f;
Then change the MoveToNewFormation() function like this:
private void MoveToNewFormation()
{
squadMembers = GameObject.FindGameObjectsWithTag("Squad Member");
float step = speed * Time.deltaTime;
for (int i = 0; i < squadMembers.Length; i++)
{
squadMembers[i].transform.LookAt(newpos[i]);
squadMembers[i].transform.position =
Vector3.MoveTowards(squadMembers[i].transform.position, newpos[i], step);
if(Vector3.Distance(squadMembers[i].transform.position,newpos[i])<threshold){
squadMembers[i].transform.rotation = qua[i];
}
}
//if (squadMembers[i].transform.position == newpos[i])
}
I can use W S A D or arrow keys to turn the ship. But when i press on Z the ship fast moving up. When i press on X it will stop the ship on place.
I can't figure out how to make the ship move forward.
But nothing make the ship move forward.
I used a break point and i see in Start in the SpacecraftControl script on the line:
Debug.Log("Transform forward is : " + transform.forward);
Whem i put the mouse cursor on the forward of transform i see: 0.0,1.0,0.0
And inside the forward i see: x = 0 y = 0 and z = -1.192093E-07
Here is a small short video clip i recorded now showing what happen when running the game and then pressing on Z.
Please watch all the video the the Z pressing is start from second 14.
Video Clip
In my ship inspector i have: Transform, Local Rotation, Mesh Filter, Mesh Renderer, Animator, RigidBody > Gravity unchecked, Mesh Collider > Convex is checked and the scripts: Spacecraft Control and UserInput.
Then in the menu i went to: Edit > Project Settings > Input
In Input i addeed a new place one size to 19. And called the new Axes: Throttle
And the scripts first the SpacecraftControl:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(Rigidbody))]
public class SpacecraftControl : MonoBehaviour
{
public float MaxEnginePower = 40f;
public float RollEffect = 50f;
public float PitchEffect = 50f;
public float YawEffect = 0.2f;
public float BankedTurnEffect = 0.5f;
public float AutoTurnPitch = 0.5f;
public float AutoRollLevel = 0.1f;
public float AutoPitchLevel = 0.1f;
public float AirBreaksEffect = 3f;
public float ThrottleChangeSpeed = 0.3f;
public float DragIncreaseFactor = 0.001f;
private float Throttle;
private bool AirBrakes;
private float ForwardSpeed;
private float EnginePower;
private float cur_MaxEnginePower;
private float RollAngle;
private float PitchAngle;
private float RollInput;
private float PitchInput;
private float YawInput;
private float ThrottleInput;
private float OriginalDrag;
private float OriginalAngularDrag;
private float AeroFactor = 1;
private bool Immobolized = false;
private float BankedTurnAmount;
private Rigidbody _rigidbody;
Collider[] cols;
void Start()
{
_rigidbody = GetComponent<Rigidbody> ();
OriginalDrag = _rigidbody.drag;
OriginalAngularDrag = _rigidbody.angularDrag;
for (int i = 0; i < transform.childCount; i++)
{
foreach (var componentsInChild in transform.GetChild(i).GetComponentsInChildren<WheelCollider>())
{
componentsInChild.motorTorque = 0.18f;
}
}
Debug.Log("Transform forward is : " + transform.forward);
}
public void Move(float rollInput, float pitchInput, float yawInput, float throttleInput, bool airBrakes)
{
this.RollInput = rollInput;
this.PitchInput = pitchInput;
this.YawInput = yawInput;
this.ThrottleInput = throttleInput;
this.AirBrakes = airBrakes;
ClampInput ();
CalculateRollandPitchAngles ();
AutoLevel ();
CalculateForwardSpeed ();
ControlThrottle ();
CalculateDrag ();
CalculateLinearForces ();
CalculateTorque ();
if (Throttle < 0.1f)
{
Vector3 currentVelocity = _rigidbody.velocity;
Vector3 newVelocity = currentVelocity * Time.deltaTime;
_rigidbody.velocity = currentVelocity - newVelocity;
}
}
void ClampInput()
{
RollInput = Mathf.Clamp (RollInput, -1, 1);
PitchInput = Mathf.Clamp (PitchInput, -1, 1);
YawInput = Mathf.Clamp (YawInput, -1, 1);
ThrottleInput = Mathf.Clamp (ThrottleInput, -1, 1);
}
void CalculateRollandPitchAngles()
{
Vector3 flatForward = transform.forward;
flatForward.y = 0;
if (flatForward.sqrMagnitude > 0)
{
flatForward.Normalize ();
Vector3 localFlatForward = transform.InverseTransformDirection (flatForward);
PitchAngle = Mathf.Atan2 (localFlatForward.y, localFlatForward.z);
Vector3 flatRight = Vector3.Cross (Vector3.up, flatForward);
Vector3 localFlatRight = transform.InverseTransformDirection (flatRight);
RollAngle = Mathf.Atan2 (localFlatRight.y, localFlatRight.x);
}
}
void AutoLevel()
{
BankedTurnAmount = Mathf.Sin (RollAngle);
if (RollInput == 0)
{
RollInput = -RollAngle * AutoRollLevel;
}
if (PitchInput == 0f)
{
PitchInput = -PitchAngle * AutoPitchLevel;
PitchInput -= Mathf.Abs (BankedTurnAmount * BankedTurnAmount * AutoTurnPitch);
}
}
void CalculateForwardSpeed()
{
Vector3 localVelocity = transform.InverseTransformDirection (_rigidbody.velocity);
ForwardSpeed = Mathf.Max (0, localVelocity.z);
}
void ControlThrottle()
{
if (Immobolized)
{
ThrottleInput = -0.5f;
}
Throttle = Mathf.Clamp01 (Throttle + ThrottleInput * Time.deltaTime * ThrottleChangeSpeed);
EnginePower = Throttle * MaxEnginePower;
}
void CalculateDrag()
{
float extraDrag = _rigidbody.velocity.magnitude * DragIncreaseFactor;
//_rigidbody.drag = (AirBrakes ? (OriginalDrag + ) * AirBreaksEffect : OriginalDrag * extraDrag);
_rigidbody.drag = AirBrakes ? (OriginalDrag * AirBreaksEffect) : OriginalDrag;
_rigidbody.drag *= extraDrag;
_rigidbody.angularDrag = OriginalAngularDrag * ForwardSpeed / 1000 + OriginalAngularDrag;
}
void CalculateLinearForces()
{
Vector3 forces = Vector3.zero;
forces += EnginePower * transform.forward;
_rigidbody.AddForce (forces);
}
void CalculateTorque()
{
Vector3 torque = Vector3.zero;
torque += PitchInput * PitchEffect * transform.right;
torque += YawInput * YawEffect * transform.up;
torque += -RollInput * RollEffect * transform.forward;
torque += BankedTurnAmount * BankedTurnEffect * transform.up;
_rigidbody.AddTorque (torque * AeroFactor);
}
public void Immobilize()
{
Immobolized = true;
}
public void Reset()
{
Immobolized = false;
}
}
And the UserInput script:
using UnityEngine;
using System.Collections;
public class UserInput : MonoBehaviour {
SpacecraftControl _spacecraftcontrol;
// Use this for initialization
void Start ()
{
_spacecraftcontrol = GetComponent<SpacecraftControl> ();
}
void FixedUpdate()
{
float roll = Input.GetAxis ("Horizontal");
float pitch = Input.GetAxis ("Vertical");
bool airBrakes = Input.GetButton ("Fire1");
float throttle = Input.GetAxis ("Throttle");
_spacecraftcontrol.Move (roll, pitch, 0, throttle, airBrakes);
}
}