I am trying to get touch controls to work for iOS, the result i want is drag left to move left, drag right to move right and tap to jump. At the moment the functions do work but what happens is if i don't want to jump and just want to drag the character will jump first as soon as touch the screen. Is there a way i can get the result i want i have added my code below. I want the first touch to just register for the drag and when the player lifts and then taps or maybe double taps the character will jump. Thanks in advance.
//Mobile Touch Drag Controls
{
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
// Get movement of the finger since last frame
Vector2 touchDeltaPosition = Input.GetTouch(0).deltaPosition;
// Move object across X plane
transform.Translate(touchDeltaPosition.x * speed, 0, 0);
}
}
////Mobile Touch To Jump
{
if (Input.GetMouseButtonDown(0))
{
transform.Translate(Vector3.up * jumpForce * Time.deltaTime, Space.World);
}
When the button goes down, there is no way to know what the player wants to do. You should jump when the finger goes up instead.
An easy solution is to use a boolean to know if the player has moved before the finger was released. If yes, you don't want to jump
public class PlayerController // Or whatever name your class has
{
private bool _moved;
private void Update()
{
//Mobile Touch Drag Controls
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
// Get movement of the finger since last frame
Vector2 touchDeltaPosition = Input.GetTouch(0).deltaPosition;
// Move object across X plane
transform.Translate(touchDeltaPosition.x * speed, 0, 0);
_moved = true; // Remember that the player moved
}
////Mobile Touch To Jump
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Ended)
{
if(!_moved) // Only jump if we didnt move
{
transform.Translate(Vector3.up * jumpForce * Time.deltaTime, Space.World);
}
_moved = false;
}
}
}
I assume this code goes in your update function. This will work fine but here is a more elegant solution in case you want to try it:
Your class can implement IDragHandler and IEndDragHandler (among others, look for all possibilities that extend IEventSystemHandler)
When you implement these, you can override functions that get called when the user touches the screen, such as public void OnDrag(PointerEventData eventData)
With this you don't have to use the Update function anymore
Related
I'm trying to make a simple snake game on unity that can be played on a mobile device. I got to the point where the snakes head is able to move continuously in the direction that the user swiped in, but the movement of the snake's head is kind of choppy and I'd like it to be more fluid.
I have this in the public class:
Vector2 dir = Vector2.right;
My start function looks like this:
void Start() {
dragDistance = Screen.height * 5 / 100; //dragDistance is 5% height of the screen
InvokeRepeating("Move", 0.3f, 0.075f);
}
Inside of my Update function I have this:
if (Mathf.Abs(lp.x - fp.x) > dragDistance || Mathf.Abs(lp.y - fp.y) > dragDistance)
{//It's a drag
//check if the drag is vertical or horizontal
if (Mathf.Abs(lp.x - fp.x) > Mathf.Abs(lp.y - fp.y))
{ //If the horizontal movement is greater than the vertical movement...
if ((lp.x > fp.x)) //If the movement was to the right)
{ //Right swipe
dir = Vector2.right;
}
else
{ //Left swipe
dir = -Vector2.right;
}
}
else
{ //the vertical movement is greater than the horizontal movement
if (lp.y > fp.y) //If the movement was up
{ //Up swipe
dir = Vector2.up;
}
else
{ //Down swipe
dir = -Vector2.up;
}
}
}
And after the Update function I have:
void Move()
{
// Move head into new direction
transform.Translate(dir);
}
Thanks for your help.
This is simply due to how you are currently moving.
Using InvokeRepeating to move your character will be super choppy as you are just moving "forward" by a certain amount every time InvokeRepeating repeats.
You really shouldn't be using that to move your character. Typically you want to you some form of Rigidbody and physics to move. Perhaps try something like this :
public Rigidbody2D rb;
void FixedUpdate() {
rb.MovePosition((rb.position + dir) * Time.fixedDeltaTime);
}
That could be a very simple answer. Check out some more details on that method here.
Also just try looking up some tutorials on movement in Unity.
The problem is you aren't moving every frame. You could fix this by moving in the update() like so:
transform.Translate(dir * speed * Time.deltaTime);
Instead of doing InvokeRepeating and Move().
But what if you want to make the snake stop at each square like it does now, but move smoothly from square to square? You would need to save the snake's next position in your move() function and then smoothly move the snake there in Update(). It would look something like this:
void Move()
{
// Move head to next position
prevSquare = transform.position; // keep track of the previous square
// so we can use lerp() later on
nextSquare = transform.position + dir;
progress = 0; // progress is the percent distance between the two squares
isMoving = true; //we probably need isMoving so we can decide what to do if they
//swipe while snake is moving, I'll leave it to you to figure
//out
}
update()
{
...
if(isMoving)
{
progress = progress + time.deltaTime;
transform.position = Vector2.Lerp(prevSquare, nextSquare, progress);
if(progress > 1.0f)
{
isMoving = false;
}
}
}
I am developing a game where objects fall from the top of the screen, and you try to and tap on them before they reach the bottom of the screen. The code works fine when I played the game in the editor (with the touch controls swapped to mouse controls), except when I run the game on a phone, the game only seems to register a successful hit if you tap slightly in front of the object in the direction that it is traveling, and does not register a hit if you tap towards the back end or center of the object. I have built and ran the game over 10 times now, each time trying to fix this issue but nothing seems to help. My theory at the moment is that my code for the touch controls have too much going on and/ or have redundancies and by the time it checks whether or not an object is at the position of the touch, the object has moved to a different location. Any thoughts on why the hit boxes are off, and is there a better way to do hit detection with touch screen?
void FixedUpdate()
{
if (IsTouch())
{
CheckTouch(GetTouchPosition());
}
}
// Returns true if the screen is touched
public static bool IsTouch()
{
if (Input.touchCount > 0)
{
if (Input.GetTouch(0).phase == TouchPhase.Began)
{
return true;
}
}
return false;
}
// Gets the position the touch
private Vector2 GetTouchPosition()
{
Vector2 touchPos = new Vector2(0f, 0f);
Touch touch = Input.GetTouch(0);
if (Input.GetTouch(0).phase == TouchPhase.Began)
{
touchPos = touch.position;
}
return touchPos;
}
// Checks the position of the touch and destroys the ball if the
ball is touched
private void CheckTouch(Vector2 touchPos)
{
RaycastHit2D hit =
Physics2D.Raycast(Camera.main.ScreenToWorldPoint(
(Input.GetTouch(0).position)), Vector2.zero);
if (hit.collider != null)
{
destroy(hit.collider.gameObject);
}
}
Ok, I have a 3D Unity game built for iOS where these controls will be implemented (in a first person shooter manner):
Camera follows just behind character
Keeping finger on screen makes character walk, finger up makes it stop
THIS is the problem: wherever the user has started touching the screen, whatever direction they drag (forward, backwards, left right) the character will walk indefinitely in that direction.
This must have done already but I cant find anything anywhere. I have the camera as a child of the character, so that's taken care of but for char movement all I have is:
void Update()
{
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
Vector3 target = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -10f));
transform.Translate(Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime) - transform.position);
}
Which doesn't work as needed. What can I do here?
You can just try to use any king of UI joysticks (https://assetstore.unity.com/packages/tools/input-management/joystick-pack-107631) for example
And access its parameters something like this:
public class TestMovement : MonoBehaviour
{
public float Speed;
public FloatingJoystick Joystick; //Set in the inspector, prefab from the asset
private void Update()
{
transform.Translate(new Vector2(Joystick.Horizontal, Joystick.Vertical) * Speed * Time.deltaTime);
}
}
I am making a game where an object follows my finger. But I do not want the object to jump to my finger if I tap anywhere on the screen. I am using a raycast to do this. It works perfectly except if I move my finger too fast the object freezes.
My theory is that because of it being in Update() that once I move my finger off the object it detects that I can no longer drag it. I have no idea how to fix this.
public GameObject Bigball;
public GameObject Smallball;
// Update is called once per frame
private void Update ()
{
// lets ball follow finger
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved || Input.GetTouch(0).phase == TouchPhase.Stationary)
{
var touch = Input.GetTouch(0);
Vector3 fingerPos = touch.position;
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(fingerPos);
if (Physics.Raycast(ray, out hit) && (hit.collider.tag == "ball"))
{
Smallball.SetActive(false);
Bigball.SetActive(true);
Vector3 pos = new Vector3((Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position)).x,
(Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position)).y, 0);
Bigball.transform.position = pos;
}
}
else
{
Bigball.SetActive(false);
Smallball.SetActive(true);
Smallball.transform.position = new Vector3(Bigball.transform.position.x, Bigball.transform.position.y, 0);
}
}
It might make more sense to only Raycast once when the finger touches the screen. If that Raycast finds an object to drag, simply store that object and move it to follow the finger every Update(), as long as the finger continues to touch the screen.
Once the finger is released, just forget about the object until the next touch finds a new object to drag.
An alternative would be to use no Raycasts at all but build on IBeginDragHandler, IDragHandler and IEndDragHandler, as Bijan already mentioned.
I am currently trying to implement a wheel shaped menu for an android game.
The menu should rotate when the user moves his finger up or down the y axis of his screen.
Swipe up --> rotate counter clockwise
Swipe down --> rotate clockwise
I am able to rotate the menu via touch input, but the problem is the following:
Every time the finger touches the display, the rotation is reset.
E.g. You can swipe up, the menu rotates and stays in place. But when you want to swipe up again to rotate even further, the rotation resets and starts over.
I guess the problem is very simple, but I tried a lot of stuff and nothing ever changes.
My code:
class{
// ...
void Update(){
if(Input.touches.Length > 0 && Input.GetTouch(0).phase == TouchPhase.Moved){
transform.rotation *= Quaternion.AngleAxis(Input.GetTouch(0).deltaPosition.y, Vector3.forward);
}
}
}
Thanks in advance!
You need a variable to store the current rotation and simply add the change in angle each update.
public float angle;
void Update() {
if ( Input.touches.Length > 0 && Input.GetTouch(0).phase == TouchPhase.Moved ) {
angle += Input.GetTouch(0).deltaPosition.y;
transform.rotation *= Quaternion.AngleAxis( angle, Vector3.forward );
}
}