How can i take only one finger swerve input - c#

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;

Related

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.

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;
}

unity screen delta to world delta

I want to convert the user touch delta position to delta position of gameobject in the world, is there a util to do that?
//drag
if (duringmove && (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved))
{
Debug.Log("will move " + progressTrans.gameObject.name);
endPos = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position);
//should convert te deltaPos here
progressTrans.localPosition += Vector3.right * Input.GetTouch(0).deltaPosition.x;
progressTrans.localPosition = new Vector3(Mathf.Clamp(progressTrans.localPosition.x, mostLeft, mostRight), progressTrans.localPosition.y, progressTrans.localPosition.z);
}
Note that I'm building a 2d game using a camera with Orthographic mode
Since it seems your object will not have its transform.position.z changing over time, you can use something like this:
//drag
if (duringmove && (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved))
{
Debug.Log("will move " + progressTrans.gameObject.name);
Vector3 touchPosition = Input.GetTouch(0).position;
touchPosition.z = progressTrans.position.z;
// World touch position
endPos = Camera.main.ScreenToWorldPoint(touchPosition);
// To local position
progressTrans.InverseTransformPoint(endPos);
endPos.x = Mathf.Clamp(endPos.x, mostLeft, mostRight);
endPos.y = progressTrans.localPosition.y;
endPos.z = progressTrans.localPosition.z;
// If you need the delta (to lerp on it, get speed, ...)
Vector3 deltaLocalPosition = endPos - progressTrans.localPosition;
progressTrans.localPosition = endPos;
}
Hope this helps,

Input.GetTouch(0).deltaPosition is moving 'faster' on axis

I have a function that moves a sprite relative to the finger position. I mean that the finger can touch any part of the screen and move the player sprite without moving the sprite to the finger position.
The issue that I have is that it's moving the sprite faster than the actual finger position:
Lets say that i have the finger at (0,0) and the sprite at (10,10); I move the finger 10 units on the X axis and I expect the sprite to move at (20,10), but it's actually moving more units than expected. Let's say it moved to (25,10).
I think it's related to the deltaPosition values. Here's the function (the transform in the arguments is the transform of the sprite that I'm moving):
private Vector2 MovePlayerRelativeToFinger(Transform transform)
{
Vector2 position = transform.position;
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
touchPosition = Input.GetTouch(0).deltaPosition;
position = new Vector2((touchPosition.x * Time.deltaTime) + transform.position.x, (touchPosition.y * Time.deltaTime) + transform.position.y);
return position;
}
else
{
return position;
}
}
Change the condition in outer-if statement to
Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Stationary
TouchPhase.Moved will be true during all the Update when the finger in moved and its deltaPosition will have value right from the point movement is started. So it gets added up into a huge value like, say, your touch started from x and you moved to x + 2 within the next Update. deltaPosition will be 2 during this Update. And if you have moved to x + 5 within the next Update, deltaPosition will be 5 instead of 3 as you want it to be.
If things have to be that accurate, try this
if(Input.touchCount > 0 &&Input.GetTouch(0).phase == TouchPhase.Moved) {
newDelta = Input.GetTouch(0).deltaPosition - oldDelta;
position = new Vector2((newDelta.x * Time.deltaTime) + transform.position.x, (newDelta.y * Time.deltaTime) + transform.position.y);
olDelta += Input.GetTouch(0).deltaPosition
return position;
}
Initialize oldDelta to 0
Based on your question, I'm not sure why you are including the time there. Or maybe you are missing the transform to world space? Does this work:
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
return (transform.position + (Vector3)((1.0f/100) * Input.GetTouch(0).deltaPosition));
}
else
{
return transform.position;
}

Rotating object breaks limitations/Bounds

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);
}
}

Categories