I have a very simple model for a plane that can move around in the x and y directions, while an ocean scrolls by to make it look as though the plane is flying. Whenever the plane moves, I adjust its roll and pitch.
I compute and maintain the position, roll and pitch, inside the object script. Every update step I re-assign them to the Unity properties of the object.
The problem comes where I tried to add a barrel-roll effect. This code works fine:
transform.Translate(position - transform.position);
Vector3 rotChange = new Vector3 (-pitch, 0, -roll) -
transform.rotation.eulerAngles;
transform.Rotate (rotChange);
So long as I'm keeping all the rotation within +- 30 degrees. When the plane is doing a barrel roll, it updates each step with the following:
velocity = new Vector3 (barrelRollDirections.x * barrelRollXSpeed,
barrelRollDirections.y * barrelRollYSpeed, 0);
roll += barrelRollTurnSpeed * Time.deltaTime;
if (roll < -(fullRotation/2)) {
roll += fullRotation;
}
if (roll > (fullRotation/2)) {
roll -= fullRotation;
}
The funny thing is that spinning the plane around actually works properly if the plane isn't moving along the x or y axis when I do it. But if it is, it jumps all over the place and only reappears in the proper final position once the roll is over.
I tried a number of ways to fix this problem:
-Convert my desired orientation to a quaternion using Quaternion.Euler and assigning that directly to transform.rotate. This didn't solve anything, and it also causes the plane to jitter and shake while at the bounds of its movement.
-Save the initial orientation of the plane in a quaternion, then, every update step, reassign that initial rotation to the plane and THEN call Transform.Rotate(new Vector3(-pitch,0,-roll)). I thought this would solve my problem if it was something like gimbal lock, since then I would just, every step, be applying rotations that were less than 180 degrees. But no dice.
EDIT: I found it! After asking this question, I figured I'd go work on different objects since I was waiting for an answer. There I discovered that the transform.Translate function moves objects in a different direction based on their rotation. When I changed to simply reassigning the position, it worked fine. As per usual, the act of talking to someone else helped to resolve the issue.
Related
I'm currently trying to make a game to put my relatively new coding knowledge to test. I figured I learn a lot quicker by just doing things and seeing what happens. I'm currently working on a plane and the last couple of days I made a functional camera system (to rotate around the player based on the mouse) and some animations of the plane. I'm currently trying to create a movement system in which the down key rotates the player up. I noticed however that my AddForce function doesn't do what I want it to do. I'd like the plane to move in the Z axis depending on the current rotation of said plane instead of it just heading into the Z angle of the world.
I'm currently working on this snippet:
//Acceleration
if (gameObject.CompareTag("Player") && playerRb.velocity.z > -349)
{
playerRb.AddRelativeForce(0, 0, -playerSpeed, ForceMode.Acceleration);
}
else if (gameObject.CompareTag("Player") && playerRb.velocity.z < -349)
{
playerRb.velocity = new Vector3(playerRb.velocity.x, playerRb.velocity.y, -350);
}
//
//Movement
//Up/down controls
if (gameObject.CompareTag("Player") && Input.GetKey(KeyCode.DownArrow))
{
transform.Rotate((0.07f * playerSpeed) * Time.deltaTime, 0, 0);
}
As you can see I already tried AddRelativeForce as another topic mentioned but that doesn't seem to work for me. The moment it hits an edge it just falls down into the abyss regardless of it's rotation.
Additionally, I was considering using another AddForce function on the rotate as it doesn't leave the ground (considering it keeps hitting the floor with its tail), is there any way to apply a force on an object from a specific position/angle? (such as the front of the plane).
Thanks in advance!
I think the reason your plane is acting strange is not because of your addrelative force call -- it's because you are setting velocity. playerRb.velocity = new Vector3(playerRb.velocity.x, playerRb.velocity.y, -350); assigns the velocity to push you -350 in the world's z axis, which probably isn't what you want. Velocity is in the global space. Remove that and see what happens.
On a more general note, If you're using physics, try to use only forces and torquees. Using transform.Rotate to change a rigidbody's rotation while physics is active will just make your life difficult, and make the rigidbody do strange things. I do change the velocity of rigidbodies sometimes, but not in every frame. I just don't know enough about physics to mess with velocities that way.
Also, make sure you're doing these things in a FixedUpdate function, since that is where input that affects physics is supposed to be checked.
I have a 2d arrow rotating to always face the a target (the target in this case is the cursor), the pivot is my player character. I need to restrict this arrow to only follow the target if it is inside an angle of the player, an example would be 90 degrees, so it would only follow if the cursor is in the top right part of the screen.
I have worked with vector directions and methods such as Vector2D.angle, but they all seem to have some restriction i can't workaround, Vector2D.angles restriction is that the 3rd position it uses to calculate the angle is the world center(0, 0), my player is mobile so that doesn't work.
So i think what im asking is if theres a way to store an angle, and then check if something is within that.
Here is the code i use for rotating my arrow, theres more to the script but i removed the unnecesary parts:
public float speed;
public Transform target;
void Update()
{
Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = target.position - transform.position;
target.position = mousePosition;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
Quaternion rotation = Quaternion.AngleAxis(angle, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, speed * Time.deltaTime);
Sorry if this is formatted poorly, its my first time posting here, thank you a million times if you are able to help me, i have been stuck on this for days.
Was asked to clarify question so here is an attempt:
This picture shows an example of what i mean, the arrow is rotating around the center of the circle, (it is rotating so it always points towards my cursor). What i need is a way to restrict it so it only points towards the cursor if it is within a specific angle (red lines in picture), that's what im having trouble with. I can't find a way to store this threshold and i can't seem to find a way to compare the cursors direction with it.
I think it would be possible if it was possible to choose a custom center for Vector2D.angle, but that doesn't seem to be the case.
I hope this clarifies what my question, i may just very well be stupid and overlooking something obvious but i really can't find a way to make this possible.
thanks again.
Important fields/inputs
First, we need to know the boundary directions in world space:
public Vector2 boundaryDirectionLeft;
public Vector2 boundaryDirectionRight;
The important piece is that the angle made clockwise from boundaryDirectionLeft to boundaryDirectionRight is the region the arrow shall remain inside. In the case of your image, boundaryDirectionLeft could be Vector2.up and boundaryDirectionRight could be something like new Vector2(1f,-1f).
We also need to know which local direction the arrow is facing before any rotation is applied. E.g., if the arrow is always pointing with the local red arrow axis (the local right direction), this would be Vector2.right:
public Vector2 localArrowDirection;
And lastly, we need a top speed for our rotation, so we don't warp the arrow around when it's time to rotate. A good value to start with might be 360f, 360 degrees per second. Try experimenting with different values to find one that you like:
public float maxRotationSpeed;
The update procedure
In Update, determine the direction to the target:
Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = mousePosition - transform.position;
Then, we need to know where the arrow is currently pointing. We can use Transform.TransformVector to find where the local localArrowDirection is pointing in world space:
Vector2 currentDirection = transform.TransformVector(localArrowDirection);
Then, determine the signed angle from boundaryDirectionLeft to the target direction, the same from boundaryDirectionLeft to boundaryDirectionRight, and the same from boundaryDirectionLeft to the current facing direction:
float directionAngle = Vector2.SignedAngle(boundaryDirectionLeft, direction);
float boundaryAngle = Vector2.SignedAngle(boundaryDirectionLeft, boundaryDirectionRight);
float currentAngle = Vector2.SignedAngle(boundaryDirectionLeft, currentDirection);
These values range from [-180,180], but we want them expressed in the range [0,360) to make the math easier later, so we can add 360f and use Mathf.Repeat on that sum:
directionAngle = Mathf.Repeat(directionAngle+360f, 360f);
boundaryAngle = Mathf.Repeat(boundaryAngle+360f, 360f);
currentAngle = Mathf.Repeat(currentAngle+360f, 360f);
At this point directionAngle is how many clockwise degrees from boundaryDirectionLeft that target is, and boundaryAngle is how many boundaryDirectionRight is, and currentAngle is the same for what direction we're currently facing.
So, now, we need to know how to properly clamp the angle between 0 and boundaryAngle. Anything too far above boundaryAngle is actually closer to the left boundary and should be clamped to the left boundary. In fact, since everything is between 0 and 360, anything higher than boundaryAngle+(360f-boundaryAngle)/2f is closer to the left. So, we just set anything higher than that to be 0 degrees away from boundaryDirectionLeft:
if (directionAngle > boundaryAngle + (360f - boundaryAngle)/2f)
{
directionAngle = 0f;
}
So, now we can clamp directionAngle with a high of boundaryAngle (it is already bottom clamped at 0f, so we can use Mathf.Min here):
directionAngle = Mathf.Min(directionAngle, boundaryAngle);
Now we can limit the angular difference between directionAngle and currentAngle using our maxRotationSpeed:
float deltaAngle = Mathf.Clamp(directionAngle-currentAngle,
-maxRotationSpeed * Time.deltaTime,
maxRotationSpeed * Time.deltaTime);
Now we can rotate the transform deltaAngle degrees clockwise (in world space):
transform.Rotate(0f,0f,deltaAngle,Space.World);
I am trying to fire projectiles from a player who is moving at very high speeds. The issue is that the bullets do not seem to inherit the angular velocity. If the player is moving in a straight line at 500 U/s, then the bullet correctly inherits that velocity and adds its own speed onto that:
GameObject bullet = Instantiate(projectile, gun.transform.position, gun.transform.rotation);
bullet.GetComponent<Rigidbody>().velocity = r.velocity + bullet.transform.forward *bulletSpeed;
However, if the player is turning really quickly, it does not inherit the rotational velocity, and the bullet looks like it veers off to the side.
How can I implement this? I tried assigning the angular velocity of the player to the bullet, but this just caused the bullet to spin while moving in the same direction.
Some extra details:
I am using rigidbody.MoveRotation() to rotate my player, but I manually calculated angularVelocity to assign it to the bullet
I tried moving the player with AddTorque(), but could not produce any results with the bullets
The immediate problem is that the projectile isn't part of the physics simulation until the moment you create and launch it, meaning the rotation of the player up to that point will not be able to influence its movement. I suppose this could work if you created the projectile at the start, attached it to the gun with a weak joint, then broke the joint and added to its velocity when it was fired - but it really would be simpler if you just "faked" it.
Applying Circular Motion
This is similar to a classic circular motion example you'd find in a physics textbook. When your bullet travels in a circle around the player (inside the gun), its normal path (if released) would be tangent to the circular path around the player. So when the bullet is fired (and hence released from the circular path), that angular velocity of the player would translate into a linear velocity of the bullet. Here's a simple diagram I put together that represents the situation with a ball on a string, being spun in a circle:
Getting the Tangential Velocity of the Bullet
You don't want to be applying any kind of angular velocity to the projectile after it has been launched - as you saw, this will only spin the projectile on its own axis. Rather, you want to apply an additional velocity vector to the projectile, which is tangent to the rotation of the player (and perpendicular to the facing of the bullet). So how do we do this?
Well, as per this website, the formula for tangential velocity at any point on something spinning in a circle is:
velocity = angularVelocity x radius
A key point to remember is that the angularVelocity is in radians, not degrees.
So, let's put this all together. Here's a general idea of how you might code this:
FireGun() {
// Assumes the current class has access to the rotation rate of the player
float bulletAngularVelocityRad = Mathf.Deg2Rad(rotationRateDegrees)
// The radius of the circular motion is the distance between the bullet
// spawn point and the player's axis of rotation
float bulletRotationRadius =
(r.transform.position - gun.transform.position).magnitude;
GameObject bullet =
Instantiate(projectile, gun.transform.position, gun.transform.rotation);
// You may need to reverse the sign here, since bullet.transform.right
// may be opposite of the rotation
Vector3 bulletTangentialVelocity =
bulletAngularVelocityRad * bulletRotationRadius * bullet.transform.right;
// Now we can just add on bulletTangentialVelocity as a component
// vector to the velocity
bullet.GetComponent<Rigidbody>().velocity =
r.velocity + bullet.transform.forward * bulletSpeed + bulletTangentialVelocity;
}
Hope this helps! It's never a bad idea to read up on some basic kinematics/mechanics when you're working with game physics, since sometimes relying on the game physics is actually more complicated than just devising your own solution.
I'm Using Unity 2d and I made it so that the camera turned with the mouse in the x axis no problem. But I want the camera to only turn a small amount. The mouse needs to have full freedom to move but the camera needs to stop at a certain point.
I tried making an empty game object and set it so if the camera position equals that gameobject position the camera would move slightly back but that resulted in a lot if camera clipping. Also tried altering speed but of course that doesn't work. Does anyone know how I can do this without camera clipping?
Sorry for the lack of code as my computer has no interest access. Currently my code basically is:
transform.position = newvector3.Movetowards(
Input.GetAxisRaw("mouse X") * speed * Time.DeltaTime,
0f,
Input.GetAxisRaw("mouse Y") * 0 * Time.DeltaTime
);
That might be a bit inaccurate but that's the basic of it
To prevent clipping while using limited camera movement try something like this
transform.position = new vector 3(Mathf.Clamp(transform.position.x, MIN_X, MAX_X), 0, 0);
Of course replace MIN_X and MAX_X with the numbers of the minimum and maximum position you want the camera to be able to rotate to. This is off the top of my head so the code might not be 100 percent accurate. You can find Mathf.Clamp in the Unity docs if you're having trouble.
So I'm trying to make a third person controller that uses the PS4 controller with the left stick controlling the rotation and movement and the right stick controlling the camera separately. my problem right now is that when I apply this script to my game object it causes the player to Jitter back and forth. Any thoughts as to why or how I can fix it thanks.
void Update ()
{
//Defaults to the left Stick
float hAxis = Input.GetAxis("Horizontal");
float vAxis = Input.GetAxis("Vertical");
Vector3 NextDir = new Vector3(hAxis, 0, vAxis);
if (NextDir != Vector3.zero)
{
transform.rotation = Quaternion.LookRotation(NextDir); //this rotates the character correclty but causes jitter
transform.Translate(NextDir.x * Time.deltaTime * 5, NextDir.y * Time.deltaTime * 5, NextDir.z * Time.deltaTime * 5, Space.Self);
}
}
}
Perhaps the "jitter" you see is the game code accurately rotating the object according to the data it is getting from the joystick. I am guessing that by "jitter" you mean the rotation is bouncing back and forth between clockwise and counter-clockwise sporadically, when you think that you are smoothly rotating the joystick in one direction (clockwise or counter-clockwise). (If not, please describe the jitter.)
If this is the case, since you are reading the joystick at every frame, the joystick is probably picking up values that cause the vector to bounce back and forth. So, try adding a tolerance and only taking action if the vector changes by a certain amount. Or, only accept vectors that cause the rotation to continue in the same direction (CW or CCW) as the previous frame, unless the change is more than some certain amount, which would indicate that the player really does want to start turning back in the other direction.
Try increasing the value of the dead property in the horizontal and vertical axis. This should mean the joystick has to be moved further out of it's resting position before reporting a value.
It's located in Edit->Project Settings->Input modify the value for the second occurrences of those two properties ( the first two are for the keyboard )