I have a problem with trying to keep my character from rolling over, yet allow it to pitch and yaw. The suggestions I have seen have been to lock different axis, but no matter if I lock the x, y, or z axis I always run into a situation where the character can fall over. The best I can get is to lock both the y and z axis. This allows the character to change pitch to conform to the terrain. But, again, if I turn left or right while going up or down a hill and I can roll the character over.
Here is my current code for movement (in case it helps). I have no other code and my rigid body is all defaults. I have a mesh collier for the character set to convex but defaults otherwise.
Any suggestions on how to make this work?
Here's a live demo of y and z axis being locked. Just waltz up the hill and hang a left or right and you'll fall over. (ASWD controls)
https://dl.dropboxusercontent.com/u/27946381/Builds/builds.html
Thanks so much!
var speed = 3.0;
var rotateSpeed = 3.0;
function FixedUpdate() {
var hAxis = 0;
var vAxis = 0;
if( Input.GetAxis("Horizontal") > 0 ) { hAxis = 1.0; } else if( Input.GetAxis("Horizontal") < 0 ) { hAxis = -1.0; } else { hAxis = 0.0; }
if( Input.GetAxis("Vertical") > 0 ) { vAxis = 1.0; } else if( Input.GetAxis("Vertical") < 0 ) { vAxis = -1.0; } else { vAxis = 0.0; }
var rigidBody: Rigidbody = GetComponent(Rigidbody);
// Rotate around y axis
// transform.Rotate(0, hAxis * rotateSpeed, 0);
var deltaRotation : Quaternion = Quaternion.Euler(0, hAxis * rotateSpeed, 0);
rigidbody.MoveRotation(rigidbody.rotation * deltaRotation);
// Move forward / backward
var forward = transform.TransformDirection(Vector3.forward);
var currSpeed = speed * vAxis;
rigidBody.MovePosition( rigidBody.position + (forward * currSpeed) );
var animatorController: Animator = GetComponent(Animator);
animatorController.SetFloat("Speed", currSpeed);
}
well maybe you should check your rigidbody properties called gravity scale.. set it to 0 and you will never fall
I believe your problem is in these MovePosition and MoveRotation. Take a look into scripting reference - these methods basically ignores collision until body is teleported at the specified pose. I.e. you may place the body into some kind of irresolvable situation, when physics engice unable to find appropriate forces to push the body away from another collider, and therefore the body will fall through. In this case it is better to use AddForce and AddTorque.
Also, you can use CharacterController instead of RigidBody. Though it cannot be rotated at all, there are fine methods for kinematic motion with precise collision detection. And you can attach another body to the character controller and rotate it as you wish.
Changing code will change nothing.
Try changing the hitbox of the player to a big cube
(Better) Try to look into the rigidbody properties, as far as I remember I had the same problem and fixed it that way, it might help
Related
I am trying to archive without success a z-axis rotation movement around a moving object keeping always "looking at front". So it should looks like this:
The closest I got was with:
transform.RotateAround(targetPosition, Vector3.forward, moveSpeed);
But it does not keeps looking "at front".
Could someone give me a hand with this?
Thank you in advance.
Best regards.
If your object ("Lightning Bolt") has no world rotation, i.e. aligned with the world axis as your example image seems to suggest, then the easiest is to simply set the world rotation to the Quaternion Identity:
transform.rotation = Quaternion.identity;
Note that the image wont rotate if its parent object rotates. It will essentially "Billboard" your lightning object. If you want to your lightning bolt to be aligned with a parent object, then try something like:
transform.rotation = transform.parent.rotation;
Fitst store the current rotation, then rotate around point, lastly apply the previous rotation.
var rot = transform.rotation;
transform.RotateAround(targetPosition, Vector3.forward, moveSpeed);
transform.rotation = rot;
A simple solution would just manipulate actual coordinates and ignore rotation ^^ If an object moves and you want it to keep rotating around it, just make it a child object.
This is a 3d solution where we rotate around Y:
void Start() { angle = 0.0f }
void Update() {
angle += speed * Time.deltaTime; //Your starting angle that will be modified every frame
CheckAngle(ref angle);
float x = Mathf.Cos(angle * Mathf.Deg2Rad) * Radius;
float z = Mathf.Sin(angle * Mathf.Deg2Rad) * Radius;
}
static void CheckAngle(ref float Angle) //It should probably return a value by it's name here tho not ref, not sure which is the correct way of doing this
{
if (Angle > 360) //You don't want your angle to go past the boundaries of an INT
Angle = 0;
}
I a new here and i try to start working with Unity Engine.
Could somebody explain me, how works Quaternion.Slerp? Because I want to rotate some object in different angles 90, 180 and 270. My code you can see below. Unfortunately when I add 180 degrees, object make crazy things and than put rotation to (0, 180, 180) for this game object. I would like to get (180,0,0)
public float speed = 0.1F;
private float rotation_x;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
rotation_x = transform.rotation.eulerAngles.x;
rotation_x += 180;
}
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(rotation_x, transform.eulerAngles.y, transform.eulerAngles.z), Time.time * speed);
}
Most examples out there including Unity examples from their official website are using Lerp in the wrong way. They didn't even bother to describe how it works in the API documentation. They just starch it in the Update() function and call it a day.
Mathf.Lerp, Vector3.Lerp, and Quaternion.Slerp work by changing from one position/rotation to another with the t value(last parameter) being passed in.That t value is also know as time.
The min of the t value is 0f and the max is 1f.
I will explain this with Mathf.Lerp to make it easier to understand. The Lerp functions are all the-same for both Mathf.Lerp, Vector and Quaternion.
Remember that Lerp takes two values and returns values between them. If we have a value of 1 and 10 and we do Lerp on them:
float x = Mathf.Lerp(1f, 10f, 0f); will return 1.
float x = Mathf.Lerp(1f, 10f, 0.5f); will return 5.5
float x = Mathf.Lerp(1f, 10f, 1f); will return 10
As you can see, the t(0) returns the min of the number passed in, t(1) returns the max value passed in and t(0.5) will return mid point between the min and the max value. You are doing it wrong when you pass any t value that is < 0 or > 1. That code in you Update() function is doing just that. Time.time will increase every second and will be > 1 in a second, so you have problems with that.
It recommended to use Lerp in another function/Coroutine instead of the Updated function.
Note:
Using Lerp has a bad side of it when it comes to rotation. Lerp does not know how to rotate Object with the shortest path. So bear that in mind. For example, you have an Object with 0,0,90 position. Lets say you want to move the rotation from that to 0,0,120 Lerp can sometimes rotate left instead of right to reach that new position which means it take longer to reach that distance.
Let's say we want to make the rotation (0,0,90) from whatever the current rotation is. The code below will change the rotation to 0,0,90 in 3 seconds.
ROTATION OVER TIME:
void Start()
{
Quaternion rotation2 = Quaternion.Euler(new Vector3(0, 0, 90));
StartCoroutine(rotateObject(objectToRotate, rotation2, 3f));
}
bool rotating = false;
public GameObject objectToRotate;
IEnumerator rotateObject(GameObject gameObjectToMove, Quaternion newRot, float duration)
{
if (rotating)
{
yield break;
}
rotating = true;
Quaternion currentRot = gameObjectToMove.transform.rotation;
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
gameObjectToMove.transform.rotation = Quaternion.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
rotating = false;
}
INCREMENTAL ANGULAR ROTATION OVER TIME:
And to just rotate the Object to 90 in z axis, the code below is a great example of that. Please understand there is a difference between moving Object to new rotational point and just rotating it.
void Start()
{
StartCoroutine(rotateObject(objectToRotate, new Vector3(0, 0, 90), 3f));
}
bool rotating = false;
public GameObject objectToRotate;
IEnumerator rotateObject(GameObject gameObjectToMove, Vector3 eulerAngles, float duration)
{
if (rotating)
{
yield break;
}
rotating = true;
Vector3 newRot = gameObjectToMove.transform.eulerAngles + eulerAngles;
Vector3 currentRot = gameObjectToMove.transform.eulerAngles;
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
gameObjectToMove.transform.eulerAngles = Vector3.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
rotating = false;
}
All my examples are based on frame-rate of the device. You can use real-time by replacing Time.deltaTime with Time.delta but more calculation is required.
Before anything, you can't add 180 on euler angles like that, and that's mainly what is causing your problem. You'd better use quaternion directly instead, or work on the transform itself.
You can think of a quaternion as an orientation in space. In contrary to what have been said, I do recommend learning how to use them if you can. However, I don't recommend using euler angles at all... as they're suject to different writing conventions, and will fail sometimes. You can look at 'gimbal lock' if you want details about that.
Simply a slerp or lerp (standing for spherical linear interpolation, or linear interpolation respectively) is a way to interpolate (go from one orientation to another, by increasing t from 0 to 1, in a coroutine or anywhere else) between orientation A and B. The difference between the two is that the slerp is giving you the shortest path from A to B.
In the end, when t = 1, lerp(A,B,t) and slerp(A,B,t) will give you B.
In your case, if you want to instantly rotate an object in space to a specific orientation, I suggest you use Quaternion.AngleAxis which is the most forward way to describe mathematically a quaternion.
If you want to add a rotation, say 90° to you actual orientation (without animation between the two), you can do something like this :
transform.rotation *= Quaternion.AngleAxis(axis_of_rotation, angle)
or use transform.rotate (depending on the parameters, it can be a right multiply, or left : local, or world transform).
Programmers' answer is detailling how to animate your transform. But I do suggest you to investigate quaternion themselves, as it will give you global understanding of space transforms.
I want to start with a little 3D platformer on Unity. When I move, I want the character looking to the moving direction. So when I press Left/"A" I want the character instantly turning left and walking forward. Same for the other directions. The problem is that the character turns back to the default rotation when I leave the key.
The important code:
private void FixedUpdate()
{
float inputX = Input.GetAxis("Horizontal"); // Input
float inputZ = Input.GetAxis("Vertical"); // Input
if (GroundCheck()) // On the ground?
{
verticalVelocity = -gravity * Time.deltaTime; // velocity on y-axis
if (Input.GetButtonDown("Jump")) // Jump Key pressed
{
verticalVelocity = jumpPower; // jump in the air
}
}
else // Player is not grounded
{
verticalVelocity -= gravity * Time.deltaTime; // Get back to the ground
}
Vector3 movement = new Vector3(inputX, verticalVelocity, inputZ); // the movement vector
if (movement.magnitude != 0) // Input given?
{
transform.rotation = Quaternion.LookRotation(new Vector3(movement.x, 0, movement.z)); // Rotate the Player to the moving direction
}
rigid.velocity = movement * movementSpeed; // Move the character
}
The second thing is, at
transform.rotation = Quaternion.LookRotation(new Vector3(movement.x, 0, movement.z));
there is 0 on the y-axis. It says the viewing Vector is zero. When I pass in another number like movement.y the character tilts to the floor. So I do not know what to pass in there.
As for resetting when you let go of the key: your line
if (movement.magnitude != 0) // Input given?
is a good idea, but there's a good chance your controller is reporting slightly off of 0, so your character's direction will change even when you're not actually moving. I would change this to
if (movement.magnitude >.1f) // Input given?
or some other number close to (but not exactly) zero. While working on this, I would add Debug.Log(movement.magnitude); to this function and make sure that the values are in the range you expect.
On the second topic:
while it is important to have verticalVelocity in your movement vector for when you apply it to rigidBody.velocity, you don't want it in your character facing vector. If you want your character to only look in a single plane, it makes perfect sense to only give it two dimensions to consider; adding a third dimension would make it look at the sky or the ground as you mentioned. Furthermore, I would change your input-checking line to use this as well, because you only want to change facing based on whether the character is moving horizontally or not. This would make your code look something like this:
Vector3 movement = new Vector3(inputX, verticalVelocity, inputZ); // the movement vector
Vector3 horizontalMovement = new Vector3(inputX, 0f, inputZ);
if (horizontalMovement.magnitude != 0) // Input given?
{
transform.rotation = Quaternion.LookRotation(horizontalMovement); // Rotate the Player to the moving direction
}
rigid.velocity = movement * movementSpeed; // Move the character
And one final note, when you are grounded, you might want to set verticalVelocity to 0, rather than -gravity*deltaTime. This error may not be visible (the physics engine will push your character back up out of the floor), but if the user alt-tabs and there's too long between frames, your character will teleport through the floor!
Good luck.
I'm a total beginner and I'm making a simple 2D game, you have ball which you throw to collect pickups. I managed, following tutorials and searching on the web, to script the controls as I wanted them, when the mouse is clicked the force to apply is calculated by the movement on axis X and Y and on mouse button release the force is applied and the gravity turned on:
if (Input.GetMouseButton(0))
{
mouseDown = true;
}
if (!Input.GetMouseButton(0))
{
mouseDown = false;
}
if (Input.GetAxis("Mouse X") != 0 && mouseDown)
{
xForce -= Input.GetAxis("Mouse X") * 50;
}
if (Input.GetAxis("Mouse Y") != 0 && mouseDown)
{
yForce -= Input.GetAxis("Mouse Y") * 50;
}
if (Input.GetMouseButtonUp(0))
{
GetComponent<Rigidbody2D>().gravityScale = 1;
GetComponent<Rigidbody2D>().AddForce(new Vector2(xForce, yForce));
yForce = 0;
xForce = 0;
}
But while working perfectly while testing it on unity using the mouse, it behave strangely on my 2 android phones. Sometimes the ball seems to get the force only for the X axis, and even if dragging only vertically the ball has an X force applied... What could be the problem?
EDIT: I added Debug.Log(xForce) and Debug.Log(yForce) and looking at the log with adb logcat I noticed that, also when simply tapping on the screen, without dragging on neither axis, the value of those variables increase, I have really no idea on how to solve this...
Personally I would make this easier on myself by just using Input.GetMouseButtonDown and Input.GetMouseButtonUp instead. The below method will return identical results on all platforms.
On down, set a variable like "startFirePosition = worldPosOfInput".
On up, "newForce = worldPosOfInput - startFirePosition"
You can find the world position of the input by using Input.mousePosition and Camera.ScreenToWorldPoint.
Your newForce Vector2 now contains information like the drag distance (magnitude), and firing direction that you can use in your firing code.
You can assign newForce directly to your rigidbody, or tweak it first to do things like increase the magnitude, clamp direction, etc.
PS, if needed you can flip the direction your newForce will take the rigidbody by flip flopping the line "worldPosOfInput - startFirePosition" to "startFirePosition - worldPosOfInput".
I am fairly new to Unity so please bear with me I have tried looking for the answer everywhere, but have had no luck.
Basically I am using onMouseDrag to move a sprite around the background for a classroom (1366x768) that has a table. However, I want to limit where the sprite can go so that it does not end up off screen or off of the table on my background.
My sprite has the 2d box collider and rigidbody components attached (gravity is set to zero and it is at a fixed angle). I thought that by placing four 2d box colliders around the area I want to keep the sprite in it would be enough to contain it but the sprite simply goes straight through them.
I also read up about using Mathf.Clamp to restrict the area but I do not really understand how to use it from the examples I have seen.
Below is my code for moving the sprite:
using UnityEngine;
using System.Collections;
public class MovementScript : MonoBehaviour {
float x;
float y;
void Update() {
x = Input.mousePosition.x;
y = Input.mousePosition.y;
}
public void OnMouseDrag() {
transform.position = Camera.main.ScreenToWorldPoint (new Vector3 (x, y, 1.0f));
}
}
Any help would be greatly appreciated!
Moving an object using its transform is not the same as moving it.
When you use the transform, the object doesn't "move", it teleports every Update by a small amount. Unfortunately, the Rigidbody can't detect this change in position as movement and thus does not react with any colliders. Regardless, that's probably the more complicated way to do this. Using Clamp is definitely easier.
Clamp is a pretty straightforward function. it takes three args: a value, a min, and a max. If the value is less than min or greater than max, it returns that boundary. Otherwise, it returns the value itself.
For instance,
Mathf.Clamp(5, 1, 3); //returns 3
Mathf.Clamp(2, 1, 3); //returns 2
Mathf.Clamp(-2, 1, 3); //returns 1
This is simply a convenience function for something like this:
if(val > max) {
return max;
} else if(val < min) {
return min;
} else {
return val;
}
So using Clamp, you can restrict the values of your x and y coordinates:
//to avoid confusion, I'm referring to your x and y variables
//as inputX and inputY. they represent the mouse position.
public void OnMouseDrag() {
Vector3 pos = Vector3.zero;
pos.x = Mathf.Clamp(inputX, minX, maxX);
pos.y = Mathf.Clamp(inputY, minY, maxY);
pos.z = 1.0;
transform.position = Camera.main.ScreenToWorldPoint (pos);
}
Mathf.Clamp will keep the X and Y coordinates of your Transform within the range (minX, maxX) and (minY, maxY) respectively. You can create these variables as inspector variables so you can change them on the fly.