I am using an OnMouseDrag event to move an object. Trouble I have is that unless the mouse button is released the object will not drop. I want to be able to have the object drop if dragged to a certain location on screen (2d). So even if the mouse button is still down drop the object.
Here is the code I am using:
void OnMouseDown()
{
screenPoint = Camera.main.WorldToScreenPoint(gameObject.transform.position);
Debug.Log ("We clicked fire block!");
offset = gameObject.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, gameObject.transform.position.y, screenPoint.z));
}
void OnMouseDrag()
{
Vector3 curScreenPoint = new Vector3(Input.mousePosition.x, gameObject.transform.position.y, screenPoint.z);
if (curScreenPoint.x <= 1) {
return;
}
Vector3 curPosition = Camera.main.ScreenToWorldPoint(curScreenPoint)+offset;
transform.position = curPosition;
isDrag = true;
}
I didn't understand one thing: this location is a portion of the screen or is a 3D location in the game?
Posting for both cases:
1 - Location is a 3D place in the game:
Use Physics.Raycast inside your OnMouseDragUpdate method to check if your mouse cursor is hitting the area it is supposed to release the dragged object (you'll need a game object with a collider representing the location). If so, simply do isDrag = false.
Here's an example of how to do the raycast.
2 - Location is a portion of the screen:
Input.mousePosition ranges from 0 (0%) to 1 (100%), so simply check the mouse position inside your OnMouseDragUpdate method to verify if it's inside the region that you want, and if so do isDrag = false.
Related
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;
Goal: Detect when the cursor has entered a defined radius of the player.
Hello, I am in the process of trying to replicate the combat system from a game called CrossCode. The idea is that when the cursor is within a certain radius of the player, I will be able to switch to melee combat, and back to ranged once the cursor leaves this radius.
I have implemented one way I thought it could be done, however it feels slow or unreliable and I just wanted to know if there are any other methods I could possibly look into to achieve a smoother result.
Here is what I've done
attached to the player
void Update()
{
attackStyleSwitchRadius = colRef.radius;
playerCenter = colRef.transform.position;
if(Physics2D.OverlapCircle(playerCenter, attackStyleSwitchRadius, cursor))
{
meleeMode = true;
rangeMode = false;
}
else
{
meleeMode = false;
rangeMode = true;
}
}
And on a small 2D object I have this script so that it follows the cursor position.
void Update()
{
pos = Input.mousePosition;
gameObject.transform.position = Camera.main.ScreenToWorldPoint(pos);
}
when the small object enters the overlap circle it changes the bools around.
You can remove the collision detection overhead by doing something like this instead;
void Update ()
{
attackStyleSwitchRadius = colRef.radius;
playerCenter = colRef.transform.position;
var mouse = Input.mousePosition;
mouse.z = Vector3.Distance(Camera.main.transform.position, playerCenter);
var range = Vector2.Distance(Camera.main.ScreenToWorldPoint(mouse), playerCenter);
var inside = range < attackStyleSwitchRadius;
meleeMode = inside;
rangeMode = !inside;
}
Cursor
Make an object, name it cursor.
Add a small collider to the cursor object.
Add a script to the cursor object so its always at the mouses location.
Melee range zone
Add a GameObject as child of the player, name it MeleeRangeZone.
Add a collider to it, set it to be a Trigger. The size of this collider will be the players melee range,
Add a rigidbody to it so that collisions can be detected, but set the rigidbody to not rotate or change its position.
Add a script to the object and use the OnTriggerEnter and OnTriggerExit methods to detect whether or not the cursor has entered your melee zone.
You can now use the OnTriggerEnter and OnTriggerExit methods to switch between the players attack modes, meaning that when the cursor enters it changes to melee and when it exit it changes to ranged.
You can fire the ray to detect the location the cursor should have like this:
public LayerMask RaycastCollidableLayers;
public RaycastHit Hit;
public float CheckDistance = 200f;
public Transform Cursor;
void PerformRaycast(){
//Set up ray
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//Fire ray
Physics.Raycast(ray, out Hit, CheckDistance + 0.1f, RaycastCollidableLayers);
if (Hit.collider == null)
{
//Debug.Log("Raycast hit nothing");
return;
}
else //Ray hit something
{
//Move cursor to Hit.point;
Cursor.position = Hit.point;
}
}
I try to figure out if a mouse click was inside of a rect transform or not.
Each time the user clicked on the screen, a ball will be thrown. But in case the user clicks on the pause button, no ball should be thrown.
I tried to solve with this piece of code but it seems that only the top right quarter of the rect transform is recognized. Here's a short video to show the actual problem: https://youtu.be/gdyDBK6ubgo
Here's the code snippet:
void Update() {
//Check if user touch on display / click mouse button
Vector2 mousePos = new Vector3(Screen.width - Input.mousePosition.x,Screen.height - Input.mousePosition.y, 0);
if (Input.GetMouseButtonDown(0) && props.throwable && !checkCollisionWithPauseButton(mousePos) && props.remainingBalls > 0)
{
fireBall(Input.mousePosition);
}
}
bool checkCollisionWithPauseButton(Vector3 mousePos){
//TODO: This does not work very well
return pauseButton.GetComponent<RectTransform>().rect.Contains (mousePos);
}
Here's a screenshot which shows the rect transform.
Use RectTransformUtility.RectangleContainsScreenPoint to check if the mouse pointer is in the Rect Transform. This will work regardless of where the rectTransform is positioned locally.
public RectTransform rectTransform;
...
Vector2 mousePos = Input.mousePosition;
RectTransformUtility.RectangleContainsScreenPoint(rectTransform, mousePos);
None of these worked in my case. Here is what I ended up doing:
public RectTransform rectTransform;
...
Vector2 lp;
Vector2 mousePos = Input.mousePosition;
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, mousePos, Camera.main, out lp);
if (rectTransform.rect.Contains(lp))
..ect
It's not working because you're performing some unnecessary work. You don't need this line:
Vector2 mousePos = new Vector3(Screen.width - Input.mousePosition.x,Screen.height - Input.mousePosition.y, 0);
Simply pass Input.mousePosition to the Rect.Contains function and that should work.
Vector2 mousePos = Input.mousePosition;
pauseButton.GetComponent<RectTransform>().rect.Contains (mousePos);
Even though that may work, this is not how to detect clicks properly on UI or GameObjects. The EventSystems is used for that. See this for example.
I am creating a 2D game in Unity and wanted to see if anyone has any suggestion on how I can improve the script below which I am using to drag a paddle in a breakout/arkanoid style game. I know there are more complex ways to drag objects but this works ok for me but the only issue I encounter is that when I test my game on a mobile device the dragging is not 100% sharp and when I stop dragging the paddle seems to lag ever so slightly. I don’t have any issues with my mobile device as I have played other breakout games I downloaded from the Play store and the dragging is very crisp.
The script below is attached to the paddle.
Vector3 dist;
float posX;
float posY;
void OnMouseDown(){
dist = Camera.main.WorldToScreenPoint(transform.position);
posX = Input.mousePosition.x - dist.x;
posY = Input.mousePosition.y - dist.y;
}
void OnMouseDrag(){
Vector3 curPos = new Vector3(Input.mousePosition.x - posX, Input.mousePosition.y - posY, dist.z);
Vector3 worldPos = Camera.main.ScreenToWorldPoint(curPos);
transform.position = worldPos;
}
I would advise doing your translation of the x and y-axis values using the mouse's position as it's being dragged via the subtracted difference between what the mouse's position value was as it's first pressed down.
A summary of that would be:
Store a 2D vector as a private field, called previousPos (so
mousedown and mousemove can both access it) and set it's value to the result of the mouse's current position.
In the mouse drag function, get the mouse position again and assign the
value to a 2D vector called currentPos and subtract that from
the previousPos, which gives you the x and y-axis values to set the
paddle's transform position to.
Then update the previousPos at the end of the drag function, and
assign the value of currentPos to it so that next time around it
gives the new change and not the aggregated change.
*You probably want to have a boolean that you set to true when mouse is down and at the end of the drag set it to false. Use that bool in the drag as a check to see if you need to set the paddle position in the first place (only set paddle position if the boolean is true) -- Not sure if unity does this for you by only firing mouse dragged when the mouse is down or not.
An example in code:
private Vector2 _previousPos;
private bool _isMouseDown;
void OnMouseDown(){
_isMouseDown = true; //because we're in the on mouse down function
_previousPos = Input.mousePosition; //get the current mouse position
}
void OnMouseDrag(){
if(!_isMouseDown)
return;
Vector2 currentPos = Input.mousePosition; //get updated mouse pos
Vector2 paddlePos = currentPos - previousPos; //the delta change
transform.position = paddlePos; //new paddle position
_isMouseDown = false; //drag is complete, mouse btn is no longer down
_previousPos = currentPos; //reset previous pos for next mouse move
}
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.