Change audio pitch based on rigidbody speed - c#

We have some sound for when the player is moving or rolling being the player is a ball. We want to increase the pitch of the audio the faster the ball goes. I tried the below code but it doesn't do anything. I think it's because the value of p comes out too small.
I remember reading somewhere that there is something built in to handle this but I can't think of where I saw or it what it was called.
Thanks in advance!
void FixedUpdate()
{
#if UNITY_EDITOR || UNITY_STANDALONE
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 move = new Vector3(-moveHorizontal, 0.0f, -moveVertical);
move = move * (speed / 15f);
//maxSpeed = maxSpeed / 5;
#else
// Player movement in mobile devices
// Building of force vector
Vector3 move = new Vector3(-Input.acceleration.x, 0.0f, -Input.acceleration.y);
// Adding force to rigidbody
move = move * (speed / 15f);
//move = movement * speed * Time.deltaTime;
#endif
rigidbdy.AddForce(move);
var p = rigidbdy.velocity.magnitude / speed;
audio.pitch = Mathf.Clamp(p, 1.0f, 2.0f); // p is clamped to sane values
//Limits the max speed
if (rigidbdy.velocity.magnitude > maxSpeed)
{
rigidbdy.velocity = rigidbdy.velocity.normalized * maxSpeed;
}
}

You can use the map function for easy control over the pitch value.
float mapValue(float mainValue, float inValueMin, float inValueMax, float outValueMin, float outValueMax)
{
return (mainValue - inValueMin) * (outValueMax - outValueMin) / (inValueMax - inValueMin) + outValueMin;
}
You pass in AudioSource.pitch to the mainValue parameter.
For the inValueMin value, you pass in the default/MIN value of the Rigidbody.velocity.magnitude which is 0.
For the inValueMax value, you pass in the MAX value your ball can go.
You can easily determine this number with Debug.Log("RB: " + ballRigidbody.velocity.magnitude); and running the game. 10 seems to be fine for this. You must determine your own value.
The default AudioSource.pitch value is 1, so outValueMin parameter should be 1.
The outValueMax parameter will be the maximum pitch you think is acceptable to you. I found 1.5 to be ok for this so 1.5 will be used for outValueMax.
Whatever you get from the mapValue function is what you assign to the AudioSource.pitch. This gives you much more control over the pitch of you sound. You can read more about this function on the Arduino site.
Remove your current Audio code and replace it with this:
float rigidBodyMangintude = rigidbdy.velocity.magnitude;
float pitch = mapValue(rigidBodyMangintude, 0f, 10f, 1f, 1.5f);
audio.pitch = pitch;
Debug.Log("Pitch: " + pitch);
The mapValue function is at the top of this answer.

Related

How can I smooth rotation which is based on cursor position?

private float rotationZ;
Vector3 difference = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
difference.Normalize();
float rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, rotationZ);
So far my code will get the cursor position and instantly perform a transform. However, if I move the cursor near the center of the pivot, the rotation will be instantaneous and choppy. How can I add a slight smooth to this, to make it travel from point A to point B slower?
You would store the actual target value in a field and then move towards it
private Quaternion targetRotation;
private Camera camera;
private void Awake ()
{
// Camera.main is quite expensive so rather store the reference
camera = Camera.main;
// Initially set your current rotation as target
// especially important if you don't always update the target rotation but e.g.
// only if a certain button is pressed
targetRotation = transform.rotation;
}
private void Update ()
{
UpdateTargetRotation();
RotateTowardsTarget();
}
private void UpdateTargetRotation ()
{
var difference = (camera.ScreenToWorldPoint(Input.mousePosition) - transform.position).normalized;
var targetRotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
targetRotation = Quaternion.Euler(0, 0, targetRotationZ);
}
Now for the RotateTowardsTarget you have many options.
One of them would indeed be using Quaternion.Lerp with a certain interpolation factor. Here you interpolate smoothly towards the target value but very fast at the beginning and get slower and slower the closer you get to the target value regardless of how much you rotate in total. This also might never really reach the exact target value.
// Adjust in the Inspector
public float interpolationFactor = 5f;
private void RotateTowardsTarget ()
{
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, interpolationFactor * Time.deltaTime);
}
Or another option would be to rather use Quaternion.RotateTowards which rotates with a fixed speed
// Adjust in the Inspector
public float anglePerSecond = 45;
private void RotateTowardsTarget ()
{
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, anglePerSecond * Time.deltaTime);
}
You're looking for Mathf.Lerp
Example of how you could use it:
float rotationZ;
//Changes how quickly will rotate
const float rotationSpeed = 0.58f;
Vector3 difference = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
difference.Normalize();
rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
float lerpRotation = Mathf.Lerp(transform.rotation.z, rotationZ, Time.deltaTime * rotationSpeed);
transform.rotation = Quaternion.Euler(0f, 0f, lerpRotation);
You should use Quaternion.Slerp() in a coroutine.
You check the cursor position every FixedUpdate then start a coroutine that interpolates and sets the rotation every frame until the next FixedUpdate comes. The created lag is basicly unnoticable and it looks silky smooth.
For the interpolation t value you should use a variable that stores the time elapsed since the last FixedUpdate (you add that up from time.deltatimes), and to get the t you just divide it by 0.02secs.
Then when the coroutin finishes, set the rotation to the final value.

Character Movement, Acceleration C# Unity

Hi everyone Newbie here.
Top-down Zelda style game.
I'm trying to figure out how to make my player build speed to max speed then reduce speed to stoping.
I already have movement with GetRawAxis but my char moves at max speed the moment I press move with this method.
private void PlayerMovement()
{
var playerMovement = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")).normalized * Time.deltaTime * moveSpeed;
transform.position = new Vector3(
transform.position.x + playerMovement.x,
transform.position.y + playerMovement.y,
transform.position.z);
}
Here is a scenario where you can move your object in the x axis, gradually increasing the speed. You can do the same with the slowing down. Gradually decrease the speed by a value.
float acceleration = 0.6;
float maxSpeed = 30;
float speed = 0;
void Update(){
if(speed < maxSpeed){
speed += acceleration * Time.deltaTime;
}
transform.position.x = transform.position.x + speed*Time.deltaTime;
}
You could try Vector3.SmoothDamp.
This uses a Vector storing the current "speed" and dumps it slowly.
Example can be found here:
https://docs.unity3d.com/ScriptReference/Vector3.SmoothDamp.html

Unity enemy ai always firing bullets high over the player

Got an issue where the enemy will fire at the player, but always seems to go high or to the side of the player even when the player is stationary and isn't moving. Am I doing something wrong in my code which creates this wild issue or is it just a random annoying bug?
Using the same script for the player albeit it under a different name works, which leads me to believe the issue lies within the fire point. Under the player's script I fire like so:
// Get the place the player has clicked
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
// Holds information regarding the mouseclick
RaycastHit hitInfo;
// Now work out if we fire or not
if (Physics.Raycast(ray, out hitInfo))
{
if(hitInfo.distance < maxRange)
{
FireAtPoint(hitInfo.point);
Whereas in the enemy script it is just done through the player's position.
// Holds information regarding the mouseclick
RaycastHit hitInfo;
// Now work out if we fire or not
if (Physics.Raycast(player.transform.position,transform.forward, out hitInfo))
{
Is this underlying issue in the Physics.Raycast call then?
Rest of code for reference:
//More above this but doesn't influence the firing
if (Physics.Raycast(player.transform.position,transform.position, out hitInfo))
{
if (hitInfo.distance < maxRange)
{
FireAtPoint(hitInfo.point);
}
}
private void FireAtPoint(Vector3 point)
{
// Get the velocity to fire out at
var velocity = FiringVelocity(point, angle);
Rigidbody rg = Instantiate(bulletPrefab.gameObject, firePoint.position, firePoint.rotation).GetComponent<Rigidbody>();
EnemyBulletController newProjectile = rg.GetComponent<EnemyBulletController>();
newProjectile.speed = velocity;
}
private Vector3 FiringVelocity(Vector3 destination, float angle)
{
// Get the direction of the mouse click from the player, then get the height differential.
Vector3 direction = destination - transform.position;
float height = direction.y;
height = 0;
// Get the distance in a float of the vector3
float distance = direction.magnitude;
// Turn the firing angle into radians for calculations, then work out any height differential
float AngleRadians = angle * Mathf.Deg2Rad;
direction.y = distance * Mathf.Tan(AngleRadians);
distance += height / Mathf.Tan(AngleRadians);
// Calculate the velocity magnitude
float velocity = Mathf.Sqrt(distance * Physics.gravity.magnitude / Mathf.Sin(2 * AngleRadians));
// Return the normalized vector to fire at.
return velocity * direction.normalized;
}
Picture for reference:
Your equation for computing the velocity looks doubtful. Let's re-derive it:
The equations of free-fall motion under constant gravity are:
After rearranging by substituting the first into the second, we find an expression for the firing velocity:
This is different to what you have, as you are missing the h/d term; said term also gives a constraint on the allowed values of θ:
(Basically means that if you fire directly at the target the bullet would never reach due to gravity)
There are many other problems with your code; just to list three:
Why set height to zero?
Why add a correction to distance? The correction has no physical interpretation.
The fix suggested by #BasillePerrnoud
Amended code:
private Vector3 FiringVelocity(Vector3 destination, float angle)
{
Vector3 direction = destination - transform.position;
float height = direction.y;
float distance = Mathf.Sqrt(direction.x * direction.x + direction.z * direction.z); // *horizontal* distance
float radians = angle * Mathf.Deg2Rad;
float hOverd = height / distance;
float tanAngle = Mathf.Tan(radians);
if (tanAngle <= hOverd)
// throw an exception or return an error code, because no solution exists for v
float cosAngle = Mathf.Cos(radians);
direction.Y = distance / cosAngle;
float velocity = Mathf.Sqrt((distance * Physics.gravity.magnitude) /
(2 * cosAngle * cosAngle * (tanAngle - hOverd)));
return velocity * direction.normalized;
}
I think you use Raycast wrongly. According to the doc, the second argument is the direction, not the destination:
if (Physics.Raycast(player.transform.position,transform.position, out hitInfo))
Should be
if (Physics.Raycast(transform.position, player.transform.position -
transform.position, out hitInfo))
That would explain why it is not firing at the right moment and why the direction is not accurate (since hitInfo is wrong)

Unity Player falling very slowly

I created controls for a 3D Platformer game. Somehow the player is falling down very very slowly.
My player object got 2 components, the default capsule collider and the default Rigidbody. I didnt change anything there.
So my code is this one here:
float movementSpeed = 8;
float currentMovementSpeed;
float speedSmoothTime = 0.1f;
float turnSmoothTime = 0.2f;
float jumpPower = 5;
float airControlPercentage = 0.2f;
float turnSmoothVelocity;
float speedSmoothVelocity;
bool isGrounded;
private void FixedUpdate()
{
isGrounded = GroundCheck(); // Is player grounded?
Vector2 inputDirection = (new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"))).normalized;
if (Input.GetButtonDown("Jump") && isGrounded) // Jump handling
Debug.Log("Player Jump");
if (inputDirection != Vector2.zero)
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, Mathf.Atan2(inputDirection.x, inputDirection.y) * Mathf.Rad2Deg + cameraTransform.eulerAngles.y, ref turnSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime)); // Rotate
currentMovementSpeed = Mathf.SmoothDamp(currentMovementSpeed, movementSpeed * inputDirection.magnitude, ref speedSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));
playerRigid.velocity = transform.forward * currentMovementSpeed + Vector3.up * playerRigid.velocity.y * Time.deltaTime; // Move
currentMovementSpeed = (new Vector2(playerRigid.velocity.x, playerRigid.velocity.z)).magnitude;
}
private float GetModifiedSmoothTime(float smoothTime) // Limit the control while in air
{
if (isGrounded)
return smoothTime;
if (airControlPercentage == 0)
return float.MaxValue;
return smoothTime / airControlPercentage;
}
private bool GroundCheck() // Player is grounded?
{
if (true)
return true;
return false;
}
Does someone knows what to do here?
It probably has something to do with your current gravity. Check in edit -> project settings -> physics the value of your gravity. In my case is -9,81. Change it to a higher value and see what happens.
I finally got it. How to fix it:
In this line of code
playerRigid.velocity = transform.forward * currentMovementSpeed + Vector3.up * playerRigid.velocity.y * Time.deltaTime;
Take out
* Time.deltaTime
Now the player is falling correctly.
Actually , transform.forward effect the y component of the body thus effects the gravity acting on the body.
Use
rb.velocity = new Vector3(horizontal , -1 , vertical) * speed ; .
It will work, or simply use AddForce to drive the player.
when you want to use gravity calculations, changing the rigidbody in your code can do this.
//rb.velocity = moveInput * moveSpeed;
for example will screw with your movement, even when a button isn't being pressed.
using something like:
if(Input.GetButtonDown("Jump") && Mathf.Abs(rb.velocity.y) < 0.001f)
{
rb.AddForce(new Vector2(0, jumpForce), ForceMode2D.Impulse);
}
will simply add forces on top of the calculations, instead of changing them prior to
In my case, the slow descending of my game character was solved by eliminating unnecessary calls to the physics system in the FixedUpdate() function. For example, when a joystick's X-axis and/or Y-axis are in the zero position.
I call addForce, velocity, rb transforms, etc. only if the absolute joystick value (- and +) exceeds a minimum value, in my case 0.04. Without these tests, these physics calls are done every time FixedUpdate() is called. This seems to overload the physics system, because at the same time, physics is also processing gravity etc.
Note that merely setting the joystick's dead zones in the Unity Input System doesn't solve this problem.
void FixedUpdate()
{
if (Mathf.Abs(stick.x) > 0.04) // prevent unnecessary physics call.
{
rb.transform.eulerAngles = rb.transform.eulerAngles - new Vector3(0, stick.x * Time.deltaTime * RotationSpeed * -1, 0);
}
if (Mathf.Abs(stick.y) > 0.04) // prevent unnecessary physics call.
{
rb.velocity = transform.forward * stick.y * Speed * Time.deltaTime;
}
}

Difficulty with projectile's tracking code

I wrote some code for a projectile class in my game that makes it track targets if it can:
if (_target != null && !_target.IsDead)
{
Vector2 currentDirectionVector = this.Body.LinearVelocity;
currentDirectionVector.Normalize();
float currentDirection = (float)Math.Atan2(currentDirectionVector.Y, currentDirectionVector.X);
Vector2 targetDirectionVector = this._target.Position - this.Position;
targetDirectionVector.Normalize();
float targetDirection = (float)Math.Atan2(targetDirectionVector.Y, targetDirectionVector.X);
float targetDirectionDelta = targetDirection - currentDirection;
if (MathFunctions.IsInRange(targetDirectionDelta, -(Info.TrackingRate * deltaTime), Info.TrackingRate * deltaTime))
{
Body.LinearVelocity = targetDirectionVector * Info.FiringVelocity;
}
else if (targetDirectionDelta > 0)
{
float newDirection = currentDirection + Info.TrackingRate * deltaTime;
Body.LinearVelocity = new Vector2(
(float)Math.Cos(newDirection),
(float)Math.Sin(newDirection)) * Info.FiringVelocity;
}
else if (targetDirectionDelta < 0)
{
float newDirection = currentDirection - Info.TrackingRate * deltaTime;
Body.LinearVelocity = new Vector2(
(float)Math.Cos(newDirection),
(float)Math.Sin(newDirection)) * Info.FiringVelocity;
}
}
This works sometimes, but depending on the relative angle to the target projectiles turn away from the target instead. I'm stumped; can someone point out the flaw in my code?
Update: thinking about it and trying stuff has led me to the conclusion that it has something to do with when the direction (being in radians) is below 0 and the current projectile angle is above 0.
The variable targetDirectionDelta is not always the shortest direction to the target. If the absolute value of targetDirectionDelta is greater than PI radians, it will appear the projectile is turning away from the target. Turning in the other direction is shorter and expected.
Example:
currentDirection = 2
targetDirection = -2
The projectile can turn -4 radians (in the negative direction), or 2*(PI-2) radians (about 2.2 radians) (in the positive direction).
For this case, your code always calculates the longer direction, but you are expecting the projectile to turn towards the shorter direction:
targetDirectionDelta = targetDirection - currentDirection

Categories