Unity3D How to make object go away form mouse click position 2D - unity3d

Im thinking about it two days. I still did not made any progress.
I wonder how to do that objects fly away from mouse click position in 2D view?
I tried like that:
pos = Input.mousePosition;
Vector3 realWorldPos = Camera.main.ScreenToViewportPoint(pos);
print("MOuse pos: " + realWorldPos);
//print(realWorldPos);
Vector3 velo = GetComponent<Rigidbody2D>().velocity;
if (realWorldPos.x < 0.5)
{
velo = new Vector3((realWorldPos.x * speed), velo.y);
}
else if(realWorldPos.x > 0.5)
{
velo = new Vector3((realWorldPos.x * speed) * (-1), velo.y);
}
if (realWorldPos.y < 0.5)
{
velo = new Vector3(velo.x, realWorldPos.y * speed);
}
else if (realWorldPos.y > 0.5)
{
velo = new Vector3(velo.x, (realWorldPos.y * speed) * (-1));
}
GetComponent<Rigidbody2D>().velocity = velo;
But it doesnt work as I want.
Is it possible to do this?

For this to work your Rigidbody2D must have Gravity Scale set to 0.
This is a simple test code that works for me, is placed on a sprite object:
public class PushPlayer : MonoBehaviour
{
public float pushPower = 50.0f;
Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
if(Input.GetMouseButtonDown(0))
{
Vector3 dir = transform.position - Camera.main.ScreenToWorldPoint(Input.mousePosition);
dir = dir.normalized;
rb.AddForce(dir * pushPower, ForceMode2D.Force);
// as alternative:
rb.velocity = dir * pushPower;
}
}
}
You need to adjust the values a bit, also in the regidbody (like drag) to get it the way you want.
Edit:
transform.position - Camera.main.ScreenToWorldPoint(Input.mousePosition): calculate the directional vector from the mouse position to the player position (have a look a vector algebra if you are not familiar with this) which is the direction away from the click (in a straight line).
dir.normalized: this shortens the vector to a length (= magnitude) of 1 (again have a look at vectors) so it really is just a direction. You could omit this and reduce the factor, but doing it this way your factor equals the force you use.

Related

Unity3D: World Rotation to Local Rotation

I'm really struggling with this. I have a turret that is mounted on a spaceship. It's a child object of the ship. The turret is allowed to point left, right, up, but not down, because that would be pointing through the spaceship.
So, I need to limit the rotation of the turret so it won't point down. I started with this code:
`
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TurretScriptTest : MonoBehaviour
{
public float rotateSpeedBase = 1f;
public float rotateSpeedCurr = 1f;
public float yMin = 0;
public float yMax = 1;
public Transform target;
// Start is called before the first frame update
void Start()
{
rotateSpeedCurr = rotateSpeedBase;
}
// Update is called once per frame
void Update()
{
if (target && target.gameObject.activeSelf)
{
// Rotate
RotateWithLock();
}
}
public virtual void RotateWithLock()
{
if (target)
{
Vector3 targetDir = target.position - transform.position;
float step = rotateSpeedCurr * Time.deltaTime;
Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, step, 0.0f);
newDir.y = Mathf.Clamp(newDir.y, yMin, yMax);
transform.rotation = Quaternion.LookRotation(newDir);
}
}
}
`
This works great except for one thing. The spaceship is also rotating all over the place. As soon as the parent spaceship goes off its original axis the above code is worthless. The turret will point through the spaceship or whatever else is in the way.
I'm assuming that what I need to do is convert everything to local rotation and position so that "down" for the turret will always be its base, not "down" in world space. So I try this code:
`
public virtual void RotateWithLock()
{
if (target)
{
Vector3 targetDir = target.position - transform.position;
float step = rotateSpeedCurr * Time.deltaTime;
Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, step, 0.0f);
Vector3 newDirLocal = transform.InverseTransformDirection(newDir);
newDirLocal.y = Mathf.Clamp(newDirLocal.y, yMin, yMax);
transform.localRotation = Quaternion.LookRotation(newDirLocal);
}
}
`
Now the turret doesn't move at all. What to do?
If your turret's gun is a child of the turret's base, then you can just use "transform.localRotation" to get the rotation relative to the base.
If not, then try this:
If you have the Transform of the base, then you can use "Quaternion.Inverse" to get the inverse of the base's rotation. Then just take the LookRotation and multiply it by this inverse (Quaternion1 * Quaternion2 just adds them together). To convert that to a Direction Vector for the clamp calculations, just do "Quaternion * new Vector3(0, 0, 1)"
Vector3 targetDir = target.position - transform.position;
//Gets rotation to the target
Quaternion rotToTarget = Quaternion.LookRotation(newDirLocal);
float step = rotateSpeedCurr * Time.deltaTime;
//Gets new rotation
Quaternion newRot = Quaternion.RotateTowards(transform.rotation, rotToTarget, step);
//Gets the local rotation by adding (yes adding) the inverse of the base rotation
//Make sure to set the turretBase variable to the turret base's GameObject
newRot = newRot * Quaternion.Inverse(turretBase.transform.rotation);
//Gets a vector that points to the direction
Vector3 dirFromRot = newRot * new Vector3(0, 0, 1);
//This sets the y-limit. The yLimit should be less than 1.
//I am using your code, but a more effective way would be to limit the angle of the quaternion.
dirFromRot.y = Mathf.Clamp(dirFromRot.y, -yLimit, yLimit);
//Gets the LookRotation of this clamped Vector3 and sets the result.
transform.localRotation = Quaternion.LookRotation(dirFromRot.normalized);

Unity3D Part Movement

With the help from people here (and the Unity forum) I am almost happy with the build i am making.
This build is showing a Booster Pump, used for boosting waterpressure for industrial cleaning.
So far i can zoom, rotate, Pan and press keys 1-3 to show/hide internal parts.
I would like to make the internal parts MOVE out to position on keypress, instead of just appearing in position (like they do now - 1st. image), so I have this movement script.
But when i rotate/move the Booster Pump, the it does not look right (2nd image).
The moved part is not following the main part of the Booster Pump.
From what I gather it have to do with local or world space, but I dont know which and how to implement it?
enter image description here
enter image description here
{
Vector3 EndPos;
Vector3 StartPos;
private float distPos;
public float MovSpeed;
private float direction = 0f; // The direction of travel
public float maxDistance = 2f; // The maximum move distance
void Start()
{
StartPos = transform.position;
EndPos = StartPos + Vector3.back * maxDistance;
}
void Update()
{
if (Input.GetKey(KeyCode.Space) && distPos == 1f)
direction = -1f;
if (Input.GetKey(KeyCode.Space) && distPos == 0f)
direction = 1f;
distPos = Mathf.Clamp01(distPos + (Time.deltaTime * MovSpeed * direction));
if (distPos == 0f || distPos == 1f) direction = 0f;
transform.position = Vector3.Lerp(StartPos, EndPos, distPos);
}
If the internal parts are child objects of the parent, and you want them to move around independently while also continuing to rotate and move with their parent, you should change their localPositions. By changing local position instead of world, you are telling them to move that distance away from their initial positions, relative to the center of their parent - no matter where the parent is or how it's rotated at the time.
Try changing
StartPos = transform.position;
to
StartPos = transform.localPosition;
and then change
transform.position = Vector3.Lerp(StartPos, EndPos, distPos);
to
transform.localPosition = Vector3.Lerp(StartPos, EndPos, distPos);

Unity Mouse Input without Ray/Raycast and Colliders

First of all thank you for your time. I'm quite new in this so I'm struggling a bit. I'm trying to make a drag and release shooter which doesn't really depend on colliders or raycasts but solely depends on mouse delta and camera position. The way I'm trying to have it done is I'm mapping mouse position (x,y) to velocity direction such as new Vector3(mouseX, 0f, mouseY) then rotating it about Y axis to match the visual drag on the screen.
void OnMouseUp()
{
releasePos = Input.mousePosition;
Vector2 m_direction = releasePos - initialPos;
Vector3 direction = new Vector3(m_direction.x, 0, m_direction.y);
float userDrag = Mathf.Clamp(direction.magnitude, 0f, maxVelocity);
direction = Translation(direction);
direction = Vector3.Normalize(direction);
rigidbody.velocity = (direction * userDrag) * force;
}
private Vector3 Translation(Vector3 direction)
{
Vector3 camPos = Camera.main.transform.position;
camPos.y = 0;
float angle = Vector3.Angle(new Vector3(0f, 0f, 1f), camPos);
Quaternion translatedAngle = Quaternion.AngleAxis(angle, Vector3.up);
direction = translatedAngle * direction;
But as the angle changes it kinda fails to deliver what I'm asking for. Is there a way I can avoid bunch of if, else statements for the angle value or a shorter way of doing this?
Example
OnMouseUp is fundamentally a collider method.
Meaning, the mouse MUST be over the GameObject's collider for the GameObject this code is attached to. If you want to move away from using colliders, then you need to move away from this method.
The generic works-anywhere way of saying "is the mouse button up or down?" is the static methods available in the Input class:
Input.GetMouseButtonDown() True on the single frame after the mouse button is depressed
Input.GetMouseButtonUp() True on the single frame after the mouse button is released
Input.GetMouseButton() True any frame the mouse button is depressed (otherwise false)
So I think I have a solution finally and I thought I'd share it for people who might be interested.
The challenge was to rotate mouse delta's direction according to camera position so it gives a natural vibe to the player when dragging and releasing the ball for putting. I did some tests to see where my code had problems rotating delta direction properly(so it matches the screen) and realized when I'm "looking from" (0,0,1) for comparison while the camera's x position is positive it works fine. But when x is negative it doesn't because Vector3.Angle doesn't return a negative value as far as I'm concerned so I just multiplied the result with minus. Seems to work.
Here is the final code:
private void Start()
{
rigidbody = GetComponent<Rigidbody>();
}
void OnMouseDown()
{
ballPos = Input.mousePosition;
}
void OnMouseUp()
{
Vector2 m_direction = Input.mousePosition - ballPos;
Vector3 direction = new Vector3(m_direction.x, 0, m_direction.y);
float userDrag = Mathf.Clamp(direction.magnitude, 0f, maxVelocity);
direction = Translation(direction);
direction = Vector3.Normalize(direction);
rigidbody.velocity = (direction * userDrag) * force;
}
private Vector3 Translation(Vector3 direction)
{
float angle = GetAngle();
Quaternion translatedAngle = Quaternion.AngleAxis(angle, Vector3.up);
direction = translatedAngle * direction;
return direction;
}
private float GetAngle()
{
Vector3 camPos = Camera.main.transform.position;
camPos.y = 0;
if (camPos.x > 0)
{
angle = Vector3.Angle(Vector3.forward, camPos);
}
else if (camPos.x <0)
{ angle = -Vector3.Angle(Vector3.forward, camPos); }
return angle;
}
please do share if you have suggestions regarding the code.

Restricting movement of a 2d object to backward only in UNITY

I'm making a simple project in Unity where there is a Ball attached to a SpringJoint2d component the ball is on an angled slope, like in the image below:
I simply want the user to be able to drag the ball backward along the edge of the slope only,in other words I don't want the user to be able to move the ball away from the slope or into it.
I'v been trying several ways I thought could do the job hers the script of the dragging with what I tried:
(This Is the updated version)
public class ball : MonoBehaviour
{
public Rigidbody2D rb;
public Transform spring;
public Transform calcpoint;
private Vector3 start;
private Vector3 end;
private bool isPressed = false;
RaycastHit2D[] hits = new RaycastHit2D[2];
RaycastHit2D[] hits2 = new RaycastHit2D[2];
float factor = 0;
private void OnMouseDown()
{
if (!isPressed)
{
isPressed = true;
rb.isKinematic = true;
}
}
private void OnMouseUp()
{
isPressed = false;
rb.isKinematic = false;
StartCoroutine(release());
}
/// <summary>
/// release the ball from the spring joint after a small amount of time
/// </summary>
/// <returns></returns>
IEnumerator release()
{
yield return new WaitForSeconds(0.1f);
rb.GetComponent<SpringJoint2D>().enabled = false;
}
// Update is called once per frame
void Update()
{
if (isPressed)
{
if (Vector3.Distance(spring.position, rb.position) > 3f || spring.position.x < (rb.position.x - 1)) return;//restrict the dragging of the ball to not go beyond the spring point and not too far back
float angle = 0;
if (checkGround() > 1)//if we hit the slope with the ray cast downward from the mouse/Tap position
{
angle = Mathf.Abs(Mathf.Atan2(hits[1].normal.x, hits[1].normal.y) * Mathf.Rad2Deg); //get angle
factor = (float)(((45 - angle) * 0.02) + 1) * (angle / 45);//an inaccurate formula to offset the ball to be on top of the slope that works just fine with some glitches
rb.position = hits[1].point + new Vector2(0, factor * 1f);//position the ball at the point were the ray cast downward from the mouse hit
//(that puts the ball center on the line of the slope) so I offset it usinf the formula above
}
}
}
private int checkGround()
{
int h = Physics2D.RaycastNonAlloc(Camera.main.ScreenToWorldPoint(Input.mousePosition), -Vector2.up, hits); //cast downwards
return h;
}
}
here are the settings on the ball:
and the slope setup:
The dragging of the ball works fine ,at one point the player could drag it in the air or into the slope, I managed to fix that with the new code so now the player could only drag it on the edge, though my calculations are still a bit flawed and when the slopes angle is changed the ball would dip a bit inside the slope and that causes some problems at release.
The method used to try to solve the problem is simple, when the player start dragging the ball I cast a ray from the mouse downward and pit the ball on the point of impact with the slope ,offsetting it to sit on top of it,right ow the problem is that the offsetting part is not accurate enough.
I hope I explained myself a bit better this time Thanks:)
After lots of trial and error I did manage to come up with a perfect solution to the problem so I thought I might as well share the answer maybe it will help someone.
here is the updated code I changed my method of restricting the movement completely now I use simple linear line equation as shown below:
private void OnMouseDown()
{
if (!isPressed)
{
//cast a ray on the slope
if (checkGround() > 1)
{
angle = Mathf.Abs(Mathf.Atan2(hits[1].normal.x, hits[1].normal.y) * Mathf.Rad2Deg); //get angle
slope = Mathf.Tan(angle * Mathf.Deg2Rad);//get the slope steepiness
}
isPressed = true;
rb.isKinematic = true;
}
}
private void OnMouseUp()
{
isPressed = false;
rb.isKinematic = false;
StartCoroutine(release());
}
void Update() {
if (isPressed)
{
xMove = Camera.main.ScreenToWorldPoint(Input.mousePosition).x - spring.position.x;//get how much the mouse moved backward or forward
if (xMove < -3f ) xMove = -3f; //restrict the drag range to 3 backward
if (xMove > 0.3f) xMove = 0.3f;//restrict the drag range to 0.3 forward
xpos = spring.position.x+xMove;//since the ball and the spring start at exactly the same position the new ball's x position would be the spring x + the x movement we calculated above
ypos = (xMove * slope)- spring.position.y; //the y posistion would be y=mx+b so the the x movement * the slop steepiness - the starting y position
rb.position = new Vector2(xpos, -ypos);//set the new position of the ball
}
}
private int checkGround()
{
int h = Physics2D.RaycastNonAlloc(Camera.main.ScreenToWorldPoint(Input.mousePosition), -Vector2.up, hits); //cast downwards
return h;
}

Rotate camera based on mouse postitions around a object Unity3D

Okay, So I have come this far:
public class CameraScript : MonoBehaviour {
public void RotateCamera()
{
float x = 5 * Input.GetAxis("Mouse X");
float y = 5 * -Input.GetAxis("Mouse Y");
Camera.mainCamera.transform.parent.transform.Rotate (y,x,0);
}
}
My camera has a parent which I rotate based on my mouse position. The only problem is that I can only swipe with the mouse to rotate the object. How can I rotate the object which is my camera is attached to based on my mouse position if I just click next to the object. Thanks in advance!
The value will be in the range -1...1 for keyboard and joystick input.
If the axis is setup to be delta mouse movement, the mouse delta is
multiplied by the axis sensitivity and the range is not -1...1. Unity Document
Note: This link is usefull please check it.
So you need to change your code like this.
public void RotateCamera()
{
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); // Gets mouse position to Unity World coordinate system
Camera.mainCamera.transform.parent.transform.Rotate (mousePosition);
}
if there are problem you can do like this
public void RotateCamera()
{
Vector3 position = new Vector3(Input.mousePosition.x, Input.mousePosition.y,0);
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(position); // Gets mouse position to Unity World coordinate system
Camera.mainCamera.transform.parent.transform.Rotate (mousePosition);
}
one more option is rotateTowards.
public float speed=10; //any value > 0
public void RotateCamera()
{
Vector3 targetDir = Camera.main.ScreenToWorldPoint(Input.mousePosition) - Camera.mainCamera.transform.parent.transform.position;
float step = speed * Time.deltaTime;
Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, step, 0.0F);
Camera.mainCamera.transform.parent.transform.rotation = Quaternion.LookRotation(newDir);
}
Maybe some syntax errors, i don't check them.