I am trying to create a player movement along the x-axis based on finger position.
What I need to Happen: Not multi-Touch. I want it so a player can put a single finger down and grab that position. Then check to see if the player dragged finger along screen on x-axis and move player left or right based off where they dragged finger from first touch.
So if they touch the screen and if drag left: move left by speed and if it changes to drag right move right by speed.
Any help would be Awesome.
The easiest way would be to stored first touch position and then compare the X with that position:
public class PlayerMover : MonoBehaviour
{
/// Movement speed units per second
[SerializeField]
private float speed;
/// X coordinate of the initial press
// The '?' makes the float nullable
private float? pressX;
/// Called once every frame
private void Update()
{
// If pressed with one finger
if(Input.GetMouseButtonDown(0))
pressX = Input.touches[0].position.x;
else if (Input.GetMouseButtonUp(0))
pressX = null;
if(pressX != null)
{
float currentX = Input.touches[0].position.x;
// The finger of initial press is now left of the press position
if(currentX < pressX)
Move(-speed);
// The finger of initial press is now right of the press position
else if(currentX > pressX)
Move(speed);
// else is not required as if you manage (somehow)
// move you finger back to initial X coordinate
// you should just be staying still
}
}
`
/// Moves the player
private void Move(float velocity)
{
transform.position += Vector3.right * velocity * Time.deltaTime;
}
}
WARNING: this solution will only work for devices with touch input available (because of Input.touches use).
using the code #programmer provided in this answer: Detect swipe gesture direction
You can easily detect the direction you are swiping/dragging. Replace the Debug
void OnSwipeLeft()
{
Debug.Log("Swipe Left");
}
void OnSwipeRight()
{
Debug.Log("Swipe Right");
}
With functions that move your character. If you use RigidBody to move your character you can use https://docs.unity3d.com/ScriptReference/Rigidbody.MovePosition.html. If its a normal object, you can move it by tweaking the transform.position.
If you need more information on how to move rigidbody\normal objects let me know what type of game you have and more details on how you set up the player.
Related
Brand new to Unity/C# so this might be a stupid error. I have a player and a push-able box that when pushed towards another object (that usually the player cannot walk through) causes the box to stop moving and the player to get stuck and be unable to move but stuck mid-animation.
They basically just all get stuck in eachother
https://i.stack.imgur.com/rkNtu.png
I followed tutorials for a lot of these things but couldn't manage to find one for pushing the box so I did it by myself, which is what I'm thinking caused these issues.
The player has a 2D circle collider and a 2D rigidbody (with a dynamic body type and discrete collision detection).
It also has all of its code to do with walking that looks like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
//Movement speed variable
public float moveSpeed;
//Giving information of where the collisions layer is (edited in actual unity)
public LayerMask collisionsLayer;
//Variable to check if the character is moving
private bool isMoving;
private Vector2 input;
//Animation SetUp
private Animator animator;
private void Awake()
{
animator = GetComponent<Animator>();
}
//End of Animation SetUp
private void Update()
{
//If the player is NOT moving
if (!isMoving)
{
//'GetAxisRaw' gives 1 or -1 --> right = 1, left = -1
input.x = Input.GetAxisRaw("Horizontal");
input.y = Input.GetAxisRaw("Vertical");
//Making it so that the player cannot move diagonally
if (input.x != 0) input.y = 0;
if (input != Vector2.zero)
{
//Animation section
animator.SetFloat("moveX",input.x);
animator.SetFloat("moveY",input.y);
//End of animation section
var targetPos = transform.position;
//Adds 1 or -1 depending on the key input (GetAxisRaw)
targetPos.x += input.x;
targetPos.y += input.y;
if (IsWalkable(targetPos))
//Calls coroutine 'IEnumerator Move'
StartCoroutine(Move(targetPos));
}
}
//for animation
animator.SetBool("isMoving",isMoving);
//Special type of function w/ 'IEnumerator' as return type. Used to do something over a period of time.
IEnumerator Move(Vector3 targetPos)
{
isMoving = true;
//Checks if the diff between the target position & the players current position is greater than a vry small value
while ((targetPos - transform.position).sqrMagnitude > Mathf.Epsilon)
{
//If there is a diff btwn targetpos & the current position --> we will move the player towards the target position by a very small amount
transform.position = Vector3.MoveTowards(transform.position, targetPos, moveSpeed * Time.deltaTime);
//Stops execution of the coroutine, resumes it in the next update function
yield return null;
}
//Sets the players current position to the target position
transform.position = targetPos;
isMoving = false;
}
}
//Checking if the next available tile is actually able to be walked on / if the next tile is blocked
private bool IsWalkable(Vector3 targetPos)
{
if(Physics2D.OverlapCircle(targetPos, 0.1f, collisionsLayer) != null)
{
return false;
}
return true;
}
}
The box has a box collider 2D and a 2D rigidbody (dynamic bodytype; discrete collision detection)
And the collisions tilemap (which is set to be used by the composite) has a tilemap collider 2D, rigidbody 2D (static) and a composite collider 2D
If anyone actually knows how to make it so that the entire game doesn't break when I try to move the box past objects, that'd be really helpful because I've been working on this for ages and haven't figured anything out at all
You should try running the debugger. I think you're finding yourself in an interlocked condition, where your character can't get to the target position (because it's blocked), but it hasn't reached the target position (because it's blocked), so it keeps trying to move, which prevents you from giving it a new command.
I think the easiest way to work around this is to cache your target directions - should be in positive x, positive y, or negative x, or negative y. Then, if you detect the user trying to move in the opposite direction you cancel the move and start a new one.
There are lots of ways to work around this, though, and I do see you are trying to check the target before moving to it, but you're checking the raw input, which may overshoot an obstacle. For example, if the tree is at 0.5 meters, but you're looking at a target of 1 meter, then the target is clear. You get blocked by the tree at 0.5 meters, and because you never reach 1 meter you never exit the coroutine.
You've got to run a debugger and step through the code and see what specifically isn't responding.
this question is a bit long but please bare with me.
I am working with Unity3D's newer input system, and I've been able to set up camera movement so when the user clicks the middle mouse button, they can drag the camera around along the X- & Y-axis. However, has a very parallax-y feel to it, which I am trying to avoid. From my understanding this is happening because the speed of the mouse movement & the camera movements are not the same (See, visual).
My InputController class:
public class InputController : MonoBehaviour
{
private InputControls inputControls;
[Header("Camera Movement")]
[SerializeField] CameraController cameraController;
private InputAction.CallbackContext context;
void Awake()
{
inputControls = new InputControls();
}
private void Start()
{
inputControls.Mouse.MiddleButton.started += MiddleButton_started;
}
public void MiddleButton_started(InputAction.CallbackContext ctx)
{
context = ctx;
}
private void Update()
{
bool mouseIsDown = context.performed;
if (mouseIsDown)
{
Vector2 delta = inputControls.Mouse.DeltaPosition.ReadValue<Vector2>();
cameraController.Move(delta, false);
}
}
}
My CameraController class:
public class CameraController : MonoBehaviour
{
[SerializeField] Vector2 minPosition;
[SerializeField] Vector2 maxPosition;
[SerializeField] float speed = 3;
[SerializeField] float zPosition = -10f;
private Camera mainCam;
private void Start()
{
mainCam = GetComponent<Camera>();
}
public void Move(Vector2 deltaPosition, bool convertToViewportPoint = true)
{
if (convertToViewportPoint) deltaPosition = mainCam.ScreenToViewportPoint(deltaPosition);
Vector3 moveTo = new Vector3(deltaPosition.x, deltaPosition.y, 0);
Vector3 target = transform.position - moveTo;
float clampedX = Mathf.Clamp(target.x, minPosition.x, maxPosition.x);
float clampedY = Mathf.Clamp(target.y, minPosition.y, maxPosition.y);
transform.position = Vector3.Lerp(transform.position, new Vector3(clampedX, clampedY, -10), speed * Time.deltaTime);
}
}
Now, in this version of the CameraController, the parallax-y feel might be coming from the fact that the Lerp speed is constant, whereas the speed of the mouse movement is not.
However, I've tested it with various magnitudes (ie., the magnitudes of the delta mouse position, the current mouse position, the current mouse position subtracted from the delta mouse position, etc...) with generally the same results -- either the camera moves too fast for the mouse or vice versa.
From my limited but growing understanding of trig, magnitudes represents the speed of a vector but I CANNOT get the speed of the camera movement and the speed of the mouse movement to match up. I would like for this to happen so that when the user clicks a certain point, that point generally stays under the cursor when in movement (See other visual).
Can someone help me understand how I might achieve this?
Thanks kindly.
What I did to solve this, in case anyone comes across this, is get the position of the cursor when the mouse is first clicked. We can call that dragOrigin. Then we can get the change in position as the mouse moves (polling the input through Unity's input system can be done through an Update function). Then we subtract the new mouse position from dragOrigin. Once we have that difference -- lets call it deltaPosition -- we just add it to the camera's current position.
To ensure that the speed matches that of the mouse, run the deltaPosition and the currentMousePosition through SmoothDamp to get the velocity of the change. Its magnitude will be the speed you want the camera to move.
So for example:
// Call this when the user first clicks the mouse.
// Lets assume this is part of a class that controls the camera.
public void SetDragOrigin(Vector2 mousePosition)
{
// could probably cache Camera.main for efficiency
dragOrigin = Camera.main.ScreenToWorldPoint(mousePosition);
}
// This is called in an update function of another class that deals with input.
// (Or perhaps you can simply pass the input controls to the class controlling the camera, but that's not the case here).
public void Move(Vector2 newMousePosition)
{
mousePosition = mainCam.ScreenToWorldPoint(mousePosition);
Vector2 deltaPosition = dragOrigin - mousePosition;
Vector3.SmoothDamp(deltaPosition, mousePosition, ref dragVelocity, 0.1f, 250f, Time.deltaTime);
cameraPosition += new Vector3(deltaPosition.x, deltaPosition.y, 0) * dragVelocity.magnitude * Time.deltaTime;
// anything else you want to do like clamping camera position
// ....
}
One thing to note here, is that the 5th argument of SmoothDamp is the maxSpeed. I hardcoded it in this example, but you might want to play around with it. When I did not set the maxSpeed, there was some wonky things happening to the dragVelocity over time, so it may be better to set the maxSpeed to what suits you best.
want to make a game where there is a 2D ball which moves to the position of the cursor. To get the position of the cursor, I use this code:
Vector2 PixelPos = Input.mousePosition;
Then to convert the screen position to world position I use this code:
Vector2 Pos = Camera.main.ScreenToWorldPoint(PixelPos);
The problem is that the main camera move up so that if forces the player to move. But when it move I get some weird movement with the ball.(as the camera is moving up it is always moving the ball)
Is there an alternate way to make this work??
Or more simply can I replace this piece of code:
Vector2 Pos = Camera.main.ScreenToWorldPoint(PixelPos);
with some other things which does not require a camera to convert the screen positions to world position?
Thanks!
Since you want the cursor to be moved only by the player and to be updated relative to world space, not screen space, you need to implement a virtual cursor that exists in world space.
First, create your virtual cursor as a GameObject. On Update you can update its position with
float sensitivity = 1f;
transform.position += sensitivity * new Vector2(
Input.GetAxis("Mouse X"),
Input.GetAxis("Mouse Y")
);
Then, instead of using the camera to find the cursor position, you just check the `transform.position` of that virtual cursor `GameObject`.
Second, you'll need to lock the built-in cursor. You can do that with
Cursor.lockState = CursorLockMode.Locked;
If you need to undo that (for instance, if you bring up a menu, and need to use the regular cursor without moving the virual cursor around), then you can use:
Cursor.lockState = CursorLockMode.None;
or, if you need the cursor to stay in the window:
Cursor.lockState = CursorLockMode.Confined;
If you only want to move the ball to the last position clicked/touched then here is an easier solution.
Keep track of the goal position for the ball, and if one has been set yet:
private Vector2 moveGoalPos;
private bool moveGoalSet= false;
Only change moveGoalPos on frames where the mouse is clicked/the screen is touched.:
bool isTouched;
if (isMouseEnable) {
isTouched = Input.GetMouseButtonDown(0);
PixelPos = Input.mousePosition;
} else {
isTouched = Input.touchCount > 0;
Touch touch = Input.GetTouch(0);
PixelPos = touch.position;
}
if (isTouched) {
moveGoalPos= Camera.main.ScreenToWorldPoint(PixelPos);
moveGoalSet= true;
}
However, on every frame, you'll want to move the ball to the world space moveGoalPos (only if a goal has been set):
if (moveGoalSet) {
Vector2 OffsetPos = moveGoalPos + CursorOffSet;
GCursor.transform.position = OffsetPos;
print(OffsetPos);
Vector2 LerpPos = Vector2.Lerp(rb.transform.position, OffsetPos, 0.05f);
rb.MovePosition(LerpPos);
}
When you need to stop the ball from moving to the last touched/clicked position (for instance, if you change or reset a level), you'll need to reset moveGoalSet:
moveGoalSet = false;
I have a sphere in that is moving foward and I need it to follow my finger smoothly when it swipes on the screen but only in the x axis.
I trying to make the exact movent like Rolling Sky, Color Road
The ball Should follow my finger when it swipes in the x axis and move smoothly.
Just like in the games I mention before.
I try OnMouseDrag and a lot of ways but it dont work correctly beacuse it dont dont follow the finger or it dont move while finger is swiping.
From what I understand, it seems you want to get the position of the user's finger and move the ball accordingly?
You could achieve this with Touch.position
Example:
private void Update()
{
Touch touch = Input.GetTouch(0); // Get the touch data for the first finger
Vector2 position = touch.position; // Get the position in screen-space
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(position); // Convert the position to world space
Vector3 ballPosition = ball.transform.position;
ball.transform.position = new Vector3(worldPosition.x, ballPosition.y, ballPosition.z); // Move the ball
}
I think you should handle the input separately from the sphere.
Put this on a script to detect the swiping on the screen.
bool inputBegan = false;
Vector2 beganPosition;
void Update()
{
if (!inputBegan && Input.GetMouseButtonDown (0))
{
inputBegan = true;
beganPosition = Input.mousePosition;
}
else if (inputBegan && Input.GetMouseButtonUp (0))
{
//If you want a threshold you need to change this comparison
if (beganPosition.x > Input.mousePosition)
{
MoveBallLeft ();
}
else
{
MoveBallRight ();
}
inputBegan = false;
}
}
The movement of the balls you can achieve it adding or substracting Vector2.right * movingDistance to the sphere Transform if you want it to teleport or making the sphere move slowly using a targetPosition and currentPosition and each Update cycle add a little bit of transform distance.
I solved it doing this and setting the variable NavigationMultiplier to 10000
I have a class below that I attach to a object in order to make it rotate around its pivot. I sent the pivot of the sprite via the inspector.
This works exactly how I want it too, BUT the issue I am having is that whenever I touch and drag it, and then touch and drag it again, it snaps to a new position.
What I would like for it to do is, when it is rotated and then rotated again, the sprite stays in its same rotation and not snap to a new position and I would like the angle of the sprite to be reset to 0. The next then is that I want the angle to continually rotate. So if I rotate it in the positive direction, the angle should keep increasing in the positive direction and not change..Such as 0---> 360 ----> 720 -----> etc, etc. And then when the mouse is released, the sprite stays in the same position but the angle is now set back to 0. And then when clicked again to rotate, it rotates from that exact position.
Here is my code thus far which works well for rotating, but I would like to modify it to achieve the above scenario. Any help with this?
public class Steering : MonoBehaviour {
float prevAngle,wheelAngle,wheelNewAngle = 0;
public SpriteRenderer sprite;
void Start () {
}
void Update () {
}
public float GetAngle(){
return wheelAngle;
}
void OnMouseDrag(){
Vector3 mouse_pos = Input.mousePosition;
Vector3 player_pos = Camera.main.WorldToScreenPoint(this.transform.position);
mouse_pos.x = mouse_pos.x - player_pos.x;
mouse_pos.y = mouse_pos.y - player_pos.y;
wheelNewAngle = Mathf.Atan2 (mouse_pos.y, mouse_pos.x) * Mathf.Rad2Deg;
if (Input.mousePosition.x > sprite.bounds.center.x) {
wheelAngle += wheelNewAngle - prevAngle;
} else {
wheelAngle -= wheelNewAngle - prevAngle;
}
this.transform.rotation = Quaternion.Euler (new Vector3(0, 0, wheelAngle));
Debug.Log (wheelAngle);
prevAngle = wheelNewAngle;
}
void OnMouseUp(){
prevAngle = wheelNewAngle;
wheelAngle = 0;
}
}
By angle of the sprite, do you mean the rotation? I'm not sure how the position is changing if there's nothing in your code doing that. Does it always move to the same position? I'm having a little trouble visualizing how your system is supposed to look but I hope this helps.
It looks like you might want to store the previous mouse position so you can get the relative amount to rotate each frame.
At the top:
Vector3 prevMousePos = Vector3.zero;
This method will help get the position when the player pressed:
void OnMouseDown(){
prevMousePos = Input.mousePosition;
}
Then in OnMouseDrag() get the difference between the two mouse positions to get the relative position (if you moved the mouse left, right, up, or down since pressing):
Vector3 mouseDiff = Input.mousePosition - prevMousePos;
With this it will use the relative mouse position after pressing instead of the current one, which should smooth things out.