Moving around GameObject - c#

I'm trying to create MOB AI using rigidbody. I want to make the mob (GameObject) walk around the world using
mobrigid.AddForce((((goal - transform.position).normalized)*speed * Time.deltaTime));
(goal is a random place around mob).
here is where it gets complicated, some mobs fly up in the air at extreme speeds while others move normaly at slow speed. (And yes, I do make sure the goal.Y is a place on the ground and not air). I tried fixing it by changing the drag, but that leads to mobs walking on air and not falling with gravity.
I'm really lost, can't figure out how do I simply move a gameobject around with rigidbody without getting this odd behavior.
Logs of mobs (with Y of goal changed to 0):
Edit:
Image of the mob:
my movement logic:
case MOBTYPE.EARTHMOB:
switch (currentstatus)
{
case MOBSTATUS.STANDING:
if (mobrigid.IsSleeping())
{
mobrigid.WakeUp();
}
goal = this.transform.position;
goal.x = Random.Range(goal.x - 150, goal.x + 150);
goal.z = Random.Range(goal.z -150, goal.z + 150);
goal.y = 0;
currentstatus = MOBSTATUS.WALKING;
// Debug.Log("Transform:"+this.transform.position+ "Goal:" + goal+ "goal-transform:" + (goal - transform.position));
break;
case MOBSTATUS.WALKING:
if (Random.Range(1, 100) == 5)
{
currentstatus = MOBSTATUS.STANDING;
}
if (mobrigid.IsSleeping())
{
mobrigid.WakeUp();
}
mobrigid.AddForce((((goal - transform.position).normalized) * 10000 * Time.deltaTime));
// transform.LookAt(goal);
var distance = Vector3.Distance(goal, gameObject.transform.position);
if (distance <=5)
{
currentstatus = MOBSTATUS.STANDING;
}
break;
}
break;
Terrain Image:

The Rigidbody.AddForce function is used to add force to an Object along a direction. Since you have a position you need to move a Rigidbody to, you have to use the Rigidbody.MovePosition function. You may also want to mark the Rigidbody as kinematic.
A simple coroutine function to move a Rigidbody object to specific position:
IEnumerator MoveRigidbody(Rigidbody rb, Vector3 destination, float speed = 50f)
{
const float destThreshold = 0.4f;
while (true)
{
Vector3 direction = (destination - rb.position).normalized;
rb.MovePosition(rb.position + direction * speed * Time.deltaTime);
float dist = Vector3.Distance(rb.position, destination);
//Exit function if we are very close to the destination
if (dist <= destThreshold)
yield break;
yield return null;
//yield return new WaitForFixedUpdate();
}
}
It better to call this from another coorutine funtion so that you can yield or wait for it to finish then do other task like generating the random postion again and moving the Rigidbody there.
You want to generate new postion then move the Rigidbody there, this is an example of how to call the function above:
IEnumerator StartMoveMent()
{
Rigidbody targetRb = GetComponent<Rigidbody>();
while (true)
{
//Generate random position
Vector3 destination = new Vector3();
destination.y = 0;
destination.x = UnityEngine.Random.Range(0, 50);
destination.z = UnityEngine.Random.Range(0, 50);
//Move and wait until the movement is done
yield return StartCoroutine(MoveRigidbody(targetRb, destination, 30f));
}
}
And to start the StartMoveMent function:
void Start()
{
StartCoroutine(StartMoveMent());
}
While what I said above should work, I do recommend you use Unity's built in pathfinding system. Here is a tutorial for that. It simplifies finding paths to follow towards a destination with NavMesh which can also be baked during run-time.

Related

Movement Code Affected by Fps in Unity 2D

My current player movement code provides 0.32 translation on each key click.
If it is clicked 2 times very quickly, it should be 0.64.
I provide this with coroutine, but it is affected by fps.
How do I ensure that it is not affected by fps?
I know Time.deltaTime, but how do I get exactly 0.32 if I multiply by Time.deltaTime?
My current move code:
public float move = 0.32f; //movement limitation
public float repeat = 4; // I perform 0.32 in 4 parts to be more smooth.
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftArrow) //Exapmle key. I have 4 ways to move
{
StartCoroutine("Left");
}
}
IEnumerator Left()
{
for (int i = 1; i <= repeat; i++)// as I said, I perform 0.32 in 4 parts to be more smooth.
{
transform.position = transform.position + new Vector3(-move / repeat, 0, 0);
yield return null;
}
}
If you wanted to be smooth rather use a fixed speed and do e.g.
// distance to move
public float move = 0.32f;
// speed in Unity units per second
public float speed = 1f;
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftArrow)
{
StartCoroutine(Left);
}
}
IEnumerator Left()
{
// Pre-calculate the target position
// This is the huge advantage of IEnumerators -> you can store local variables over multiple frames
var targetPosition = transform.position + Vector3.left * move;
// track how far you have already moved
var moved = 0f;
// Loop until you moved enough
while(moved < move)
{
// the step is now frame-rate independent and moves the object with a fixed value per second
var step = speed * Time.deltaTime;
// every frame move one such step towards the target without overshooting
transform.position = Vector3.MoveTowards(transform.position, targetPosition, step);
// keep track of the movement
moved += step;
yield return null;
}
// and to end up with a clean value in the end
transform.position = targetPosition;
}
However
If it is clicked 2 times very quickly, it should be 0.64
this wouldn't be covered. In order to do this I would actually use a completely different approach without Coroutines at all and rather simply use e.g.
// distance to move
public float move = 0.32f;
// speed in Unity units per second
public float speed = 1f;
Vector3 targetPosition;
void Start()
{
targetPosition = transform.position;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftArrow)
{
targetPosition += Vector3.left;
}
...
transform.position = Vector3.MoveTowards(transform.position, targetPosition, speed * Time.deltaTime);
}
so now you can smash your move keys in any direction as much as you like and it will simply sum them all up into the one targetPosition and always keep moving towards it smoothly but with a fixed velocity.
or another alternative would be to interpolate and rather make the object move faster if it is further away from the target but slower the closer it comes
// 5 here is an arbitrary number based on experience -> tweak it according to your needs
public float interpolation = 5f;
...
transform.position = Vector3.Lerp(transform.position, targetPosition, interpolation * Time.deltaTime);

Unity2D Tile Physics

I have some tiles that the Player can navigate on and interact by hitting them with his head from below them. Basically when jumping they can hit a tile, that tile will go up a bit and come back down to it's initial position.
Since the tiles don't have a RigidBody2D because they need to be suspended in air, I took the following approach to the Tile moving up and down:
Current functionality
Tile.cs
Detect collision with Player's head
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Player Head")
{
collidedPlayer = true;
Vector2 upperPosition = new Vector2(transform.position.x, transform.position.y + .40f);
StartCoroutine(MovingBlock(0.35f, transform.position, upperPosition));
isLethal = false;
}
}
Move Tile
IEnumerator MovingBlock(float time, Vector2 startpos, Vector2 endpos)
{
float elapsedTime = 0;
while (elapsedTime < time)
{
transform.position = Vector2.Lerp(startpos, endpos, (elapsedTime / time));
elapsedTime += Time.deltaTime;
isLethal = true;
yield return null;
}
elapsedTime = 0f;
while (elapsedTime < time)
{
transform.position = Vector2.Lerp(endpos, startpos, (elapsedTime / time));
elapsedTime += Time.deltaTime;
isLethal = false;
yield return null;
}
isLethal = false;
}
The Problem
There are cases where multiple tiles are stacked on top of each other, when Player jumps and hits one tile, that tiles must also hit the tile on top of it and so on for eventual other tiles on top.
I've tried detecting the tile on top and eventually triggering the Coroutine for it as well but it doesn't seem to detect the collision:
Layer 8 being Tile layer - I'm checking for isLethal since I now that's when the tile below is jumping
private void OnCollisionEnter2D(Collision2D collision)
{
// print("Layer: " + collision.gameObject.layer);
// print("Name: " + collision.gameObject.name);
// print("Is lethal: " + isLethal);
if (collision.gameObject.layer == 8 && isLethal)
{
print("tile to tile");
Vector2 upperPosition = new Vector2(collision.transform.position.x, collision.transform.position.y + .40f);
StartCoroutine(MovingBlock(0.35f, collision.transform.position, upperPosition));
}
}
I can't seem to figure this out, I've even experimented with adding a RigidBody and no Gravity Scale but that has it's issues. I'm open to any kind of fix, even a different logic.
are you building levels or randomly generating them? If you're building them you should be able to just make a longer collider down to the lowest hittable tile for each tile you want to be affected. If you're randomly generating maybe try sending a raycast from each tile checking for other hittable tiles and sending a function call between them selves when the hit occurs to start the move block coroutine.

Unity2D move object up and back down with smooth transition

(I'm a beginner so please have patience with me).
How it needs to happend:
I have a player that can jump and hit a tile, on collision the tile with move a fixed distance up and come back down with a smooth transition.
What I have so far:
I am detecting the collision now there's only the matter of moving the tile up and down.
The catch
The tiles are suspended in air so basically they either don't have a RigidBody2D or they have one with gravity scale 0
What I've tried
Basically I've tried 2 solutions:
(I'm not limited to these 2, I want to implement the solution that is correct so I am open to other ideas)
I was thinking to simply take the current position, calculate another vector with another position, and lerp to the new position and then lerp back to the initial one.
Vector2 initialPosition = transform.position;
Vector2 targetPosition = new Vector2(initialPosition.x + 4f, initialPosition.y + 4f);
print("Initial position: " + initialPosition);
print("Target position: " + targetPosition);
Vector2.Lerp(initialPosition, targetPosition, 1f);
I tried adding a rigidbody with scale 0
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ground")
{
Jumping = false;
anim.SetInteger("State", 0);
}
// print("Colision layer: " + collision.collider.gameObject.layer);
if (collision.collider.gameObject.layer == Mathf.Log(layerMask.value, 2))
{
GameObject Tile = collision.gameObject;
Rigidbody2D rigidBody = Tile.GetComponent<Rigidbody2D>();
rigidBody.gravityScale = 1;
rigidBody.AddForce(new Vector2(0, 10f), ForceMode2D.Force);
StartCoroutine(MoveTileWithForce(rigidBody));
}
}
IEnumerator MoveTileWithForce(Rigidbody2D TileRigidBody)
{
yield return new WaitForSeconds(1);
TileRigidBody.AddForce(new Vector2(0, -5f), ForceMode2D.Force);
TileRigidBody.gravityScale = 0;
print("END MY COROUTINE, gravityScale: " + TileRigidBody.gravityScale);
}
I think the best way you can achieve this is in this simple way:
1
Make sure you have a box collider for your character and a Rigidbody and the same for the block you wanna move. For both of the colliders set on trigger. If you want to have another collider for the player in order to make him touch the ground or hit enemies you can add another box collider, but make sure you have one on trigger on his head
2
Add a script to the block you wanna move and also a tag to the player, for example "player"
3
Inside of this new script check if the block is triggering the player:
void OnTriggerEnter2D(Collision other)
{
if(other.compareTag("player"))
{ StartCoroutine(MovingBlock(0.5f, transform.position, upperPosition));}
|
It will enter inside the if when the player touches the block, it will start a coroutine that I will now write so you will understand the 0.5f and the other variables
IEnumerator MovingBlock(float time, Vector2 startpos, Vector2 endpos)
{
float elapsedTime = 0;
while (elapsedTime < time)
{
transform.position= Vector2.Lerp(startpos, endpos, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
elapsedTime = 0f;
while (elapsedTime < time)
{
transform.position= Vector2.Lerp(endpos, startpos, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
}
Basically, the Coroutine will move the object from startpos to endpos and then back again in the amount of time that you decide (in this case 0.5 seconds). I have used the Vector2 variable upperposition and it's just the height you want to reach with the platform.
If you want, you can also add inside the coroutine some yield return new WaitForSeconds(timeToWait) and make the platform wait in a certain position the amount of seconds you want (timeToWait)
Declare Vector2 initialPosition at the beginning of your class.
On Start() of the tile object you should get the initialPosition = transform.position.
And in the OnCollisionEnter2D(Collision2D collision) you could start a coroutine to bring the tile back down.
So for example:
Player Script:
public class Player : MonoBehaviour
{
public Rigidbody2D rb;
//Initialize the rigidbody on the editor.
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
rb.AddForce(new Vector2(0f, 1000f));
}
}
}
And the tile script:
public class MyTile : MonoBehaviour
{
Vector2 initialPosition;
float speed = 2f; //the speed the tile will go down.
private void Start()
{
initialPosition = transform.position;
}
private void OnCollisionEnter2D(Collision2D collision)
{
//The amount you want to go up on the Y component, for this example I used 2.
transform.position = new Vector2(transform.position.x, transform.position.y + 2f);
StartCoroutine(MoveTileDown());
}
IEnumerator MoveTileDown()
{
while (transform.position.y > initialPosition.y)
{
transform.position = Vector2.Lerp(transform.position, initialPosition, Time.deltaTime * speed);
yield return null; //Make it run every frame, just like Update()
}
StopCoroutine(MoveTileDown());
}
}
There are a lot of ways you can achieve the same result, this is just one of them.

How to realistically reflect a 3d sphere in Unity with C#

I've been trying to realistically reflect a 3d sphere on the walls of a box for a while now in Unity. For some reason, the reflection is generally correct, but when the ball hits a wall in certain directions, the reflection is incorrect.
To illustrate what happens to the ball upon hitting a wall: T = top wall, R = right wall, L = left wall, and B = bottom wall. Let r = the ball comes/goes to the right, l = for the left, and s = the ball stops/slows down significantly. The instructions below take this format: Xyz, where X = the wall the ball is about to hit, y = the ball's initial direction, z = the reflection. The game has a top-down perspective, and the instructions are based on the wall's perspective. I'm also new to C#, so the code is potentially eye burning.
Instructions: Tll, Trl; Bll, Brl; Rls or after hitting another wall Rlr, Rrl; Lls or after hitting another wall Llr, Lrl
Generally, when the ball stops, it jumps in the air. I wonder if this is because the angle reflects along the wrong axis, but why would this only sometimes happen? Also, when only one key is held, the ball bounces back and forth until it leaves the arena. I know about discrete and continuous hit detection, and the setting is on discrete, but the walls generally contain the ball well enough, with this case being the exception.
What I tried:
Figuring out how to use Vector3.Reflect. I do not understand what variables this function should contain and how to apply this to my code. I did look at the Unity Documentation page for help, but it did not answer my question.
Changing the negative signs, as the angle has to be a reflection on the y-axis, and this does change how the reflections work, but does not solve the problem. The current way the negatives are ordered are the most ideal I found.
Giving the ball a physics material for bounciness.
Adding a small number to the denominator in the arctan equation to help prevent a division by zero. This did not help at all.
Creating different equations (basically changing the negatives) for varying combinations of positive and negative accelerations. Since there is a certain positive or negative acceleration associated with each button press (see Movement script), and there seems to be an issue with said signs, I wondered if associating each acceleration with its own set of equations could solve the problem. It did not work.
Checked if the walls were at different angles.
Deleting the variables xA and yA, or placing them in different spots.
Experimented with finding the speed of the object, but had no idea how to implement it.
The code for the Movement script for the player named Controller:
public class Movement : MonoBehaviour
{
public static float xAcceleration = 0.0f;
public static float yAcceleration = 0.0f;
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.W)) //If the key W is pressed:
{
Vector3 position = this.transform.position; //Variable position is set to transform the players placement in the game.
if (yAcceleration >= -5 && yAcceleration <= 5) //If the y vector of the acceleration is >= -5 and <= 5:
{
yAcceleration = yAcceleration + 0.01f; //The y vector of the acceleration increases by 0.01 as long as the key W is pressed.
}
position.z = position.z + (0.1f * yAcceleration); //The position of the object on the z-axis (pretend it is the y-axis in the game world) is transformed by its original position plus its speed times its yAcceleration.
this.transform.position = position; //The gameObject is now transformed to a position equal to the variable position by the z-axis.
}
else //If the key W is let go of:
{
Vector3 position = this.transform.position;
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position; //The position of the gameObject continues to update, but its acceleration does not change. Basically, it continues to move forward.
}
//The rest of the code is very similar to the above, but I included it just in case there was something wrong.
if (Input.GetKey(KeyCode.S))
{
Vector3 position = this.transform.position;
if (yAcceleration >= -5 && yAcceleration <= 5)
{
yAcceleration = (yAcceleration) - 0.01f;
}
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position;
}
if (Input.GetKey(KeyCode.A))
{
Vector3 position = this.transform.position;
if (xAcceleration >= -5 && xAcceleration <= 5)
{
xAcceleration = (xAcceleration) - 0.01f;
}
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
if (Input.GetKey(KeyCode.D))
{
Vector3 position = this.transform.position;
if (xAcceleration >= -5 && xAcceleration <= 5)
{
xAcceleration = (xAcceleration) + 0.01f;
}
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
}
}
This is the code for the collider and reflection:
public class Collider : MonoBehaviour
{
public float xA;
public float yA;
void OnCollisionEnter(Collision collision) //If a gameObject enters the collision of another object, this immediately happens once.
{
if (gameObject.tag == "Boundary") //If the gameObject has a tag named Boundary:
{
yA = -Movement.yAcceleration; //yA stores the value of yAcceleration after being called from script Movement as a negative. Its a reflection.
Movement.xAcceleration = (Movement.xAcceleration * -Mathf.Atan(yA / Movement.xAcceleration)); //xAcceleration is changed based on this equation: A * artan(A_y / A_x). The 0.000001 was here, adding to A_x to help prevent a 0 as the denominator.
xA = Movement.xAcceleration; //This is declared now...
Movement.yAcceleration = (Movement.yAcceleration * Mathf.Atan(Movement.yAcceleration / xA)); //This uses xA because Movement.xAcceleration is changed, and the yAcceleration calculation is based on the xAcceleration prior the collision.
}
}
void OnCollisionStay(Collision collision)
{
if (gameObject.tag == "Boundary")
{
yA = Movement.yAcceleration; //The same thing happens as before.
Movement.xAcceleration = (Movement.xAcceleration * -Mathf.Atan(yA / Movement.xAcceleration));
xA = Movement.xAcceleration;
Movement.yAcceleration = (Movement.yAcceleration * Mathf.Atan(Movement.yAcceleration / xA));
Movement.xAcceleration = -Movement.xAcceleration / 2; //On collision, the ball is reflected across the x-axis at half its speed.
Movement.yAcceleration = Movement.yAcceleration / 2; //yAcceleration is half its original value.
}
}
}
The picture below is the game setup. I apologize that it is a link; I do not have enough Reputation to merit a loaded image on this page. Also, if anything is unclear, please message me.
https://i.stack.imgur.com/VREV4.png
I would really appreciate the help. Thanks!
One very important note here: As soon as there is any Rigidbody involved you do not want to set any values through the .transform - This breaks the physics and collision detection!
Your Movement should rather alter the behavior of the Rigidbody e.g. by simply changing its Rigibody.velocity
I would then also place the collision check directly into the balls's component and check whether you hit a wall ("Boundary")
Then another note: Your code is currently frame-rate dependent. It means that if your target device runs with only 30 frames per second you will add 0.3 per second to the acceleration. If you run however on a more powerful device that manages to run with 200 frames per second then you add 2 per second.
You should rather define the de/increase per second and multiply it by Time.deltaTime
All together maybe something like this
public class Movement : MonoBehaviour
{
// Adjust these settings via the Inspector
[SerializeField] private float _maxMoveSpeed = 5f;
[SerializeField] private float _speedIncreasePerSecond = 1f;
// Already reference this via the Inspector
[SerializeField] private Rigidbody _rigidbody;
private void Awake()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
}
// Get User Input in Update
private void Update()
{
var velocity = _rigidbody.velocity;
velocity.y = 0;
if (Input.GetKey(KeyCode.W) && velocity.z < _maxMoveSpeed)
{
velocity.z += _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.S) && velocity.z > -_maxMoveSpeed)
{
velocity.z -= _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.A) && velocity.x > -_maxMoveSpeed)
{
velocity.x -= _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.D) && velocity.x < _maxMoveSpeed)
{
velocity.x += _speedIncreasePerSecond * Time.deltaTime;
}
// clamp to the max speed in case you move diagonal
if(velocity.magnitude > _maxMoveSpeed)
{
velocity = velocity.normalized * _maxMoveSpeed;
}
_rigidbody.velocity = velocity;
}
}
And then finally simply add a PhysicsMaterial with desired settings to the walls and ball.
I used Friction = 0f and Bounciness = 0.7f for ball and walls. For slow movements you also might want/have to adjust the Bounce Threshold in the Project's Physics Settings otherwise there will be no bouncing if the velocity is smaller then 2 by default.
This depends a bit on your definition of "realistic". I disabled gravity so the ball also has no rotation and angular friction:

How can I use multiple enums (two enums) to control two axis the object will rotate at the same time?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotate : MonoBehaviour
{
public enum Axistorotate { Back, Down, Forward, Left, Right, Up, Zero };
public Vector3[] vectorAxises = new Vector3[7];
public Axistorotate[] myAxis;
public float angle;
public float speed;
private bool stopRotation = true;
// Start is called before the first frame update
void Start()
{
vectorAxises[0] = Vector3.back;
vectorAxises[1] = Vector3.down;
vectorAxises[2] = Vector3.forward;
vectorAxises[3] = Vector3.left;
vectorAxises[4] = Vector3.right;
vectorAxises[5] = Vector3.up;
vectorAxises[6] = Vector3.zero;
StartCoroutine(RotateObject());
}
public Vector3 GetAxis(Axistorotate axis)
{
return vectorAxises[(int)axis];
}
// Update is called once per frame
void Update()
{
if(Input.GetKeyDown(KeyCode.S))
{
stopRotation = false;
}
if (Input.GetKeyDown(KeyCode.C) && stopRotation == false)
{
stopRotation = true;
StartCoroutine(RotateObject());
}
}
IEnumerator RotateObject()
{
while (stopRotation == true)
{
for (int i = 0; i < myAxis.Length; i++)
{
transform.Rotate(GetAxis(myAxis[i]), angle);
}
yield return new WaitForSeconds(speed);
}
}
}
For some reason while the game is running and I'm changing one of the enums to forward or back or down each time it's rotating another direction. If I'm starting when both enums on back states and then changing one of them to down it looks like it's rotating to the left or right and then when changing back to back it's not rotating like it was when they were both set to back.
How can I update the vectorAxises array in real time while the game is running to show in the Inspector the current axis state for example : vectorAxises[0] -> 1, 0, -1 ..... vectorAxises[7] -> 0,-1, 0 I want that when I'm changing one of the enums that it will show it on the vectorAxises.
Maybe I need to create another vectorAxises array one for each enum ?
And maybe when doing two myAxis it's changing the same one the same angle so it's not realy two enums that change individual axis ?
transform.Rotate has an optional parameter
relativeTo
Determines whether to rotate the GameObject either locally to the GameObject or relative to the Scene in world space.
which by default is Space.Self.
So your
transform.Rotate(GetAxis(myAxis[i]), angle);
is always done in the local coordinate system of the GameObject. This local system is rotated along with the GameObject so the local transform.up, transform.forward etc axis change all the time.
Instead make it rotate around world axis
transform.Rotate(GetAxis(myAxis[i]), angle, Space.World);
How can I update the vectorAxises array in real time while the game is running to show in the Inspector the current axis state
This should already be the case. Or do you mean you want to see the currently "selected" value. You should use Debugging and Breakpoints for that. Since you do
for (int i = 0; i < myAxis.Length; i++)
{
transform.Rotate(GetAxis(myAxis[i]), angle);
}
without any further yield return the object will directly "jump" into the new rotation and in the inspector you would always only see the last GetAxis(myAxis[i]).
If you are looking for a smooth rotation then checkout Dest's answer slightly modified you could e.g. let the object rotate within 1 second
while (stopRotation == true)
{
// calculate the target rotation
Quaternion rotationQuaternion = Quaternion.identity;
for (int i = 0; i < myAxis.Length; i++)
{
rotationQuaternion *= Quaternion.AngleAxis(angle, GetAxis(myAxis[i]));
}
// before starting to rotate store initial and target rotation
var initialRotation = transform.rotation;
var targetRotation = initialRotation * rotationQuaternion;
// could also get this from the Inspector e.g.
var rotationDuration = 1;
// Do a smooth rotation from the initial to target rotation
// within the defined rotationDuration
var timePassed = 0f;
do
{
// additionally ease-in and -out the rotation
var lerpFactor = Mathf.SmoothStep(0, 1, timePassed / rotationDuration);
transform.rotation = Quaternion.Slerp(initialRotation, targetRotation, lerpFactor);
timePassed += Time.deltaTime;
// let this state be rendered
yield return null;
} while(timePassed < rotationDuration);
// if you still want the pause
yield return new WaitForSeconds(speed);
}
Just out of curiosity: Why even use an enum here? Couldn't you directly iterate through the vectorAxises index instead and only add those entries you will be using?
Use Quaternions for multiple rotations at the same time:
Quaternion rotationQuaternion = Quaternion.identity;
for (int i = 0; i < myAxis.Length; i++)
{
rotationQuaternion *= Quaternion.AngleAxis(angle, GetAxis(myAxis[i]));
}
transform.rotation *= rotationQuaternion;
It should fix your problem with wrong rotations

Categories