Dependent Sliders - c#

I have 4 sliders on my stage:
s1sl-Slider
s2sl-Slider2
s3sl -Slider3
When changing the value of s1sl upwards, s2sl, s3sl should decrease in proportion to s1sl (the opposite is true for decreasing). I can’t figure out how to implement this
public GameObject s1sl;
public GameObject s2sl;
public GameObject s3sl;
// Start is called before the first frame update
void Start()
{
s1sl = GameObject.Find("Slider");
s2sl = GameObject.Find("Slider2");
s3sl = GameObject.Find("Slider3");
s1sl.GetComponent<Slider>().maxValue = 80;
s1sl.GetComponent<Slider>().minValue = 10;
s2sl.GetComponent<Slider>().maxValue = 80;
s2sl.GetComponent<Slider>().minValue = 10;
s3sl.GetComponent<Slider>().maxValue = 80;
s3sl.GetComponent<Slider>().minValue = 10;
}
// Update is called once per frame
void Update()
{
if (s1sl.GetComponent<Slider>().onValueChanged > s1sl.GetComponent<Slider>().value)
{
s2sl.GetComponent<Slider>().onValueChanged = s2sl.GetComponent<Slider>().onValueChanged - (s2sl.GetComponent<Slider>().value / 2);
s3sl.GetComponent<Slider>().onValueChanged = s3sl.GetComponent<Slider>().onValueChanged - (s3sl.GetComponent<Slider>().value / 2);
}
else
if (s2sl.GetComponent<Slider>().onValueChanged > s2sl.GetComponent<Slider>().value)
{
s1sl.GetComponent<Slider>().onValueChanged = s1sl.GetComponent<Slider>().onValueChanged - (s1sl.GetComponent<Slider>().value / 2);
s3sl.GetComponent<Slider>().onValueChanged = s3sl.GetComponent<Slider>().onValueChanged - (s3sl.GetComponent<Slider>().value / 2);
}
else
if (s3sl.GetComponent<Slider>().onValueChanged > s3sl.GetComponent<Slider>().value)
{
s1sl.GetComponent<Slider>().onValueChanged = s1sl.GetComponent<Slider>().onValueChanged - (s1sl.GetComponent<Slider>().value / 2);
s2sl.GetComponent<Slider>().onValueChanged = s2sl.GetComponent<Slider>().onValueChanged - (s2sl.GetComponent<Slider>().value`your text` / 2);
}
}

Related

cannot convert from UnityEngine.Vector3 to System.Collections.Generic.List<UnityEngine.Vector3>

I'm making a total war demo game where you drag on the screen to move your units. This bug has been bothering me and I don't know what's the reason behind it. The error I get is below.
FormationScript.cs(119,44): error CS1503: Argument 1: cannot convert from 'UnityEngine.Vector3' to 'System.Collections.Generic.List<UnityEngine.Vector3>'
Here is the code. (sorry for its length)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Debug = UnityEngine.Debug;
public class FormationScript : MonoBehaviour
{
public GameObject[] units;
public Transform formationBarrier;
float unitWidth = 2.0f; // This also includes the gap between them, in this case the unit width is 1 and the gap is also 1
private float numberOfUnits;
private double unitsPerRow;
private float numberOfRows;
Vector3 startClick;
Vector3 endClick;
Vector3 selectedAreaSize;
Vector3 selectedAreaPos;
float startMinusEndX;
float startMinusEndZ;
private List<List<Vector3>> availablePositions;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Formation();
}
void Formation()
{
if (Input.GetMouseButtonDown(1))
{
Plane plane = new Plane(Vector3.up, 0);
float distance;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (plane.Raycast(ray, out distance))
{
startClick = ray.GetPoint(distance);
}
Debug.Log(startClick);
}
if (Input.GetMouseButtonUp(1))
{
Plane plane = new Plane(Vector3.up, 0);
float distance;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (plane.Raycast(ray, out distance))
{
endClick = ray.GetPoint(distance);
}
Debug.Log(endClick);
}
// ======================================================================================================
/*if (startClick.x - endClick.x < 0 || startClick.z - endClick.z < 0)
{
startMinusEndX = startClick.x + endClick.x;
startMinusEndZ = startClick.z + endClick.z;
}
else
{
startMinusEndX = startClick.x - endClick.x;
startMinusEndZ = startClick.z - endClick.z;
}
if (startMinusEndX > 0 && startMinusEndZ > 0)
{
formationBarrier.localScale = new Vector3(startMinusEndX, 1, startMinusEndZ);
formationBarrier.localPosition = new Vector3((startClick.x + endClick.z) / 2, 0, (startClick.z + endClick.z) / 2);
}
else if (startMinusEndX < 0 && startMinusEndZ > 0)
{
formationBarrier.localScale = new Vector3(startMinusEndX, 1, startMinusEndZ);
formationBarrier.localPosition = new Vector3((startClick.x + endClick.z) * 2, 0, (startClick.z + endClick.z) / 2);
}
*/
startMinusEndX = startClick.x - endClick.x;
startMinusEndZ = startClick.z - endClick.z;
formationBarrier.localScale = new Vector3(startMinusEndX, 1, startMinusEndZ);
formationBarrier.localPosition = new Vector3((startClick.x + endClick.z) / 2, 0, (startClick.z + endClick.z) / 2);
// ======================================================================================================
selectedAreaSize = formationBarrier.localScale;
selectedAreaPos = formationBarrier.localPosition;
numberOfUnits = units.Length;
unitsPerRow = (unitWidth / numberOfUnits) * selectedAreaSize.x;
unitsPerRow = Math.Round(unitsPerRow, 0);
if (unitsPerRow < 0)
{
unitsPerRow = unitsPerRow * -2;
}
if (numberOfRows % 1 == 0)
{
numberOfRows = numberOfUnits % (float)unitsPerRow;
for (int i = 0; i > numberOfRows; i++) // i is the number of rows
{
//availablePositions.Add(i);
for (int j = 0; j > numberOfUnits / unitsPerRow; j++) // j is the number of units / the units per row
{
Vector3 pos = new Vector3((selectedAreaPos.x / ((float)unitsPerRow + 1)) + ((j - 1) * (selectedAreaPos.x / ((float)unitsPerRow + 1))), 0.0f, i * 2);
availablePositions.Add(pos); // Heres where the error's coming from
}
}
}
else if (numberOfUnits % (float)unitsPerRow != 0)
{
numberOfRows = numberOfUnits % (float)unitsPerRow;
}
Debug.Log(unitsPerRow);
Debug.Log(numberOfRows);
}
}
I am pretty new to Unity so go easy : )
There is a syntax error in your code. You are inserting pos which is of type Vector3 into availablePositions, which is a List of List of Vecotr3.
Either change availablePositions definition:
private List<Vector3> availablePositions;
or convert pos to list before adding to availablePositions:
availablePositions.Add(new List<Vector3>{pos});

Is there any way to solve a ODEs system in Unity the way I need?

I want to "grow" a PreFab in unity using some parameters that are the numerical solution of a ODEs system.
I have two scripts: one that literally grows the prefab and another one that solves an ODE using 4th order Runge-Kutta numerical method.
The code for ODE solution is this one:
using System;
namespace RungeKutta4
{
class Program
{
static void Main(string[] args)
{
//Incrementers to pass into the known solution
double t = 0.0;
double T = 1.0;
double dt = 0.1;
// Assign the number of elements needed for the arrays
int n = (int)(((T - t) / dt)) + 1;
// Initialize the arrays for the time index 's' and estimates 'annualGrowth' at each index 'i'
double[] annualGrowth = new double[n]; //y
double[] s = new double[n]; //t
// RK4 Variables
double dy1;
double dy2;
double dy3;
double dy4;
// RK4 Initializations condizioni iniziali
int i = 0;
s[i] = 0.0; //t0 = 0
annualGrowth[i] = 1.0; //y(t0) = y(0) = 1
// Iterate and implement the Rk4 Algorithm
while (i < annualGrowth.Length - 1)
{
dy1 = dt * equation(s[i], annualGrowth[i]);
dy2 = dt * equation(s[i] + dt / 2, annualGrowth[i] + dy1 / 2);
dy3 = dt * equation(s[i] + dt / 2, annualGrowth[i] + dy2 / 2);
dy4 = dt * equation(s[i] + dt, annualGrowth[i] + dy3);
s[i + 1] = s[i] + dt;
annualGrowth[i + 1] = annualGrowth[i] + (dy1 + 2 * dy2 + 2 * dy3 + dy4) / 6;
double t_rounded = Math.Round(t + dt, 2);
if (t_rounded % 1 == 0)
{
Console.WriteLine(" y(" + t_rounded + ")" + " " .PadRight(10) + annualGrowth[i + 1]);
}
i++;
t += dt; //t = t + dt
};//End Rk4
Console.ReadLine();
}
// Differential Equation
public static double equation(double t, double annualGrowth)
{
double y_prime;
double k = 0.1;
double maxHeight = 5.0;
return y_prime = k * annualGrowth * (1-(annualGrowth/maxHeight));
}
}
}
Then I have the "growing" script in unity:
public class Growth : MonoBehaviour
{
SetupScene setup;
TimeManager timeManager;
float currentLenght;
float currentWidth;
Genetic geneticInfo;
bool isMaxLenght;
bool isMaxWeigth;
GameObject newInternode;
float annualLengthGrowth = 0.05f;
float annualWidthGrowth = 0.01f;
Vector3 annualGrowth;
bool hasChild;
// Start is called before the first frame update
void Awake()
{
annualGrowth = new Vector3(annualWidthGrowth, annualLengthGrowth, annualWidthGrowth);
setup = GameObject.Find("Setup").GetComponent<SetupScene>();
timeManager = GameObject.Find("Time Manager").GetComponent<TimeManager>();
currentLenght = 0f;
currentWidth = 0f;
geneticInfo = new Genetic(1);
}
// Update is called once per frame
void Update()
{
Debug.Log("ciao, sono nato " + gameObject.name);
Debug.Log(timeManager.IsYearElapsed + " " + gameObject.name);
if (timeManager.IsYearElapsed)
{
float scaleFactorXZ = 1f;
float scaleFactorY = 1f;
if (gameObject.name != "Seed")
{
scaleFactorXZ = geneticInfo.InternodeMaxWidtht;
scaleFactorY = geneticInfo.InternodeMaxLenght;
}
currentLenght = (gameObject.transform.lossyScale.y + annualGrowth.y);
currentWidth = (gameObject.transform.lossyScale.x + annualGrowth.x);
if(currentWidth > geneticInfo.InternodeMaxWidtht)
{
currentWidth = geneticInfo.InternodeMaxWidtht;
isMaxWeigth = true;
}
if(currentLenght > geneticInfo.InternodeMaxLenght)
{
currentLenght = geneticInfo.InternodeMaxLenght;
isMaxLenght = true;
}
gameObject.transform.localScale = new Vector3(currentWidth/scaleFactorXZ, currentLenght/scaleFactorY, currentWidth/scaleFactorXZ);
if (isMaxLenght)
{
if (!hasChild && setup.Manager.InternodeNumber < geneticInfo.MaxInternodeNumber)
{
setup.Manager.InternodeNumber++;
Debug.Log("nuovo internodo creato");
setup.LastInternodePosition = gameObject.transform.position + new Vector3(0f, geneticInfo.InternodeMaxLenght, 0f);
newInternode = Instantiate(setup.Prefab, new Vector3(setup.LastInternodePosition.x, setup.LastInternodePosition.y, setup.LastInternodePosition.z), Quaternion.identity);
newInternode.transform.localScale = Vector3.zero;
newInternode.transform.parent = setup.LastInternode.transform;
setup.LastInternode = newInternode;
newInternode.AddComponent<Growth>();
hasChild = true;
}
if (isMaxWeigth)
{
gameObject.GetComponent<Growth>().enabled = false;
}
}
}
}
}
I want to link the numerical solution of ODE script to the value float annualLengthGrowth = 0.05f;in the unity script that is, for now, hardcoded. The problem is that at each update (or timestep) the initial value of ODE system must be the final numerical calculated value of the previous step.
For example, given that at first timestep, the initial value of annualGrowth in ODE script is 1.0, the final calculated value is (for example) annualGrowthT1 = 2.35f, I have to use this value in Unity script at first time step as annualLengthGrowth. At next time step, the initial value for ODE script should be annualGrowthT1 = 2.35f, then the calculated final value should be (for example) annualGrowthT2 = 3.56f and this value must be used in the second time step in unity script as new annualLengthGrowth. And so on.
It should be clear thank to this picture:
I tried to put all together but the unity console gives me the error:
error CS0161: 'Growth.Runge.runge(float, float, float, float, Growth.Runge.Function)': not all code paths return a value.
I thin I missed a return somewhere in the code but I don't know where.
Here it is my new code:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Growth : MonoBehaviour
{
SetupScene setup; //mi serve per prendere il prefab
TimeManager timeManager;
float currentLenght;
float currentWidth;
Genetic geneticInfo;
bool isMaxLenght; //internodo ha raggiunto la lunghezza massima
bool isMaxWeigth;
GameObject newInternode;
float annualLengthGrowth = Runge.runge(0.0f, 1.0f, 1.0f, .1f, new Runge.Function(Equation.equation)); //0.05f; // per ora lo fissiamo da codice, successivamente verra' calcolato
float annualWidthGrowth = 0.01f;
Vector3 annualGrowth;
bool hasChild;
// Start is called before the first frame update
void Awake()
{
annualGrowth = new Vector3(annualWidthGrowth, annualLengthGrowth, annualWidthGrowth);
setup = GameObject.Find("Setup").GetComponent<SetupScene>();
timeManager = GameObject.Find("Time Manager").GetComponent<TimeManager>();
currentLenght = 0f;
currentWidth = 0f;
geneticInfo = new Genetic(1);
}
// Update is called once per frame
void Update()
{
Debug.Log("ciao, sono nato " + gameObject.name);
Debug.Log(timeManager.IsYearElapsed + " " + gameObject.name);
if (timeManager.IsYearElapsed)
{
//calcolo lunghezza e larghezza corrente
float scaleFactorXZ = 1f;
float scaleFactorY = 1f;
if (gameObject.name != "Seed")
{
scaleFactorXZ = geneticInfo.InternodeMaxWidtht;
scaleFactorY = geneticInfo.InternodeMaxLenght;
}
currentLenght = (gameObject.transform.lossyScale.y + annualGrowth.y);
Debug.Log("Lunghezza corrente internodo = " + currentLenght);
currentWidth = (gameObject.transform.lossyScale.x + annualGrowth.x);
if(currentWidth > geneticInfo.InternodeMaxWidtht)
{
currentWidth = geneticInfo.InternodeMaxWidtht;
isMaxWeigth = true;
}
if(currentLenght > geneticInfo.InternodeMaxLenght)
{
currentLenght = geneticInfo.InternodeMaxLenght;
isMaxLenght = true;
}
gameObject.transform.localScale = new Vector3(currentWidth/scaleFactorXZ, currentLenght/scaleFactorY, currentWidth/scaleFactorXZ);
if (isMaxLenght)
{
//crea nuovo internodo come figlio di quello corrente
if (!hasChild && setup.Manager.InternodeNumber < geneticInfo.MaxInternodeNumber)
{
setup.Manager.InternodeNumber++;
Debug.Log("nuovo internodo creato");
setup.LastInternodePosition = gameObject.transform.position + new Vector3(0f, geneticInfo.InternodeMaxLenght, 0f);
newInternode = Instantiate(setup.Prefab, new Vector3(setup.LastInternodePosition.x, setup.LastInternodePosition.y, setup.LastInternodePosition.z), Quaternion.identity);
newInternode.transform.localScale = Vector3.zero;
newInternode.transform.parent = setup.LastInternode.transform;
setup.LastInternode = newInternode;
//creare nuova classe gestore Gerarchia
newInternode.AddComponent<Growth>();
hasChild = true;
}
if (isMaxWeigth)
{
gameObject.GetComponent<Growth>().enabled = false;
}
}
}
}
public class Runge
{
//declare a delegate that takes a double and returns
public delegate float Function(float t, float annualGrowth);
public static float runge(float a, float b, float value, float step, Function f)
{
float t, w, k1, k2, k3, k4;
t = a;
w = value;
for (int i = 0; i < (b - a) / step; i++)
{
k1 = step * f(t, w);
k2 = step * f(t + step / 2f, w + k1 / 2f);
k3 = step * f(t + step / 2f, w + k2 / 2f);
k4 = step * f(t + step, w + k3);
w = w + (k1 + 2f * k2 + 2f * k3 + k4) / 6f;
t = a + i * step;
//Console.WriteLine("{0} {1} ", Math.Round(t, 1), w);
}
}
}
class Equation
{
public static float equation(float t, float annualGrowth)
{
float y_prime;
float k = 0.1f; //fattore di crescita, deve essere non hardcodato
float maxHeight = 5.0f; //altezza max, deve essere non hardcodato
return y_prime = k * annualGrowth * (1 - (annualGrowth / maxHeight));
}
}
}

Drawing a Turkey in unity2d using Verlet method

This is a homework problem(Do not copy, copy is not smart): We need to draw a 2d turkey in unity 2d using Verlet method to update the positions of the vertices. However, We don't know the forces involved to trace the Turkey. Here is a picture of the Turkey. Is there a trick?
Here is the codes that we started:
public class GenerateTurkeys : MonoBehaviour
{
LineRenderer lineRenderer = gameObject.AddComponent<LineRenderer>();
// Start is called before the first frame update
int numberOfTurkeys;
int NUM_PARTICLES;
float fTimeStep;
Vector3[] m_position = new Vector3[NUM_PARTICLES];
Vector3[] m_acceleration = new Vector3[NUM_PARTICLES];
Vector3[] m_oldPosition = new Vector3[NUM_PARTICLES];
void Start()
{
NUM_PARTICLES = 100;
numberOfTurkeys = 0;
}
// Verlet integration step void ParticleSystem::
Verlet()
{
for (int i=0; i<NUM_PARTICLES; i++)
{
Vector3 x = m_position[i];
Vector3 temp = x;
Vector3 oldx = m_oldPosition[i];
Vector3 a = m_acceleration[i];
x += x-oldx+a* fTimeStep*fTimeStep;
oldx = temp;
}
}
void DrawLine(float[] heights)
{
LineRenderer lineRenderer = GetComponent<LineRenderer>();
var t = Time.time;
for (int i = 0; i < NUM_PARTICLES; i++)
{
lineRenderer.SetPosition(i, );
}
}
// Update is called once per frame
void Update()
{
}
}
I'm not sure that I correctly decided, due to the fact that the contour does not match, but in the solution the speed is exactly iterative. I think this is a small error of physics in Unity, Because if you use the resulting accelerations, you can draw the graph very accurately.
Here is code:
public class Drawer : MonoBehaviour
{
[SerializeField]
private Transform[] m_Dots;
private Rigidbody2D m_Dot;
private Vector2[] m_Acceler;
float deltaT = 0.5f;//for example
private void Start()
{
m_Acceler = GetAcceler();
var go = new GameObject("Tracer");
var tr = go.AddComponent<TrailRenderer>();
tr.widthMultiplier = 0.1f;
tr.time = 50f;
m_Dot = go.AddComponent<Rigidbody2D>();
m_Dot.bodyType = RigidbodyType2D.Kinematic;
m_Dot.gravityScale = 0;
StartCoroutine(VerletCoroutine());
}
private Vector2[] GetAcceler()
{
Vector2[] result = new Vector2[m_Dots.Length];
float T = deltaT;
int len = m_Dots.Length;
result[0] = An(m_Dots[1].position, m_Dots[0].position, m_Dots[0].position, T);
for (int i = 1 ; i < len - 1 ; i++, T += deltaT)
{
result[i] = An(m_Dots[i + 1].position, m_Dots[i].position, m_Dots[i].position, T);
}
result[len - 1] = An(m_Dots[0].position, m_Dots[len - 1].position, m_Dots[len - 1].position, T);
return result;
}
private Vector2 An(Vector2 Xnext, Vector2 Xn, Vector2 Xprev, float t)
{// a[n] = (x[n+1] - 2*x[n]+x[n-1])/t^2
return (Xnext - 2 * Xn + Xprev) / t * t;
}
private IEnumerator VerletCoroutine()
{
m_Dot.transform.position = m_Dots[0].position;
Vector2 Vprev = Vector2.zero;
int len = m_Acceler.Length - 1;
float t = 0;
int i = 0;
while (true)
{
t += Time.deltaTime;
if (t >= deltaT)
{
i++;
if (i > len)
{
break;
}
t = 0;
Vprev = Vector2.zero;
}
Vprev = Vnext(Vprev, m_Acceler[i], m_Acceler[i], t);
m_Dot.velocity = Vprev;
yield return new WaitForEndOfFrame();
}
m_Dot.velocity = Vector3.zero;
yield return null;
}
private Vector2 Vnext(Vector2 Vn, Vector2 Anext, Vector2 An, float t)
{//v[n+1]= v[n]+0,5(a[n+1] +a[n]) * t
var v = Vn + 0.5f * (Anext + An) * t;
return v;
}
}

How can I prevent from adding more and more new objects when pressing F to change formation?

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.

Coroutine gets stuck at random times?

The question says it all, I have a Coroutine that I am using to create some spinning animation. The problem is that it suddenly stops randomly (at different times). I don't know what might be causing this as it works most of the times (8 times out of 10) on PC. I also built the game and tried it on an android phone but it only works (3 times out of 10). Any idea what might be causing this?
This is my Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpinWheelScript : MonoBehaviour {
public CanvasGroup spinWheelCanvas;
public GameObject[] rewards;
public GameObject[] pointerDir;
public GameObject[] ringDir;
public GameObject pointer;
public GameObject ring;
private Quaternion pointerTargetRotation;
private Quaternion ringTargetRotation;
private bool spinIsEnabled = false;
private bool isSpinning = false;
private bool lastSpin = false;
private bool animationIsEnabled = false;
private float time;
private float pSpeed;
private float rSpeed;
private float pointerRotateFloat;
private float ringRotateFloat;
private int rewardEnabler = 0;
private int randomReward;
private int pRandomDir;
private int rRandomDir;
private int plastRandomDir = 0;
private int rlastRandomDir = 0;
private void Update()
{
if (spinIsEnabled == false)
{
// Do Nothing
}
else if (spinIsEnabled == true && isSpinning == false)
{
// Select reward
if (rewardEnabler == 0)
{
RewardSelector();
rewardEnabler = 1;
}
StartCoroutine(Spin());
}
if (lastSpin == true)
{
Debug.Log(randomReward);
pointerRotateFloat = ((360 - (randomReward * 60)) - 30);
ringRotateFloat = ((360 - (randomReward * 60)) - 30);
if (pointerRotateFloat > ringRotateFloat)
{
pSpeed = (pointerRotateFloat);
rSpeed = (pointerRotateFloat / ringRotateFloat) * (ringRotateFloat);
}
else
{
rSpeed = (ringRotateFloat);
pSpeed = (ringRotateFloat / pointerRotateFloat) * (pointerRotateFloat);
}
Quaternion pointerTargetRotation = Quaternion.Euler(new Vector3(0, 0, pointerRotateFloat));
pointer.transform.rotation = Quaternion.RotateTowards(pointer.transform.rotation, pointerTargetRotation, pSpeed * Time.deltaTime);
Quaternion ringTargetRotation = Quaternion.Euler(new Vector3(0, 0, ringRotateFloat));
ring.transform.rotation = Quaternion.RotateTowards(ring.transform.rotation, ringTargetRotation, rSpeed * Time.deltaTime);
if ((pointer.transform.rotation == pointerTargetRotation) && (ring.transform.rotation == ringTargetRotation))
{
lastSpin = false;
isSpinning = false;
spinIsEnabled = false;
animationIsEnabled = true;
}
}
}
IEnumerator Spin()
{
isSpinning = true;
pSpeed = 0;
rSpeed = 0;
time = 0;
while (time < 15)
{
pRandomDir = PointerRandomDirection(); // Function to pick a random number.
rRandomDir = RingRandomDirection(); // Function to pick a random number.
for (;;)
{
pointerRotateFloat = (((pRandomDir + 1) * 60) - 30) - pointer.transform.rotation.z;
ringRotateFloat = (((rRandomDir + 1) * 60) - 30) - ring.transform.rotation.z;
if (pointerRotateFloat > ringRotateFloat)
{
pSpeed = (pointerRotateFloat);
rSpeed = (pointerRotateFloat / ringRotateFloat) * (ringRotateFloat);
}
else
{
rSpeed = (ringRotateFloat);
pSpeed = (ringRotateFloat / pointerRotateFloat) * (pointerRotateFloat);
}
pointerTargetRotation = Quaternion.Euler(new Vector3(0, 0, pointerRotateFloat));
pointer.transform.rotation = Quaternion.RotateTowards(pointer.transform.rotation, pointerTargetRotation, pSpeed * Time.deltaTime);
ringTargetRotation = Quaternion.Euler(new Vector3(0, 0, ringRotateFloat));
ring.transform.rotation = Quaternion.RotateTowards(ring.transform.rotation, ringTargetRotation, rSpeed * Time.deltaTime);
Debug.Log("Before the if");
if ((pointer.transform.rotation == pointerTargetRotation) && (ring.transform.rotation == ringTargetRotation))
break;
yield return null;
}
time++;
}
lastSpin = true;
}
private int RewardSelector()
{
randomReward = Random.Range(0, rewards.Length);
return randomReward;
}
private int PointerRandomDirection()
{
int pRandomDir = plastRandomDir;
if (pointerDir.Length <= 1)
return 0;
while (pRandomDir == plastRandomDir)
{
pRandomDir = Random.Range(0, pointerDir.Length);
}
plastRandomDir = pRandomDir;
return pRandomDir;
}
private int RingRandomDirection()
{
int rRandomDir = rlastRandomDir;
if (ringDir.Length <= 1)
return 0;
while (rRandomDir == rlastRandomDir)
{
rRandomDir = Random.Range(0, ringDir.Length);
}
rlastRandomDir = rRandomDir;
return rRandomDir;
}
public void OnSpinButtonClick()
{
if(spinIsEnabled == false && isSpinning == false)
spinIsEnabled = true;
spinWheelCanvas.interactable = false;
}
}
Thanks in advance.
If you don't find the answer using your existing solution, here is an alternative solution you might appreciate, which arguably is more simple.
A simpler way to achieve the spinning effect and stopping at random angles:
Make 2 animations (In the Animation/Animator windows), a 0-180 degree animation and a 180-360 degree animation, then have them transition between each other.
Making just one 0-360 animation will most likely look buggy, hence I advise to make it in 2 parts.
You can then decide the speed of this animation by adjusting the animation speed and Animator parameters (which control what animations are playing) through code.
To have the spinning wheel stop at random angles you could have a float value in the animator called "Speed", the higher the speed the longer time it takes to stop the animation, hence random stopping angles are achieved.
When the speed hits 0, set the animation speed to 0 as well, that should stop the animation at the designated angle.
Feel free to ask questions if you don't understand what I mean.

Categories

Resources