I have a script that can rotate an object by touch or mouse on it but I want to rotate the object when mouse goes over the corner of the object only. How can I do this?
The code I'm using is
private float baseAngle = 0.0f;
void OnMouseDown(){
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
pos = Input.mousePosition - pos;
baseAngle = Mathf.Atan2(pos.y, pos.x) * Mathf.Rad2Deg;
baseAngle -= Mathf.Atan2(transform.right.y, transform.right.x) *Mathf.Rad2Deg;
}
void OnMouseDrag(){
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
pos = Input.mousePosition - pos;
float ang = Mathf.Atan2(pos.y, pos.x) *Mathf.Rad2Deg - baseAngle;
transform.rotation = Quaternion.AngleAxis(ang, Vector3.forward);
}
Put invisible GameObjects or RectTransforms onto the corners of your object you want to rotate, and use them as the controls for the parent object.
One way of doing so would be to add colliders to the corners of your object.
Using OnCollisionStay(), you could then trigger the appropriate functions when the mouse button is pressed. I've done similar myself, and this way does work.
Another method to the madness:
You could raycast where you are clicking, if your raycast is within an appropriate distance to the corners (which you could calculate based on its dimensions) then permit the rotation on click.
Related
Tried doing 2D and 3D. I want the player to click and drag objects to stack them. I tried copying and pasting code from a video and it still didn't work.
UPDATE! Now it kinda works but it teleports the object far off the screen. I want the player to be able to smoothly click and drag the object.
The code as of now:
using UnityEngine;
using System.Collections;
public class DragObject : MonoBehaviour
{
private float deltax, deltay;
private void OnMouseDown()
{
deltax = 1f;//Camera.main.ScreenToWorldPoint(Input.mousePosition).x - transform.position.x;
deltay = 1f; //Camera.main.ScreenToWorldPoint(Input.mousePosition).y - transform.position.y;
}
private void OnMouseDrag()
{
Vector2 currentTouchPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
transform.position = new Vector2((Input.mousePosition).x - deltax, (Input.mousePosition.y) - deltay);
}
}
It's not clear at all what exact problem is for you, you obviously need to give more info and describe your issue better. But I'll try to help as I can.
To get mouse position you need to use:
Input.mousePosition
If you need mouse position in 2d coordinates you can use this:
Vector2 currentTouchPos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
This method works regardless of your camera type or how the camera moves between frames
First, when the object is first clicked, get the plane at the object's position perpendicular to the direction of the camera:
Plane dragPlane = new Plane(Camera.main.transform.forward, transform.position);
Then, get the ray coming out of the camera from where the mouse is:
Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
Find out where that ray intersects using Plane.Raycast:
float enter = 0.0f;
if (dragPlane.Raycast(camRay, out enter))
{
Vector3 fingerPosition = camRay.GetPoint(enter);
This gives you a position in world space, which is where you can consider the finger to currently be at. Just put the object there:
transform.position = fingerPosition;
Altogether it might look like this:
void OnMouseDrag()
{
Plane dragPlane = new Plane(Camera.main.transform.forward, transform.position);
Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
float enter = 0.0f;
if (dragPlane.Raycast(camRay, out enter))
{
Vector3 fingerPosition = camRay.GetPoint(enter);
transform.position = fingerPosition;
}
}
No OnMouseDown necessary.
An alternative is to keep track of how fingerPosition changes over time, and Translate the transform based on that. Keeping as much math as possible in world units makes it easy:
Vector3 prevFingerPosition = Vector3.zero;
// Same code as before but we use it in different places so it goes in its own method.
// If the Raycast fails, return a decent guess.
private Vector3 GetMouseOnPositionInWorldSpace()
{
Plane dragPlane = new Plane(Camera.main.transform.forward, transform.position);
Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
float enter = 0.0f;
if (dragPlane.Raycast(camRay, out enter))
{
return camRay.GetPoint(enter);
}
return prevFingerPosition;
}
void OnMouseDown()
{
prevFingerPosition = GetMouseOnPositionInWorldSpace();
}
void OnMouseDrag()
{
Vector3 fingerPosition = GetMouseOnPositionInWorldSpace();
transform.Translate(fingerPosition-prevFingerPosition);
prevFingerPosition = fingerPosition;
}
There is an issue behind using OnMouseDrag to handle dragging objects around is that if the cursor moves too quickly, it can move off the object in the span of a single frame, and it will "drop" the object.
If that's not desirable, you might want to set a bool to be true in OnMouseDown that remembers that the object was clicked and a move the logic out of OnMouseDrag and into Update where it can do its thing if that bool is true. And when the mouse is released, set that bool back to false.
I have a cog that the user can turn to rotate a drawbridge. Currently I have the cog and the drawbridge rotating at the same rate, like so: https://gyazo.com/14426947599095c30ace94a046e9ca21
Here is my current code:
[SerializeField] private Rigidbody2D thingToRotate;
void OnMouseDrag()
{
Vector3 mousePosition = Input.mousePosition;
mousePosition = Camera.main.ScreenToWorldPoint(mousePosition);
Vector2 direction = new Vector2(
mousePosition.x - transform.position.x,
mousePosition.y - transform.position.y
);
transform.right = direction;
thingToRotate.transform.up = transform.right;
}
I want it so that when the user turns the cog it only turns the object a little bit, so the user can turn the cog a few times before the drawbridges closes.
I've tried adding to the drawbridges euler angle. I've tried setting the drawbridges rotation to the cog rotation and dividing that rotation by 2.
Don't set fixed orientations but use the proper methods instead.
Mathf.SignedAngle to determine the angle difference between current transform.right and the direction
If using RigidBody2D use Rigidbody2D.MoveRotation instead of setting the rotation through the Transform component.
Then I would store the totalAngle that was rotated in order to e.g. invoke some event when enough rotation was done.
The thingToRotate you simply rotate only to totalAngle / factor.
// Adjust in the Inspector how often the cog thing has to be turned
// in order to make the thingToRotate perform a full 360° rotation
public float factor = 5f;
private float totalAngle = 0f;
[SerializeField] private Rigidbody2D thingToRotate;
private void OnMouseDrag()
{
var mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
// this is shorter ;)
Vector2 direction = (mousePosition - transform.position).normalized;
// get the angle difference you will move
var angle = Vector2.SignedAngle(transform.right, direction);
// rotate yourselve correctly
transform.Rotate(Vector3.forward * angle);
// add the rotated amount to the totalangle
totalAngle += angle;
// for rigidBodies rather use MoveRotation instead of setting values through
// the Transform component
thingToRotate.MoveRotation(totalAngle / factor);
}
I recently started developing my very first top down 2d game. My problem is not knowing exactly how to get the bullet to go where the mouse is facing at the time of the activation of the bullet. I have a face mouse function as seen here
void faceMouse()
{
Vector3 mousePosition = Input.mousePosition;
mousePosition = Camera.main.ScreenToWorldPoint(mousePosition);
Vector2 direction = new Vector2(
mousePosition.x - transform.position.x,
mousePosition.y - transform.position.y);
transform.up = direction;
}
However, I am not sure how to incorporate that if at all to be able to shoot at the location of my mouse. Thanks in advance!
You could try Quaternion.LookRotation() which creates a rotation based on a forward and upward vector (Documentation). Then you need to assing that rotation to your object. I use it something like this:
cursorPos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
var forwardDirection = transform.position - cursorPos;
//Vector3.forward because is the z axis which is up/down in 2D
Quaternion playerRotation = Quaternion.LookRotation(forwardDirection, Vector3.forward);
transform.rotation = playerRotation;
Im trying to get my GameObject to point towards the mouse. It has a child object with a sprite. The childs rotation is set to 0 on all zxis. The sprite points upwards (positive X) at start. The GameObject rotates with the mouse pointer but its always turning its right side towards the mouse pointer. Also when i add force forward it accelerates at the same direction the sprite is pointing which as stated before is not the direction the mouse is. What is my code missing?
var cam = Camera.main;
// Distance from camera to object. We need this to get the proper calculation.
float camDistance = cam.transform.position.y - transform.position.y;
// Get the mouse position in world space. Using camDis for the Z axis.
Vector3 mouse = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, camDistance));
float AngleRad = Mathf.Atan2(mouse.y - transform.position.y, mouse.x - transform.position.x);
float angle = (180 / Mathf.PI) * AngleRad;
rb2d.rotation = angle;
Your shape is rotated by 90 degrees due to the way that your calculations resolve the angle, you can account for this by using:
rb2.rotation = angle - 90;
I want to restrict player movement in the sphere, the schematic diagram show as below. If player movement is out of range, then restrict player to sphere max radius range.
How can I write C# code to implement it, like this?
These are my current steps:
Create 3D sphere
Create C# code append to sphere object
My code so far:
public Transform player;
void update(){
Vector3 pos = player.position;
}
I don't know how you calculate your player`s position but before assigning the new position to the player you should check and see if the move is eligible by
checking the new position distance form the center of the sphere
//so calculate your player`s position
//before moving it then assign it to a variable named NewPosition
//then we check and see if we can make this move then we make it
//this way you don't have to make your player suddenly stop or move it
//back to the bounds manually
if( Vector3.Distance(sphereGameObject.transform.position, NewPosition)< radius)
{
//player is in bounds and clear to move
SetThePlayerNewPosition();
}
What #Milad suggested is right but also include the fact you won't be able to "slide" on the sphere border if your movement vector even slightly goes outside the sphere :
(sorry for the crappy graphic skills...)
What you can do if you want to be able to "slide" on the sphere interior surface is get the angle formed between the player position and the X vector and then apply this angle with the :
public Transform player;
public float sphereRadius;
void LateUpdate()
{
Vector3 pos = player.position;
float angle = Mathf.Atan2(pos.y, pos.x);
float distance = Mathf.Clamp(pos.magnitude, 0.0f, sphereRadius);
pos.x = Mathf.Cos(angle) * distance;
pos.y = Mathf.Sin(angle) * distance;
player.position = pos;
}
Just make sure using this won't counter effect your player movement script (that's why I put it in LateUpdate() in my example).