wheel collider steering in new input management - c#

I'm trying to use the new 3D drive input system for my car's movement, more specifically the steering angle.
when i use Input.GetAxis("Horizontal") my wheel goes smoothly from 0 to 30 and as i press keys.
when i use context.ReadValue() it jumps from 0 to 30 with a click and returns to 0 when released without being smooth.
I can't get the wheel to rotate at the angle according to the key press and come back smoothly like in the old system, can anyone help me?
Inputmanage input;
public float currentangle = 0f;
public float angle = 30f;
public void Onmovement(InputAction.CallbackContext context) {
currentangle = angle * context.ReadValue<float>();
wheels[0].steerAngle = currentangle;
wheels[1].steerAngle = currentangle;
}

Related

Steering Wheel code in Unity Engine using C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class SteeringWheel : MonoBehaviour, IDragHandler, IPointerUpHandler, IPointerDownHandler
{
public bool Wheelbeingheld = false;
public RectTransform Wheel; //makes a gameobject treat like an object like of a rectangular shape, so that we could use many functionalities of the RECT feature, such as worldToScreenPoint, object.position, etc.
public float WheelAngle = 0f;
public float LastWheelAngle = 0f; //the previous angle of the wheel
private Vector2 center;
public float MaxSteerAngle = 200f;
public float ReleaseSpeed = 300f;
public float OutPut; // the output angle with which the steering wheel is rotated.
void Start()
{
}
// Update is called once per frame
void Update()
{
if(!Wheelbeingheld && WheelAngle != 0f)
{
float DeltaAngle = ReleaseSpeed * Time.deltaTime; //distance = speed * time; ...as angle is the distance between from and to position on the axis of the object.
if(Mathf.Abs(DeltaAngle)>Mathf.Abs(WheelAngle)) //Mathf.abs - removes the negative sign if there in the float value and return the ABSOLUTE value.
WheelAngle = 0f; //wheelangle will always end to 0. not more or less than that.
else if (WheelAngle>0f) //wheel coming back code how it will through these values and functions, so that wheels comes back to (0,0) position like that.
WheelAngle -= DeltaAngle; //wheel back, while on right side steered as wheel angle greater than 0
else
WheelAngle += DeltaAngle; //wheel back, while on left side steered as wheel angle greater than 0
}
Wheel.localEulerAngles = new Vector3(0, 0, -WheelAngle); //the animation - how the wheel will rotate if it will. Or Vector3.back * wheelAngle; wheel angle will be 0; the wheel is rotating on z axis so thats why written on the z axis place; and minus sign on wheel angle, becuz the wheel has to come back from wherever it is in the opposite direction.
OutPut = WheelAngle/MaxSteerAngle; //returns value through rotation of wheel, from -1 to 1.
}
public void OnPointerDown(PointerEventData data)
{
Wheelbeingheld = true;
center = RectTransformUtility.WorldToScreenPoint(data.pressEventCamera, Wheel.position); //world to screen point.
LastWheelAngle = Vector2.Angle(Vector2.up, data.position - center); //Syntax: Vector2.Angle(Vector2 from, Vector2 to)
}
public void OnDrag(PointerEventData data)
{
float NewAngle = Vector2.Angle(Vector2.up, data.position - center);
if((data.position - center).sqrMagnitude >= 400)
{
if(data.position.x > center.x)
WheelAngle += NewAngle - LastWheelAngle;
else
WheelAngle -= NewAngle - LastWheelAngle;
}
WheelAngle = Mathf.Clamp(WheelAngle, -MaxSteerAngle, MaxSteerAngle); //Clamp(float value, float min, float max)
LastWheelAngle = NewAngle;
}
public void OnPointerUp(PointerEventData data)
{
OnDrag(data); // Performs one last DragEvent, just in case. As when you leave the wheel, the wheel rotates back, so till then also it will assess the wheel angle and run the code of drag.
Wheelbeingheld = false;
}
}
Got this code from somewhere online and it works absolutely fine and as expected. The comments are of my own for my understanding. Check if possible if I have understood things right.
I am not able to understand 5 things for which I would love if anyone helps me with it:
"Wheel Angle" and "Last Wheel Angle" - Why is it needed if "New Angle" accurately does the job? And why not also put that in update function?
What is the speed as in frames per second of "OnPointerDown" or "...Up" and "OnDrag" functions if refreshed. Do they compare to Update or FixedUpdate?
What is data.pressEventCamera and why is it used? It's not even in the syntax of "WorldToScreenPoint" function.
What does "sqr.Magnitude" actually do? I have a guess but want to confirm.
Why is Delta Angle used and what actually is with that name?
Hoping for satisfying answers soon.
1: It is on OndDrag to stop the wheel from spinning when it has not been rotated by the user. If it was in Update it would rotate every frame. The newangle doesn't take into consideration the current angle, so is then added to the current angle to give a smoother rotation.
2: OnPointerDown is called when the pointer is pressed down on the scrollbar.
3: The camera associated with the last OnPointerPress event.
4: sqr.Magnitude is, well, the magnitude of the vector squared
5: delta anything means the change in. This is the change in the steering wheels angle.
Please do note that most of these answers came from the unity docs - you could always do a search on them: https://docs.unity3d.com/2018.3/Documentation/ScriptReference/index.html

How to make a ground object follow a flying object in unity?

I'm trying to make an object on the ground follow a flying object, for example a drone leading a human (for now i'm just using shapes - a cube and a capsule). My cube follows the capsule like i desire but i want the cube to follow the capsule on the ground only, rather than go up on the y-axis with the capsule. Right now, it follows the capsule everywhere, I want the capsule to lead while the cube follows along on the ground.
I have done some research on Google and Youtube but I have not seen any results. Please let me know how I can achieve this.
This is the code script attached to the cube(ground object)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class follow_target : MonoBehaviour
{
public Transform mTarget;
float mSpeed = 10.0f;
const float EPSILON = 0.1f;
Vector3 mLookDirection;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
mLookDirection = (mTarget.position - transform.position).normalized;
if((transform.position - mTarget.position).magnitude > EPSILON)
transform.Translate(mLookDirection * Time.deltaTime * mSpeed);
}
}
If the ground is planar, you can just set the y component to 0 (or whatever the ground y vector is).
If the ground changes in topology, you can do a raycast down from the capsule to get the hit point (vector3). You can use the hit point y component for the height. After that you will need to set the cubes rotation so that it is aligned to the ground. You could do that with a raycast as well, there are a number of examples of that online.
I hope that helps get you in the right direction.
Assuming a flat ground on Y = 0
either make sure your objects sticks to the ground so set
private const float EPSILONSQR = EPSILON * EPSILON;
void Update()
{
var difference = mTarget.position - transform.position;
mLookDirection = difference.normalized;
if(difference.sqrmagnitude > EPSILONSQR)
{
// In general be aware that Translate by default moves in the
// objects own local space coordinates so you probably would rather
// want to use Space.World
transform.Translate(mLookDirection * Time.deltaTime * mSpeed, Space.World);
var pos = transform.position;
// reset the Y back to the ground
pos.y = 0;
transform.position = pos;
}
}
or simply already map the direction down on the XZ plane (ignoring any difference in Y) like
private const float EPSILONSQR = EPSILON * EPSILON;
void Update()
{
var difference = mTarget.position - transform.position;
mLookDirection = difference.normalized;
// simply ignore the difference in Y
// up to you if you want to normalize the vector before or after doing that
mLookDirection.y = 0;
if(difference.sqrmagnitude > EPSILONSQR)
{
transform.Translate(mLookDirection * Time.deltaTime * mSpeed, Space.World);
}
}

Calculating changing Mathf.Clamp() values for object of changing size

I've been wracking my brain over this problem for a while. I have a controllable object (only moveable up and down) that I have clamped on the top and bottom. However, every time a certain trigger is activated, the object shrinks 10% until it is 90% of the original size. The problem comes in when I shrink it. When it shrinks, the min and max values of the clamp don't change at all and this results in the object clamping to soon on the min and max. They need to decrease and increase respectively so that it clamps in the exact same place. How would one do this? I've tried so many different methods but to no avail...
This is the way I shrink my player:
playerWidth /= 1.1f;
playerHeight /= 1.1f;
Vector3 scale = new Vector3(playerWidth, playerHeight, 1f);
playerOneShrink.transform.localScale = scale;
Many Thanks!
EDIT 1:
Let me give an example using a game that might help get my point across better. Basically think flappy bird, but the player uses the up and down arrows to move and not space-bar. When the bird dies, the bird would reset but become smaller. It seems that because the bird is smaller it now moves 'less' up and down, and doesn't touch the top and bottom of the screen anymore. I want the bird to still be touching the top and bottom of the screen.
EDIT 2:
So apparently my first edit didn't clarify as much as I thought so I'm going to show my code for a test scene I'm trying this all in. I haven't shown the changing of minValue and maxValue because that's what I'm struggling with...
private static Rigidbody2D playerOneRB;
GameObject player1;
float verticalMovement = 0;
public static float playerHeight = 0.3417f;
public static float playerWidth = 0.05400001f;
public float minValue = 0;
public float maxValue = 10;
public static float p1VerticalSpeed = 3;
public GameObject playerOneShrink;
// Start is called before the first frame update
void Start()
{
playerOneRB = GameObject.FindGameObjectWithTag("P1 Normal").GetComponent<Rigidbody2D>();
player1= GameObject.Find("P1 Normal");
}
// Update is called once per frame
void Update()
{
verticalMovement = Input.GetAxis("Player 1 V"); //Looking for axis defined in our input manager
playerOneRB.velocity = new Vector3(0f, verticalMovement * p1VerticalSpeed, playerOneRB.velocity.x);
Vector3 scale1 = new Vector3(playerWidth, playerHeight, 1f);
Vector3 position = transform.position;
player1.transform.position = new Vector3(player1.transform.position.x, (Mathf.Clamp(player1.transform.position.y, minValue, maxValue)), 0f); //Restricting how far the players can move (aka, not off the screen)
if (Input.GetKeyDown(KeyCode.Alpha1))
{
playerWidth /= 1.1f;
playerHeight /= 1.1f;
scale1 = new Vector3(playerWidth, playerHeight, 1f);
paddleOneShrink.transform.localScale = scale1;
}
}
Here's a photo of what's happening:
1. Photo of position it reaches before shrinking
2. Photo of position it reaches after shrinking
I want the player in image 2 to be able to get to the same top position as in image 1 after it shrinks.
It sounds like you want to scale the object, but keep it pivoted, so if it's at the top most position, and scales, it stays at the top most position? If so, the code and answers here will help:
https://answers.unity.com/questions/14170/scaling-an-object-from-a-different-center.html
You'll of course need to add in some conditional code to check whether it's a top or bottom pivot.
Hope I understood your problem.
So let's assume the scale of the player is 1 unit and you have to clamp in y axis between 0 and 10.
so you will have to clamp the Y position of player like
Vector3 scale = new Vector3(playerWidth, playerHeight, 1f);
Vector3 position = transform.position;
position.y = mathf.clamp(0 + scale.x ,10 - scale.x ,position.y );
transform.position = position;
so if the player's scale is 1 then it will limit the y position between 1 (0+1) and 9 (10-1).
if the player's scale is 0.9 then it will be between 0.9 (0+0.9 ) and 9.1 (10-0.9)
if the scale is 3 then between 3 (0+3) and 7 (10-3)

How to make rts camera smooth when zooming in Unity

right now I am trying to make rts camera zooming with panning when close to the ground. The problem I have now is that I use mouse scroll wheel for zooming and it makes the zooming feel like it's laggy. It looks like it jumps some Y value and teleports rather than smoothly move to the desired position. Also, I would like to know how to make the camera stop at the minimum Y value because what is happening now is that it stops at around 22 rather than 20 which is my minimum Y value for the camera movement.
I tried zooming with + and - on my numpad and it worked how I wanted (smoothly zooming in and out without skipping) but not all players have numpad and I feel it is more suitable to zoom with mouse wheel.
{
private const int levelArea = 100;
private const int scrollArea = 25;
private const int scrollSpeed = 25;
private const int dragSpeed = 70;
private const int zoomSpeed = 50;
// Maximum/minimum zoom distance from the ground
public int zoomMin = 20;
public int zoomMax = 120;
private const int panSpeed = 40;
// Minimal/maximal angles for camera
private const int panAngleMin = 30;
private const int panAngleMax = 90;
void Update()
{
// Init camera translation for this frame.
var translation = Vector3.zero;
// Zoom in or out
var zoomDelta = Input.GetAxis("Mouse ScrollWheel") * zoomSpeed * Time.deltaTime;
if (zoomDelta != 0)
{
translation -= Vector3.up * zoomSpeed * zoomDelta;
}
// Start panning camera if zooming in close to the ground or if just zooming out.
var pan = transform.eulerAngles.x - zoomDelta * panSpeed;
pan = Mathf.Clamp(pan, panAngleMin, panAngleMax);
// When to start panning up the camera
if (zoomDelta < 0 || transform.position.y < (zoomMax -20))
{
transform.eulerAngles = new Vector3(pan, 0, 0);
}
// Move camera with arrow keys
translation += new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// Move camera with mouse
if (Input.GetMouseButton(2)) // MMB
{
// Hold button and drag camera around
translation -= new Vector3(Input.GetAxis("Mouse X") * dragSpeed * Time.deltaTime, 0,
Input.GetAxis("Mouse Y") * dragSpeed * Time.deltaTime);
}
else
{
// Move camera if mouse pointer reaches screen borders
if (Input.mousePosition.x < scrollArea)
{
translation += Vector3.right * -scrollSpeed * Time.deltaTime;
}
if (Input.mousePosition.x >= Screen.width - scrollArea)
{
translation += Vector3.right * scrollSpeed * Time.deltaTime;
}
if (Input.mousePosition.y < scrollArea)
{
translation += Vector3.forward * -scrollSpeed * Time.deltaTime;
}
if (Input.mousePosition.y > Screen.height - scrollArea)
{
translation += Vector3.forward * scrollSpeed * Time.deltaTime;
}
}
// Keep camera within level and zoom area
var desiredPosition = transform.position + translation;
if (desiredPosition.x < -levelArea || levelArea < desiredPosition.x)
{
translation.x = 0;
}
if (desiredPosition.y < zoomMin || zoomMax < desiredPosition.y)
{
translation.y = 0;
}
if (desiredPosition.z < -levelArea || levelArea < desiredPosition.z)
{
translation.z = 0;
}
// Move camera parallel to world axis
transform.position += translation;
}
}
I would like to have a smooth transition from the position where the camera is now and the desired position after scrolling in/out. And also I would like to know how to make the camera to stop at the minimum/maximum zoom distance rather than to stop close to it. Thank you for helping. Video how the camera movement looks like: https://youtu.be/Lt3atJEaOjA
Okay, so I'm going to do three things here. First and foremost, I'm going to recommend that if you're working with advanced camera behavior, you probably want to at least consider using Cinemachine. I'd walk you through it myself, but given my lack of personal experience with it, I'd probably be doing you a disservice by even trying. There are many good tutorials out there. Youtube and Google should provide.
The second thing I'll do is solve your problem in the most direct way I can manage, and after that, we'll see if we can't come up with a better method for solving your problem.
So, the key here is that Unity's scrollwheel input is pretty binary. When you check a scrollwheel axis, the result is directly based on how many "clicks" your wheel has gone through since the last frame update, but what you really want is something with a bit of give. By default, Unity can actually do this with most of its axis inputs: You might notice that if you use WASD in a default Unity project, there's a sort of "lag" to it, where you'll take your fingers off the keys but you'll still keep receiving positive values from Input.GetAxis() for a few frames. This is tied to the Gravity value in your input settings, and Input.GetAxisRaw() is actually used to circumvent this entirely. For whatever reason, scrollwheel axes don't seem to be affected by axis gravity, so we essentially have to implement something similar ourselves.
// Add this property to your class definition (so it persists between updates):
private float wheelAxis = 0;
// Delete this line:
var zoomDelta = Input.GetAxis("Mouse ScrollWheel") * zoomSpeed * Time.deltaTime;
// And put these three new lines in its place:
wheelAxis += Input.GetAxis("Mouse ScrollWheel");
wheelAxis = Mathf.MoveTowards(wheelTotal, 0f, Time.deltaTime);
var zoomDelta = Mathf.Clamp(wheelAxis, -0.05f, 0.05f) * zoomSpeed * Time.deltaTime;
Right, so we do a few things here. Every update, we add the current scrollwheel values to our wheelAxis. Next, we apply the current Time.deltatime as "gravity" via the Mathf.MoveTowards() function. Finally, we call what's mostly just your old zoomDelta code with a simple modification: We constrain the wheelAxis with Mathf.Clamp to try and regulate how fast the zooming can happen.
You can modify this code in a couple of ways. If you multiply the Time.deltaTime parameter you can affect how long your input will "persist" for. If you mess with the Mathf.Clamp() values, you'll get faster or slower zooming. All in all though, if you just want a smooth zoom with minimal changes to your code, that's probably your best bet.
So!
Now that we've done that, let's talk about your code, and how you're approaching the problem, and see if we can't find a somewhat cleaner solution.
Getting a good camera working is surprisingly non-trivial. Your code looks like a lot of code I see that tries to solve a complex problem: It looks like you added some feature, and then tested it, and found some edge cases where it fell apart, and then patched those cases, and then tried to implement a new feature on top of the old code, but it kinda broke in various other ways, etc etc, and what we've got at this point is a bit messy.
The biggest issue with your code is that the camera's position and the camera's rotation are closely tied together. When working with characters, this is usually fine, but when working with a camera, you want to break this up. Think about where the camera is and what the camera is looking at as very separate things to keep track of.
So here's a working camera script that you should be able to plug in and just run with:
using UnityEngine;
public class RTSCamera : MonoBehaviour
{
public float zoomSpeed = 100f;
public float zoomTime = 0.1f;
public float maxHeight = 100f;
public float minHeight = 20f;
public float focusHeight = 10f;
public float focusDistance = 20f;
public int panBorder = 25;
public float dragPanSpeed = 25f;
public float edgePanSpeed = 25f;
public float keyPanSpeed = 25f;
private float zoomVelocity = 0f;
private float targetHeight;
void Start()
{
// Start zoomed out
targetHeight = maxHeight;
}
void Update()
{
var newPosition = transform.position;
// First, calculate the height we want the camera to be at
targetHeight += Input.GetAxis("Mouse ScrollWheel") * zoomSpeed * -1f;
targetHeight = Mathf.Clamp(targetHeight, minHeight, maxHeight);
// Then, interpolate smoothly towards that height
newPosition.y = Mathf.SmoothDamp(transform.position.y, targetHeight, ref zoomVelocity, zoomTime);
// Always pan the camera using the keys
var pan = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")) * keyPanSpeed * Time.deltaTime;
// Optionally pan the camera by either dragging with middle mouse or when the cursor touches the screen border
if (Input.GetMouseButton(2)) {
pan -= new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * dragPanSpeed * Time.deltaTime;
} else {
var border = Vector2.zero;
if (Input.mousePosition.x < panBorder) border.x -= 1f;
if (Input.mousePosition.x >= Screen.width - panBorder) border.x += 1f;
if (Input.mousePosition.y < panBorder) border.y -= 1f;
if (Input.mousePosition.y > Screen.height - panBorder) border.y += 1f;
pan += border * edgePanSpeed * Time.deltaTime;
}
newPosition.x += pan.x;
newPosition.z += pan.y;
var focusPosition = new Vector3(newPosition.x, focusHeight, newPosition.z + focusDistance);
transform.position = newPosition;
transform.LookAt(focusPosition);
}
}
While I encourage you to go through it in your own time, I'm not going to drag you through every inch of it. Instead, I'll just go over the main crux.
The key idea here is that rather than controlling the camera's height and orientation directly, we just let the scrollwheel dictate where want the camera's height to be, and then we use Mathf.SmoothDamp() to move the camera smoothly into that position over several frames. (Unity has many useful functions like this. Consider Mathf.MoveTowards() for an alternative interpolation method.) At the very end, rather than trying to fiddle with the camera's rotation values directly, we just pick a point in front of us near the ground and point the camera at that spot directly.
By keeping the camera's position and orientation completely independent of each other, as well as separating out the "animation" of the camera's height, we avoid a lot of headaches and eliminate a lot of potential for messy interwoven bugs.
I hope that helps.
Cinemachine is pretty good, but I suggest people learn on your own quite a bit before using Cinemachine so that you better understand what's going on in the background.

How to calculate cursor delta like in FPS games?

I'm making an XNA game and I'd like the cursor to stop when the mouse button is held down. Right now by setting the cursor position every 1/60 of a second to a certain location makes the cursor look laggy and sometimes if the cursor moves fast enough to reach a screen border, from that moment and until the next position reset the delta position is not calculated correctly. Also, starting at the edge of the screen and moving towards that same edge won't get any correct deltas.
How do I get the correct cursor deltas and not get bummed by cursor stopping at screen edges?
If the position you are locking the mouse to is too close to the edge, you are out of luck.
I suggest you hide the cursor at the beginning of the drag motion, save the location and secretly move the cursor to the center of the screen. When dragging ends just restore the cursor to its previous location.
Easily, supposing I understood what you meant.
In order to move a 1st person camera according to the mouse, we need to calculate the delta each frame, and then move the mouse back to the center of the screen.
To do so we will store the location of the center of the screen. We also need a MouseState variable and a yaw and a pitch floats to store our current camera rotations. I also like to set the rotation speed according the game's resolution:
MouseState currentMouseState;
Vector2 screenCenter;
float YrotationSpeed;
float XrotatiobSpeed;
float yaw = 0;
float pitch = 0;
...
protected override void LoadContent()
{
YrotationSpeed = (float)graphics.PreferredBackBufferWidth / 10000.0f;
XrotatiobSpeed = (float)graphics.PreferredBackBufferHeight / 12000.0f;
screenCenter = new Vector2(graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height) / 2;
}
Now, to calculate how much did the mouse move in the last frame, we will use this function:
private void HandleMouse(GameTime gameTime)
{
currentMouseState = Mouse.GetState();
float amount = (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
if (currentMouseState.X != screenCenter.X)
{
yaw -= YrotationSpeed * (currentMouseState.X - screenCenter.X) * amount;
}
if (currentMouseState.Y != screenCenter.Y)
{
pitch -= XrotatiobSpeed * (currentMouseState.Y - screenCenter.Y) * amount;
if (pitch > MathHelper.ToRadians(90))
pitch = MathHelper.ToRadians(90);
if (pitch < MathHelper.ToRadians(-50)) //Preventing the user from looking all the way down (and seeing the character doesn't have any legs..)
pitch = MathHelper.ToRadians(-50);
}
Mouse.SetPosition((int)screenCenter.X, (int)screenCenter.Y);
}
Finally:
To update the camera and view Matrix, you could use the following function, that should be executed after we update our yaw and pitch:
public void UpdateCamera(float yaw, float pitch, Vector3 position)
{
Matrix cameraRotation = Matrix.CreateRotationX(pitch) * Matrix.CreateRotationY(yaw);
Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
Vector3 cameraFinalTarget = position + cameraRotatedTarget;
Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);
view = Matrix.CreateLookAt(position, cameraFinalTarget, cameraRotatedUpVector);
}
EDIT: Well, as I re-read the question I had realized my answer isn't quite what you were asking for. Nevertheless, it does show how to calculate the mouse delta without it getting to the borders of the screen.
To match my answer to your question, all you need are tiny adjustments.
By the way, unless you are willing to draw an image of a mouse in the middle of the screen, any methood WILL look kinda laggy.
Therefore, I highly advice you to hide the cursor.
To do so, just add this line:
this.IsMouseVisible = false;

Categories