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,
Related
I'm making a mobile game in Unity3D. And there I wanted to make it so that I could throw object along the curve with a swipe. If I do it with AddForce, it only flies in a straight line, and I want it to spin like in the photo. Help, please
In this case object flies in a straight line
void OnMouseDown()
{
//get soccer ball starting position when swiping
startPos = Input.mousePosition;
startPos.z = transform.position.z - Camera.main.transform.position.z;
startPos = Camera.main.ScreenToWorldPoint(startPos);
}
void OnMouseUp()
{
//get soccer ball end position when swipe finished
Vector3 endPos = Input.mousePosition;
endPos.z = transform.position.z - Camera.main.transform.position.z;
endPos = Camera.main.ScreenToWorldPoint(endPos);
//get the proper direction and z force
Vector3 force = endPos - startPos;
force.z = force.magnitude * 3;
force.x = force.x * 1.5f;
//check if you swiped (not just clicked) and if ball had not been fired already
if (startPos != endPos && GetComponent<Rigidbody>().isKinematic == true)
{
GetComponent<Rigidbody>().isKinematic = false;
GetComponent<Rigidbody>().AddForce(force * Force);
gameObject.transform.Rotate(new Vector3(0, 0, force.z / 3));
//destroy ball after some time and add a new one to shoot
StartCoroutine(destroyBall());
StartCoroutine(newBall());
}
}
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;
My code below works for detecting swipes and adding force to my game object. However, my aim is to let the user swipe only upwards(Can be up left, up right but never to down). I mean only if user swipes up, AddForce method will be called. When user swipes down nothing will happen. Is it possible to do that, if yes how?
public class SwipeScript : MonoBehaviour
{
Vector2 startPos, endPos, direction;
float touchTimeStart, touchTimeFinish, timeInterval;
[Range(0.05f, 1f)]
public float throwForse = 0.5f;
void Update()
{
if (Input.touchCount > 0 && Input.GetTouch (0).phase == TouchPhase.Began)
{
touchTimeStart = Time.time;
startPos = Input.GetTouch(0).position;
}
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Ended)
{
touchTimeFinish = Time.time;
timeInterval = touchTimeFinish - touchTimeStart;
endPos = Input.GetTouch(0).position;
direction = startPos - endPos;
GetComponent<Rigidbody2D>().AddForce(-direction / timeInterval * throwForse);
}
}
}
Why wouldn't you just clamp the direction to be nonnegative? For instance, before the last line add something like:
This would be for upward and right:
direction = Vector2.Max(direction, Vector2.Zero);
and this would be for all upward:
direction = new Vector2(direction.x, Math.Max(direction.y, 0));
and I suggest you use
direction = endPos - startPos;
instead of the other way as in your original code, as this way it represents that movement vector correctly. Then remove the negative sign in AddForce in front of the direction as well. Thus:
void Update()
{
if (Input.touchCount > 0 && Input.GetTouch (0).phase == TouchPhase.Began)
{
touchTimeStart = Time.time;
startPos = Input.GetTouch(0).position;
}
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Ended)
{
touchTimeFinish = Time.time;
timeInterval = touchTimeFinish - touchTimeStart;
endPos = Input.GetTouch(0).position;
Vector2 direction = endPos - startPos;
direction = new Vector2(direction.x, Math.Max(direction.y, 0));
GetComponent<Rigidbody2D>().AddForce(direction / timeInterval * throwForse);
}
}
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;
}
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;
}