Rotate an object using Quaternion.RotateTowards() inside a Coroutine? - c#

I am trying to rotate an object inside a Coroutine using Quaternion.RotateTowards(). But its not rotating smoothly like it does in Update().
Instead, it snaps to the target rotation.
What is wrong? This is my script:
public class SpinWheelScript : MonoBehaviour {
public GameObject[] rewards;
public GameObject[] pointerDir;
public GameObject[] ringDir;
public GameObject pointer;
public GameObject ring;
private Quaternion pointerTargetRotation;
private Quaternion ringTargetRotation;
private bool spinIsEnabled = true;
private bool isSpinning = false;
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());
}
}
IEnumerator Spin()
{
isSpinning = true;
pSpeed = 0;
rSpeed = 0;
// Speed increasing spin for 5 seconds
PointerRandomDirection();
RingRandomDirection();
while (true)
{
pointerRotateFloat = (((pRandomDir + 1) * 60) - 30) - pointer.transform.rotation.z;
ringRotateFloat = (((rRandomDir + 1) * 60) - 30) - ring.transform.rotation.z;
pSpeed = (pointerRotateFloat / 2);
rSpeed = (ringRotateFloat / 2);
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);
if ((pointer.transform.rotation == pointerTargetRotation) && (ring.transform.rotation == ringTargetRotation))
break;
}
yield return new WaitForSeconds(1);
Debug.Log("Done!");
}
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;
}
}

You are not yielding a Wait-Instruction inside the coroutine.
Inside (maybe at the end) of the while (true) loop, simply write something like yield return null;. This will tell the coroutine to delay the execution of the next line of code to the next frame.

Related

The npc will move to the first or second point, then stop

The enemy will pick 1 or 2 positions, and then stop moving. If I get close enough, it will change to the chase state and follow me. So I do not know what causes them to freeze.
This holds the state machine, I intend to have more states and interactions, but I wanted to test the basic functionality, and it is broken.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BaseAI : MonoBehaviour
{
private enum State
{
Roaming,
ChaseTarget,
}
private PathfindAI pathMovement;
private Vector3 startingPosition;
public Vector3 roamPosition;
public PlayerScript player;
public Vector3 randomDirection;
[SerializeField] private State state;
public Vector3 playerPos;
private void Awake()
{
// pathMovement = GetComponent<PathfindAI>();
pathMovement = FindObjectOfType<PathfindAI>();
player = FindObjectOfType<PlayerScript>();
state = State.Roaming;
}
private void Start()
{
startingPosition = transform.position;
roamPosition = GetRoamingPos();
}
private void Update()
{
playerPos = player.PlayerPos();
switch (state)
{
default:
case State.Roaming:
pathMovement.MoveTo(roamPosition);
float reachedPos = 10f;
if (Vector3.Distance(transform.position, roamPosition) < reachedPos)
{
//reached destination
roamPosition = GetRoamingPos();
}
FindTarget();
break;
case State.ChaseTarget:
pathMovement.MoveTo(playerPos);
float attackRange = 10f;
if (Vector3.Distance(transform.position, playerPos) < attackRange)
{
//target within striking distance
}
break;
}
}
private Vector3 GetRoamingPos()
{
randomDirection = new Vector3(Random.Range(-1f, 1f), Random.Range(-1f, 1f), transform.position.z).normalized;
return startingPosition + randomDirection * Random.Range(10f, 70f);
}
private void FindTarget()
{
float targetRange = 50f;
if (Vector3.Distance(transform.position, playerPos) < targetRange)
{
state = State.ChaseTarget;
}
}
}
This allows them to move on the path from the pathfinding script. It uses A-star and I used a few tutorials to write it, mostly from CodeMonkey's youtube channel.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PathfindAI : MonoBehaviour
{
[SerializeField] float speed = 5f;
private int currentPathIndex;
private List<Vector3> pathVectorList;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Debug.Log(currentPathIndex);
}
public Vector3 GetPosition()
{
return transform.position;
}
public void MoveTo(Vector3 targetPosition)
{
SetTargetPos(targetPosition);
HandleMove();
}
private void SetTargetPos(Vector3 targetPosition)
{
currentPathIndex = 0;
pathVectorList = AStar.Instance.FindPathVector3(GetPosition(), targetPosition);
if (pathVectorList != null && pathVectorList.Count > 1)
{
pathVectorList.RemoveAt(0);
}
}
public void HandleMove()
{
if (pathVectorList != null)
{
Vector3 targetPosition = pathVectorList[currentPathIndex];
if(Vector3.Distance(transform.position, targetPosition) > 5f)
{
Vector3 moveDir = (targetPosition - transform.position).normalized;
float distanceBefore = Vector3.Distance(transform.position, targetPosition);
transform.position = transform.position + moveDir * speed * Time.deltaTime;
}
else
{
currentPathIndex++;
if(currentPathIndex >= pathVectorList.Count)
{
StopMoving();
}
}
}
}
private void StopMoving()
{
pathVectorList = null;
}
}
I suspect the error involves the HandleMove function. When I change where and how I call it; I can get it to break completely. It may be me setting the pathVectorList to null; however, it should be set back to a valid value on the next update when GetRoamingPos is called again within the roaming state.
when I go into the inspector and manually move the npc, it will find its way back to the position it chose. And if I move the player close enough it will chase me.
This is my A-Star Implementation
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AStar
{
private const int MOVE_STRAIGHT_COST = 10;
private const int MOVE_ANGLE_COST = 14;
public static AStar Instance { get; private set; }
private PathingGrid<PathNode> grid;
private List<PathNode> openList;
private List<PathNode> closedList;
public AStar(int width, int height)
{
Instance = this;
grid = new PathingGrid<PathNode>(width, height, 10f, Vector3.zero, (PathingGrid<PathNode> g, int x, int y) => new PathNode(g, x, y));
}
public PathingGrid<PathNode> GetGrid()
{
return grid;
}
public List<PathNode> FindPath(int startX, int startY, int endX, int endY)
{
PathNode startNode = grid.GetGridObject(startX, startY);
PathNode endNode = grid.GetGridObject(endX, endY);
openList = new List<PathNode> { startNode };
closedList = new List<PathNode>();
for (int x = 0; x<grid.GetWidth(); x++)
{
for (int y = 0; y<grid.GetHeight(); y++)
{
PathNode pathNode = grid.GetGridObject(x, y);
pathNode.gCost = int.MaxValue;
pathNode.CalcFCost();
pathNode.cameFromNode = null;
}
}
startNode.gCost = 0;
startNode.hCost = CalculateDistanceCost(startNode, endNode);
startNode.CalcFCost();
while (openList.Count > 0)
{
PathNode currentNode = GetLowestFCost(openList);
if (currentNode == endNode)
{
//reached final node
return CalculatedPath(endNode);
}
openList.Remove(currentNode);
closedList.Add(currentNode);
foreach (PathNode neighbourNode in GetNeighboursList(currentNode))
{
if (closedList.Contains(neighbourNode))
{
continue;
}
if (!neighbourNode.isWalkable)
{
closedList.Add(neighbourNode);
continue;
}
int tentativeGCost = currentNode.gCost + CalculateDistanceCost(currentNode, neighbourNode);
if (tentativeGCost < neighbourNode.gCost)
{
neighbourNode.cameFromNode = currentNode;
neighbourNode.gCost = tentativeGCost;
neighbourNode.hCost = CalculateDistanceCost(neighbourNode, endNode);
neighbourNode.CalcFCost();
if(!openList.Contains(neighbourNode))
{
openList.Add(neighbourNode);
}
}
}
}
//out of nodes on openList
return null;
}
private List<PathNode> GetNeighboursList(PathNode currentNode)
{
List<PathNode> neighbourList = new List<PathNode>();
if (currentNode.x - 1 >= 0)
{
//left
neighbourList.Add(GetNode(currentNode.x - 1, currentNode.y));
//left down
if (currentNode.y - 1 >= 0)
neighbourList.Add(GetNode(currentNode.x - 1, currentNode.y - 1));
//left up
if (currentNode.y + 1 < grid.GetHeight())
neighbourList.Add(GetNode(currentNode.x - 1, currentNode.y + 1));
}
if (currentNode.x + 1 < grid.GetWidth())
{
//right
neighbourList.Add(GetNode(currentNode.x + 1, currentNode.y));
//right down
if (currentNode.y - 1 >= 0)
neighbourList.Add(GetNode(currentNode.x + 1, currentNode.y - 1));
//right up
if (currentNode.y + 1 < grid.GetHeight())
neighbourList.Add(GetNode(currentNode.x + 1, currentNode.y + 1));
}
//down
if (currentNode.y - 1 >= 0)
neighbourList.Add(GetNode(currentNode.x, currentNode.y - 1));
//up
if (currentNode.y + 1 < grid.GetHeight())
neighbourList.Add(GetNode(currentNode.x, currentNode.y + 1));
return neighbourList;
}
public PathNode GetNode(int x, int y)
{
return grid.GetGridObject(x, y);
}
public List<Vector3> FindPathVector3(Vector3 startWorldPosition, Vector3 endWorldPosition)
{
grid.GetXY(startWorldPosition, out int startX, out int startY);
grid.GetXY(endWorldPosition, out int endX, out int endY);
List<PathNode> path = FindPath(startX, startY, endX, endY);
if (path == null)
{
return null;
}
else
{
List<Vector3> vectorPath = new List<Vector3>();
foreach(PathNode pathNode in path)
{
vectorPath.Add(new Vector3(pathNode.x, pathNode.y) * grid.GetCellSize() + Vector3.one * grid.GetCellSize() * .5f);
}
return vectorPath;
}
}
private List<PathNode> CalculatedPath(PathNode endNode)
{
List<PathNode> path = new List<PathNode>();
path.Add(endNode);
PathNode currentNode = endNode;
while (currentNode.cameFromNode != null)
{
path.Add(currentNode.cameFromNode);
currentNode = currentNode.cameFromNode;
}
path.Reverse();
return path;
}
private int CalculateDistanceCost(PathNode a,PathNode b)
{
int xDistance = Mathf.Abs(a.x - b.x);
int yDistance = Mathf.Abs(a.y - b.y);
int remaining = Mathf.Abs(xDistance - yDistance);
return MOVE_ANGLE_COST * Mathf.Min(xDistance, yDistance) + MOVE_STRAIGHT_COST * remaining;
}
private PathNode GetLowestFCost(List<PathNode> pathNodeList)
{
PathNode lowestFCostNode = pathNodeList[0];
for ( int i = 1; i < pathNodeList.Count; i++)
{
if (pathNodeList[i].fCost < lowestFCostNode.fCost)
{
lowestFCostNode = pathNodeList[i];
}
}
return lowestFCostNode;
}
}
One interesting point to note is that whenever my random point is chosen outside of the grid I have made, the error is not thrown where I would expect. Instead they claim that in my calculateDistance Cost"int xDistance = Mathf.Abs(a.x - b.x);" is not Finding an instanced Pathnode. Which makes sense because there wouldn't be one there. However, I would not expect that to be where the first error occurs with an out of bounds endpoint.

Stopwatch in c# (unity)

i'm making a door (similiar to the Doom 64's ones) and i have this code:
public class aperturaPorta : MonoBehaviour
{
public Transform playerCheck;
public Vector3 halfSize = new Vector3 (3f, 3f, 0.5f);
public LayerMask playerLayer;
bool playerEntering = false;
public BoxCollider collider;
public MeshRenderer renderer;
bool aprendo = false;
bool chiudendo = false;
public float openSpeed;
int counter = 1;
public int tick = 99;
// Update is called once per frame
void Update()
{
playerEntering = Physics.CheckBox(playerCheck.position, halfSize, Quaternion.identity, playerLayer, QueryTriggerInteraction.UseGlobal);
if (playerEntering && Input.GetButtonDown("e")) {
aprendo = true;
chiudendo = false;
}
if (counter == 100) {
chiudendo = true;
}
if (aprendo) {
transform.position += new Vector3 (0f, openSpeed, 0f);
counter += 1;
if (counter > tick) {
aprendo = false;
chiudendo = true;
}
}
if (chiudendo) {
transform.position -= new Vector3 (0f, openSpeed, 0f);
counter -= 1;
if (counter < 1) {
chiudendo = false;
}
}
}
}
This work but the door start closing when it finishes openening but it's too fast so i want to implement a two or three seconds stopwatch so that when it finishes the door start closing, how can i do it? thank you
ps: excuse me but i'm a newbie in unity
If I understand correctly you want a simple delay when the door is open before it closes? Keeping the same code structure you can add an other counter for that.
public float openSpeed;
int counter = 1;
public int tick = 99;
public int delayTicks = 100;
private int delayCounter = 0;
// Update is called once per frame
void Update()
{
playerEntering = Physics.CheckBox(playerCheck.position, halfSize, Quaternion.identity, playerLayer, QueryTriggerInteraction.UseGlobal);
if (playerEntering && Input.GetKeyDown(KeyCode.P))
{
aprendo = true;
chiudendo = false;
}
if (counter == 100)
{
chiudendo = true;
}
if (aprendo)
{
transform.position += new Vector3(0f, openSpeed, 0f);
counter += 1;
if (counter > tick)
{
delayCounter = delayTicks;
aprendo = false;
}
}
if (delayCounter > 0)
{
delayCounter--;
if (delayCounter <= 0)
{
chiudendo = true;
}
}
else if (chiudendo)
{
transform.position -= new Vector3(0f, openSpeed, 0f);
counter -= 1;
if (counter < 1)
{
chiudendo = false;
}
}
}

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.

How can i keep update the targets array when destroying and creating new objects again?

The problem is in this script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class PatrolData
{
public Transform target = null;
public float minDistance = 5f;
public float lingerDuration = 5f;
public float desiredHeight = 10f;
public float flightSmoothTime = 10f;
public float maxFlightspeed = 10f;
public float flightAcceleration = 1f;
public float levelingSmoothTime = 0.5f;
public float maxLevelingSpeed = 10000f;
public float levelingAcceleration = 2f;
}
public class PatrolOverTerrain : MonoBehaviour
{
public FlyToOverTerrain flyOverTerrain;
public LookAtCamera lookAtCamera;
public enum PatrolMode { Clamp, Wrap, PingPong };
public PatrolData[] patrolPoints;
public PatrolMode mode = PatrolMode.Wrap;
private int iterator = 0;
private int index = 0;
private float lingerDuration = 0f;
private int oldLength = 0;
public List<GameObject> TeleportationBooths = new List<GameObject>();
public Vector3 distanceFromTarget;
private void Start()
{
GameObject[] tempObj = GameObject.FindGameObjectsWithTag("Teleportation Booth");
for (int i = 0; i < tempObj.Length; i++)
{
//Add to list only if it does not exist
if (!TeleportationBooths.Contains(tempObj[i]))
{
TeleportationBooths.Add(tempObj[i]);
}
}
//Get the current Size
if (tempObj != null)
{
oldLength = tempObj.Length;
}
GeneratePatrolPoints();
}
private void OnEnable()
{
if (patrolPoints.Length > 0)
{
lingerDuration = patrolPoints[index].lingerDuration;
}
}
private void Update()
{
//Check if oldLength has changed
if (oldLength != TeleportationBooths.Count)
{
//Update oldLength
oldLength = TeleportationBooths.Count;
//Call your the function
GeneratePatrolPoints();
}
int length = patrolPoints.Length;
if (!flyOverTerrain) return;
if (patrolPoints.Length < 1) return;
if (index < 0) return;
// Getting exception out of index on line 89.
// Need to make a list also for the Cubes(buildings).
var patrol = patrolPoints[index];
if (lingerDuration <= 0)
{
iterator++;
switch (mode)
{
case PatrolMode.Clamp:
index = (iterator >= length) ? -1 : iterator;
break;
case PatrolMode.Wrap:
iterator = Modulus(iterator, length);
index = iterator;
break;
case PatrolMode.PingPong:
index = PingPong(iterator, length);
break;
}
if (index < 0) return;
patrol = patrolPoints[index];
flyOverTerrain.target = patrol.target;
flyOverTerrain.desiredHeight = patrol.desiredHeight;
flyOverTerrain.flightSmoothTime = patrol.flightSmoothTime;
flyOverTerrain.maxFlightspeed = patrol.maxFlightspeed;
flyOverTerrain.flightAcceleration = patrol.flightAcceleration;
flyOverTerrain.levelingSmoothTime = patrol.levelingSmoothTime;
flyOverTerrain.maxLevelingSpeed = patrol.maxLevelingSpeed;
flyOverTerrain.levelingAcceleration = patrol.levelingAcceleration;
lookAtCamera.target = patrol.target;
lookAtCamera.RotationSpeed = 3;
lingerDuration = patrolPoints[index].lingerDuration;
}
Vector3 targetOffset = Vector3.zero;
if ((bool)patrol.target)
{
targetOffset = transform.position - patrol.target.position;
}
float sqrDistance = patrol.minDistance * patrol.minDistance;
if (targetOffset.sqrMagnitude <= sqrDistance)
{
flyOverTerrain.target = null;
lookAtCamera.target = null;
lingerDuration -= Time.deltaTime;
}
else
{
flyOverTerrain.target = patrol.target;
lookAtCamera.target = patrol.target;
}
distanceFromTarget = transform.position - patrol.target.position;
}
private int PingPong(int baseNumber, int limit)
{
if (limit < 2) return 0;
return limit - Mathf.Abs(limit - Modulus(baseNumber, limit + (limit - 2)) - 1) - 1;
}
private int Modulus(int baseNumber, int modulus)
{
return (modulus == 0) ? baseNumber : baseNumber - modulus * (int)Mathf.Floor(baseNumber / (float)modulus);
}
public void GeneratePatrolPoints()
{
patrolPoints = new PatrolData[TeleportationBooths.Count];
for (int i = 0; i < patrolPoints.Length; i++)
{
patrolPoints[i] = new PatrolData();
patrolPoints[i].target = TeleportationBooths[i].transform;
patrolPoints[i].minDistance = 30f;
patrolPoints[i].lingerDuration = 3f;
patrolPoints[i].desiredHeight = 20f;
patrolPoints[i].flightSmoothTime = 10f;
patrolPoints[i].maxFlightspeed = 10f;
patrolPoints[i].flightAcceleration = 3f;
patrolPoints[i].levelingSmoothTime = 0.5f;
patrolPoints[i].maxLevelingSpeed = 10000f;
patrolPoints[i].levelingAcceleration = 2f;
}
}
}
In this part inside the Update i'm comparing the old length of a list with the current length:
//Check if oldLength has changed
if (oldLength != TeleportationBooths.Count)
{
//Update oldLength
oldLength = TeleportationBooths.Count;
//Call your the function
GeneratePatrolPoints();
}
So in case i create a new objects without destroying the old ones first it will be fine the length is not the same and it will call GeneratePatrolPoints() and will update the targets with the new targets just added:
patrolPoints[i].target = TeleportationBooths[i].transform;
The problem is when i check the ui toggle and first destroy the objects and then create them again the length is the same as before so it will not call GeneratePatrolPoints() and will not update the targets.
So i'm getting missing object exception.
I'm updating the list but i also need to update the targets again.
In this script i decide if the create new objects and to add them to the list so the length will not be the same and everything is right or to destroy first the current objects and then create new ones but then the length will be the same:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GenerateObjectsButton : MonoBehaviour
{
[SerializeField]
private InstantiateObjects[] instantiateobjects;
private bool toggleOnOf;
public Toggle toggle;
private void Start()
{
toggle.onValueChanged.AddListener((value) =>
{
MyListener(value);
});
}
public void MyListener(bool value)
{
if (value)
{
//do the stuff when the toggle is on
toggleOnOf = true;
}
else
{
//do the stuff when the toggle is off
toggleOnOf = false;
}
}
public void OnButton()
{
for (int i = 0; i < instantiateobjects.Length; i++)
{
if (toggleOnOf == false)
{
instantiateobjects[i].generateObjectOnTerrain();
}
else
{
instantiateobjects[i].DestroyObjects();
instantiateobjects[i].generateObjectOnTerrain();
}
}
}
}
The problem is with the first script with comparing the length with the list count.
The solution is to add a new bool variable in the GenerateObjectsButton script:
public bool destroyed = false;
And set it to true after destroying and creating new objects again:
public void OnButton()
{
for (int i = 0; i < instantiateobjects.Length; i++)
{
if (toggleOnOf == false)
{
instantiateobjects[i].generateObjectOnTerrain();
}
else
{
instantiateobjects[i].DestroyObjects();
instantiateobjects[i].generateObjectOnTerrain();
destroyed = true;
}
}
}
Now destroyed is true.
Back to the PatrolOverTerrain script.
Now i'm not only checking if the length is not the same but also if destroyed is true:
//Check if oldLength has changed
if (oldLength != TeleportationBooths.Count)
{
//Update oldLength
oldLength = TeleportationBooths.Count;
//Call your the function
GeneratePatrolPoints();
}
GameObject go = GameObject.Find("Button");
var destroyed = go.GetComponent<GenerateObjectsButton>().destroyed;
if (destroyed)
{
GeneratePatrolPoints();
}
Now i know when i only created new objects and added them by comparing length or when first destroyed the objects and then created new so the length didn't change but the list it self did.
Anyway this is a working solution.

Categories

Resources