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);
}
}
Related
I was working on my 2D game in unity and i made this system to set the character to jump, and move around, but for some reason, i can only move mid air when i was moving on the ground, but if i stand still and then jump, i can not move mid air
Further Explaination: imagine you want to pick a coin in the air, and there is an obstacle under the coin, thus you have to jump while moving forward to pick it up, my problem is that you can only move mid air, if you were moving on the floor originally, but if you jump and THEN move right or left, the system just ignores it
My code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(CapsuleCollider2D))]
public class CharacterController2D : MonoBehaviour
{
// Move player in 2D space
public float maxSpeed = 3.4f;
public float jumpHeight = 6.5f;
public float gravityScale = 1.5f;
public Camera mainCamera;
bool facingRight = true;
float moveDirection = 0;
bool isGrounded = false;
Vector3 cameraPos;
Rigidbody2D r2d;
CapsuleCollider2D mainCollider;
Transform t;
// Use this for initialization
void Start()
{
t = transform;
r2d = GetComponent<Rigidbody2D>();
mainCollider = GetComponent<CapsuleCollider2D>();
r2d.freezeRotation = true;
r2d.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
r2d.gravityScale = gravityScale;
facingRight = t.localScale.x > 0;
if (mainCamera)
{
cameraPos = mainCamera.transform.position;
}
}
// Update is called once per frame
void Update()
{
// Movement controls
if ((Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D)) && (isGrounded || Mathf.Abs(r2d.velocity.x) > 0.01f))
{
moveDirection = Input.GetKey(KeyCode.A) ? -1 : 1;
}
else
{
if (isGrounded || r2d.velocity.magnitude < 0.01f)
{
moveDirection = 0;
}
}
// Change facing direction
if (moveDirection != 0)
{
if (moveDirection > 0 && !facingRight)
{
facingRight = true;
t.localScale = new Vector3(Mathf.Abs(t.localScale.x), t.localScale.y, transform.localScale.z);
}
if (moveDirection < 0 && facingRight)
{
facingRight = false;
t.localScale = new Vector3(-Mathf.Abs(t.localScale.x), t.localScale.y, t.localScale.z);
}
}
// Jumping
if (Input.GetKeyDown(KeyCode.W) && isGrounded)
{
r2d.velocity = new Vector2(r2d.velocity.x, jumpHeight);
}
// Camera follow
if (mainCamera)
{
mainCamera.transform.position = new Vector3(t.position.x, cameraPos.y, cameraPos.z);
}
}
void FixedUpdate()
{
Bounds colliderBounds = mainCollider.bounds;
float colliderRadius = mainCollider.size.x * 0.4f * Mathf.Abs(transform.localScale.x);
Vector3 groundCheckPos = colliderBounds.min + new Vector3(colliderBounds.size.x * 1f, colliderRadius * 0.9f, 0);
// Check if player is grounded
Collider2D[] colliders = Physics2D.OverlapCircleAll(groundCheckPos, colliderRadius);
//Check if any of the overlapping colliders are not player collider, if so, set isGrounded to true
isGrounded = false;
if (colliders.Length > 0)
{
for (int i = 0; i < colliders.Length; i++)
{
if (colliders[i] != mainCollider)
{
isGrounded = true;
break;
}
}
}
// Apply movement velocity
r2d.velocity = new Vector2((moveDirection) * maxSpeed, r2d.velocity.y);
// Simple debug
Debug.DrawLine(groundCheckPos, groundCheckPos - new Vector3(0, colliderRadius, 0), isGrounded ? Color.green : Color.red);
Debug.DrawLine(groundCheckPos, groundCheckPos - new Vector3(colliderRadius, 0, 0), isGrounded ? Color.green : Color.red);
}
}```
if ((Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D)) && (isGrounded || Mathf.Abs(r2d.velocity.x) > 0.01f))
In this line you check if you are grounded isGrounded or have some horizontal velocity Mathf.Abs(r2d.velocity.x) > 0.01f.
If you have any of those you give horizontal control.
So if you are moving horizontal en then jump Mathf.Abs(r2d.velocity.x) > 0.01f is true and gives you control in the air.
But if you are standing still and the jump isGrounded is false bequase you are in the air and Mathf.Abs(r2d.velocity.x) > 0.01f is false so you cant control your jump in the air.
If you always want control in the air remove the (isGrounded || Mathf.Abs(r2d.velocity.x) > 0.01f) check
my ball slows down when I press the screen, which is making the game go very slowly, When I let go the ball starts speeding up again because of gravity.
{
private Vector3 touchPosition;
private Rigidbody2D rb;
private Vector3 direction;
private float moveSpeed = 10;
// Use this for initialization
private void Start()
{
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
private void Update()
{
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
touchPosition = Camera.main.ScreenToWorldPoint(touch.position);
touchPosition.z = 0;
direction = (touchPosition - transform.position);
rb.velocity = new Vector2(direction.x, 0) * moveSpeed;
if (touch.phase == TouchPhase.Ended)
rb.velocity = Vector2.zero;
}
}
}
Each time you press the button, you're telling the RigidBody you want the y velocity to be 0, essentially stopping to ball from falling.
Try this instead:
private void Update ( )
{
if ( Input.touchCount > 0 )
{
Touch touch = Input.GetTouch ( 0 );
touchPosition = Camera.main.ScreenToWorldPoint ( touch.position );
touchPosition.z = 0;
direction = ( touchPosition - transform.position );
var v = rb.velocity;
v.x = direction.x * moveSpeed;
rb.velocity = v;
if ( touch.phase == TouchPhase.Ended )
rb.velocity = Vector2.zero;
}
}
Or, you could even just get the x component in the first place with:
var x = touchPosition.x - transform.position.x;
rb.velocity = new Vector2 ( x, rb.velocity.y );
Either way should do the trick. The second is slightly more performant.
If you are looking to maintaining the current velocity of the object(horizontaly), then
rb.velocity = new Vector2(direction.normalized.x* rb.velocity.x, 0);
You are currently resetting the velocity of the object to the movement speed to its basic speed (and the distance between your finger, which I dont believe you want). In addition, it is unclear if you have vertical movement with the 0 in y, which will stop the object's vertical movement. If you do you need the vertical movement to be maintain, you need to replace 0 with rb.velocity.y.
rb.velocity = new Vector2(direction.normalized.x* rb.velocity.x * moveSpeed, rb.velocity.y );
or
rb.velocity = new Vector2(direction.normalized.x * moveSpeed,1)* rb.velocity;
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 found this script: https://unity3d.college/2018/01/30/unity3d-ugui-hold-click-buttons/
I am using Vuforia btw.
I was wondering how to link it to my movement script.
using UnityEngine;
public class MyDragBehaviour : MonoBehaviour
{
void Update()
{
if (Input.touchCount == 1 && Input.GetTouch(0).phase ==
TouchPhase.Moved)
{
// create ray from the camera and passing through the touch
position:
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
// create a logical plane at this object's position
// and perpendicular to world Y:
Plane plane = new Plane(Vector3.up, transform.position);
float distance = 0; // this will return the distance from the camera
if (plane.Raycast(ray, out distance))
{ // if plane hit...
Vector3 pos = ray.GetPoint(distance); // get the point
transform.position = pos;
// pos has the position in the plane you've touched
}
}
}
}
The current movement script will move my object instantly to where the tap happened on screen. I would like to have it so that you have to long press the object before moving it to avoid having the object jump around on screen.
EDIT
using UnityEngine;
using UnityEngine.EventSystems;
public class MyDragBehaviour : MonoBehaviour
{
float pointerDownTimer = 0;
const float requiredHoldTime = 0.5f; //has to hold for 0.5 seconds
void Update()
{
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
pointerDownTimer += Time.deltaTime;
if (pointerDownTimer >= requiredHoldTime)
{
pointerDownTimer = 0;
if (!EventSystem.current.IsPointerOverGameObject())
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
// create ray from the camera and passing through the touch position:
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
// create a logical plane at this object's position
// and perpendicular to world Y:
Plane plane = new Plane(Vector3.up, transform.position);
float distance = 0; // this will return the distance from the camera
if (plane.Raycast(ray, out distance))
{ // if plane hit...
Vector3 pos = ray.GetPoint(distance); // get the point
transform.position = pos; // pos has the position in the plane you've touched
} //whatever happens when you click
}
}
else
{
pointerDownTimer = 0;
}
}
}
}
You just need to implement a simple timer, that increases when you press and resets when you release:
float pointerDownTimer = 0;
const float requiredHoldTime = 0.5f //has to hold for 0.5 seconds
void Update(){
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
pointerDownTimer += Time.deltaTime;
if (pointerDownTimer >= requiredHoldTime){
...... //whatever happens when you click
}
} else{
pointerDownTimer = 0;
}
}
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,