I am trying to rotate my gameObject in all axes (x,y,z) with mouse drag function. The gameObject should be rotated smoothly on release. With some adjustments I have achieved to rotate it in y-direction when mouse drags Left-Right. However I am finding it really complicated to rotate in the other 2 axes. Can someone explain how do I achieve this?
using System.Collections.Generic;
using System.Collections;
using UnityEngine.EventSystems;
using UnityEngine;
public class RotateModel : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
public float rotationSpeed;
public float rotationDamping;
private float _rotationVelocity;
private float _preRotationVelocity;
private float _preRotationVelocityY;
private float _rotationVelocityY;
private bool _dragged;
public void OnBeginDrag (PointerEventData eventData) {
_dragged = true;
}
public void OnDrag (PointerEventData eventData) {
_preRotationVelocity = eventData.delta.x * rotationSpeed;
_rotationVelocity = Mathf.Clamp (_preRotationVelocity, -8f, 8f);
this.transform.Rotate (Vector3.up, -_rotationVelocity, Space.World);
}
public void OnEndDrag (PointerEventData eventData) {
_dragged = false;
}
private void Update () {
if (!_dragged && !Mathf.Approximately (_rotationVelocity, 0)) {
float deltaVelocity = Mathf.Min (
Mathf.Sign (_rotationVelocity) * Time.deltaTime * rotationDamping,
Mathf.Sign (_rotationVelocity) * _rotationVelocity
);
_rotationVelocity -= deltaVelocity;
this.transform.Rotate (Vector3.up, -_rotationVelocity, Space.World);
}
}
}
There are multiple ways to rotate objects in 3D. So the first step should be to determine what model you want to use. Also consider if you need to allow rotation around all three axes, and if you can provide tools to align rotation to other objects. Another point is if you want to rotate around the objects local axes, or the world axes, or allow switching between the two. Some examples of rotation models:
Separate axes
Use a key or button to determine what axis you should rotate around. This is probably the easiest alternative to implement, just switch Vector3.up for other vectors depending on the pressed key or button.
Rotation widgets
Render 3D circles around your object for each axis you want to rotate around. When the user clicks on the circle you would rotate around that axis. I personally prefer this model, but it might be the most difficult to implement. This blender example shows this style.
Virtual trackball
As the name suggest this essentially simulates a virtual trackball, and allow for rotation in all three dimensions at the same time. This article about 3d rotation explains the functioning, and links to a paper with more details. I personally find that this can be complicated to use, since all axes are affected and you cannot rotate around each axis in turn.
Related
trying to rotate object both in sinusoidal and circular form along 3 axis
using UnityEngine;
public class time_behaviour : MonoBehaviour
{
float time;
public float rate;
public float x_axis_amplitude;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
time =Time.time;
float x_rotation = x_axis_amplitude*Mathf.Asin(Mathf.Pow(Mathf.Sin(time*rate),2))*Mathf.Rad2Deg;
float y_rotation = 2*(Mathf.Acos(Mathf.Cos(time*rate))*Mathf.Rad2Deg);
transform.localRotation = Quaternion.Euler(x_rotation,y_rotation,0);
Debug.Log(transform.rotation.eulerAngles);
}
}
here is the code
as you can see i am trying to revolve object in sin form along x axis and circularly along y axis
i got desired outcome for x axis but for y axis the rotation keep flipping after 360 degree ,
Is there anyway that after 360 degree it dont flip its motion and rotate forever
I have tried doing transform.Rotate() but it dont rotate uniformly,it just keeps object stationary at that angle
For a school assignment I am trying to make a tank point and shoot game, where I need align the tank to the plane normal using raycasting. But when I do this the Y axis rotaton gets locked and I can't rotate it any more.
The idea is that if a plane is angled, the tank is sticking to it and the transform.up is defined by the plane normal and the tank is able to go up and down the plane and also rotate on it.
This is the code.
public class PlayerController : MonoBehaviour
{
public GameObject player;
public float movementSpeed;
public float rotationSpeed;
RaycastHit hitinfo;
public float hoverHeight = 0.7f;
float offsetdistance;
//public Vector2 moveVal;
public Vector2 moveVal;
public Vector3 Dir;
public float moveSpeed;
//get OnMovie input and put it in a vector to be used later on
void OnMove(InputValue value)
{
moveVal = value.Get<Vector2>();
}
void Update()
{
//moves player
PlayerHover();
player.transform.Translate(new Vector3(0, 0, moveVal.y) * moveSpeed * Time.deltaTime);
player.transform.Rotate(0, moveVal.x * rotationSpeed * Time.deltaTime, 0, Space.Self);
}
void PlayerHover()
{
if (Physics.Raycast(transform.position, -Vector3.up, out hitinfo, 20f))
{
offsetdistance = hoverHeight - hitinfo.distance;
transform.up = hitinfo.normal;
transform.position = new Vector3(transform.position.x, transform.position.y + offsetdistance, transform.position.z);
}
}
You are probably using the wrong overload of transform.Rotate, you should probably be using:
public void Rotate(Vector3 axis, float angle, Space relativeTo = Space.Self);
Where the axis parameter should be the plane normal. Assuming you want to rotate around the plane normal
If you want to set the rotation directly you might need to use SetPositionAndRotation, you should be able to get the quaternion from LookDirection using the plane normal for the forward parameter, but you may need to play around with the axes or apply a 90-degree rotation to get the desired axis to point in the right direction.
In general if you want to rotate an object so that one axis matches up to some other axis you can:
Calculate the angle between the axes, i.e. compute the dot-product and apply the ACos function. Or use any built in methods.
Calculate an orthogonal vector by taking the cross-product between the two vectors
Use this axis and angle in the Rotate function above to produce the desired rotation. Or any other functions that produce a rotation that take an axis and angle.
Use the up-direction of your tank and the plane normal as input axes for the algorithm above if you want to rotate your tank to be orientated to the plane.
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.
As the title says, I have a scene with three objects, a cube, a sphere, and a cylinder-like you can see in the image below.
What I'm trying to achieve is that when I press the "Rotate" button, the three objects rotate in an anti-clockwise direction, so the cylinder goes where the cube was, the cube goes where the sphere was, and the sphere goes where the cylinder was. If I click the button again, they rotate once again and so on. So far, I managed to make them rotate around an empty object at the center of the "triangle" they form with their initial position.
This is what happens when I first click the "Rotate" button:
As you can see, the object rotates, but they don't keep the same coordinate of the object that was previously in that position, so I was thinking of having them exchange coordinates. How can I achieve that or making them rotate how I want to?
Here is the code I wrote so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RotateObject : MonoBehaviour {
//rotating objects
[SerializeField]
private GameObject cube;
[SerializeField]
private GameObject sphere;
[SerializeField]
private GameObject cylinder;
private Renderer cubeRenderer;
private Renderer sphereRenderer;
private Renderer cylinderRenderer;
//rotation target
public GameObject target;
private void Start() {
cubeRenderer = cube.GetComponent<Renderer>();
sphereRenderer = sphere.GetComponent<Renderer>();
cylinderRenderer = cylinder.GetComponent<Renderer>();
gameObject.GetComponent<Button>().onClick.AddListener(Rotate);
}
void Rotate() {
//rotate objects by 120 degrees
cube.transform.RotateAround(target.transform.position, Vector3.up, -120);
sphere.transform.RotateAround(target.transform.position, Vector3.up, -120);
cylinder.transform.RotateAround(target.transform.position, Vector3.up, -120);
}
}
Thanks in advance for helping me!
The easiest way to make an object(s) rotate around a point is to have it/them as a child of the point then rotate the point itself.
Object Hierarchy
Rotator Script to be put on the parent object:
public class RotateObjects : MonoBehaviour
{
[SerializeField] Button rotateButton;
// Start is called before the first frame update
void Start()
{
rotateButton.onClick.RemoveAllListeners();
rotateButton.onClick.AddListener(RotateClockwise);
}
void RotateClockwise()
{
float newRotation = (360 / transform.childCount);
transform.Rotate(new Vector3(0, newRotation, 0));
}
}
You are rotating around an object. Like the earth rotates around the sun; that means it will change its position. Use
transform.rotation = new Quaternion(rotx, roty, rotz, rotw);
or
transform.Rotate(rotx, roty, rotz);
Instead of rotating about a point rotate the shape, it's self.
I have a simple movement that relvolves around moving foward depending on where I look. The camera is made to follow the player. The problem I am havaing is that whenever I hit an object, my character(along with the camera) start spinning crazily all over the place. (I am new to coding)
Here is the code to the movement of where my character is facing :
public class Player : MonoBehaviour {
public float movementSpeed = 10;
public float turningSpeed = 60;
public Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody> ();
}
void Update() {
float horizontal = Input.GetAxis("Mouse X") * turningSpeed *
Time.deltaTime;
transform.Rotate(0, horizontal,0);
float vertical = Input.GetAxis("Mouse Y")* turningSpeed * Time.deltaTime;
transform.Rotate(vertical, 0, 0);
float movement = Input.GetAxis("Foward") * movementSpeed *
Time.deltaTime;
transform.Translate(0, 0,movement);
}
}
(Sorry for bad format)
The Mouse X just makes it so that it rotates on the x axis of mouse(same with y axis). The Foward is just the vertical input preset in unity.
Here is the code to the lookAt player :
public class LookAtCamera : MonoBehaviour {
public GameObject target;
void LateUpdate() {
transform.LookAt(target.transform);
}
}
Answer
I believe your rigidbody is being more heavily affected than what you desire by the bounce force occuring as you hit the object.
Possible solutions
Depending on which collider type acts as your legs, the weight of the objects involved, what behaviour you intend and more the solution can vary.
Possible solution 1:
One option is to freeze the rotation of your object on it's Rigidbody component, you can decide which axes to freeze rotation on. If freezed they can't be pushed by outside forces.
Possible solution 2:
If you wish the player to only be affected by your script you can set the rigidbody kinematic, meaning that it only is affected by scripts and animation. This is a contrast to being set to dynamic where other objects can affect it.
Possible solution 3:
Play around with the angular drag and mass of the rigidbody to slow down rotation.