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.
Related
So decided to give game dev a try, picked up unity,Now I decided to create a simple ping pong game.
My game has Bat.cs class, ball.cs, and GameHandler.cs.
The GameHandler.cs initializes the batRight, batLeft, ball using the Bat.cs, Ball.cs class and prefabs, it also keeps track of if someone scores then starts a serve by stopping the rally:
public class GameHandler : MonoBehaviour
{
public Bat bat;
public Ball ball;
public Score score;
public static Vector2 bottomLeft;
public static Vector2 topRight;
public static bool rallyOn = false;
public static bool isServingLeft = true;
public static bool isServingRight = false;
public static bool isTwoPlayers = true;
static Bat batRight;
static Bat batLeft;
public static Ball ball1;
static Score scoreLeft;
static Score scoreRight;
// Start is called before the first frame update
void Start()
{
//Convert screens pixels coordinate into games coordinate
bottomLeft = Camera.main.ScreenToWorldPoint(new Vector2(0, 0));
topRight = Camera.main.ScreenToWorldPoint(new Vector2(Screen.width, Screen.height));
ball1 = Instantiate(ball) as Ball;
//Debug.Log("GameHandler.Start");
batRight = Instantiate(bat) as Bat;
batRight.Init(true);
batLeft = Instantiate(bat) as Bat;
batLeft.Init(false);
ball1.transform.position = new Vector3(batLeft.transform.position.x + ball1.getRadius() * 2, batLeft.transform.position.y);
//Instatiate scores
scoreLeft = Instantiate(score, new Vector2(-2, -4), Quaternion.identity) as Score;
scoreRight = Instantiate(score, new Vector2(2, -4), Quaternion.identity) as Score;
}
private void Update()
{
if (isServingLeft)
{
ball1.transform.position = new Vector3(batLeft.transform.position.x + ball1.getRadius() * 2, batLeft.transform.position.y);
if (Input.GetKey(KeyCode.LeftControl))
{
rallyOn = true;
isServingLeft = false;
}
}
if (isServingRight)
{
ball1.transform.position = new Vector3(batRight.transform.position.x - ball1.getRadius() * 2, batRight.transform.position.y);
if (isTwoPlayers && Input.GetKey(KeyCode.RightControl))
{
rallyOn = true;
isServingRight = false;
}
else
{
StartCoroutine(batRight.serveAi());
if (GameHandler.rallyOn)
{
StopCoroutine(batRight.serveAi());
}
}
}
}
public static void increaseScoreByOne(bool isRight)
{
rallyOn = false;
if (isRight)
{
scoreRight.increaseScore();
isServingRight = true;
}
else
{
scoreLeft.increaseScore();
isServingLeft = true;
}
}
}
The ball.cs listens to the static GameHandler.rallyOn and starts moving the ball accordingly, it also changes direction of the ball if it hits a vertical wall or the bat:
public class Ball : MonoBehaviour
{
[SerializeField]
float speed;
float radius;
public Vector2 direction;
public Vector3 ballPosition;
// Start is called before the first frame update
void Start()
{
direction = Vector2.one.normalized; // direction is (1, 1) normalized
//Debug.Log("direction * speed * Time.deltaTime:" + direction * speed * Time.deltaTime);
radius = transform.localScale.x / 2;
}
// Update is called once per frame
void Update()
{
ballPosition = transform.position;
if (GameHandler.rallyOn)
{
startRally();
}
}
void startRally()
{
transform.Translate(direction * speed * Time.deltaTime);
Debug.Log("direction * speed * Time.deltaTime:" + direction * speed * Time.deltaTime);
if ((transform.position.y + radius) > GameHandler.topRight.y && direction.y > 0)
{
direction.y = -direction.y;
}
if ((transform.position.y + radius) < GameHandler.bottomLeft.y && direction.y < 0)
{
direction.y = -direction.y;
}
if ((transform.position.x + radius) > GameHandler.topRight.x && direction.x > 0)
{
// Left player scores
GameHandler.increaseScoreByOne(false);
//For no, just freeza the script
// Time.timeScale = 0;
//enabled = false;
}
if ((transform.position.x - radius) < GameHandler.bottomLeft.x && direction.x < 0)
{
// right player scores
GameHandler.increaseScoreByOne(true);
}
}
void OnTriggerEnter2D(Collider2D collision)
{
if(collision.tag == "Bat")
{
if (collision.GetComponent<Bat>().isRight && direction.x > 0)
{
direction.x = -direction.x;
}
if (!collision.GetComponent<Bat>().isRight && direction.x < 0)
{
direction.x = -direction.x;
}
}
}
public float getRadius()
{
return radius;
}
}
The bat.cs initiazes the two paddles, and either listens to user input if its 2 players or listens to player and use AI if it is player vs CPU.:
public class Bat : MonoBehaviour
{
float height;
[SerializeField] // this make this appear on editor without making this field public
float speed;
string input;
public bool isRight;
string PLAYER1_INPUT = "PaddleLeft";
string PLAYER2_INPUT = "PaddleRight";
// Start is called before the first frame update
void Start()
{
height = transform.localScale.y;
}
// Update is called once per frame
void Update()
{
if (GameHandler.isTwoPlayers)
{
if (isRight)
{
movePaddleonUserInput(PLAYER2_INPUT);
}
else
{
movePaddleonUserInput(PLAYER1_INPUT);
}
}
else
{
if (isRight)
{
movePaddleAi();
}
else
{
movePaddleonUserInput(PLAYER1_INPUT);
}
}
}
void movePaddleAi()
{
if (isRight && GameHandler.ball1.direction.x > 0 && GameHandler.ball1.ballPosition.x > 0)
{
//transform.Translate(direction * speed * Time.deltaTime);
if ((transform.position.y) > GameHandler.ball1.ballPosition.y && GameHandler.ball1.direction.y < 0)
{
transform.Translate(GameHandler.ball1.direction * speed * Time.deltaTime * Vector2.up);
}
if ((transform.position.y) < GameHandler.ball1.ballPosition.y && GameHandler.ball1.direction.y > 0)
{
transform.Translate(GameHandler.ball1.direction * speed * Time.deltaTime * Vector2.up);
}
}
}
void movePaddleonUserInput(string input)
{
// move = (-1 -> 1) * speed * timeDelta(this keep move framerate independent)
float move = Input.GetAxis(input) * speed * Time.deltaTime;
//Debug.Log((transform.position.y + height / 2) + " > "+ GameHandler.topRight.y+ "&&" + move +" > 0");
//Restrict paddle movement to to the screen
// (top edge of paddle > Screen top and we are moving up)
if ((transform.position.y + height / 2) > GameHandler.topRight.y && move > 0)
{
move = 0;
}
// (bottom edge of paddle < Screen top and we are moving down)
if ((transform.position.y - height / 2) < GameHandler.bottomLeft.y && move < 0)
{
move = 0;
}
transform.Translate(move * Vector2.up);
}
public void Init(bool isRightPaddle)
{
isRight = isRightPaddle;
Vector2 pos;
if (isRightPaddle)
{
isRight = isRightPaddle;
pos = new Vector2(GameHandler.topRight.x, 0);
// offset since center is the anchor
pos -= Vector2.right * transform.localScale.x;
input = "PaddleRight";
}
else
{
pos = new Vector2(GameHandler.bottomLeft.x, 0);
// offset since center is the anchor
pos += Vector2.right * transform.localScale.x;
input = "PaddleLeft";
}
transform.name = input;
transform.position = pos;
}
public IEnumerator serveAi()
{
yield return new WaitForSecondsRealtime (2f);
GameHandler.rallyOn = true;
GameHandler.isServingRight = false;
}
}
Now I also have score.cs and mainMenu scene , but no need to include that for this question, What I am struggling with now is the AI not stopping after it scores a point, I want the AI paddle to stop for a few seconds before it serves.
As you can see in the code, I added a yield return new WaitForSecondsRealtime(2f);
public IEnumerator serveAi()
{
yield return new WaitForSecondsRealtime (2f);
GameHandler.rallyOn = true;
GameHandler.isServingRight = false;
}
But this only cause the paddle to wait for the first time it scores , and then if it scores again in quick interval, it doesn't wait at all.
Not sure what I doing wrong here, thanks for your time.
The more plausible explanation to me would be that GameHandler.rallyOn and isServing* aren't all being reset to the correct values to prevent the serve from occurring before the coroutine even begins, or another check is needed somewhere to prevent a serve from occurring if all of the correct booleans aren't set. Try placing appropriate breakpoints and stepping through in a debugger.
You'd probably be better off using WaitForSeconds rather than WaitForSecondsRealtime, by the way, at least if there's a chance you might want to allow the game to be paused in the future.
Realized that the coroutine is called multiple times even before its finished due do it being called from update function in GameHandler.cs. Needed a way to check if the co-routine is already running and not start a new one from update if so, used the Game.isServing variable to do so:
public IEnumerator serveAi()
{
GameHandler.isServingRight = false; // coroutine started , no need to start new one
yield return new WaitForSecondsRealtime (2f);
GameHandler.rallyOn = true;
}
And my update in GameHandler is already checking for that variable before starting the coroutine:
private void Update()
{
if (isServingLeft)
{
ball1.transform.position = new Vector3(batLeft.transform.position.x + ball1.getRadius() * 2, batLeft.transform.position.y);
if (Input.GetKey(KeyCode.LeftControl))
{
rallyOn = true;
isServingLeft = false;
}
}
if (isServingRight)
{
ball1.transform.position = new Vector3(batRight.transform.position.x - ball1.getRadius() * 2, batRight.transform.position.y);
if (isTwoPlayers && Input.GetKey(KeyCode.RightControl))
{
rallyOn = true;
isServingRight = false;
}
else
{
StartCoroutine(batRight.serveAi());
}
}
}
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.
Im using Unity and have asked this on there forums however have not had any replies. Ive found this example from a plugin where I am trying to use the kinect to rotate an object using my right hand to rotate it right and the left to rotate left. I have managed to get the object to do this apart from it stops at each side of the object, but cant work out what part of the code is doing this.
Thanks
using UnityEngine;
using System.Collections;
using System;
public class GestureListener : MonoBehaviour, KinectGestures.GestureListenerInterface
{
// GUI Text to display the gesture messages.
public GUIText GestureInfo;
private bool raiselefthand;
private bool raiserighthand;
public bool IsSwipeLeft()
{
if(raiserighthand)
{
raiserighthand = false;
return true;
}
return false;
}
public bool IsSwipeRight()
{
if(raiselefthand)
{
raiselefthand = false;
return true;
}
return false;
}
public void UserDetected(uint userId, int userIndex)
{
// detect these user specific gestures
KinectManager manager = KinectManager.Instance;
manager.DetectGesture(userId, KinectGestures.Gestures.RaiseLeftHand);
manager.DetectGesture(userId, KinectGestures.Gestures.RaiseRightHand);
if(GestureInfo != null)
{
GestureInfo.GetComponent<GUIText>().text = "Swipe left or right to change the slides.";
}
}
public void UserLost(uint userId, int userIndex)
{
if(GestureInfo != null)
{
GestureInfo.GetComponent<GUIText>().text = string.Empty;
}
}
public void GestureInProgress(uint userId, int userIndex, KinectGestures.Gestures gesture,
float progress, KinectWrapper.NuiSkeletonPositionIndex joint, Vector3 screenPos)
{
// don't do anything here
}
public bool GestureCompleted (uint userId, int userIndex, KinectGestures.Gestures gesture,
KinectWrapper.NuiSkeletonPositionIndex joint, Vector3 screenPos)
{
string sGestureText = gesture + " detected";
if(GestureInfo != null)
{
GestureInfo.GetComponent<GUIText>().text = sGestureText;
}
if(gesture == KinectGestures.Gestures.RaiseRightHand)
raiserighthand = true;
else if(gesture == KinectGestures.Gestures.RaiseLeftHand)
raiselefthand = true;
return true;
}
public bool GestureCancelled (uint userId, int userIndex, KinectGestures.Gestures gesture,
KinectWrapper.NuiSkeletonPositionIndex joint)
{
// don't do anything here, just reset the gesture state
return true;
}
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PresentationScript : MonoBehaviour
{
public bool slideChangeWithGestures = true;
public bool slideChangeWithKeys = true;
public float spinSpeed = 5;
public bool autoChangeAlfterDelay = false;
public float slideChangeAfterDelay = 10;
public List<Texture> slideTextures;
public List<GameObject> horizontalSides;
// if the presentation cube is behind the user (true) or in front of the user (false)
public bool isBehindUser = false;
private int maxSides = 0;
private int maxTextures = 0;
private int side = 0;
private int tex = 0;
private bool isSpinning = false;
private float slideWaitUntil;
private Quaternion targetRotation;
private GestureListener gestureListener;
void Start()
{
// hide mouse cursor
Cursor.visible = false;
// calculate max slides and textures
maxSides = horizontalSides.Count;
maxTextures = slideTextures.Count;
// delay the first slide
slideWaitUntil = Time.realtimeSinceStartup + slideChangeAfterDelay;
targetRotation = transform.rotation;
isSpinning = false;
tex = 0;
side = 0;
if(horizontalSides[side] && horizontalSides[side].GetComponent<Renderer>())
{
horizontalSides[side].GetComponent<Renderer>().material.mainTexture = slideTextures[tex];
}
// get the gestures listener
gestureListener = Camera.main.GetComponent<GestureListener>();
}
void Update()
{
// dont run Update() if there is no user
KinectManager kinectManager = KinectManager.Instance;
if(autoChangeAlfterDelay && (!kinectManager || !kinectManager.IsInitialized() || !kinectManager.IsUserDetected()))
return;
if(!isSpinning)
{
if(slideChangeWithKeys)
{
if(Input.GetKeyDown(KeyCode.PageDown))
RotateToNext();
else if(Input.GetKeyDown(KeyCode.PageUp))
RotateToPrevious();
}
if(slideChangeWithGestures && gestureListener)
{
if(gestureListener.IsSwipeLeft())
RotateToNext();
else if(gestureListener.IsSwipeRight())
RotateToPrevious();
}
// check for automatic slide-change after a given delay time
if(autoChangeAlfterDelay && Time.realtimeSinceStartup >= slideWaitUntil)
{
RotateToNext();
}
}
else
{
// spin the presentation
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, spinSpeed * Time.deltaTime);
// check if transform reaches the target rotation. If yes - stop spinning
float deltaTargetX = Mathf.Abs(targetRotation.eulerAngles.x - transform.rotation.eulerAngles.x);
float deltaTargetY = Mathf.Abs(targetRotation.eulerAngles.y - transform.rotation.eulerAngles.y);
if(deltaTargetX < 1f && deltaTargetY < 1f)
{
// delay the slide
slideWaitUntil = Time.realtimeSinceStartup + slideChangeAfterDelay;
isSpinning = false;
}
}
}
private void RotateToNext()
{
// set the next texture slide
tex = (tex + 1) % maxTextures;
if(!isBehindUser)
{
side = (side + 1) % maxSides;
}
else
{
if(side <= 0)
side = maxSides - 1;
else
side -= 1;
}
if(horizontalSides[side] && horizontalSides[side].GetComponent<Renderer>())
{
horizontalSides[side].GetComponent<Renderer>().material.mainTexture = slideTextures[tex];
}
// rotate the presentation
float yawRotation = !isBehindUser ? 360f / maxSides : -360f / maxSides;
Vector3 rotateDegrees = new Vector3(0f, yawRotation, 0f);
targetRotation *= Quaternion.Euler(rotateDegrees);
isSpinning = true;
}
private void RotateToPrevious()
{
// set the previous texture slide
if(tex <= 0)
tex = maxTextures - 1;
else
tex -= 1;
if(!isBehindUser)
{
if(side <= 0)
side = maxSides - 1;
else
side -= 1;
}
else
{
side = (side + 1) % maxSides;
}
if(horizontalSides[side] && horizontalSides[side].GetComponent<Renderer>())
{
horizontalSides[side].GetComponent<Renderer>().material.mainTexture = slideTextures[tex];
}
// rotate the presentation
float yawRotation = !isBehindUser ? -360f / maxSides : 360f / maxSides;
Vector3 rotateDegrees = new Vector3(0f, yawRotation, 0f);
targetRotation *= Quaternion.Euler(rotateDegrees);
isSpinning = true;
}
}
I have managed to get the object to do this apart from it stops at each side of the object, but cant work out what part of the code is doing this.
I understand you ask for help finding which parts are involved in the rotation.
As such, I looked through the scripts and noticed 2 sections in the PresentationScript.
This part in the Update() method. Based on the code and the comments that are part of it.
// spin the presentation
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, spinSpeed * Time.deltaTime);
// check if transform reaches the target rotation. If yes - stop spinning
float deltaTargetX = Mathf.Abs(targetRotation.eulerAngles.x - transform.rotation.eulerAngles.x);
float deltaTargetY = Mathf.Abs(targetRotation.eulerAngles.y - transform.rotation.eulerAngles.y);
if(deltaTargetX < 1f && deltaTargetY < 1f)
{
// delay the slide
slideWaitUntil = Time.realtimeSinceStartup + slideChangeAfterDelay;
isSpinning = false;
}
This line in the Start() method is also involved.
targetRotation = transform.rotation;
A transform controls the position, scaling and as is relevant for this case, also the rotation of an object.
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.
Basically I have been given a project to develop a game in unity using c# and the device Leap Motion. The problem I am having is that I have a basic power bar that goes up and down through a range of 0 - 100, when the user releases his fist I want to shoot using the selected power. the problem I am having is I cannot figure out a way to set the bool "shoot" to true on release of the fists.
Controller m_leapController;
public GameObject CannonBarrel;
[Range(0, 100)] public float ballPower = 0f;
public bool increasing = true;
public bool shoot = false;
if (frame.Hands.Count >= 2)
{
Hand leftHand = GetLeftMostHand(frame);
Hand rightHand = GetRightMostHand(frame);
Vector3 handDiff = leftHand.PalmPosition.ToUnityScaled() - rightHand.PalmPosition.ToUnityScaled();
Vector3 newRot = CannonBarrel.transform.localRotation.eulerAngles;
float leftRightSpeed = handDiff.y * 5.0f;
float handPitch = leftHand.Direction.Pitch + rightHand.Direction.Pitch * 0.5f;
newRot.x = 0;
newRot.y = 90;
newRot.z = 70 + -handPitch * 20.0f;
shoot = true;
// if closed fist...
if (frame.Fingers.Count < 3)
{
leftRightSpeed = 0;
shoot = false;
if (increasing == true)
{
ballPower++;
if (ballPower >= 100)
{
increasing = false;
}
}
else if (increasing == false)
{
ballPower--;
if (ballPower <= 0)
{
increasing = true;
}
}
}
else
{
//Move left or right depending on hands height difference.
transform.parent.rigidbody.velocity = transform.parent.right * leftRightSpeed;
//Rotate the barrel
CannonBarrel.transform.localRotation = Quaternion.Slerp(CannonBarrel.transform.localRotation, Quaternion.Euler(newRot), 0.1f);
}
if (shoot == true)
{
Debug.Log("fired");
//add code here to spawn projectile
}
}
I hope that this code makes sense to you. All that is happening is if you're holding your fists above the Leap sensor the ball power will increase till you hit 100, then it will decrease back down to 0 and so on until you release your fists. I need a way to set the "shoot" bool to true once a power has been selected as the way I currently do it I can only set it to true either during the power selection or it gets set to true before you clench your fists.
Thanks in advance.
A somewhat simple way you could do this is by setting a bool to check if the player has clenched his fist at least once, thus setting the power. Let's call it hasClenchedFist:
if (frame.Hands.Count >= 2)
{
Hand leftHand = GetLeftMostHand(frame);
Hand rightHand = GetRightMostHand(frame);
Vector3 handDiff = leftHand.PalmPosition.ToUnityScaled() - rightHand.PalmPosition.ToUnityScaled();
Vector3 newRot = CannonBarrel.transform.localRotation.eulerAngles;
float leftRightSpeed = handDiff.y * 5.0f;
float handPitch = leftHand.Direction.Pitch + rightHand.Direction.Pitch * 0.5f;
newRot.x = 0;
newRot.y = 90;
newRot.z = 70 + -handPitch * 20.0f;
//shoot = true;
// if closed fist...
if (frame.Fingers.Count < 3)
{
leftRightSpeed = 0;
hasClenchedFist = true;
shoot = false;
if (increasing == true)
{
ballPower++;
if (ballPower >= 100)
{
increasing = false;
}
}
else if (increasing == false)
{
ballPower--;
if (ballPower <= 0)
{
increasing = true;
}
}
}
else if(hasClenchedFist)
{
shoot = true;
}
else
{
//Move left or right depending on hands height difference.
transform.parent.rigidbody.velocity = transform.parent.right * leftRightSpeed;
//Rotate the barrel
CannonBarrel.transform.localRotation = Quaternion.Slerp(CannonBarrel.transform.localRotation, Quaternion.Euler(newRot), 0.1f);
}
if (shoot == true)
{
Debug.Log("fired");
//add code here to spawn projectile
}
}
This should work, unless I've misunderstood the code/requirements.