Unity3D Rotate ground using Acceleration is Shaking - c#

I am trying to roll a ball using gravity. I am also trying to rotate the ground using the accelerometer but the ground is shaky when it rotates. I also can't set a limit on the rotation of the x Axis.
void Update()
{
tilt = new Vector3(Input.acceleration.x, Input.acceleration.y, Mathf.Clamp(Input.acceleration.z,-15,15));
tilt = Quaternion.Euler(90, 0, 90) * tilt; // Change Rotation according to camera view
if(tilt.x > 0.05f || tilt.x < -0.05f && tilt.x > -6f || tilt.x < 6f)
{
transform.localRotation = new Quaternion(-tilt.x, 0, 0, 3);
}
if (tilt.z < -0.1f || tilt.z >0.1f)
{
transform.localRotation = new Quaternion(0, 0, -tilt.z, 3);
}
}

See if this helps. This is a simple example of many ways to implement angular velocity.
Please be advised that Gimbel's lock effect is not considered in calculations. You might want to restrict the rotation in a certain range. Also you will need a dead zone for delta, e.g. if(delta < epsilon) delta = 0;.
float speed = 40f;
void Update()
{
tilt = new Vector3(Input.acceleration.x, Input.acceleration.y, Mathf.Clamp(Input.acceleration.z,-15,15));
tilt = Quaternion.Euler(90, 0, 90) * tilt; // Change Rotation according to camera view
if(tilt.x > 0.05f || tilt.x < -0.05f && tilt.x > -6f || tilt.x < 6f)
{
var delta = -tilt.x - transform.localRotation.x;
transform.localRotation += (new Quaternion(delta, 0, 0, 3) * Time.deltaTime * speed);
}
if (tilt.z < -0.1f || tilt.z >0.1f)
{
var delta = -tilt.z - transform.localRotation.z;
transform.localRotation += (new Quaternion(0, 0, -tilt.z, 3) * Time.deltaTime * speed);
}
}

Related

How can I stop the characters rotations once they get to 0 on y?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AnimatorController : MonoBehaviour
{
public Animator[] animators;
public Transform target;
public float speed = 1f;
public float rotationSpeed;
private bool endRot = false;
// Use this for initialization
void Start()
{
for (int i = 0; i < animators.Length; i++)
{
animators[i].SetFloat("Walking Speed", speed);
}
}
// Update is called once per frame
void Update()
{
float distanceFromTarget = Vector3.Distance(animators[2].transform.position, target.position);
if (distanceFromTarget < 15)
{
float speed = (distanceFromTarget / 15) / 1;
for (int i = 0; i < animators.Length; i++)
{
animators[i].SetFloat("Walking Speed", speed);
}
}
if (distanceFromTarget < 2f)
{
for (int i = 0; i < animators.Length; i++)
{
animators[i].SetFloat("Walking Speed", 0);
}
if (animators[0].transform.localRotation.y != 0 && endRot == false)
{
animators[0].transform.Rotate(Vector3.up, rotationSpeed * Time.deltaTime);
animators[1].transform.Rotate(Vector3.up, -rotationSpeed * Time.deltaTime);
}
if (animators[0].transform.localRotation.y == 0)
endRot = true;
}
}
}
Both characters start with localRotation of euler angles (0f, 180f, 0f).
I want them to rotate to 0 on Y and then stop. Tried using a flag but I guess comparing floats is wrong. Should I use StartCoroutine for that ?
I tried to make animation clip using the Timeline but once I create a new animation track and drag in one of the characters for some reason it's changing the character position and it's weapon not attached to him. Not sure why it happens with the Timeline.
You need to clamp the rotation so that you don't overshoot within a single frame of rotation. Here is one way to do that.
Set the goal rotation:
Quaternion goalRotation = Quaternion.Euler(0f,0f,0f); // or Quaternion.identity
Find how far the first transform's rotation is from that in degrees:
float angleToGoal = Quaternion.Angle(goalRotation, animators[0].transform.localRotation);
Determine which is smaller: the angle given by the speed * delta time or the angle remaining to figure out how far you should rotate this frame. This is the important clamping part:
float angleThisFrame = Mathf.Min(angleToGoal, rotationSpeed * Time.deltaTime);
Then, rotate by that amount. And if you did rotate by angleToGoal, then set endRot = true;.
This could look like so:
if (distanceFromTarget < 2f)
{
for (int i = 0; i < animators.Length; i++)
{
animators[i].SetFloat("Walking Speed", 0);
}
if (!endRot)
{
Quaternion goalRotation = Quaternion.Euler(0f,0f,0f);
float angleToGoal = Quaternion.Angle(
goalRotation,
animators[0].transform.localRotation);
float angleThisFrame = Mathf.Min(angleToGoal, rotationSpeed * Time.deltaTime);
// use axis of Vector3.down to keep angles positive for ease of use
animators[0].transform.Rotate(Vector3.down, angleThisFrame);
animators[1].transform.Rotate(Vector3.up, angleThisFrame);
// We end if we rotated the remaining amount.
endRot = (angleThisFrame == angleToGoal);
}
}
There's nothing wrong with comparing floats, you just to be aware of the precision - it is essentially impossible to stop exactly at 0. But what you can do it stop when it's very close, and then true up afterwards. For example:
if (animators[0].transform.localRotation.y >= 0.1f && endRot == false)
and then have:
if (animators[0].transform.localRotation.y <= 0.1f)
{
endRot = true;
// set y to zero, keep x and z rotation the same.
animators[0].transform.localRotation = Quaternion.Euler(animators[0].transform.localEulerAngles.x, 0, animators[0].transform.localEulerAngles.z);
}
depending on how fast you are rotating, you may need to adjust the margin so there is little difference between the point at which it stops rotating and 0.

Unity: Rotating The Camera Using The Law Of Cosine

I am making the basic controls for a game that is mouse-oriented. I want the camera to follow the player and rotate around the player using the law of cosine to find an angle. Here is my code:
//Move Camera
camX = player.transform.position.x;
camY = 15.5f;
camZ = player.transform.position.z;
//Rotate Camera
rotTriA = player.transform.position.x;
rotTriB = player.transform.position.z;
rotTriC = Mathf.Sqrt((rotTriA * rotTriA) + (rotTriB + rotTriB));
if(rotTriA > 0 && rotTriB > 0)
{
getAngle = (Mathf.Acos(((rotTriA * rotTriA)
+ (rotTriB * rotTriB)
- (rotTriC * rotTriC))
/ (2 * (rotTriA) * (rotTriB))) * Mathf.Rad2Deg);
}
else if(rotTriA > 0 && rotTriB < 0)
{
getAngle = (Mathf.Acos(((rotTriA * rotTriA)
+ (rotTriB * rotTriB)
- (rotTriC * rotTriC))
/ (2 * (rotTriA) * (rotTriB))) * Mathf.Rad2Deg) + 90;
}
else if(rotTriA < 0 && rotTriB < 0)
{
getAngle = (Mathf.Acos(((rotTriA * rotTriA)
+ (rotTriB * rotTriB)
- (rotTriC * rotTriC))
/ (2 * (rotTriA) * (rotTriB))) * Mathf.Rad2Deg) + 180;
}
else if(rotTriA < 0 && rotTriB > 0)
{
getAngle = (Mathf.Acos(((rotTriA * rotTriA)
+ (rotTriB * rotTriB)
- (rotTriC * rotTriC))
/ (2 * (rotTriA) * (rotTriB))) * Mathf.Rad2Deg) + 270;
}
else if(rotTriA == 0 && rotTriB > 0)
{
getAngle = 90;
}
else if(rotTriA == 0 && rotTriB < 0)
{
getAngle = 270;
}
else if(rotTriA > 0 && rotTriB == 0)
{
getAngle = 0;
}
else if(rotTriA < 0 && rotTriB == 0)
{
getAngle = 180;
}
else
{
getAngle = 0;
}
Debug.Log(getAngle);
if (camZ < 0)
{
camZ = player.transform.position.z + 10.5f;
transform.eulerAngles = new Vector3(135f, getAngle, 0.0f);
}
else
{
camZ = player.transform.position.z - 10.5f;
transform.eulerAngles = new Vector3(45f, getAngle, 0.0f);
}
camZ = player.transform.position.z - 15.5f;
cam = new Vector3(-camX, camY, camZ);
mainCamera.transform.position = cam;
I know how to make the camera follow the player, but I don't know how to make the camera rotate around the player without it freaking out.
Possible reasons why this is occurring: I am using raycasting to move the player, so when I move my mouse outside of the arena, the movements wont register, I might not be doing the law of cosine right because I get values up to 400 (which shouldn't be possible, given a circle is 360 degrees).
The easiest way to make a camera rotate around the player would be to take advantage of the game object heirarchy.
First make an empty GameObject and then make your camera a child of it:
EmptyObject
> Camera
Now orient your camera to look at the position of the empty object. The location of this empty object is now where your camera is "Looking". Since the camera is a child, if you rotate this object, the camera rotates around it (while maintaining the orientation of facing the empty object).
A simple script to rotate the empty object will, thanks to transform parenting, make the camera "orbit" around it while looking at it:
using UnityEngine;
public class CameraInput : MonoBehaviour
{
[SerializeField] private float rotationAcceleration; // radians/(frame^2)
[SerializeField] private float rotationSpeedMax; // radians/frame
[SerializeField] private float rotationDamping; // 0.99
private float rotationSpeed;
private void Awake()
{
rotationSpeed = rotationSpeedMax / 4;
}
private void Update()
{
if (Input.GetKey(KeyCode.A))
{
rotationSpeed += rotationAcceleration;
}
if (Input.GetKey(KeyCode.D))
{
rotationSpeed -= rotationAcceleration;
}
rotationSpeed = Mathf.Clamp(
rotationSpeed, -rotationSpeedMax, rotationSpeedMax);
transform.Rotate(0f, rotationSpeed, 0f);
rotationSpeed *= rotationDamping;
}
}
Attaching something like this to the EmptyObject object will do the trick. Now you just need this empty object to follow your character. The rotational functions accept radians as arguments so you'll want to use small [0.00 ~ 0.01] numbers. Also, this is a per-frame implementation so it'll only work smoothly while your frame rate is smooth; you'd want to eventually have Time.deltaTime factor into it.

Applying direction into current transform position

I'm not quite sure whether I pick correct title for the question, feel free to edit it If you find it misleading.
I'm developing an action game which involving music as the core of its gameplay.
It contains a set of entities (game objects) that move into several directions; top, right, left, bottom and diagonal moves (e.g: top-left, top-right, bottom-left, etc etc).
To determine the position of these entities, there is a value which need to be calculated that involving the music tempo and several properties of the entities itself, which I able produce it in Unity.
I use following function to determine the position of transform of the entities:
private Vector3 _position;
private void DeterminePosition(float offset)
{
// In actual code, the _position is initialized under Start() method
// But for this simplification sake, I'll just put it here
_position = _position == null ? new Vector3(0, 0, 1f) : _position;
if (Direction == Direction.Up || Direction == Direction.RightUp || Direction == Direction.LeftUp)
{
_position.y = offset;
}
if (Direction == Direction.Down || Direction == Direction.RightDown || Direction == Direction.LeftDown)
{
_position.y = -offset;
}
if (Direction == Direction.Left || Direction == Direction.LeftDown || Direction == Direction.LeftUp)
{
_position.x = -offset;
}
if (Direction == Direction.Right || Direction == Direction.RightDown || Direction == Direction.RightUp)
{
_position.x = offset;
}
transform.position = _position;
}
Where the offset is the value that I was talking about before.
The code works perfectly as intended, however the game need to be change.
Instead of fixed direction (e.g: Up, Right, Left, Bottom, RightUp, DownLeft etc etc) we decide to use value of degree for the direction (0 to 360 degree).
And now I've no idea how to impement this. I've tried to use following codes, but it doesn't work:
_position = new Vector3(offset, offset, transform.position.z);
// Where the direction is between 0 .. 360
transform.position = Quaternion.Euler(0, direction, 0) * _position;
Can anybody else come up with solution?
Thanks in advance!
If offset represents the distance from the new position to (0, 0, transform.position.z) (calling that the origin since only x and y positions seem to change), and direction is the angle measured counterclockwise in degrees between the positive x-axis and the vector from the origin to the the new position, you can get the new x and y positions using offset and sin and cos of the direction:
_position.x = offset * Mathf.Cos(Mathf.Deg2Rad * direction);
_position.y = offset * Mathf.Sin(Mathf.Deg2Rad * direction);
_position.z = transform.position.z; //assuming the z position stays the same
transform.position = _position;
Edit
To account for scaling, I think you will need to replace offset depending on the direction.
float multiplier = 0f;
if(0 <= direction && direction <= 45)
{
multiplier = Mathf.Abs(offset / Mathf.Cos(Mathf.Deg2Rad * direction));
}
else if(45 < direction && direction <= direction <= 135)
{
multiplier = Mathf.Abs(offset / Mathf.Sin(Mathf.Deg2Rad * direction));
}
else if(135 < direction && direction <=225)
{
multiplier = Mathf.Abs(offset / Mathf.Cos(Mathf.Deg2Rad * direction));
}
else if(225 < direction && direction <= 315)
{
multiplier = Mathf.Abs(offset / Mathf.Sin(Mathf.Deg2Rad * direction));
}
else if(315 < direction && direction <= 360)
{
multiplier = Mathf.Abs(offset / Mathf.Cos(Mathf.Deg2Rad * direction));
}
_position.x = multiplier * Mathf.Cos(Mathf.Deg2Rad * direction);
_position.y = multiplier * Mathf.Sin(Mathf.Deg2Rad * direction);
_position.z = transform.position.z;
transform.position = _position;
This will break the coordinate plane into 4 90-degree sections (if you combine 315 < direction <= 360 and 0 <= direction < 45) where you are using sin or cos of direction to create a multiplier that should account for the regions where offset needs to be scaled depending on x and y components of the displacement.

scrolling after camera rotation

I am writing a kind of RTS CameraController in Unity using C#. I already managed to implement the basic functions, but now I am facing a tricky problem. I managed to rotate my camera and afterwards move into the right direction, BUT my scrolling boundaries are not working anymore after rotating (of course because I am just checking for x on the horizontal scrolling, but after rotation I would need to check for y instead). With boundaries I mean the restriction to only scroll until a certain position is reached (x < 0).
I am a bit stuck on how to solve my problem easily. One way would seems to be using conditions and check the rotation before checking for the boundaries, but this doesn't seem like a good aproach for me, just fixing the problem, but not really taking care of the cause. Now my question: Is there an easier or better way to achieve what I am doing? Did I miss something completely?
This is how I want the camera to move in all 4 rotations. The rectangles are my the world, the arrows define my bounds. The camera should always overshoot in backwards direction and stop before the bounds in forward direction.:
Here is my update method (vertDist and horDist are just defining from when on the mousescroll should start):
void LateUpdate() {
//LEFT
if((Input.mousePosition.x < horDist || Input.GetAxis("Horizontal") < 0)
&& transform.position.x > 0)
transform.Translate(-speed, 0, 0);
//RIGHT
if((Input.mousePosition.x > Screen.width - horDist || Input.GetAxis("Horizontal") > 0)
&& transform.position.x < world.worldX)
transform.Translate(speed, 0, 0);
//UP
if((Input.mousePosition.y > Screen.height - vertDist || Input.GetAxis("Vertical") > 0)
&& transform.position.z < world.worldZ - 30) {
Vector3 temp = transform.eulerAngles;
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, transform.eulerAngles.z);
transform.Translate(0, 0, speed);
transform.eulerAngles = temp;
}
//DOWN
if((Input.mousePosition.y < vertDist || Input.GetAxis("Vertical") < 0)
&& transform.position.z > -10) {
Vector3 temp = transform.eulerAngles;
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, transform.eulerAngles.z);
transform.Translate(0, 0, -speed);
transform.eulerAngles = temp;
}
//ZOOM
//camera.fieldOfView -= Input.GetAxis("Mouse ScrollWheel");
//ROTATE
if(Input.GetButtonDown("Rotate")){
Vector3 targetPosition = transform.position;
targetPosition += transform.forward * 27;
//transform.Rotate(new Vector3(0, Mathf.Sign(Input.GetAxis("Rotate")) * 90, 0), Space.World);
transform.RotateAround(targetPosition, Vector3.up, Mathf.Sign(Input.GetAxis("Rotate")) * 90);
}
}
Use
transform.Translate(0, 0, -speed, Space.Self); // For all the Translate, use Space.Self
It will only move based on it's local position instead of world position. But you will get what you wanted.
Hope it helps.
Finally I got a solution! I decided to define a Rect, depending on the current rotation and afterwards check if the camera-position is inside the Rect. Thus I had to mess a bit around with the positions, because I don't know why Rect.Contains() only takes the x- and y- coordinate of a position. My camera just moves along the x- and z-axis, so I did this weird coordinate swapping there:
void LateUpdate() {
Rect bounds = DefineBounds();
Vector3 pos = transform.position;
//LEFT
if((/*Input.mousePosition.x < horDist ||*/ Input.GetAxis("Horizontal") < 0)) {
pos += transform.TransformDirection(-speed * Time.deltaTime, 0, 0);
//reset pos if out of bounds
if(!bounds.Contains(new Vector2(pos.x, pos.z)))
pos -= transform.TransformDirection(-speed * Time.deltaTime, 0, 0);
}
//RIGHT
if((/*Input.mousePosition.x > Screen.width - horDist ||*/ Input.GetAxis("Horizontal") > 0)) {
pos += transform.TransformDirection(speed * Time.deltaTime, 0, 0);
//reset pos if out of bounds
if(!bounds.Contains(new Vector2(pos.x, pos.z)))
pos -= transform.TransformDirection(speed * Time.deltaTime, 0, 0);
}
//UP
if((/*Input.mousePosition.y > Screen.height - vertDist ||*/ Input.GetAxis("Vertical") > 0)) {
//Set camera x-angle to 0
Vector3 temp = transform.eulerAngles;
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, transform.eulerAngles.z);
pos += transform.TransformDirection(0, 0, speed * Time.deltaTime);
//reset pos if out of bounds
if(!bounds.Contains(new Vector2(pos.x, pos.z)))
pos -= transform.TransformDirection(0, 0, speed * Time.deltaTime);
//Reset camera x-angle
transform.eulerAngles = temp;
}
//DOWN
if((/*Input.mousePosition.y < vertDist ||*/ Input.GetAxis("Vertical") < 0)) {
//Set camera x-angle to 0
Vector3 temp = transform.eulerAngles;
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, transform.eulerAngles.z);
pos += transform.TransformDirection(0, 0, -speed * Time.deltaTime);
//reset pos if out of bounds
if(!bounds.Contains(new Vector2(pos.x, pos.z)))
pos -= transform.TransformDirection(0, 0, -speed * Time.deltaTime);
//Reset camera x-angle
transform.eulerAngles = temp;
}
transform.position = pos;
//ZOOM
//camera.fieldOfView -= Input.GetAxis("Mouse ScrollWheel");
transform.position = new Vector3(pos.x, pos.y - Input.GetAxis("Mouse ScrollWheel"), pos.z);
//ROTATE
if(Input.GetButtonDown("Rotate")){
Vector3 targetPosition = transform.position;
Vector2 posXZ;
targetPosition += transform.forward * 27;
transform.RotateAround(targetPosition, Vector3.up, Mathf.Sign(Input.GetAxis("Rotate")) * 90);
bounds = DefineBounds();
pos = transform.position;
posXZ = new Vector2(pos.x, pos.z);
while(!bounds.Contains(posXZ)) {
posXZ = Vector2.MoveTowards(posXZ, bounds.center, 1);
pos.x = posXZ.x;
pos.z = posXZ.y;
}
transform.position = pos;
}
}

Unity - Touch rotate camera along X axis

I'm trying to get a unity C# script working that will rotate the camera around the X axis in a 3D environment. Currently, it flips the screen making my terrain look like it is hanging upside down. I'm just trying to get the camera to rotate on the X axis instead. Below is what I currently have.
using UnityEngine;
public class TouchCamera : MonoBehaviour {
Vector2?[] oldTouchPositions = {
null,
null
};
Vector2 oldTouchVector;
float oldTouchDistance;
void Update() {
if (Input.touchCount == 0) {
oldTouchPositions[0] = null;
oldTouchPositions[1] = null;
}
else if (Input.touchCount == 1) {
if (oldTouchPositions[0] == null || oldTouchPositions[1] != null) {
oldTouchPositions[0] = Input.GetTouch(0).position;
oldTouchPositions[1] = null;
}
else {
Vector2 newTouchPosition = Input.GetTouch(0).position;
transform.position += transform.TransformDirection((Vector3)((oldTouchPositions[0] - newTouchPosition) * camera.orthographicSize / camera.pixelHeight * 2f));
oldTouchPositions[0] = newTouchPosition;
}
}
else {
if (oldTouchPositions[1] == null) {
oldTouchPositions[0] = Input.GetTouch(0).position;
oldTouchPositions[1] = Input.GetTouch(1).position;
oldTouchVector = (Vector2)(oldTouchPositions[0] - oldTouchPositions[1]);
oldTouchDistance = oldTouchVector.magnitude;
}
else {
Vector2 screen = new Vector2(camera.pixelWidth, camera.pixelHeight);
Vector2[] newTouchPositions = {
Input.GetTouch(0).position,
Input.GetTouch(1).position
};
Vector2 newTouchVector = newTouchPositions[0] - newTouchPositions[1];
float newTouchDistance = newTouchVector.magnitude;
transform.position += transform.TransformDirection((Vector3)((oldTouchPositions[0] + oldTouchPositions[1] - screen) * camera.orthographicSize / screen.y));
transform.localRotation *= Quaternion.Euler(new Vector3(0, 0, Mathf.Asin(Mathf.Clamp((oldTouchVector.y * newTouchVector.x - oldTouchVector.x * newTouchVector.y) / oldTouchDistance / newTouchDistance, -1f, 1f)) / 0.0174532924f));
camera.orthographicSize *= oldTouchDistance / newTouchDistance;
transform.position -= transform.TransformDirection((newTouchPositions[0] + newTouchPositions[1] - screen) * camera.orthographicSize / screen.y);
oldTouchPositions[0] = newTouchPositions[0];
oldTouchPositions[1] = newTouchPositions[1];
oldTouchVector = newTouchVector;
oldTouchDistance = newTouchDistance;
}
}
}
}
If you wanted to rotate it around (like a character turning its head), in the x-z plain, then you wanted to rotate it around the y axis, not the x.
In the end, I had to make a raycast shoot out the center of the camera and when it collided with something, set that as the pivot point. Then I rotated the camera around said pivot point. Example below...
RaycastHit hit;
if (Physics.Raycast(transform.position, transform.forward, out hit, 300)){}
float distanceToGround = hit.distance;
var pivotPoint = hit.point;
// -------- Rotation ---------
transform.RotateAround(pivotPoint, Vector3.up,Mathf.Asin(Mathf.Clamp((oldTouchVector.y * newTouchVector.x - oldTouchVector.x * newTouchVector.y) / oldTouchDistance / newTouchDistance, -1f, 1f)) / 0.0174532924f);
//---------------------------------------------------------

Categories

Resources