I am currently looking for a solution to detect a long press and a tap, but I'm kind of lost, I know it has something to do with deltaPosition or deltaTime.
Can someone explain to me? I'm new with Unity.
It's for dragging a button, so I would press the button for a couple of frames, and if I drag it a couple of pixels it would enter in the DRAG_STATE, if I tap it it would go into TAPPED_STATE.
Notes :
I have to detect the distance too
I can't use the "Touch" functions, I need to simulate it with the mouse
When you detect the press, then you store the position. On following frames, you check if the position has changed:
Vector3 position;
void Update(){
if(Input.GetMouseButtonDown(0)) { this.position = Input.mousePosition;}
else if(Input.GetMouseButton(0)){
float distance = Vector3.Distance(this.position, Input.mousePosition);
}
else if(Input.GetMouseButtonUp(0)) { this.position = Vector3.zero }
}
So on enter, you just record the position, this is when the user presses down the button. Nothing else happens on that frame.
Next frame, if the user keeps pressing, we compare the previous (initial) position and the current one. This is where you should pass that distance wherever it is needed. If distance is small enough, you would discard the drag or ignore it.
Finally, when the user releases the button, we reset to 0. You may want to reset to some negative value to make sure the value is not a usable value (tho 0 is unlikely it is not impossible).
Related
I created this button prefab that is using the built-in Unity Button component, an image component with Raycast Target, and an Animator component that is playing a scaling animation when pressing the button. The problem is occurring when clicking on the edge of the button graphic and holding the mouse button down.
As the animation is scaling down the button size (as well as the image component with the Raycast Target) the mouse cursor is at some point no longer hovering over the button and the animation state is going back to normal. As the button scales back to normal size automatically (and while still holding the mouse button pressed) the button is getting pressed again - getting stuck in a loop.
How can I prevent that from happening? Do I need to modify the order of my components or maybe change something in the Animator?
To sum up what was said in the comments: you want the raycast target to stay the same size while animation plays. That's why you should probably split your Popup Button into two GameObjects: one will be the actual Button (that stays the same size) and the other will be animated, and then the question is mostly "how do I change another object's animation from button click".
To do that, you should have the Button component on the actual button, Animator component on the animated object, and in the Button's onClick list you should have the call to the animated objects' Animator Component -> SetTrigger with the name of the trigger you want as an argument. Also note that the animated object shouldn't be the raycast target, but the actual button should.
You can pick what happens when animation is triggerred again before it finishes (for example when button is clicked multiple times) by setting transition parameters in the animation controller. But in this case just separating the button from the animated image should do what you want, because the raycast target will now stay the same size and the animation won't get triggered multiple times anymore.
You can add timer for check animation is running.
example :
//your animation duration here:
private float animationduration = 0f;
bool isClickable = true;
//set your timer with default 0
private float timer = 0f;
private void Update()
{
timer += Time.deltaTime;
if (timer > animationduration)
{
isClickable = true;
}
else
{
isClickable = false;
}
if (isClickable && Input.GetMouseButtonDown(0))
{
clickFunction();
timer = 0f;
}
}
void clickFunction()
{
//ur function in here
}
then click function can't use before the animation end.
I am trying to drag an Image in a certain area. For that I am using IDragHandler. To prevent the image to go outside the area, i put four box colliders in a square shape. The box was still moving out. So, I put the fixed timestep to 0.0001. Now, when the image goes out of boundry, it pushes back the image in the specified area which is fine but I want the image to stop moving out the boundary the moment it touch the edge of the image.
Here's my code:
public class Draggable : MonoBehaviour, IDragHandler
{
public GameObject box;
private void OnCollisionEnter2D(Collision2D collision)
{
Debug.Log("Triggered");
}
public void OnDrag(PointerEventData eventData)
{
box.transform.position = eventData.position;
}
}
Don’t use physics and fixed timestep to solve this problem. It is performance killer to reduce timestep.
One way to do it : use Physics.OverlapBox ( see documentation https://docs.unity3d.com/ScriptReference/Physics.OverlapBox.html)
Do this test between your draggable object and the limit of your draggable zone in your OnDrag method. Calculate the « Wanted » position base on the event you receive and if your object overlap with your borders then dont move it, if not your are safe and you can move it.
Try to achieve your bounderies as simple numbers. Maybe create a rectangle and take its corners. Then you get the best user experience by using min and max functions, effectively "clamping" the allowed coordinates before they are actually set.
It makes sense to clamp x and y separately. That way the user can still drag along the Y axis if there is room despite the mouse being outside the boundary on the X axis.
I am on my third day of learning Unity and are currently doing the roll-a-ball tutorial. I am trying to add a restart button, that teleports the ball back to the start-position/origin. It all works good, but the ball will keep moving after I restart, but I want it to stay in a spot and not move by itself.
What I mean is that if I'm moving left at max speed and press 'R' to restart, then it will keep moving left fast for some time, but what I want to achieve is that the ball would not keep moving by itself after I restart it's position (perhaps it's speed is just decreasing so slow that it's unnoticeable, in which case I'd want it to slow down faster).
Here's my code that I just tried out and "hoped" would work, but doesn't. I think that the problem I have here is that a new axisHorizontal/axisVertical variable value is assigned again the next frame, the value being taken from Input.GetAxis, which from what I have heard is not possible to change.
That means that a movementForce bigger than 0/0/0 and AddForce will be activated again the next frame.
using UnityEngine;
using System.Collections;
public class PlayerScript : MonoBehaviour {
public float movementSpeed = 7f;
private Rigidbody rigidSphere;
void Start() {
rigidSphere = GetComponent<Rigidbody>();
}
void FixedUpdate() {
float axisHorizontal = Input.GetAxis("Horizontal");
float axisVertical = Input.GetAxis("Vertical");
Vector3 movementForce = new Vector3(axisHorizontal, 0.0f, axisVertical);
rigidSphere.AddForce(movementForce * movementSpeed);
if (Input.GetKeyDown(KeyCode.R)) {
transform.position = new Vector3(0, 0, 0);
rigidSphere.AddForce(0, 0, 0);
axisHorizontal = 0;
axisVertical = 0;
}
}
}
I tried googling on how to change the value that is taken from GetAxis, but others claim it's impossible to do so. So I have really no idea how to make it not keep moving forever...
And I also got a 'bonus' question that has arised during those two days that I have been learning Unity, but they are not worth a seperate question, perhaps if they have been already answered before (I've been unable to find any documentation that'd help me on it), then just give me a link on where I could find the answer to my problem.
The question is that how do I make the ball stop moving after some time after I press a button (W/A/S/D, the default values for positive/negative buttons in Input Manager)? For example, I press 'W' just for a second, but the ball will keep moving forward forever and will never stop.
What I want to achieve is after I for example press 'W' for a second, after a few seconds the ball will completely stop, it just seems that the value from GetAxis never goes back to 0. I tried to google about it, and I think it's something to do with Gravity in the Input Manager - I tried changing it, but the ball will still keep moving forever, the only thing I noticed did change was the speed of the ball.
I'd be really grateful if someone could help me with these problems.
I am trying to add a restart button, that teleports the ball back to
the start-position/origin. It all works good, but the ball will keep
moving after I restart, but I want it to stay in a spot and not move
by itself.
I see that you tried to solve this by using rigidSphere.AddForce(0, 0, 0);. This will only add 0,0,0 to the existing force the ball is moving at. The solution to this is to set the Rigidbody velocity to 0.
Replace rigidSphere.AddForce(0, 0, 0); with rigidSphere.velocity = Vector3.zero;
Even when you do this, Input.GetAxis value increases and decreases over time before reaching 0. So, on the next frame, the ball might still be moving due to this.
To stop this, also replace Input.GetAxis with Input.GetAxisRaw.
Input.GetAxisRaw will immidiately return to 0 when the key is released.
What I want to achieve is after I for example press 'W' for a second,
after a few seconds the ball will completely stop, it just seems that
the value from GetAxis never goes back to 0.
Since the ball is rolling, you need to increase the angular drag of the ball. This will make the ball stop rolling after releasing the key. The angular drag is on the Rigidbody attached to the ball. Also increase the drag if that's not enough.
I'm working on a Windows app using XNA.
Actually I succeed moving my sprite, but I want to add a different action when the user touch the sprite but don't move it. I know TouchlocationState Enum exist but I don't understand the difference between Moved and Pressed.
For now I use Released and that's enough, I update the sprite position while it's not released and then I check collision.
So how can I add only one touch method when it's clicked? I mean when the user tap the sprite but don't move it.
Some code:
TouchPanelCapabilities touchCap = TouchPanel.GetCapabilities();
if (touchCap.IsConnected)
{
TouchCollection touches = TouchPanel.GetState();
if (touches.Count >= 1)
{
Vector2 PositionTouch = touches[0].Position;
if (touches[0].State == TouchLocationState.Released)
{
// Pause button click and others buttons
Mouseclik((int)PositionTouch.X, (int)PositionTouch.Y);
}
if (!PausePopUp)
{
CheckMoove(PositionTouch);
if (touches[touches.Count - 1].State == TouchLocationState.Released)
{
// this is where i try to add/check if its only "click" on my sprite
if (touches[0].Position == touches[touches.Count - 1].Postion)
{
TempoRectangle = ListSprite[save].ShapeViser;
isclicked = true;
}
My goal is to add a picturebox above the sprite to display information then if an other sprite is touched while the picturebox is diplayed, I want to draw a line between these 2 sprites.
The difference is simple:
TouchLocationState.Pressed means that a new location is detected.
TouchLocationState.Moved means that the location position was updated or pressed at the same position
This means that you will get only a Pressed state for each touch action, a sequence of Moved every cycle while the touch is pressed, and when you release it you'll get a Released.
This is explained in the link you provided, too.
I want to implement zoom feature using two-fingers slide in/out gesture that is commonly met in games such as Angry Birds. Now i'm using slider zoom and it feels not so good as the simple gesture. So i've tried to look at the gestures implementation in MonoGame but haven't figured out what actualy can help me to achieve described beahviour.
Any help will be appreciated, thanks!
Short answer: you need to use the TouchPanel gesture functionality to detect the Pinch gesture, then process the resultant gestures.
The longer answer...
You will get multiple GestureType.Pinch gesture events per user gesture, followed by a GestureType.PinchComplete when the user releases one or both fingers. Each Pinch event will have two pairs of vectors - a current position and a position change for each touch point. To calculate the actual change for the pinch you need to back-calculate the prior positions of each touch point, get the distance between the touch points at prior and current states, then subtract to get the total change. Compare this to the distance of the original pinch touch points (the original positions of the touch points from the first pinch event) to get a total distance difference.
First, make sure you initialize the TouchPanel.EnabledGestures property to include GestureType.Pinch and optionally GestureType.PinchComplete depending on whether you want to capture the end of the user's pinch gesture.
Next, use something similar to this (called from your Game class's Update method) to process the events
bool _pinching = false;
float _pinchInitialDistance;
private void HandleTouchInput()
{
while (TouchPanel.IsGestureAvailable)
{
GestureSample gesture = TouchPanel.GetGesture();
if (gesture.GestureType == GestureType.Pinch)
{
// current positions
Vector2 a = gesture.Position;
Vector2 b = gesture.Position2;
float dist = Vector2.Distance(a, b);
// prior positions
Vector2 aOld = gesture.Position - gesture.Delta;
Vector2 bOld = gesture.Position2 - gesture.Delta2;
float distOld = Vector2.Distance(aOld, bOld);
if (!_pinching)
{
// start of pinch, record original distance
_pinching = true;
_pinchInitialDistance = distOld;
}
// work out zoom amount based on pinch distance...
float scale = (distOld - dist) * 0.05f;
ZoomBy(scale);
}
else if (gesture.GestureType == GestureType.PinchComplete)
{
// end of pinch
_pinching = false;
}
}
}
The fun part is working out the zoom amounts. There are two basic options:
As shown above, use a scaling factor to alter zoom based on the raw change in distance represented by the current Pinch event. This is fairly simple and probably does what you need it to do. In this case you can probably drop the _pinching and _pinchInitialDistance fields and related code.
Track the distance between the original touch points and set zoom based on current distance as a percentage of initial distance (float zoom = dist / _pinchInitialDistance; ZoomTo(zoom);)
Which one you choose depends on how you're handling zoom at the moment.
In either case, you might also want to record the central point between the touch points to use as the center of your zoom rather than pinning the zoom point to the center of the screen. Or if you want to get really silly with it, record the original touch points (aOld and bOld from the first Pinch event) and do translation, rotation and scaling operations to have those two points follow the current touch points.