How to prevent tilting over in a unity object - unity3d

I've got a hover tank and I'm working on a controller for it. The goal is to have the float above the ground, but I don't want it to tilt over more the a degree or two. I want it to basically stay level all the time.
I'm using a Rigidbody on the tank to control it with .MovePosition and .MoveRotation. You can see the FixedUpdate function below. I've got a section below to keep thinks level, where I check if there tank is tipping past its maxTilt amount. If it is, the keep it at the max.
This makes the tank very jittery all the time. It looks like it's bouncing up and down quickly. I think its due to the hover forces, but I'm not sure.
How can I keep the tank level while still letting it hoover?
FixedUdpate
void FixedUpdate () {
if (!isServer) {
return;
}
CheckGrounded ();
Hoover ();
if (_moveForward) {
float moveAmount = moveSpeed * Time.deltaTime;
_rigidbody.MovePosition(_rigidbody.position + _rigidbody.transform.forward * moveAmount);
}
if (_moveBackward) {
float moveAmount = (-moveSpeed * 0.6f) * Time.deltaTime;
_rigidbody.MovePosition(_rigidbody.position + _rigidbody.transform.forward * moveAmount);
}
if (_turnLeft) {
Quaternion rotateAmount = Quaternion.Euler(new Vector3(0f, -angularSpeed, 0f) * Time.deltaTime);
_rigidbody.MoveRotation(_rigidbody.rotation * rotateAmount);
}
if (_turnRight) {
Quaternion rotateAmount = Quaternion.Euler(new Vector3(0f, angularSpeed, 0f) * Time.deltaTime);
_rigidbody.MoveRotation(_rigidbody.rotation * rotateAmount);
}
if (_jump && _isGrounded) {
_isJumping = true;
}
if (_isJumping && _jumpTimeLeft > 0) {
float moveAmount = jumpSpeed * Time.deltaTime;
_rigidbody.MovePosition(_rigidbody.position + _rigidbody.transform.up * moveAmount);
_jumpTimeLeft -= Time.deltaTime;
} else if (_isJumping) {
_isJumping = false;
_jumpTimeLeft = jumpTime;
}
// Keep things level
Vector3 rotation = _rigidbody.rotation.eulerAngles;
if (rotation.x > maxTilt) {
rotation.x = maxTilt;
} else if (rotation.x < -maxTilt) {
rotation.x = -maxTilt;
}
if (rotation.y > maxTilt) {
rotation.y = maxTilt;
} else if (rotation.y < -maxTilt) {
rotation.y = -maxTilt;
}
if (rotation.z > maxTilt) {
rotation.z = maxTilt;
} else if (rotation.z < -maxTilt) {
rotation.z = -maxTilt;
}
Quaternion q = new Quaternion ();
q.eulerAngles = rotation;
_rigidbody.rotation = q;
}
Hoover
void Hoover() {
foreach (Transform hoverPoint in hooverPoints) {
Ray ray = new Ray (hoverPoint.position, -hoverPoint.up);
RaycastHit hitInfo;
if (Physics.Raycast (ray, out hitInfo, hooverHeight)) {
float distance = Vector3.Distance (hoverPoint.position, hitInfo.point);
if (distance < hooverHeight) {
_rigidbody.AddForceAtPosition (hoverPoint.up * hooverForce * (1f - distance / hooverHeight), hoverPoint.position, ForceMode.Force);
}
}
}
}

I'm thinking that the reason you are seeing 'jitters' is because...
All physics calculations and updates occur immediately after FixedUpdate.
https://docs.unity3d.com/Manual/ExecutionOrder.html
Because you are adjusting the tilt in FixedUpdate(), which is then immediately followed by the Physics Engine running its calculations this will sometimes alter the tilt value giving a 'jitter'. The reason I say sometimes is because FixedUpdate() can run multiple times per frame (FPS dependant), which potentially means the following call order for a single frame:
FixedUpdate() // start frame
PhysicsEngine
FixedUpdate() // end frame
In the case above, there would be no jitter because you re-correct the tilt after the physics engine has done its thing - by complete fluke. However when you don't get a second FixedUpdate() call on a frame, you will have:
FixedUpdate() // start frame
PhysicsEngine // end frame
Which will result in your jitter.
So my suggestion is to break up your FixedUpdate() and offset any tilt corrections to LateUpdate(). Becuase LateUpdate() is always the last update call prior to the rendering of the frame.

Related

Rotating an object around the same pivot gives different results

Rotating an object around the same pivot gives different results
So I'm trying to make a door that opens when clicked on, and I have a door that works good as is, but for some reason when I try to make it work for another door using RotateAround, it moves along the Z axis when closing. The code is identical to the other door that works and works when opening this door, so I have no clue as to why it's having trouble closing for this one. The code I'm using to rotate them is as follows
IEnumerator CloseDoor()
{
float timer = 1f;
float speed = 30f;
Vector3 pivot = new Vector3(door.transform.position.x, door.transform.position.y,
door.transform.position.z + 1);
while (timer > 0)
{
door.transform.RotateAround(pivot, Vector3.down, speed * Time.deltaTime);
yield return new WaitForSeconds(0.001f);
timer -= Time.deltaTime;
}
}
IEnumerator OpenDoor()
{
float timer = 1f;
float speed = 30f;
Vector3 pivot = new Vector3(door.transform.position.x, door.transform.position.y,
door.transform.position.z + 1);
while (timer > 0)
{
door.transform.RotateAround(pivot, Vector3.up, speed * Time.deltaTime);
yield return new WaitForSeconds(0.001f);
timer -= Time.deltaTime;
}
}
I'll start with some overall advices, regarding your code.
Don't use Time.deltaTime with new WaitForSeconds(...). Time.deltaTime is an amout of time, that passed between Update() calls. But your logic is not inside Update(). You use own time inervals with new WaitForSecons(...), but if the specified amout of time is less then Time.deltaTime, then it will be executed every Time.deltaTime after all Update() calls. It works good for you only because your time interval 0.001f is low enough to be executed every Update(). When your argument in new WaitForSeconts(...) becomes more them Time.deltaTime, the rotation speed of the door becomes too low to door be opened completely. To made your code more clear and safe, return null with yield instruction. In this case, you can be shure, that coroutine will be executed every Update(). More info here.
Your code works with transform positioning, and probably you have physics in your game. All changes with physical object supposed to be done in the FixedUpdate(). In your case your cant return new WaitForFixedUpdate() in yield instruction and use Time.fixedDeltaTime with it.
So speaking about main question. In your code, you are doing pretty unclear and unsafe thing, like hardcoding pivot with global position offset here:
Vector3 pivot = new Vector3(door.transform.position.x, door.transform.position.y,
door.transform.position.z + 1);
Probably, not all doors will have same rotation, and for some of them offset, just with z coordinate will be wrong. Also it becomes wrong after door opening, because the position and rotation of the door changed when you rotating it around some point, that is not the center of the door. So you should base on local transform point, like this:
public class Door : MonoBehaviour
{
private bool _doorOpened = false;
private bool _doorOpening = false;
[SerializeField] // to see in the inspector.
private Vector3 _localDoorRotatePoint = new Vector3(0.5f, 0f, 0f);
// Update is called once per frame
void Update()
{
if (!_doorOpening && Input.GetKeyDown(KeyCode.E))
{
if (!_doorOpened)
{
StartCoroutine(OpenDoor());
}
else
{
StartCoroutine(CloseDoor());
}
}
}
IEnumerator CloseDoor()
{
_doorOpening = true;
var timer = 1f;
var speed = 30f;
// in my case _localDoorRotate point is (0.5f, 0f, 0). In your case it will be like (0, 0, 1f) or something like this.
// remember, that this point is in local transform coordinates, and in scales with this transform scale vector.
var pivot = transform.TransformPoint(_localDoorRotatePoint);
while (timer > 0)
{
transform.RotateAround(pivot, Vector3.down, speed * Time.fixedDeltaTime);
yield return new WaitForFixedUpdate();
timer -= Time.fixedDeltaTime;
}
_doorOpening = false;
_doorOpened = false;
}
IEnumerator OpenDoor()
{
_doorOpening = true;
var timer = 1f;
var speed = 30f;
// in my case _localDoorRotate point is (0.5f, 0f, 0f). In your case it will be like (0f, 0f, 1f) or something like this.
// remember, that this point is in local transform coordinates, and in scales with this transform scale vector.
var pivot = transform.TransformPoint(_localDoorRotatePoint);
while (timer > 0)
{
transform.RotateAround(pivot, Vector3.up, speed * Time.fixedDeltaTime);
yield return new WaitForFixedUpdate();
timer -= Time.fixedDeltaTime;
}
_doorOpening = false;
_doorOpened = true;
}
}
Helpfull links, that can help you with understanding this code:
Transform.TransformPoint
Transform.up

Delay when calculating a players movement direction

I am trying to determine the direction of a transform. I originally tried to do this by tracking the rigidbody.velocity but that property seems unreliable so I am having to calculate the direction manually. The yellow line draws fine and it does point in the wrong direction however it appears to be delayed
I am calling the following method in my update method:
void DetermineMovementDirection()
{
currentLoc = (transform.position - prevLoc) / Time.deltaTime;
Vector3 t = (prevLoc + currentLoc).normalized;
Debug.DrawLine(transform.position, transform.position + t * 5, Color.yellow);
}
I would expect the yellow line to always point in the direction the player was moving rather than have a long delay. How can I fix this?
As requested the movement function:
void Update()
{
float inputZ = Input.GetAxis("Horizontal");
float inputX = Input.GetAxis("Vertical");
if (movementAllowed)
if (inputZ != 0 || inputX != 0)
{
transform.eulerAngles = new Vector3(0, Mathf.Atan2(inputZ, inputX) * 180 / Mathf.PI, 0);
transform.Translate(moveSpeed * inputZ * Time.deltaTime, 0f, moveSpeed * inputX * Time.deltaTime, Space.World);
}
}
And I am simply updating at the end of the update method:
prevLoc = transform.position;

How to optimize jump like in Flappy Bird in Unity?

I can write somehow this code for optimization?
If not use coroutines, when I click on space the next jump has more force and so on.
If use rb.MovePosition, the character will move as if 15 fps. I know, change Time in settings. But I want to know if exist another method...
private void Update() {
if(Input.GetKeyDown(KeyCode.Space)) {
StopAllCoroutines();
StartCoroutine(Jump());
}
}
private IEnumerator Jump() {
if(rb.bodyType != RigidbodyType2D.Dynamic) {
rb.bodyType = RigidbodyType2D.Dynamic;
}
rb.constraints = RigidbodyConstraints2D.FreezePositionY;
_pos = transform.position;
for (float t = 0; t < 1; t += Time.deltaTime * 4f)
{
transform.position = Vector3.Lerp(transform.position, new Vector3(transform.position.x, _pos.y + .35f, transform.position.z), t);
yield return null;
}
rb.constraints = RigidbodyConstraints2D.None;
}
Rigidbodies exist so you don't need to directly adjust an object's transform. Since you have a Rigidbody2d you can just set the velocity directly:
public float jumpSpeed = 5f; // Whatever feels right
private void FixedUpdate() {
if(Input.GetKeyDown(KeyCode.Space)) {
rb.velocity = Vector2.up * jumpSpeed;
}
}
(Edited to use velocity instead of AddForce)

How to make script only translate on one axis?

I would like my sprite to move only on axis, so I took out the x-axis from the code. However, when I try to compile it in Unity, it returns with this error:
Assets/Scripts/TongueMove.js(19,83): BCE0024: The type 'UnityEngine.Vector2' does not have a visible constructor that matches the argument list '(float)'.
Furthermore, what would I add so that it only lasts a certain time, before returning to its original position?
#pragma strict
function Start() {
transform.position.z = -0.5;
}
function Update () {
if (Input.GetKeyDown ("space"))
{
moveTo(transform.position.y + 11.8, 20); //transform.position.y + how much I want to move, speed
}
transform.position.z = -0.5;
}
function moveTo (posY : float, speed : float)
{
while (transform.position.y != posY)
{
transform.position = Vector2.MoveTowards (transform.position, new Vector2(posY), speed * Time.deltaTime);
yield;
}
}
Your problem is when you create the new Vector2() into the while loop. Vector2 needs 2 parameters.
If you don't like to modify X axis try this:
while (transform.position.y != posY)
{
transform.position = Vector2.MoveTowards (transform.position, new Vector2(transform.position.X, posY), speed * Time.deltaTime);
yield;
http://docs.unity3d.com/ScriptReference/Vector2.html

object movement as wave in Unity 3d

I created a object in unity
GameObject monsterclone =
(GameObject)Instantiate(monsterPrefab, floorPosition, Quaternion.identity);
This object should move in a wave style from a limit1 to limit2.
Then move back from limit2 to limit1.
Y position as well x position have to change in a specific way.
Vector3 nPos = mfloorPos + new Vector3(2f, 0f, 0f);
Vector3 oPos = mfloorPos + new Vector3(-2f, 0f, 0f);
How can I do it?
I can't exactly write the code without knowing more specific but i think this question is already asked any this link will help you out MOVE OBJECT as wave
EDIT:
I think the flat up and float down functionality will work for you for moving one point to another
example:
var floatup;
function Start(){
floatup = false;
}
function Update(){
if(floatup)
floatingup();
else if(!floatup)
floatingdown();
}
function floatingup(){
transform.position.y += 0.3 * Time.deltaTime;
yield WaitForSeconds(1);
floatup = false;
}
function floatingdown(){
transform.position.y -= 0.3 * Time.deltaTime;;
yield WaitForSeconds(1);
floatup = true;
}
example taken from
float amplitudeX = -25.0f;
float amplitudeY = 5.0f;
float omegaX = 0.5f;
float omegaY = 4.0f;
float index;
void Update () {
index += Time.deltaTime;
float x = amplitudeX*Mathf.Cos (omegaX*index);
float y = Mathf.Abs (amplitudeY*Mathf.Sin (omegaY*index));
if(transform.position.x > 24){
transform.eulerAngles = new Vector3(270, -90, 0);
}
if(transform.position.x < -24){
transform.eulerAngles = new Vector3(270, 90, 0);
}
transform.localPosition= new Vector3(x,y,20);
}
If this is a consitant wave and not dependant on speed I would use an animation to create a literal wave curve of the Position.Y value (much in the same principle as Ravindra Shekhawat has explained.) you can find out more about animation here.
Here is some code (untested) that you could go off. It is in c# so I hope it proves no issue with putting in to JavaScript.
bool monsterMoving = false;
void Update(){
//check monster moving to start updating position
if(monsterMoving == true){
//moving animation controls up and down "wave" movement
animation.CrossFade('moving');
//Lerp changes position
Transform.Lerp(transform.Position, oPos, Time.deltaTime);
if (transform.Position == oPos) {
//We are at destination, stop movement
monsterMoving = false;
}
} else {
// if monster stopped moving, return to idle animation (staying still)
animation.CrossFade('idle');
}
}
// function to send a new position to the monster object
void MoveTo(Vector3 newPos){
oPos = newPos;
monsterMoving = true;
}