Long tap on the screen for character movement unity - c#

i want to implement the long tap on the screen..If the user long taps on the screen the position of the x and y axis decreases and as soon as he releases the tap the x increases and the y decreases. i have messed around with somethings but no luck..heres the code i have been trying.
public class move : MonoBehaviour
{
public Vector2 velocity = new Vector2(40,40);
public float forwardspeed=0.02f;
Vector2 movement;
// Use this for initialization
void Start ()
{
Debug.Log("start+"+Input.touchCount);
movement.x+=(forwardspeed);
movement.y-=(forwardspeed);
rigidbody2D.velocity = movement;
}
// Update is called once per frame
void FixedUpdate ()
{
int i=0;
while (i < Input.touchCount)
{
// Is this the beginning phase of the touch?
if (Input.GetTouch(i).phase == TouchPhase.Began)
{
Debug.Log("input touch count"+Input.touchCount);
// rigidbody2D.gravityScale =0;
movement.x+=(forwardspeed);
movement.y+=(forwardspeed);
rigidbody2D.velocity = movement;
}
else if (Input.GetTouch(i).phase == TouchPhase.Ended)
{
movement.x += (forwardspeed);
movement.y -= (forwardspeed);
rigidbody2D.velocity = movement;
}
++i;
}
}
}

You can use Input.touchCount to do exactly what you need with very simple code.
If you want some behavior to occur while the user is touching the screen, that means you want the behavior to occur while Input.touchCount is non-zero. For example,
void FixedUpdate() {
if(Input.touchCount > 0) { //user is touching the screen with one or more fingers
//do something
} else { //user is not currently touching the screen
//do something else
}
}
Specific to your code, you would want to set the character's velocity to some value while Input.touchCount is zero, and then set it to a different value while it's not zero.
void FixedUpdate() {
if(Input.touchCount > 0) {
rigidbody2D.velocity = new Vector2(forwardspeed, forwardspeed);
} else {
rigidbody2D.velocity = new Vector2(forwardspeed, -forwardspeed);
}
}
Note the negative sign in the else block. Instead of adding and subtracting values like you were doing before, we're just setting the velocity to +/- depending on the state.

Related

Is there a way for skip Vector2.Lerp to reach 0

I have a scene that contains a square sprite in the middle at 0,0,0 position as player and two more square sprites on top and bottom of it with some space between all.
I want the player start to move when I press the up arrow ker or the down arrow key. When the player hits one of those squares on top or the bottom the player must return back to it's first position.
{
Rigidbody2D playerRb;
float axisY = 0f;
bool canMove = true;
public float playerSpeed = 0f;
public float returnSpeed = 0f;
public float speed = 1f;
void Start()
{
playerRb = GetComponent<Rigidbody2D>();
}
//decides to which axis player gonna move to
private void Update()
{
if (Input.anyKey)
{
axisY = Input.GetAxisRaw("Vertical");
}
}
private void FixedUpdate()
{
PlayerMovement(axisY, playerSpeed);
}
//give velocity to player depending on axis
void PlayerMovement(float axis, float speed)
{
switch (axisY)
{
case 1:
if (canMove)
{
canMove = false;
playerRb.velocity = new Vector2(0, axis * speed);
}
break;
case -1:
if (canMove)
{
canMove = false;
playerRb.velocity = new Vector2(0, axis * speed);
}
break;
default:
//do nothing
break;
}
}
//return back after hit
private void OnCollisionEnter2D(Collision2D collision)
{
axisY = 0;
playerRb.velocity = Vector2.zero;
//the code below is my way to get back before i decide to lerp
//playerRb.MovePosition(Vector2.MoveTowards(playerRb.position,Vector2.zero,returnSpeed));
StopAllCoroutines();
StartCoroutine(BackToZero());
}
IEnumerator BackToZero()
{
float t = 0;
while (t < 1)
{
t += Time.deltaTime;
playerRb.MovePosition(Vector2.Lerp(playerRb.position, Vector2.zero, returnSpeed));
yield return null;
}
canMove = true;
}
}
I managed to achieve this with my code but the problem is when the lerp gets closer to 0 it is taking more time to reach there. I have to wait for one second to reach to the position and my canMove bool value gets back to true.
How can I skip this and when my object position is close to 0 point snap it to there?
The problem come from the fact you use playerRb.position as the start position of your Lerp in your coroutine.
To fix this, I recommend you to buffer the playerRb.position in a member variable at the OnCollisionEnter() method right before starting your coroutine and use this buffered position in your Lerp as start position.
This way you should have a nice linear movement at a sweet linear speed without the "damping"
Hope that helped ;)
Here is the solution. When player object's position is too close to the starting position you will detect it then snap it to the position immediately by
playerRb.MovePosition().
Then enable player to move right after by setting canMove bool to true.
private void OnCollisionEnter2D(Collision2D collision)
{
axisY = 0;
playerRb.velocity = Vector2.zero;
Vector2 playerCurrentPosition = playerRb.position;
//playerRb.MovePosition(Vector2.MoveTowards(playerRb.position,Vector2.zero,returnSpeed));
StopAllCoroutines();
StartCoroutine(BackToZero(playerCurrentPosition));
}
IEnumerator BackToZero(Vector2 playerStartPos)
{
float t = 0;
while (t < 1)
{
t += Time.deltaTime;
if (Mathf.Round(Mathf.Abs(playerRb.position.y*2))==0)
{
playerRb.MovePosition(Vector2.zero);
canMove = true;
StopAllCoroutines();
}
else
playerRb.MovePosition(Vector2.Lerp(playerRb.position, Vector2.zero, returnSpeed));
yield return null;
}
canMove = true;
}
}
Try Vector3.MoveTowards(); in a while loop until you have reached your target destination

How to make a wallrun in unity

I`m trying to make a wallrun in unity like the wallrun of the Prince of Persia.
The idea is that when you are touching the wall and you press "left shift" the player would run some seconds without falling.
I would like to add that if you press "enter" the character would jump to the side using the Wall as support for impulse.
You can watch this in the first minutes of the video:
https://www.youtube.com/watch?v=zsnB7HEiLr0
I have made this script for the character controller:
public Transform playerPosition;
//controls the x movement. (right/left)
public float horizontalmove;
//controls the y movement. (forward/back)
public float verticalmove;
//controls the movement direction.
private Vector3 playerInput;
//Here I store my character controller.
public CharacterController player;
//controls the player speed.
public float playerSpeed;
//controls de movement direction according to camera
public Vector3 movePlayer;
//controls the last movement
public Vector3 lastMovePlayer;
public float gravity = 9.8f;
public float fallVelocity;
public float jumpForce = 5.0f;
public float verticalSpeed;
private RaycastHit HitR;
private RaycastHit HitL;
//Here I store the main camera
public Camera mainCamera;
//It stores the camera direction when the player is looking forward.
private Vector3 camForward;
//It stores the camera direction when the player is looking right.
private Vector3 camRight;
//Checks
//The meaning of Caida is fall.
public bool Caida;
//The meaning of salto is jump.
public bool Salto;
public bool Wallrun;
public bool WallrunCount;
// Start is called befoe the first frame update
void Start()
{
//i store the character controler.
player = GetComponent<CharacterController>();
Caida = true;
}
// Update is called once per frame
void Update()
{
if (Wallrun == true)
{
Caida = false;
}
if (Salto == true)
{
fallVelocity -= gravity * Time.deltaTime;
movePlayer = lastMovePlayer;
}
if (Caida == true)
{
fallVelocity -= gravity * Time.deltaTime;
}
if (player.isGrounded && Wallrun == false)
{
Caida = false;
Salto = false;
WallrunCount = false;
//I assign the horizontal move to the w and s keys.
horizontalmove = Input.GetAxis("Horizontal");
//I assign the vertical move to the a and d keys.)
verticalmove = Input.GetAxis("Vertical");
//controls the movement direction
playerInput = new Vector3(horizontalmove, 0, verticalmove);
//limits the player speed. With this method if teh player waalks in diagonal doesn´t
//exceed the speed limit.
playerInput = Vector3.ClampMagnitude(playerInput, 1);
// It calls the function that give the camera look direction.
camDirection();
//Here, It`s calculates the player movement considering the camera point and the movement
//we have assing to teh player earlier
//With this method the player always moves looking to the camera
movePlayer = playerInput.x * camRight + playerInput.z * camForward;
//The movement * the speed we want.
movePlayer = movePlayer * playerSpeed;
//we are going to say to the player where is looking at.
player.transform.LookAt(player.transform.position + movePlayer);
//It gives the gravity to the player.
fallVelocity = -gravity * Time.deltaTime;
if (Input.GetKeyDown(KeyCode.Space))
{
Salto = true;
fallVelocity = jumpForce;
}
}
else if (!player.isGrounded && Salto == false && Wallrun == false)
{
Caida = true;
}
movePlayer.y = fallVelocity;
//we give the movement to th eplayer.
player.Move(movePlayer * Time.deltaTime);
lastMovePlayer = movePlayer;
}
private void OnTriggerStay(Collider other)
{
if (Input.GetKeyDown(KeyCode.LeftShift) && Wallrun == false && WallrunCount == false)
{
if (Input.GetKey("w"))
{
Wallrun = true;
WallrunCount = true;
fallVelocity = 5f;
movePlayer.y = fallVelocity;
movePlayer.z = movePlayer.z * 1.6f;
if (Physics.Raycast(transform.position, transform.right, out HitR, 1))
{
movePlayer.x = 1.6f;
}
else if (Physics.Raycast(transform.position, -transform.right, out HitL, 1))
{
movePlayer.x = -1.6f;
}
StartCoroutine(wallrunTime());
}
}
}
void camDirection()
{
//we store the forward and right directions here.
camForward = mainCamera.transform.forward;
camRight = mainCamera.transform.right;
//we block the direction and the camera direction because we are not going to use it.
camForward.y = 0;
camRight.y = 0;
//It gives as the normalized vectors.
camForward = camForward.normalized;
camRight = camRight.normalized;
}
private void OnControllerColliderHit(ControllerColliderHit hit)
{
if (!player.isGrounded && hit.normal.y < 0.1f)
{
if (Input.GetKeyDown(KeyCode.Space))
{
fallVelocity = jumpForce;
movePlayer = hit.normal * 7;
player.transform.LookAt(player.transform.position + movePlayer);
}
}
}
IEnumerator wallrunTime()
{
yield return new WaitForSeconds(1);
Wallrun = false;
}
As you can see, when the player enters the leftshift it checks to what direction is moving the character, if this is front (w) the script make the z movement * 1.6 (to make the character run a bit in the wall) and the character go a bit up bit the y axis.Then, the script checks if the Wall it i son the right or on the left and, depending on where the wall is, sticks the character to that wall.
private void OnTriggerStay(Collider other)
{
if (Input.GetKeyDown(KeyCode.LeftShift) && Wallrun == false && WallrunCount == false)
{
if (Input.GetKey("w"))
{
Wallrun = true;
WallrunCount = true;
fallVelocity = 5f;
movePlayer.y = fallVelocity;
movePlayer.z = movePlayer.z * 1.6f;
if (Physics.Raycast(transform.position, transform.right, out HitR, 1))
{
movePlayer.x = 1.6f;
}
else if (Physics.Raycast(transform.position, -transform.right, out HitL, 1))
{
movePlayer.x = -1.6f;
}
StartCoroutine(wallrunTime());
}
}
}
With this method, I can make the character jump when the player enters space because the character is hitting the wall (a condition required to bounce in the wall).
And after some seconds, the character falls simulating a wallrun.
IEnumerator wallrunTime()
{
yield return new WaitForSeconds(1);
Wallrun = false;
}
The problem is that this works perfectly if the character makes the wallrun when is looking in the same direction as the axes of the environment.
When the character z axis is looking at the same direction of the environment z axes it works perfectly. But when the axes are not looking at the same direction is a disaster. I show you a video I have recorded.
https://youtu.be/KH7rE9kh5d0
The problem, I suppose, is that with the code I have written, I'm moving the character according to the axes of the environment, and not its axes, so I have to tell him to move according to his own.
My teacher says I might have to change the way I make the character move. I would like to know if you can tell me a way to fix this without changing the movement method or if it is not possible, how can I change that movement.
Thank you in advance.

Unity2D - moving 2D object in a grid like, timed fashion

I have been trying to make this work for a while and I am failing.
I have a Rigidbody2D in a top down 2D level and I am trying to simply move it along the coordinates (the levels are gridlike) so one step / button press equals exactly one square walked. It should only be possible to walk in one of the four directions, and no matter when the user stops the walking motion, it should end on a square. A good game equivalent of what I want to achieve is the lufia / breath of fire / any similar RPG series. I've tried using coroutines to get the update function to wait one second after every step, but that does not seem to work. The closest I've come to is the code down below. Thanks guys!
public class PlayerMovement2D : MonoBehaviour
{
Rigidbody2D rbody;
Animator anim;
float speed = 1.25f;
Vector2 pos;
void Start()
{
rbody = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
pos = rbody.position;
}
void FixedUpdate()
{
Vector2 movement_vector = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
if (movement_vector.x != 0 || movement_vector.y != 0)
{
if (movement_vector.x > 0)
{
pos += Vector2.right;
pos.y = 0;
}
else if (movement_vector.x < 0)
{
pos += Vector2.left;
pos.y = 0;
}
else if (movement_vector.y > 0)
{
pos += Vector2.up;
pos.x = 0;
}
else if (movement_vector.y < 0)
{
pos += Vector2.down;
pos.x = 0;
}
anim.SetBool("IsWalking", true);
anim.SetFloat("Input_x", movement_vector.x);
anim.SetFloat("Input_y", movement_vector.y);
}
else
{
anim.SetBool("IsWalking", false);
}
rbody.position = Vector2.MoveTowards(rbody.position, pos, speed * Time.deltaTime);
//transform.position = Vector3.MoveTowards(transform.position, pos, Time.deltaTime * speed);
pos = rbody.position;
}
}
I think what you try to program is similar to the RogueLike game that is part of the tutorial collection in Unity website. Check first the intro video to confirm that is what you are planning to achieve
https://unity3d.com/es/learn/tutorials/projects/2d-roguelike-tutorial/project-introduction?playlist=17150
If so, this is how they handle it:
using UnityEngine;
using System.Collections;
//The abstract keyword enables you to create classes and class members that are incomplete and must be implemented in a derived class.
public abstract class MovingObject : MonoBehaviour
{
public float moveTime = 0.1f; //Time it will take object to move, in seconds.
public LayerMask blockingLayer; //Layer on which collision will be checked.
private BoxCollider2D boxCollider; //The BoxCollider2D component attached to this object.
private Rigidbody2D rb2D; //The Rigidbody2D component attached to this object.
private float inverseMoveTime; //Used to make movement more efficient.
//Protected, virtual functions can be overridden by inheriting classes.
protected virtual void Start ()
{
//Get a component reference to this object's BoxCollider2D
boxCollider = GetComponent <BoxCollider2D> ();
//Get a component reference to this object's Rigidbody2D
rb2D = GetComponent <Rigidbody2D> ();
//By storing the reciprocal of the move time we can use it by multiplying instead of dividing, this is more efficient.
inverseMoveTime = 1f / moveTime;
}
//Move returns true if it is able to move and false if not.
//Move takes parameters for x direction, y direction and a RaycastHit2D to check collision.
protected bool Move (int xDir, int yDir, out RaycastHit2D hit)
{
//Store start position to move from, based on objects current transform position.
Vector2 start = transform.position;
// Calculate end position based on the direction parameters passed in when calling Move.
Vector2 end = start + new Vector2 (xDir, yDir);
//Disable the boxCollider so that linecast doesn't hit this object's own collider.
boxCollider.enabled = false;
//Cast a line from start point to end point checking collision on blockingLayer.
hit = Physics2D.Linecast (start, end, blockingLayer);
//Re-enable boxCollider after linecast
boxCollider.enabled = true;
//Check if anything was hit
if(hit.transform == null)
{
//If nothing was hit, start SmoothMovement co-routine passing in the Vector2 end as destination
StartCoroutine (SmoothMovement (end));
//Return true to say that Move was successful
return true;
}
//If something was hit, return false, Move was unsuccesful.
return false;
}
//Co-routine for moving units from one space to next, takes a parameter end to specify where to move to.
protected IEnumerator SmoothMovement (Vector3 end)
{
//Calculate the remaining distance to move based on the square magnitude of the difference between current position and end parameter.
//Square magnitude is used instead of magnitude because it's computationally cheaper.
float sqrRemainingDistance = (transform.position - end).sqrMagnitude;
//While that distance is greater than a very small amount (Epsilon, almost zero):
while(sqrRemainingDistance > float.Epsilon)
{
//Find a new position proportionally closer to the end, based on the moveTime
Vector3 newPostion = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime);
//Call MovePosition on attached Rigidbody2D and move it to the calculated position.
rb2D.MovePosition (newPostion);
//Recalculate the remaining distance after moving.
sqrRemainingDistance = (transform.position - end).sqrMagnitude;
//Return and loop until sqrRemainingDistance is close enough to zero to end the function
yield return null;
}
}
//The virtual keyword means AttemptMove can be overridden by inheriting classes using the override keyword.
//AttemptMove takes a generic parameter T to specify the type of component we expect our unit to interact with if blocked (Player for Enemies, Wall for Player).
protected virtual void AttemptMove <T> (int xDir, int yDir)
where T : Component
{
//Hit will store whatever our linecast hits when Move is called.
RaycastHit2D hit;
//Set canMove to true if Move was successful, false if failed.
bool canMove = Move (xDir, yDir, out hit);
//Check if nothing was hit by linecast
if(hit.transform == null)
//If nothing was hit, return and don't execute further code.
return;
//Get a component reference to the component of type T attached to the object that was hit
T hitComponent = hit.transform.GetComponent <T> ();
//If canMove is false and hitComponent is not equal to null, meaning MovingObject is blocked and has hit something it can interact with.
if(!canMove && hitComponent != null)
//Call the OnCantMove function and pass it hitComponent as a parameter.
OnCantMove (hitComponent);
}
//The abstract modifier indicates that the thing being modified has a missing or incomplete implementation.
//OnCantMove will be overriden by functions in the inheriting classes.
protected abstract void OnCantMove <T> (T component)
where T : Component;
}
Link to this part of the tutorial:
https://unity3d.com/es/learn/tutorials/projects/2d-roguelike-tutorial/moving-object-script?playlist=17150
You can use Vector2.Lerp method in combination of Unity's Coroutine system.
public class Movement
: MonoBehaviour
{
IEnumerator m_MoveCoroutine;
float m_SpeedFactor;
void Update()
{
// if character is already moving, just return
if ( m_MoveCoroutine != null )
return;
// placeholder for the direction
Vector2 direction; // between { -1, -1 } and { 1, 1 }
// calculate your direction based on the input
// and set that direction to the direction variable
// eg. direction = new Vector2(Input.GetAxisRaw("Horizontal") > 0 ? 1 : -1,...)
// then check if direction is not { 0, 0 }
if( direction != Vector2.zero )
{
// start moving your character
m_MoveCoroutine = Move(direction);
StartCoroutine(m_MoveCoroutine);
}
}
IEnumerator Move(Vector2 direction)
{
// Lerp(Vector2 a, Vector2 b, float t);
Vector2 orgPos = transform.Position; // original position
Vector2 newPos = orgPos + direction; // new position after move is done
float t = 0; // placeholder to check if we're on the right spot
while( t < 1.0f ) // loop while player is not in the right spot
{
// calculate and set new position based on the deltaTime's value
transform.position = Vector2.Lerp(orgPos, newPos, (t += Time.deltaTime * m_SpeedFactor));
// wait for new frame
yield return new WaitForEndFrame();
}
// stop coroutine
StopCoroutine(m_MoveCoroutine);
// get rid of the reference to enable further movements
m_MoveCoroutine = null;
}
}
This method assumes that you can move in the specified direction. But you still should check if your new position is "walkable" before starting the Move Coroutine.
I think you want to do the movement in a coroutine that, while it is active, disabled further movement until it's done.
bool isIdle = true;
void Update() {
if(isIdle) {
// Your movement code that gives pos
StartCoroutine(Move(pos));
}
}
IEnumerator Move(Vector2 destination) {
isIdle = false;
do {
transform.position = Vector2.MoveTowards(transform.position, destination, speed * Time.deltaTime);
yield return new WaitForEndOfFrame();
} while (transform.position != destination);
isIdle = true;
}
Let me know if you don't understand, need further clarification or if this approach doesn't work!

Unity game Drag Object with finger movement

I am new to Unity and develop mobile 2d game,now I am able to make an object move right and left when I touch the screen before or after the screen center. But I want to touch the object and drag it on the x axis while my finger is still touching the screen and move,so I want the object to be in the same x position of my finger,
Any One can help me how to do it correctly:
here is the code of how I am moving the object if I touched before or after the screen center:
public class paddle : MonoBehaviour {
public Rigidbody2D rb;
public float speed;
public float maxX;
bool currentisAndroid=false;
// Use this for initialization
void Start () {
rb = GetComponent<Rigidbody2D> ();
#if UNITY_ANDROID
currentisAndroid=true;
#else
currentisAndroid=false;
#endif
}
// Update is called once per frame
void Update () {
if (currentisAndroid == true) {
if (Input.GetTouch (0).position.x < Screen.width/2 && Input.GetTouch (0).phase == TouchPhase.Stationary)
moveLeft ();
else if (Input.GetTouch (0).position.x > Screen.width/2 && Input.GetTouch (0).phase == TouchPhase.Stationary)
moveRight ();
else
stop ();
} else {
float x = Input.GetAxis ("Horizontal");
//if (Input.GetTouch (0).position.x == rb.position.x && Input.GetTouch (0).phase == TouchPhase.Moved)
if (x == 0)
stop ();
if (x < 0)
moveLeft ();
if (x > 0)
moveRight ();
Vector2 pos = transform.position;
pos.x=Mathf.Clamp (pos.x,-maxX,maxX);
transform.position = pos;
}
}
void moveLeft()
{
rb.velocity = new Vector2 (-speed, 0);
}
void moveRight()
{
rb.velocity = new Vector2 (speed, 0);
}
void stop()
{
rb.velocity = new Vector2 (0, 0);
}
public float getposition()
{
return rb.position.y;
}
}
Easiest way:
Add component DragRigidbody script and you will be able to drag objects via mouse or touchScreen touches.
If I Understand correctly:
1 - Raycast from finger location vertically with your camera into the scene.
2 - select the hit object.
3 - map your camera to world coordinates and move that object according to hit point of your ray with map or game object\objects.
Physics.Raycast();
RaycastHit.collider();
Camera.main.ScreenToWorldPoint(Input.GetTouch(<0 or 1 or all>).position);
If you want to move object across a map you can track your touch and when its close to the corners move the camera on that direct (horizontal - vertical).

Smoothly lerp a rigidbody

I'm wanting my player to move to the left when the player hits the left of the screen. Right now, my player only moves as and when I hold down my mouse button. What I'm really wanting is for him to continually move to the left until he hits the target position when I press the screen once.
Can someone please tell me what I'm missing from my code to allow this to happen?
void FixedUpdate()
{
if(Input.GetMouseButtonDown(0))
{
if(Input.mousePosition.x < (Screen.width*2)/3 && Input.mousePosition.y > Screen.height/3)
{
if(position == middle)
{
MoveLeft();
}
}
}
}
void MoveLeft()
{
var pos = rigidbody.position;
float xPosition = left.transform.position.x;
pos.x = Mathf.Lerp(pos.x, xPosition, speed * Time.deltaTime);
rigidbody.position = pos;
}
My method is in the FixedUpdate because I'm moving the players rigidbody as oppose to translating the actual player.
Right now the player only moves when you hit mouse button because that's how your code is written: you check if the mouse is pressed every frame, and only if it is move the rigidbody.
If you want the player to move regardless of whether the mouse is still pressed or not, you should create some kind of variable to save the state of the player, and set to move left when the mouse button is pressed and set it to stop when the player reaches his target.
If I correctly understood your goal, it would look something like this:
bool moveLeft = false;
void FixedUpdate()
{
if(Input.GetMouseButtonDown(0)
&& (Input.mousePosition.x < (Screen.width*2)/3 && Input.mousePosition.y > Screen.height/3))
{
moveLeft = true;
}
if (moveLeft
&& (position == middle))
{
MoveLeft();
}
else
{
moveLeft = false;
}
}
void MoveLeft()
{
var pos = rigidbody.position;
float xPosition = left.transform.position.x;
pos.x = Mathf.Lerp(pos.x, xPosition, speed * Time.deltaTime);
rigidbody.position = pos;
}

Categories

Resources