Relative input but global movement in Unity - c#

I'm working on a spaceflight game in unity and stumbled across this simple logical error. I want to be able to add relative movement to my spaceship, but also for it to keep the momentum in that direction when you turn. I've sot a separate script for looking around and movement. Here is my movement source code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController ship;
public float speed = 0.001f;
float momentum = 0f;
Vector3 velocity;
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
}
void Update()
{
momentum = momentum + Input.GetAxis("Vertical");
Vector3 move = transform.forward * momentum * 0.001f;
ship.Move(move);
}
}
I know this is a simple question, my brain is just kind of stuck right now. Thanks! ~OwenPT

Do I understand correctly that you want the spaceship fly forward and when it turns right or left it must continue flying forward?
Your spaceship must have component rigidbody (I think it does) and you get mode him using function GetComponent().AddForce. There you can choose the ForceMode. There are 4 kinds of force_mode:
Force - Add a continuous force to the rigidbody, using its mass.
Acceleration - Add a continuous acceleration to the rigidbody, ignoring its mass.
Impulse - Add an instant force impulse to the rigidbody, using its mass.
VelocityChange - Add an instant velocity change to the rigidbody, ignoring its mass.
So, choose the one you need and the spaceship must continue flying forward after turning

You've neglected your velocity variable. You're applying a scalar speed (momentum) in the direction of facing (transform.forward).
If you want to have a feel of momentum, you need to deal with Acceleration:
public CharacterController Ship;
public float AccelerationFactor = 1.0f;
public float BrakingFactor = 0.1f;
public float MaxSpeed = 20.0f;
private Vector3 Velocity;
void Update()
{
var totalBraking = Velocity.normalized * BrakingFactor;
var totalAcceleration = transform.forward * AccelerationFactor * Input.GetAxis("Vertical");
Velocity = Velocity + (totalAcceleration + totalBraking) * Time.deltaTime;
Velocity = Vector3.ClampMagnitude(Velocity, MaxSpeed);
Ship.Move(Velocity * Time.deltaTime);
}

Related

How to make a game object move to the opposite direction of a constantly moving object? Unity 2D

I have a game object that constantly rotates around another one:
What I am trying to achieve is to have the circle move in the opposite direction of the square when I press the space key, the square will simulate a propeller, for example:
This is the code I have, which kinda works:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] GameObject rotatingPropellant;
[SerializeField] float rotationSpeed;
[SerializeField] float impulseForce;
Rigidbody2D playerRb;
void Start()
{
playerRb = GetComponent<Rigidbody2D>();
}
void Update()
{
RotatePropellant();
Move();
}
void RotatePropellant()
{
rotatingPropellant.transform.RotateAround(transform.position, new Vector3(0f, 0f, 1f), rotationSpeed * Time.deltaTime);
}
void Move()
{
if (Input.GetButtonDown("Jump"))
{
playerRb.velocity = -rotatingPropellant.transform.position * impulseForce * Time.deltaTime;
}
}
}
But sometimes the circle moves up even if the square is up (meaning that the circle should be pushed down), not sure if maybe the signs are not correct or if I should be approaching this with a different method
So this:
playerRb.velocity = -rotatingPropellant.transform.position * impulseForce * Time.deltaTime;
relies on rotatingPropellant position relative to World's zero position (Vector3.zero), which might work only if the propellant revolves around this zero point.
What you should probably do instead is get the difference:
Vector3 dir = (rotatingPropellant.transform.position - transform.position).normalized;
playerRb.velocity = dir * impulseForce * Time.deltaTime;
Also, instead of changing velocity, you can add force instead:
Vector3 dir = (rotatingPropellant.transform.position - transform.position).normalized;
playerRb.AddForce(dir * impulseForce, ForceMode2D.Impulse);
To get movement direction, you can use this vector formula AB = B - A. This formula will give you the position vector of A Related to B space and if you normalize this vector you will get the movement direction
A is sphere and B is Square at here. Check Vector Addition and Substraction.

Unity Simple Character Controller Script not working as expected

Im scratching my head over this one, really cant figure it out.
Im trying to implement the player movement from this example:
https://www.youtube.com/watch?v=LNidsMesxSE
Starts at minute 4:25 and ends at minute 5:20
This script tries translating all that to unity.So I started all the way over from scratch and just want to make a simple movement script.
You can plug this into any Unity version, throw the script onto and object with a CharacterController component, add a child object with a mesh that will tilt, the main object will rotate around its Y axis and move.
I do recommend using a simple T pose character or atleast a long capsule so you can better see what is happening than when you would be using a cube to test this.
The weird glitches im having is that the object randomly spasms out even though im always only adding extremely small rotations and movements every frame. And 95% of the time it doesnt happen, so I havent been able to pinpoint exactly what is causing this.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterMove : MonoBehaviour
{
public float speed = 200f;
public float rotationSpeed = 180f;
public float tiltFactor = 8f;
public bool normalizeVelocity = false;
CharacterController cc;
Vector3 velocity;
Transform armatureBody;
void Start()
{
cc = GetComponent<CharacterController>();
armatureBody = transform.GetChild(0);
}
void Update()
{
velocity = new Vector3(Input.GetAxis("Horizontal"), 0,
Input.GetAxis("Vertical"));
}
void FixedUpdate()
{
if ((velocity != Vector3.zero))
{
var ccVelocity = cc.velocity; // CharacterController velocity. This might be the problem, but without it I cannot rotate the object towards the actual forward velocity.
Quaternion toRotation;
toRotation = Quaternion.LookRotation(ccVelocity, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
if (Input.GetKey(KeyCode.S)) print("new toRotation: " + toRotation);
armatureBody.localEulerAngles = new Vector3(velocity.z * tiltFactor, 0, -velocity.x * tiltFactor); // Tilt Body towards velocity This causes the weirdest and most consistest twitching bugs when the tilt is backwards so the object is moving backwards, maybe because im using LookRotation to look backwards?
}
velocity = Quaternion.Euler(0, transform.eulerAngles.y, 0) * velocity; // forward is always forward
cc.SimpleMove(velocity * Time.deltaTime * speed);
}
}
This script does 4 things,
get the velocity from player input.
rotate the main Object(this script) towards the velocity
add tilt towards the velocity, to the child armatureBody, this is a t Pose character in my case.
move the main Object this script is on.
Any help would be appreciated.
I tried removing the vector3 != Zero check, I tried normalizing the vectors, and I tried using a different Quaternion rotation method, but all with the same faulty results.
Check in Update() if Input.forward > threshold if so rotate to velocity. Dont rotate to velocity when only going sideways or backwards, so we can now strafe sideways and walk backwards when standing still or run and rotate to our velocity with the AD keys when running.
void Update()
{
velocity = new Vector3(Input.GetAxis("Horizontal"), 0,
Input.GetAxis("Vertical"));
if (velocity.z > 0.2f)
{
forwardVelocityThreshold = true;
}
else forwardVelocityThreshold = false;
}
Then in our FixedUpdate() we check for that bool and apply the rotateToVelocity if true.
Adjust the following if statement:
if (ccVelocity != Vector3.zero && forwardVelocityThreshold)
{
toRotation = Quaternion.LookRotation(ccVelocity, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.fixedDeltaTime);
}
Now it works as intended, I also changed the deltaTime to FixedDeltaTime as suggested by Voidsay in the comments, although I have heard conflicting things about this. Some say FixedUpdate always uses fixedDeltaTime even if you are using deltaTime, other say no, always use fixedDeltaTime in FixedUpdate.

Unity3D Player walking through and on the Stone

Hello guys my Player is walking on the Stone and through the Stone. The Player called Champ has a Box Collider and the Stone has a Mesh Collider. Also the Player has Rigidbody. I tried everthing i found but nothing helped me with my problem.
MovePlayer.cs Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MovePlayer : MonoBehaviour
{
Rigidbody rb;
public float speed = 10f;
private Vector3 moveDirection;
public float rotationSpeed = 0.05f;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
moveDirection = new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical")).normalized;
}
void FixedUpdate()
{
rb.MovePosition(rb.position + transform.TransformDirection(moveDirection * speed * Time.deltaTime));
RotatePlayer();
}
void RotatePlayer()
{
if (moveDirection != Vector3.zero)
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(moveDirection.normalized), rotationSpeed);
}
transform.Translate(moveDirection * speed * Time.deltaTime, Space.World);
}
}
Player Settings in Inspector
Stone Settings in Inspector
Scene Preview
Thank you for help guys! :)
So guys i found out the Solution with the help of the guys posted above.
The problem was that my player speed was too high in the Code the speed was on float 10, but i changed the velocity in the Unity Inspector of Player to float 50.
So my first step to solve the problem was to set the speed down to float 10, but i still wanted to move with a speed of 50f...
The Solution for this problem was that in Unity 2020.3.24f1 and higher (probably lower) you can go to Edit>Project Settings>Physics and set the "Default Max Depenetration Velocity" to the speed you want the objects stopping and not going through. In my case i wanted to move with speed = 50f so i needed to change Default Max Depenetration Velocity to 50.
I hope i can help someone with this Answer in future!
Best Wishes
Max G.
Tested your code and collisions seem to be working fine on my end.
Tested it by adding the script to a GameObject with box collider and creating a small level using cubes. Also made a wall that I modified to use mesh-collider instead of box collider. Player collided normally with objects in the scene.
You should double check your Layer collision matrix from Project Settings > Physics whether you've set layers player and wall to collide.
You could also try adding new cube to the scene and setting its layer to wall to see if player collides with it. If it does then the there might be issues with the mesh of the stone.
If not then I would disable animator and Gravity Body components from the player to make sure they're not interfering with the collisions
Rigidbody.MovePosition basically makes the player teleport which can cause unexpected behaviors. It's generally recommended to use Rigidbody.AddForce instead. For precise movement ForceMode.VeloictyChange can be used.
public float maxVelocityChange = 5.0f;
void moveUsingForces(){
Vector3 targetVelocity = moveDirection;
// Doesn't work with the given RotatePlayer implementation.
// targetVelocity = transform.TransformDirection(targetVelocity);
targetVelocity *= speed;
// Apply a force that attempts to reach our target velocity
Vector3 velocity = rb.velocity;
Vector3 velocityChange = (targetVelocity - velocity);
velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
velocityChange.y = 0;
rb.AddForce(velocityChange, ForceMode.VelocityChange);
}
In this code you have applied motion twice and the problem is that transform.Translate is used. Remember that Rigidbody class methods are sensitive to colliders and recognize them, but transform is not the same and only applies a point-to-point shift. To solve the problem, I think you will not need a duplicate motion code with translate in the rotate section.
void RotatePlayer()
{
if (moveDirection != Vector3.zero)
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(moveDirection.normalized), rotationSpeed);
}
// removed translate
}

Set rigidbody.velocity to direction of mouse in Unity2d: Updated Version

Yes I know this question was asked before by SuperSonyk! That post is here:
Set rigidbody.velocity to direction of mouse in Unity2d
However- the link referenced in the answer to that post is now legacy material, and I can't bring myself to understand it in Unity's current version.
My Problem: In short, I want to make my player accelerate in the direction of my cursor. My game is currently a TOP-DOWN Space Shooter in 2D. My player already looks at my mouse correctly, using cam.ScreenToWorldPoint. But relentlessly trying to add force seems futile for me, since I am fairly new to coding.
If I have any other issues in my code, It would be great if anyone could point them out!
Here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
//================= MOVEMENT =====================
private float thrust = 0.5f;
private float maxSpeed = 10f;
public Rigidbody2D rb;
//================= AIMING =======================
public Camera cam;
Vector2 mousePos;
// Update is called once per frame: Good for INPUTS
void Update()
{
//aiming
mousePos = cam.ScreenToWorldPoint(Input.mousePosition);
}
// Called Set amount of times. Good for PHYSICS CALCULATIONS
void FixedUpdate()
{
Move();
speedLimiter();
//aim
Vector2 lookDirection = mousePos - rb.position;
float angle = Mathf.Atan2(lookDirection.y, lookDirection.x) * Mathf.Rad2Deg - 90f;
rb.rotation = angle;
}
//======= MY MOVE FUNCTION DOES NOT WORK. HELP? ======
void Move()
{
if (Input.GetButtonDown("Accelerate"))
{
rb.velocity = new Vector3(0, thrust, 0) * Time.deltaTime;
}
}
void speedLimiter()
{
if (rb.velocity.magnitude > maxSpeed)
{
rb.velocity = Vector3.ClampMagnitude(rb.velocity, maxSpeed);
}
}
}
Please summarize the edits clearly, thank you!
In general since this is a 2D rigidbody also the velocity is a Vector2 and you should probably rather use Vector2.ClampMagnitude
and then in
rb.velocity = new Vector3(0, thrust, 0) * Time.deltaTime;
do you only want to move in global Y direction? Also a velocity is already a "per second" value -> it makes no sense to multiply by Time.deltaTime if you reassign a new velocity. Since it says thrust I guess you rather wanted to add to the existing velocity.
What you would rather do is save your mouse direction you already have and do
public class PlayerController : MonoBehaviour
{
...
void FixedUpdate()
{
// I would update the aim BEFORE moving
// Use a NORMALIZED direction! Makes things easier later
Vector2 moveDirection = (mousePos - rb.position).normalized;
float angle = Mathf.Atan2(moveDirection.y, moveDirection.x) * Mathf.Rad2Deg - 90f;
rb.rotation = angle;
// OPTIONAL: Redirect the existing velocity into the new up direction
// without this after rotating you would still continue to move into the same global direction
rb.velocity = rb.velocity.magnitude * moveDirection;
Move(moveDirection);
speedLimiter();
}
void Move(Vector2 moveDirection)
{
if (Input.GetButtonDown("Accelerate"))
{
// Instead of re-assigning you would probably add to the existing velocity
// otherwise remove the "+" again
rb.velocity += moveDirection * thrust * Time.deltaTime;
}
}
void speedLimiter()
{
if (rb.velocity.magnitude > maxSpeed)
{
// You are working with Vector2 so use the correct method right away
rb.velocity = Vector2.ClampMagnitude(rb.velocity, maxSpeed);
}
}
}
You already have half of the work by having your player looking at the mouse. We will be using the vector lookDirection as the direction of the movement for the player, normalize it so its length is 1 and multiply it by thrust. This way, we have a vector in the direction of the mouse and of length thrust as intended.
void Move()
{
if (Input.GetButtonDown("Accelerate"))
{
Vector2 lookDirection = (mousePos - rb.position);
rb.AddForce(lookDirection.normalized * thrust);
}
}
Note that we work here with Vector2 since you are using a Rigidbody2D.
Also, I don't think that using Time.DeltaTime is relevant here. When you apply force to a physic object, you don't need to use it, by definition, the function FixedUpdate() has a fixed frequency. If you look at one of Unity's tutorial, you can see that they don't multiply it by Time.DeltaTime
Last tip: you may want to be playing around with the drag property of your Rigidbody2D to have your player slow down over time when not accelerating, otherwise, it will be sliding out of control and the player won't be able to stop it.
Don't hesitate to tell me if my answer is not clear for you or does not entirely satisfy you, it's my first answer here!
EDIT: I forgot to tell you but as derHugo mentioned, since you are using Rigidbody2D, you must use Vector2.ClampMagnitude to limit your speed in your speedLimiter function. Also, I don't think that you need your if since the function Vector2.ClampMagnitude will only change the value of the velocity if it exceeds the maximum.
Thanks to everyone who responded!
With the help of Brackeys community, I managed to solve this by using this method:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
//================= MOVEMENT =====================
private float thrust = 10f;
private float maxSpeed = 100f;
public Rigidbody2D rb;
//================= AIMING =======================
public Camera cam;
Vector2 mousePos;
// Update is called once per frame: Good for INPUTS
void Update()
{
//aiming
mousePos = cam.ScreenToWorldPoint(Input.mousePosition);
}
// Called Set amount of times. Good for PHYSICS CALCULATIONS
void FixedUpdate()
{
Move();
speedLimiter();
//aim
Vector2 lookDirection = mousePos - rb.position;
float angle = Mathf.Atan2(lookDirection.y, lookDirection.x) * Mathf.Rad2Deg - 90f;
rb.rotation = angle;
}
void Move()
{
if (Input.GetKey("up"))
{
rb.AddRelativeForce(Vector2.up*thrust);
}
}
void speedLimiter()
{
if (rb.velocity.magnitude > maxSpeed)
{
rb.velocity = Vector3.ClampMagnitude(rb.velocity, maxSpeed);
}
}
//rb.AddForce(new Vector3(0, thrust, 0));
}

Unity3D C#. Using the transform.RotateAround() function while keep the distance between two objects constant

I am trying to practice making third person controllers in Unity 3D. I am a beginner, and I am completely baffled on how to make my controller functional. I have been doing hours of research, but no thread I can find seem to answer my question. I have two scripts, a CameraController, and a CharacterController. My code is below.
CameraController:
using UnityEngine;
using System.Collections;
public class CameraController : MonoBehaviour {
public GameObject target;
public float rotationSpeed;
Vector3 offset;
Vector3 CameraDestination;
// Use this for initialization
void Start () {
offset = transform.position - target.transform.position;
CameraDestination = offset + transform.position;
rotationSpeed = 50f;
transform.position = CameraDestination;
}
// Update is called once per frame
void Update () {
transform.LookAt (target.transform.position);
float h = Input.GetAxisRaw ("Horizontal");
transform.RotateAround (target.transform.position, Vector3.up, Time.deltaTime * h * rotationSpeed);
target.transform.Rotate (0f, Time.deltaTime * h * rotationSpeed, 0f);
}
}
CharacterController:
using UnityEngine;
using System.Collections;
public class CharController : MonoBehaviour {
public float playerSpeed = 10f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
float Vertical = Input.GetAxis("Vertical");
transform.position += transform.forward * Time.deltaTime * playerSpeed * Vertical;
}
}
When either the left or right arrow key is pressed, both the player and the camera rotates. If I try to attach the camera to the player as a children, the camera's rotation becomes messed up, but if I do not attach the camera to the player, the camera stops following the player. If I try to set the camera to a specific position relative to the player, it stops revolving around the player like it is intended to do. I simply can not come up with a method that works.Thank you for answering my question in advance!
When I go about this, I like to have an empty gameObject which has 2 children, the camera and then the mesh of the character.
> CharacterController
> Camera
> CharacterRig
When you want to rotate the character, rotate the CharacterController, then when you want to rotate the Camera around the character, change your code to:
transform.RotateAround (CharacterRig.transform.position, Vector3.up, Time.deltaTime * h * rotationSpeed);
This will allow your camera to rotate irrespective of any character animation and should solve your issue. This is very important if you want to implement animations later, as you don't want to parent the camera to the thing that is being animated because it will move with the animation.
P.s. Your code appears fine. Certain that it is purely the way you have set up the game objects.

Categories

Resources