I have a platform that must rotate simultaneously in x, y & z directions in a random order, the maximum degree measure of rotation of vectors x & z is 10 degrees, and rotation in the "y" direction is 360 degrees.
Here is the code:
using UnityEngine;
public class PlaneRotating : MonoBehaviour
{
void Start() {
}
void Update()
{
Vector3 euler = transform.eulerAngles;
euler.z = Random.Range(-10f, 10f);
euler.y = Random.Range(0f, 360f);
euler.x = Random.Range(-10f, 10f);
transform.eulerAngles = euler;
}
}
Everything works perfectly, except for a certain rotation speed, since I don't know how to set it.
Can you tell me how to set the speed?
You could lerp between positions, its not the best way to control the speed because large rotations will be faster and smaller rotations will be slower but you can have a stable rotations, time wise.
using UnityEngine;
public class PlaneRotating : MonoBehaviour
{
public Transform transform;
Vector3 lastPos
Vector3 newPos
float t;
void Start()
{
lastPos = transform.eulerAngles;
NewAngle();
}
void Update()
{
transform.eulerAngles = Vector3.Lerp(lastPos, newPos, t);
t+=0.01f;
if(t>1)
NewAngle();
}
void NewAngle()
{
lastPos = newPos;
newPos = new Vector3(
Random.Range(-10f, 10f),
Random.Range(0f, 360f),
Random.Range(-10f, 10f));
t = 0;
}
}
you can then play with Time.deltaTime to make it consistent this is only an example.
If you want to control speed
you can just add two vectors and you do the randomization periodically every x seconds or x frames. you change the direction. there are plenty of good videos on youtube but you need to be more clear if you want to have a good answer. my first answer doewnt let you control the speed with random numbers, only makes smooth transitions and you can choose how fast they change. for constant speeds you have to do something like the bellow
void Update()
{
transform.eulerAngles += new Vector3(0,1,0);
}
let me know if I can be of any further help.
Related
I'm trying to get an object to rotate smoothly on the z axis when it comes into contact with a collider. However it just seems to snap really quickly and I can't find a way to smooth this out.
I've split the rotation out into 2 scenarios - one for when the player enters the collider (which needs to be smooth), and one for when the player is already in the collider (which needs to be snappy). this is so that when the player enters the objects gravity (collider) he'll rotate smoothly before falling to the ground. However, if the player is already within the objects gravitational pull, its snappy so they can run on the ground (a circle) without any wierd floating or rolling occuring.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GravitationalBody : MonoBehaviour
{
[Range(0, 0.3f)] [SerializeField] private float m_OrientationSmoothing = 0.3f; // How much to smooth out the movement
void OnTriggerEnter2D(Collider2D obj) {
// Define direction of gravity
Vector3 gravityDirection = transform.position - obj.transform.position;
// apply gravity to all objects within range
obj.GetComponent<Rigidbody2D>().AddForce(gravityDirection);
orientObjects(obj, gravityDirection, true);
}
void OnTriggerStay2D(Collider2D obj)
{
// Define direction of gravity
Vector3 gravityDirection = transform.position - obj.transform.position;
// apply gravity to all objects within range
obj.GetComponent<Rigidbody2D>().AddForce(gravityDirection);
orientObjects(obj, gravityDirection, false);
}
// Control players Z axis rotation to point toward center of planets
void orientObjects(Collider2D obj, Vector3 gravityDirection, bool lerp)
{
float orientation = 90 + Mathf.Atan2(gravityDirection.y, gravityDirection.x) * Mathf.Rad2Deg;
if (lerp)
{
Quaternion q = Quaternion.AngleAxis(orientation, Vector3.forward);
obj.transform.rotation = Quaternion.Lerp(obj.transform.rotation, q, m_OrientationSmoothing * Time.deltaTime);
}
else obj.transform.rotation = Quaternion.Euler(0f, 0f, orientation);
}
}
I don't know if it will help but if you want a smooth rotation look at this code, I'm sure you can find inspiration here:
[SerializeField] float _degreesPerSecond = 30f;
[SerializeField] Vector3 _axis = Vector3.forward;
void Update ()
{
transform.Rotate( _axis.normalized * _degreesPerSecond * Time.deltaTime );
}
I'm attempting to make the ability to slide in an FPS game I'm working on and I've tried rotating the player object but when tested, it does nothing. Pressing the slide key I have assigned does nothing but rotate the player object in the Y and Z axis (I'm trying to rotate it on the X axis)
Here's the code I'm using (only relevant parts have been kept in):
public class SlideMovementScript : MonoBehaviour
{
public Rigidbody rig;
public CapsuleCollider capcol;
public GameObject player;
public float originalHeight;
public float reducedHeight;
public float slideSpeed = 10f;
// Start is called before the first frame update
void Start()
{
capcol = GetComponent<CapsuleCollider>();
rig = GetComponent<Rigidbody>();
originalHeight = capcol.height;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftControl) && Input.GetKey(KeyCode.W))
Sliding();
else if (Input.GetKeyUp(KeyCode.LeftControl))
GoUp();
}
private void Sliding()
{
capcol.height = reducedHeight;
player.transform.localRotation = Quaternion.Euler(1.599f, 0f, 0f);
rig.AddForce(transform.forward * slideSpeed, ForceMode.VelocityChange);
}
private void GoUp()
{
GetComponent<CapsuleCollider>().height = originalHeight;
player.transform.localRotation = Quaternion.Euler(0f, 0f, 0f);
}
}
I've gotten ZERO error messages from this but I haven't managed to find the problem
Another problem I've also had is when you start sliding, it just instantly rotates the Player object to X 1.599 Y 0 Z 0. I want it to rotate to where the player is facing but I haven't found a way to do it, even using transform.foward or other references won't work.
The Explanation (skip to script if you want) (read notes)
I reccomend using Quaternion's methods. You can use Quaternion.Slerp which can smooth out the rotation towards the player.
Slerp means spherical linear interpolation
Basically, Slerp is a 3 argumented method. the first argument is the current rotation, the second argument is the target rotation, and the third is at which point between those two you want to output.
More simply, Lerp is linear interpolation. Lerps arguments are the same, except it handles floats and not Quaternions. If you Mathf.Lerp(0,1,1); you will get 1, because 1 is all the ay to the second argument. If you do Mathf(0, 3, 0.5); you will get 1.5 because 1.5 is halfway (0.5) between them both.
You can imagine slerp like that, if you give rotation (90, 0, 0) and alerp it to (0, 0, 0) by 0.5 you will get (45, 0, 0).
On this example, I slerp from the current rotation, to the target rotation, by delta time. This way, as time goes on, it slightly increases the current rotation to be the target.
I can create the target as 1.599 rotated on the x.
To make this relative Make a parent game object to the player containing the mesh and rigidbody and maybe the collider (depends if you want the collider upright). I will call this "slidePlayer" slidePlayer must be a child of the player gameObject
Notes
Make sure to have slidePlayer to a gameObject that is a child of player, but must have the player mesh or sliding won't be visible.
Change rot to the target rotation.
Change speed to speed to rotate to the target rotation.
Here is your script I changed:
public class SlideMovementScript : MonoBehaviour
{
public Rigidbody rig;
public CapsuleCollider capcol;
public GameObject player;
public GameObject slidePlayer;
public float originalHeight;
public float reducedHeight;
public float slideSpeed = 10f;
// Start is called before the first frame update
void Start()
{
capcol = GetComponent<CapsuleCollider>();
rig = GetComponent<Rigidbody>();
originalHeight = capcol.height;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftControl) && Input.GetKey(KeyCode.W))
Sliding();
else if (Input.GetKeyUp(KeyCode.LeftControl))
GoUp();
}
private void Sliding()
{
capcol.height = reducedHeight;
Quaternion rot = Quaternion.Euler(1.599f, 0f, 0f);
float speed = 1f;
Quaternion curRot = slidePlayer.transform.localRotation;
slidePlayer.transform.localRotation = Quaternion.Slerp(curRot, rot, Time.deltaTime * speed);
rig.AddForce(transform.forward * slideSpeed, ForceMode.VelocityChange);
}
private void GoUp()
{
GetComponent<CapsuleCollider>().height = originalHeight;
Quaternion rot = Quaternion.Euler(0f, 0f, 0f);
float speed = 3f;
Quaternion curRot = slidePlayer.transform.localRotation;
slidePlayer.transform.localRotation = Quaternion.Slerp(curRot, rot, Time.deltaTime * speed);
}
}
Let me know if there are any problems in coments. Thanks.
I have a 3d world with a simple platform with a cube representing the player. When I rotate the platform the cube glides and perform as you expect when increase and decrease the friction in the physics material.
I want the cube to glide after the input for example forward is terminated. It does not. I tried to update the position with rigidbody.position and update it. I quickly understood that it would not work with the physics engine.
Now I have the following code. It does not work as expected anyway. I would like to have some pointers to solve this.
public class Player1 : MonoBehaviour
{
private float speed = 10f;
private Vector3 direction;
private Vector3 velocity;
private float vertical;
private float horizontal;
Rigidbody playerRigidBody;
// Start is called before the first frame update
void Start()
{
playerRigidBody = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
vertical = Input.GetAxisRaw("Vertical");
horizontal = Input.GetAxisRaw("Horizontal");
direction = new Vector3(horizontal, 0, vertical);
}
private void FixedUpdate()
{
velocity = direction.normalized * speed * Time.fixedDeltaTime;
playerRigidBody.MovePosition(transform.position + velocity);
}
}
Use playerRigidBody.AddForce(Vector3 direction, ForceMode forceMode) to move your player.
If you don't want your player to move at a demential speed use playerRigidBody.velocity = Vector3.Clamp(Vector3 vec3, float minValue, float maxValue);
Then play with different variables to get the result you want !
I'm trying to implement my own gravity for a character.
But for some unknown to me reason the character's landing speed is higher landing than when jumping, by quite a lot (~15 initial jump speed, ~24 final land speed). I'm ultimately trying to replicate behavior shown on the gif below.
https://imgur.com/a/q77w5kS
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class Movement : MonoBehaviour {
public float speed = 3;
public float jumpSpeed = 15;
public float gravity = -1f;
private float ySpeed = 0;
private float jumpTime = 0;
private bool direction = false; //true for going up, false for falling down - gravity
private bool previousDirection = false; //to keep track of changing direction
private CharacterController _characterController;
// Start is called before the first frame update
void Start() {
_characterController = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update() {
float dx = Input.GetAxis("Horizontal");
float dz = Input.GetAxis("Vertical");
float dy = Input.GetAxis("Jump");
// move around
Vector3 movement = new Vector3(dx, 0, dz);
// limit diagonal movement
movement = Vector3.ClampMagnitude(movement, 1);
// speed
movement *= speed;
// change of direction
if(ySpeed>0 && direction!=true)
direction = true;
else if(ySpeed<0 && direction!=false)
direction = false;
// if changed direction - peak of the jump
if(direction!=previousDirection){
Debug.Log("Jump Time = " + jumpTime);
jumpTime = Time.deltaTime;
previousDirection = direction;
}
// jump/on ground
if(_characterController.isGrounded) {
ySpeed = -0.5f;
jumpTime = 0;
if(dy > 0f){
ySpeed = jumpSpeed;
}
}
// gravity - when not on ground
else{
// dv/dt
ySpeed += gravity*jumpTime;
// add jump time
jumpTime += Time.deltaTime;
// Debug.Log(jumpTime);
Debug.Log(ySpeed);
}
movement.y = ySpeed;
// direction adjustment
movement = transform.TransformDirection(movement);
movement *= Time.deltaTime;
_characterController.Move(movement);
}
}
I think I figured out what was wrong with my idea.
ySpeed += gravity*jumpTime
So every frame I'm adding more and more acceleration downwards. This should just be: ySpeed += gravity *Time.deltaTime
(Acceleration due to gravity is then constant, not getting greater as time passes)
It is being integrated over many steps, so each Update() cycle is a slice of time that adds some velocity based on acceleration due to gravity, multiplied by the amount of time taken by that little slice.
In another words...
Gravity is a constant acceleration. I've made it a linear function of the time spent in the air. So, I start the jump with no gravity and end the jump with very high gravity.
I want to program a bouncing ball. The actual bounce shouldn't follow real physic laws but rather build an absolute sine wave. Like in this image
That is pretty easy so far. See the attached code.
However I want to slow down the movement of my ball as soon as I press a button.
I'll reduce the movement speed which will slow down the transformation on the x axis. But the ball is still bouncing pretty fast up and down, because neither frequency nor magnitude of the sine wave has changed. But when I change one or both, this will end in a weird behavior.
Imagine the ball is on the downmove at 20% of the distance from top to bottom. When I now change any sine parameters the ball will instantly move on the y axis which looks really weird and not smooth.
So my actual question is: How can I slow down an absolute sine wave (x/y axis translation) without choppy movement?
float _movementSpeed = 10.0f;
float _sineFrequency = 5.0f;
float _sineMagnitude = 2.5f;
Vector3 _axis;
Vector3 _direction;
Vector3 _position;
void Awake()
{
_position = transform.position;
_axis = transform.up;
_direction = transform.right;
}
void Update()
{
_position += _direction * Time.deltaTime * _movementSpeed;
// Time.time is the time since the start of the game
transform.position = _position + _axis * Mathf.Abs(Mathf.Sin(Time.time * _sineFrequency)) * _sineMagnitude;
}
I guess you want this (more red - lower freq, more blue - higher):
The easiest way to achieve this is to scale deltatime, for example this was generated with:
using UnityEngine;
using System.Collections;
public class Bounce : MonoBehaviour {
// for visualisation of results
public Graph graph;
[Range(0, 10)]
public float frequency = 1;
float t;
void Update()
{
t += Time.deltaTime * frequency;
float y = Mathf.Abs(Mathf.Sin(t));
float time = Time.realtimeSinceStartup;
graph.AddPoint(time, y, Color.Lerp(Color.red, Color.blue, frequency/10));
}
}
And for the completeness, the Graph class:
using UnityEngine;
using System.Collections.Generic;
public class Graph : MonoBehaviour {
struct PosAndColor
{
public Vector3 pos;
public Color color;
}
List<PosAndColor> values=new List<PosAndColor>();
public void AddPoint(float x, float y, Color c)
{
values.Add(new PosAndColor() { pos = new Vector3(x, y, 0), color = c });
}
void OnDrawGizmos()
{
for (int i = 1; i < values.Count; i++)
{
Gizmos.color = values[i - 1].color;
Gizmos.DrawLine(values[i - 1].pos, values[i].pos);
}
}
}
Another aproach would be to change the frequency with the offset of your sine to match the current amplitude. I don't think you'd need this here, but if you want to I can post some more on the subject.