I am trying to create a flight simulator, using rigid bodies. I am using AddForce to make the plane accelerate. However, when rotate the plane, there is a lot of drift. How can I stop this?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlaneController : MonoBehaviour
{
public float maxSpeed = 200f;
public float speed = 90.0f;
public Vector3 rotationSpeedY = new Vector3(0, 0, 40);
public Vector3 rotationSpeedZ = new Vector3(40, 0, 0);
public Rigidbody rb;
private void FixedUpdate()
{
rb.AddRelativeTorque(-Input.GetAxis("Horizontal") * rotationSpeedY * Time.deltaTime);
rb.AddRelativeTorque(Input.GetAxis("Vertical") * rotationSpeedZ * Time.deltaTime);
rb.AddRelativeForce(transform.forward * speed);
speed -= transform.forward.y * Time.deltaTime * 50.0f;
if (rb.velocity.magnitude > maxSpeed)
{
rb.velocity = rb.velocity.normalized * maxSpeed;
}
if (speed < 35.0f)
{
speed = 35.0f;
}
}
}
The reason for this is that you're not modeling aerodynamics in any way. When a real plane banks the wings provide a lift force according to their angle of attack vs the direction of travel and at the same time they produce drag in the opposite direction.
If you want your plane to react like the real world you will have to calculate these two forces which will be pretty simple if you're not looking for accurate aerodynamic modelling based off of wing shape. At the moment your plane is acting as if there is no atmosphere.
Related
I'm making a sphere move over a plane object. I'm trying to make the movement similar to the movement of a wheel, but I don't want to use the Wheel Collider component. I am using torque to move the sphere back and forth and I am using the rigidbody rotation (Because I read that it is not a good practice to perform these transformations directly on the geometry), but the rotation (steering) part is not working, the sphere continues to follow in same direction even rotating. Here's code below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SphereMovement : MonoBehaviour
{
float maxTorque = 30.0f;
float maxSteerAngle = 30.0f;
void Start()
{
}
void FixedUpdate()
{
var deltaRotation = GetComponent<Rigidbody>().rotation * Quaternion.Euler(new Vector3(maxSteerAngle * Input.GetAxis("Horizontal") * Time.deltaTime, 0, 0));
GetComponent<Rigidbody>().rotation = deltaRotation;
GetComponent<Rigidbody>().AddTorque(new Vector3(maxTorque * Input.GetAxis("Vertical") * Time.deltaTime, 0, 0));
}
}
Can someone help me?
Cache your GetComponent calls to improve performance (see below)
You are applying torque into global x direction, you probably want to move "forward" (depending on the rotation of the wheel). transform.forward is your friend
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SphereMovement : MonoBehaviour
{
float maxTorque = 30.0f;
float maxSteerAngle = 30.0f;
private Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
var deltaRotation = rb .rotation * Quaternion.Euler(new Vector3(maxSteerAngle * Input.GetAxis("Horizontal") * Time.deltaTime, 0, 0));
GetComponent<Rigidbody>().rotation = deltaRotation;
GetComponent<Rigidbody>().AddTorque(transform.forward * (maxTorque * Input.GetAxis("Vertical") * Time.deltaTime);
}
}
If your setup is otherwise rotated, you can try transform.right or transform.up
edit:
when you rotate your wheel, the "forward" will spin as well, so you need to ignore the y. (assuming an almost flat surface for now, you mentioned a plane)
So you would think this works:
Vector3 direction = transform.forward;
direction.y = 0;
direction = direction.normalized;
GetComponent<Rigidbody>().AddTorque(direction * (maxTorque *
Input.GetAxis("Vertical") * Time.deltaTime);
But Then it would go back or forth, depending on the current rotation.
So that won't work.
Another Idea: You could add an empty parent and rotate that on the y axis to "steer" while the child is rotated to do the actual "forward movement" like a wheel rooling over concrete.
Option without parent:
You simply apply Torque in local space using Rigidbody.AddRelativeTorque
Like this (we use Vector3.forward, don't confuse it with transform.forward! Vector3.forward is simply Vector3(0, 0, 1). but in local space that's fine because the rotation is still considered)
void FixedUpdate()
{
var deltaRotation = rb .rotation * Quaternion.Euler(new Vector3(maxSteerAngle * Input.GetAxis("Horizontal") * Time.deltaTime, 0, 0));
GetComponent<Rigidbody>().rotation = deltaRotation;
GetComponent<Rigidbody>().AddRelativeTorque(Vector3.forward * (maxTorque * Input.GetAxis("Vertical") * Time.deltaTime);
}
The problem is here:
GetComponent<Rigidbody>().AddTorque(new Vector3(maxTorque * Input.GetAxis("Vertical") * Time.deltaTime, 0, 0));
Your torque is always being applied along the global X axis, so it doesn't matter which way the sphere object is rotated. Think of it like this -- no matter which way you rotate a soccer ball, if you spin it in a northwards direction, it will roll north. For your approach to work, you need to take your torque force and apply it along the vector you want to move, rather than always the global X axis.
Ive created a character controller system already, but his movement speed does not change based on how far I push the analog stick. I'm very new to Unity and c# and would like some help. Thanks! Here is my code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.InteropServices.ComTypes;
using UnityEngine;
public class ThirdPersonMovement : MonoBehaviour
{
public CharacterController controller;
public Transform cam;
public float speed = 6f;
public float turnSmoothTime = 0.1f;
// Update is called once per frame
void Update()
{
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
UnityEngine.Vector3 direction = new UnityEngine.Vector3(horizontal, 0f, vertical);
if (direction.magnitude >= 0.1f)
{
float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg + cam.eulerAngles.y;
transform.rotation = UnityEngine.Quaternion.Euler(0f, targetAngle, 0f);
UnityEngine.Vector3 moveDir = UnityEngine.Quaternion.Euler(0f, targetAngle, 0f) * UnityEngine.Vector3.forward;
controller.Move(moveDir * speed * Time.deltaTime);
}
}
}
I think there are other ways of doing it but you can use animation for achieving that you adjust the trigger for the animation ! Example (play the slow animation if your speed is this and play speed animation if your speed is more)
Also you should do this speed = direction.magnitude because direction.magnitude holds the value of analog movement so animation plus this equals to a cool game good luck :)
I'm currently developing an FPS shooter in Unity 3D. I'm fairly new to Unity and I've been having a bit of trouble with my player movement script. Individually everything seems to work, I can rotate and move the player freely, however when I try doing the two simultaneously my player seems to lock and won't rotate.
Sometimes jumping or moving to higher ground on the terrain seems to fix the issue, however I ran a few checks with gravity disabled, no colliders, and with the player well above ground, so the problem seems to be with the script. I've also done a bit of debugging and the Rotate() code does run, just the rotation amount doesn't seem to change.
Here is the player movement script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class PlayerMov : MonoBehaviour
{
[SerializeField]
private Camera cam;
[SerializeField]
private float speed = 5f;
[SerializeField]
private float looksensitivity = 4f;
[Header("Camera View Lock:")]
[SerializeField] //min and max amount for looking up and down (degrees)
private float lowlock = 70f;
[SerializeField]
private float highlock = 85f;
private Rigidbody rb;
private float currentrotx; //used later for calculating relative rotation
public Vector3 velocity;
public Vector3 rotation; // rotates the player from side to side
public float camrotation; // rotates the camera up and down
public float jmpspe = 2000f;
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
CalcMovement();
CalcRotation();
Rotate();
}
void FixedUpdate()
{
Move();
}
private void CalcRotation()
{
float yrot = Input.GetAxisRaw("Mouse X"); //around x axis
rotation = new Vector3(0f, yrot, 0f) * looksensitivity;
float xrot = Input.GetAxisRaw("Mouse Y"); //around y axis
camrotation = xrot * looksensitivity;
}
private void CalcMovement()
{
float xmov = Input.GetAxisRaw("Horizontal");
float zmov = Input.GetAxisRaw("Vertical");
Vector3 movhor = transform.right * xmov;
Vector3 movver = transform.forward * zmov;
velocity = (movhor + movver).normalized * speed;
}
void Move()
{
//move
if (velocity != Vector3.zero)
{
rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);
}
//jump
if (Input.GetKeyDown(KeyCode.Space))
{
//add double jump limit later!
rb.AddForce(0, jmpspe * Time.deltaTime, 0, ForceMode.Impulse);
}
}
void Rotate()
{
//looking side to side
rb.MoveRotation(rb.rotation * Quaternion.Euler(rotation));
// camera looking up and down
currentrotx -= camrotation;
currentrotx = Mathf.Clamp(currentrotx, -lowlock, highlock);
cam.transform.localEulerAngles = new Vector3(currentrotx, 0, 0);
}
}
Here are the relevant components attached to my player:
(The player has a couple more components attached but I ran tests without them and the problem still occurs)
Like I said I'm a bit of a unity novice, so i'm sure I missed something small but I just can't seem to place my finger on it, I've been stuck on this for a while so any help is much appreciated.
SOLVED:
It seems the problem I had was because I was running the scene from my laptop and not a desktop which I assume is what the Unity input was built for.
I dont understand how I can make this shotgun jumps in my version of Unity: https://www.youtube.com/watch?v=FUkdz8jYt3w
How does this work?
Im using "First Person Controller" that looks like capsule
There are images: https://imgur.com/a/uvfAePX
This is for old version of Unity 4.5.5. I've tried RigidBody, but nothing happened. I've tried Transform, but again, no result.
First Person Controller:
using UnityEngine;
using System.Collections;
public class run : MonoBehaviour
{
public float speed = 6.0F;
public float jumpSpeed = 8.0F;
public float gravity = 20.0F;
private Vector3 moveDirection = Vector3.zero;
public Transform character;
public int CharacterForce = 5000;
public int time = 1;
void Update()
{
CharacterController controller =
GetComponent<CharacterController>();
if (controller.isGrounded)
{
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis ("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
moveDirection *= speed;
if (Input.GetButton("Jump"))
moveDirection.y = jumpSpeed;
}
moveDirection.y -= gravity * Time.deltaTime;
controller.Move (moveDirection * Time.deltaTime);
if (Input.GetMouseButtonDown(0))
{
Transform BulletInstance = (Transform)Instantiate(character, GameObject.Find("CameraRSP").transform.position, Quaternion.identity);
BulletInstance.GetComponent<Rigidbody>().AddForce(transform.forward * CharacterForce);
}
}
}
"Grenade" code:
using UnityEngine;
using System.Collections;
public class grenade : MonoBehaviour
{
public Transform GrenadeF;
public int force = 500;
public float radius;
void Start()
{
Collider[] col = Physics.OverlapSphere(transform.position, radius);
foreach (Collider c in col)
{
c.GetComponent<Rigidbody>().AddExplosionForce(force, transform.position, radius);
}
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(transform.position, radius);
}
}
When I used Transform I expected that this would work in ANY direction, but it worked in only one.
When I used RigidBody I expected that this would WORK, but my capsule didnt even moved.
Checkout the optional upwardsModifier parameter of
AddExplosionForce(float explosionForce, Vector3 explosionPosition, float explosionRadius, float upwardsModifier = 0.0f, ForceMode mode = ForceMode.Force));
(The API for Unity 4 is no longer available but I guess it should have been the same there)
Adjustment to the apparent position of the explosion to make it seem to lift objects.
and
Using this parameter, you can make the explosion appear to throw objects up into the air, which can give a more dramatic effect rather than a simple outward force. Force can be applied only to an active rigidbody.
By default it is 0 so if you don't pass it there won't be any upwards force.
As you can see in the example from the API
Vector3 explosionPos = transform.position;
Collider[] colliders = Physics.OverlapSphere(explosionPos, radius);
foreach (Collider hit in colliders)
{
Rigidbody rb = hit.GetComponent<Rigidbody>();
// |
// v
if (rb) rb.AddExplosionForce(power, explosionPos, radius, 3.0f);
}
they passed e.g. 3.0f as upwardsModifier. This makes the explosion
appear to be centred 3.0 units below its actual position for purposes of calculating the force direction (ie, the centre and the radius of effect are not modified).
Note: Typed on smartphone so no warrenty but I hope the idea gets clear
I have the following code for my 2D game, it makes object randomly wonder on the screen. What I am having issues with, is when an object looks at the point it is going to, I would like it to rotate as it moves forward. What is happening now, is it rotates instantly to the point it is going towards. So, how can I get it to rotate slowly and move forward at the same time?
using UnityEngine;
using System.Collections;
public class Wonder : MonoBehaviour {
protected Vector2 wayPoint;
protected float speed;
// Use this for initialization
void Start () {
speed = gameObject.GetComponent<Move>().playerSpeed;
wonder();
}
void wonder(){
wayPoint = Random.insideUnitCircle * 10;
}
// Update is called once per frame
void Update () {
Vector2 dir = wayPoint - new Vector2(transform.position.x, transform.position.y);
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(new Vector3(0, 0,Mathf.Atan2 (dir.y, dir.x) * Mathf.Rad2Deg - 90));
transform.position = Vector2.MoveTowards(transform.position, wayPoint, Time.deltaTime * speed);
float magnitude = (new Vector2(transform.position.x, transform.position.y) - wayPoint).magnitude;
if(magnitude < 3){
wonder();
}
}
}
Here is an example Image:
So, once the ship gets to its point another will be created and it will move there. I am thinking I will have to have a list of 5+ points, then calculate the arch the ship needs to take adding new points as the ship hits a way point then removing old ones after. I am not sure how to do this though...
using UnityEngine;
using System.Collections;
public class Wander : MonoBehaviour {
protected Vector3 velocity;
protected Vector2 waypoint;
protected float speed;
// Use this for initialization
void Start () {
speed = gameObject.GetComponent<Move>().playerSpeed;
RandomizeWaypoint();
}
void RandomizeWaypoint(){
waypoint = Random.insideUnitCircle * 10;
}
// Update is called once per frame
void Update () {
transform.position = Vector3.SmoothDamp( transform.position, waypoint, ref velocity, Time.deltaTime * speed );
transform.rotation = Quaternion.AngleAxis( Mathf.Atan2( velocity.y, velocity.x ) * Mathf.Rad2Deg, Vector3.forward );
if( Vector3.Distance( transform.position, waypoint ) < 3 ){
RandomizeWaypoint();
}
}
}
Untested. Vector3.SmoothDamp can be pretty handy. Note the spelling also.