Rotating object breaks limitations/Bounds - c#

I currently use the following logic to rotate an object around another object and set conditions to not exceed 90 degrees or under 1.
protected void RotationBounds(){
bRotateDown = true;
bRotateUp = true;
if (_CannonTube.transform.rotation.eulerAngles.z >= 90)
bRotateUp = false;
if (_CannonTube.transform.rotation.eulerAngles.z <= 1)
bRotateDown = false;
}
This allows me to stop the the rotation in a direction once the condition is hit. I then apply the rotation with the following mouse movement controls:
protected void RotateCannonMouse(){
if (Input.GetKey ("mouse 0")) {
if (Input.GetAxis ("Mouse Y") > 0 && bRotateUp == true && bRotateDown == true
|| Input.GetAxis ("Mouse Y") > 0 && bRotateUp == false && bRotateDown == true) {
transform.RotateAround (_SphereBase.position, -transform.forward,
Input.GetAxis ("Mouse Y") * 15);
}
if (Input.GetAxis ("Mouse Y") < 0 && bRotateUp == true && bRotateDown == true
|| Input.GetAxis ("Mouse Y") < 0 && bRotateUp == true && bRotateDown == false) {
transform.RotateAround (_SphereBase.position, -transform.forward,
Input.GetAxis ("Mouse Y") * 15);
}
}
The following functons are then called in the update method.
void Update () {
RotateCannonMouse();
RotationBounds();
}
My question/problem is that if i move rotate the object at a slow/ medium speed the conditions hit and it does as i expect. If i rotate the object fast it will break through the conditions and mess up the rotation. Has anybody came across this problem before? I was thinking maybe the update method isn't iterating quick enough or that i'm rotating the object so fast it skips the bounds values?
Thanks in advanced

collect all your data, do all your calculations and checks and only then finally rotate if neccessary. you are doing it kinda backwards. you first rotate and then try to clean up the mess. you sure can make it work, but its the hardest way for no reason or gain.
just store the return value of Input.GetAxis("Mouse Y"), add it to your calculated (or even also stored) current rotation, check if its within the bounds (or clamp it) and then, in the very end, decide if and how far you rotate.
(also you dont need to do == with a boolean, its either true or false already -> if(abool == true) is the same as if(abool) and if(abool == false) is the same as if(!abool). ofc it is not wrong, it just makes it hard to read)

This is the solution we found
//If mouse has been clicked
if (Input.GetKey ("mouse 0")) {
//Get the angle the mouse wants to move by
float mouseY = Input.GetAxis ("Mouse Y") * 5;
//If the angle is not 0 then we see how much it can move by
if (mouseY != 0) {
//Get the current angle of the cannon
float currentAngle = _CannonTube.transform.rotation.eulerAngles.z;
//Work out the new angle that the cannon will go to
float calcAngle = 0.0f;
if (mouseY < 0) {
calcAngle = currentAngle - mouseY;
} else {
calcAngle = currentAngle - mouseY;
}
//Clamp calcAngle between 5 and 85
var newAngle = Mathf.Clamp (calcAngle, 5, 85);
//calcualte the angle that we are allowed to move by.
//for example if current angle is 85 and new angle is 85 from the clamp then we move 0 degrees
var rotateAngle = currentAngle - newAngle;
//rotate the cannon
transform.RotateAround (_SphereBase.position, -transform.forward, rotateAngle);
}
}

Related

How can i take only one finger swerve input

My project is a Runner game where character constantly moves forward and if player slides left or right, character moves that position too. But in mobile, if i slide with one finger and touch with my other finger in the mean time, character starts to take 2 inputs and move wrong directions.
This is my code below:
private void Update(){
float newz = transform.position.z + movementSpeed * Time.deltaTime;
float newx = 0, swipeDelta = 0;
if(Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
swipeDelta = Input.GetTouch(0).deltaPosition.x / Screen.width;
}
newx = transform.position.x + swipeDelta * 5f *Time.deltaTime;
transform.position = new Vector3(newx, transform.position.y, newz);
}
I have set Input.touchCount to 1 because i want it to get only 1 finger input but it did not work. What should i do to make it work with one finger and make it accurate?
From your code it seems that as soon as you touch with second finger it will just not read location of your first finger.
Input.touchCount == 1 in your if statement looks like a problem for me, it means that if statement will only execute if you have a single finger on your screen. if you change it to Input.touchCount > 0 it will execute even if there are more fingers on the screens and should work correctly since you are already only taking one input with Input.GetTouch(0).
private void Update(){
float newz = transform.position.z + movementSpeed * Time.deltaTime;
float newx = 0, swipeDelta = 0;
if(Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
swipeDelta = Input.GetTouch(0).deltaPosition.x / Screen.width;
}
newx = transform.position.x + swipeDelta * 5f *Time.deltaTime;
transform.position = new Vector3(newx, transform.position.y, newz);
}
Changing Input.touchCount == 1 to Input.touchCount > 0 did not solve my problem.
I solved it by adding this line of code in the PlayerController script's Start function:
Input.multiTouchEnabled = false;

Flipping sprite by y axis when rotated past a certain point

I'm new to unity, and an amateur C# user. I have a submarine sprite that I would like to flip by the y-axis when rotated more than 90 degrees, and less than -90 degrees so that it won't be upside down. It's rotated by mouse movement which I'll give the code if necessary. I'm not sure why but, this doesn't seem to work. Any help would be appreciated!
Code:(rot90 is a bool)
if (transform.rotation.z > 90 & transform.rotation.z >-90)
{
rot90 = false;
}
if (transform.rotation.z < 90 & transform.rotation.z < -90)
{
rot90 = true;
}
if (rot90 == true)
{
Vector3 scale = transform.localScale;
scale.y = -22;
transform.localScale = scale;
}
if (rot90 == false)
{
Vector3 scale = transform.localScale;
scale.y = 22;
transform.localScale = scale;
}
transform.rotation is a Quaternion!
A Quaternion has not only 3 but 4 component x, y, z and w, and they all move in ranges between -1 and 1.
Your conditions can never become true!
You could use the eulerAngles and do e.g.
var zAngle = transform.eulerAngles.z;
// clean out the angle to a value between -180 and +180
while(zAngle > 180) zAngle -= 360;
while(zAngle < -180) zAngle += 360;
Vector3 scale = transform.localScale;
scale.y = Mathf.Abs(zAngle) > 90 ? -22 : 22;
transform.localScale = scale;
in case you are using a SpriteRenderer component you should rather go for SpriteRenderer.flipY
var zAngle = transform.eulerAngles.z;
// clean out the angle to a value between -180 and +180
while(zAngle > 180) zAngle -= 360;
while(zAngle < -180) zAngle += 360;
// You should of course rather cache this reference e.g. in Awake only once
// and then reuse it here
GetComponent<SpriteRenderer>().flipY = Mathf.Abs(zAngle) > 90;

How to make Input.GetAxis("Mouse X") get a value even when the mouse is stopped?

I'm controlling a game object using my mouse horizontally. The object moves left or right depending on the mouse position but as soon as the mouse stops moving the object stops too. What I want is if I drag the mouse to the right side of the screen and stop moving the mouse, then I want the object to move to the right as long as the mouse is in the right half of the screen and vice versa. Currently Input.GetAxis("Mouse X"); returns a 0 value when stopped. Here's the code:
float horizontalInput = Input.GetAxis("Mouse X");
I've already tried using this fix but I don't get the desired result as im using mousebuttondown to move my player in the forward direction.
if (Input.GetMouseButton(0) && (horizontalInput == 0f))
{
if (Input.mousePosition.x < Screen.width / 2)
{
horizontalInput = -1f;
}
else if (Input.mousePosition.x > Screen.width / 2)
{
horizontalInput = 1f;
}
}
Is there any other way I can achieve this? Thank you so much for your time!
I fixed it by tweaking the above if conditions to this!
if (Input.GetMouseButton(0))
{
if (Input.mousePosition.x < Screen.width / 2 && (horizontalInput < 0f))
{
horizontalInput = -1f;
}
if (Input.mousePosition.x > Screen.width / 2 && (horizontalInput > 0f))
{
horizontalInput = 1f;
}
}
Just check horizontalInput while is not 0 and store the last position each time (just override it each times).
When it's 0 check the last stored position and if it's on the right side continue to move the object until the horizontalInput is 0 or you reach the screen edge.
Of course in the Update you don't even need to loop with a while, you just need to check if horizontalInput is 0 or is not 0.
something like this:
float horizontalInput = Input.GetAxis("Mouse X");
float lastPos = 0f;
if(horizontalInput != 0){
//move object with the mouse the code you currently use
lastPos = Input.mousePosition.x;
}else if(horizontalInput == 0 && !EndOfScreen(currentObjectPos.x) && RighLeftScreen(lastPos)){
Move();
}
of course this is just pseudocode. You will need to write a function to know if EndOfScreen right of left, you will need to know if you are on the right or left side of the screen (RighLeftScreen method) and finally you need to move your object Left or Right to the end of the screen.
This is just the basic idea.

Player is no longer moving left

Wanted to flip the firePoint and now the player is no longer moving left.
void Update()
{
float h = Input.GetAxis("Horizontal");
transform.Translate(Vector3.right * h * movSpeed * Time.deltaTime);
if (h > 0 && !facingRight)
{
Flip();
}
else if (h < 0 && facingRight)
{
Flip();
}
}
private void Flip()
{
facingRight = !facingRight;
transform.Rotate(0f, 180f, 0f);
}
Transform.Translate moves in a coordinate space relative to the object if you don't use its second parameter. Since you're flipping it, you're always moving the same direction. The linked documentation states:
If relativeTo is left out or set to Space.Self the movement is applied
relative to the transform's local axes.
You want to use Space.World as the second argument (default is Space.Self):
transform.Translate(Vector3.right * h * movSpeed * Time.deltaTime, Space.World);

Why Coroutine Method Work Only Once Unity3D

I have an object that I want to move by swipe, for example when the swipe is up the object should move forward smoothly from point A to point B, swipe right the object move smoothly to the right etc...
To do that, I've tried Lerp, MoveTowards and SmoothDamp but every time the object just disappear from point A and appear on point B instantly.
So I used coroutine to give a time to the movement, and as you can see in the code bellow, there's 4 coroutine methods, each one is for a direction. the problem I have is that when playing, the first movement work properly, but in the second swipe the object didn't reach the destination point, and the third one also and the object have some weird movements.
Can you tell me what's wrong in my code?
Here's the Coroutine methods for movements:
public IEnumerator MoveForward()
{
Vector3 DestinationF = new Vector3(transform.position.x, transform.position.y, transform.position.z + DistanceF);
while (Vector3.Distance(transform.localPosition, DestinationF) > 0)
{
float totalMovementTimeF = 0.3f;
float currentMovementTimeF = 0f;
currentMovementTimeF += Time.deltaTime;
transform.localPosition = Vector3.Lerp(transform.position, DestinationF, currentMovementTimeF / totalMovementTimeF);
yield return null;
}
}
public IEnumerator MoveBackward()
{
Vector3 DestinationB = new Vector3(transform.position.x, transform.position.y, transform.position.z - DistanceB);
while (Vector3.Distance(transform.localPosition, DestinationB) > 0)
{
float totalMovementTimeB = 0.3f;
float currentMovementTimeB = 0f;
currentMovementTimeB += Time.deltaTime;
transform.localPosition = Vector3.Lerp(transform.position, DestinationB, currentMovementTimeB / totalMovementTimeB);
yield return null;
}
}
and there is still 2 coroutine methods MoveRight() and MoveLeft().
And here's the code for the swipe directions:
if (Input.GetMouseButtonDown(0))
{
//save began touch 2d point
firstPressPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y);
}
if (Input.GetMouseButtonUp(0))
{
//save ended touch 2d point
secondPressPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y);
//create vector from the two points
currentSwipe = new Vector3(secondPressPos.x - firstPressPos.x, secondPressPos.y - firstPressPos.y);
//normalize the 2d vector
currentSwipe.Normalize();
// swipe up
if (currentSwipe.y > 0 && currentSwipe.x > -0.5f && currentSwipe.x < 0.5f)
{
StartCoroutine(MoveForward());
}
// swipe down
if (currentSwipe.y < 0 && currentSwipe.x > -0.5f && currentSwipe.x < 0.5f)
{
StartCoroutine(MoveBackward());
}
//swipe left
if (currentSwipe.x < 0 && currentSwipe.y > -0.5f && currentSwipe.y < 0.5f)
{
StartCoroutine(MoveLeft());
}
//swipe right
if (currentSwipe.x > 0 && currentSwipe.y > -0.5f && currentSwipe.y < 0.5f)
{
StartCoroutine(MoveRight());
}
}
Your first Coroutine works because:
Vector3 DestinationF = new Vector3(transform.position.x, transform.position.y, transform.position.z + DistanceF);
will result in a positive position, so, the distance will be greater than 0:
while (Vector3.Distance(transform.localPosition, DestinationF) > 0)
On the other hand, while subtracting the distanceB from the z value:
Vector3 DestinationB = new Vector3(transform.position.x, transform.position.y, transform.position.z - DistanceB);
may result in a negative value, therefore:
while (Vector3.Distance(transform.localPosition, DestinationB) > 0)
will start as < 0, so the condition is never met. Check your condition. Do you want absolute values, or not equal to 0?
The issue is that you never reach the target.
Your lerping with factor
currentMovementTimeF / totalMovementTimeF
makes not much sense since you reset it every frame to
var currentMovementTimeF = Time.deltaTime;
which in the most cases will be < 0.3f (this would mean you have only about 3 frames per second) so it will always be
currentMovementTimeF < totalMovementTimeF
and therefore
currentMovementTimeF / totalMovementTimeF < 1
So you always start a new interpolation between the current position and the target. So the distance gets smaller and smaller but literally never reaches the final position actually (though it seems so).
Additionally you mixed position and localPosition there so if the GameObject is not on root level it gets even worse!
What you would want instead is probebly either using MoveTowards with a certain speed. (position based)
// adjust these in the Inspector
public float speed;
public float MoveDistance;
public IEnumerator Move(Vector3 direction)
{
var destinaton = transform.position + direction * MoveDistance;
while (Vector3.Distance(transform.position, destinaton) > 0)
{
transform.position = Vector3.MoveTowards(transform.position, MoveDistance, Time.deltaTime* speed);
yield return null;
}
}
MoveTowards makes sure there is no overshooting.
or using Lerp (time based) like
// adjust these in the Inspector
public float totalMovementTime = 0.3f;
public float MoveDistance;
public IEnumerator Move(Vector3 direction)
{
var originalPosition = transform.position;
var destination = transform.position + direction * MoveDistance;
// here you could make it even more accurate
// by moving always with the same speed
// regardless how far the object is from the target
//var moveDuration = totalMovementTime * Vector3.Distance(transform.position, destinaton);
// and than replacing totalMovementTime with moveDuration
var currentDuration = 0.0f;
while (currentDuration < totalMovementTime)
{
transform.position = Vector3.Lerp(originalPosition, destination, currentDuration / totalMovementTime);
currentDuration += Time.deltaTime;
yield return null;
}
// to be really sure set a fixed position in the end
transform.position = destinaton;
}
Another issue is that you currently still could start two concurrent coroutines which leeds to strange behaviours. You rather should either interrupt the coroutines everytime you start a new one like
if (currentSwipe.y > 0 && currentSwipe.x > -0.5f && currentSwipe.x < 0.5f)
{
// stop all current routines
stopAllCoroutines();
StartCoroutine(MoveForward());
}
or add a flag to have only one routine running and ignore input in the meantime:
private bool isSwiping;
public IEnumerator MoveForward()
{
if(isSwiping)
{
Debug.LogWarning("Already swiping -> ignored", this);
yield break;
}
isSwiping = true;
//...
isSwiping = false;
}

Categories