I just got into developing using unity and I got the fps movement going, but I decided to test myself and tried to add a sprint toggle feature with my own code and it doesn't work, the issue is when I go into play mode and press left ctrl it doesn't change the value of speed, any suggestions would be appreciated.
if (speed == 5.0f && Input.GetButtonUp("LeftControl"))
{
speed += 2f;
}
if (speed == 7.0f && Input.GetButtonUp("LeftControl"))
{
speed -= 2f;
}
Never use == for floats due to floating point inaccuracies! See e.g. Is floating point math broken?
rather store a bool flag like
private bool isSprint;
and then do e.g.
if (Input.GetButtonUp("LeftControl"))
{
// toggle the flag
isSprint = !isSprint;
// ternary expression using the first value if "isSprint" is true
speed = isSprint ? 7f : 5f;
}
Further instead of hardcoded values I would also expose those in the Inspector so you can edit them on the fly:
[SerializeField] float normalSpeed;
[SerializeField] float sprintSpeed;
and
if (Input.GetButtonUp("LeftControl"))
{
isSprint = !isSprint;
speed= isSprint ? sprintSpeed : normalSpeed;
}
Related
I sticked my character to an empty object and applied Rigidbody, script to that empty object(To change the pivot); nevertheless, the character is not moving with this code:
public class main_character : MonoBehaviour
{
public float speed;
private float max_vel = 4f;
private bool grounded = true;
private bool flip = true;
private Rigidbody2D main_vel;
private void Awake()
{
main_vel = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
float now_vel = Mathf.Abs(main_vel.velocity.x);
if (Input.GetKey("d") && flip)
{
flip_side();
if (now_vel < max_vel)
{
main_vel.AddForce(new Vector2(speed, 0f) * Time.deltaTime);
}
}
if(Input.GetKey("a") && !flip)
{
!flip_side();
if (now_vel < max_vel)
{
main_vel.AddForce(new Vector2(-speed, 0f) * Time.deltaTime);
}
}
}
void flip_side()
{
flip = !flip;
transform.Rotate(0f, 180f, 0f);
}
}
you either need to add a speed value to the top of the class, or dynamically generate it. I would also put an exception or debug that catches when you press the button while speed is set to zero.
if speed accelerates you're going to need to build it or call it in update and give it an initial speed + speed based on delta time. I would also clamp it to max speed there instead, as it prevents large values being tracked by the game engine.
--Regarding an empty parent situation, If there is no physics components, the child should retain the motion of it's parent. That being said, you must have a rigidbody to add force to. You look like you might be using 2d so I have less experience with those rigidbodies, but I imagine it would have to not be kinematic to accept force as you are doing. After checking for that, I would maybe add some debug that posts your vector2 and speed from inside your max_vel check.
I have a BlendTree that controls my Animation for Idle/walking/running. This is controlled by variable speedPercent.
For some reason, when I increase TimeScale above 1 while standing still, agent.velocity starts adding a value to the y-axis, ie (0, 1.6, 0), causing magnitude to return a value.
Everything is managed by default NavMeshAgent methods (SetDestination for example).
void Update()
{
float speedPercent = agent.velocity.magnitude / agent.speed;
animator.SetFloat("speedPercent", speedPercent);
}
Unity's NavMeshAgent can have unexpected values for the velocity when it doesn't have a path. I am not sure why, but it seems to be a commonplace issue.
I would recommend a workaround: use a ternary operator to use 0f instead of agent.velocity.magnitude / agent.speed if the agent isn't currently pathing:
void Update()
{
float speedPercent = agent.hasPath ? agent.velocity.magnitude / agent.speed : 0f;
animator.SetFloat("speedPercent", speedPercent);
}
I wish to implement a way to create a hoverbike.
my current code to hover is
readonly float yForce = 80;
Physics.Raycast(hoverbike.transform.position, Vector3.down, out hit);
Debug.Log(hit.distance);
if (hit.distance < 10 && hit.distance > 0)
{
if (hoverbike.velocity.y < 0.1)
{
hoverbike.AddForce(0, yForce, 0, ForceMode.Acceleration);
Debug.Log("applying force!");
}
}
This works, but not well, the vehicle bounces up and down. I also tried to subtract the exact same force as the bike's y velocity, but the vehicle slowly drifted down, and did not go up to my desired height of 10 units from the ground. How can I achieve this?
Simply counteracting its current velocity is easy, but how do I make it float back up to the desired height?
It is much easier to simply turn off gravity than to constantly be fighting against it; this frequent readjustment is likely the cause of your bounciness. Upon bike activation you can take the object's y velocity calculations fully into your own hands as so:
public class Bike : MonoBehaviour
{
private Rigidbody hoverbike;
private bool isBikeActive = false;
[SerializeField] private float verticalSpeedMultiplier = 1f;
[SerializeField] private float hoverHeight = 10f;
[SerializeField] private float hoverTolerance = 0.5f;
[SerializeField] private float maximumVerticalVelocity = 10f;
private void Awake()
{
hoverbike = GetComponent<Rigidbody>();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space)) { ToggleBike(); }
if (isBikeActive)
{
Physics.Raycast(hoverbike.transform.position, Vector3.down, out RaycastHit hit);
Vector3 modifiedVelocity = hoverbike.velocity;
if ((hit.distance > hoverHeight - hoverTolerance) && (hit.distance < hoverHeight + hoverTolerance))
{
modifiedVelocity.y = 0f;
}
else
{
modifiedVelocity.y = -(hit.distance - hoverHeight) * verticalSpeedMultiplier;
modifiedVelocity.y = Mathf.Clamp(modifiedVelocity.y, -maximumVerticalVelocity, maximumVerticalVelocity);
}
Debug.Log($"Distance from ground: {hit.distance}, Bike Velocity.y: {modifiedVelocity}");
hoverbike.velocity = modifiedVelocity;
}
}
private void ToggleBike()
{
isBikeActive = !isBikeActive;
hoverbike.useGravity = !isBikeActive;
}
}
Your bike will now always try to move towards the point hoverHeight units above the object below it, until it is within hoverTolerance from that point. It will also move much more smoothly towards this point, moving faster the further away it is from the intended height.
If you wish for the bike to still bob up and down a little, this can be achieved by modifying the hoverHeight slowly over time, perhaps through use of a Sine function.
Hovering is (essentially) a visual effect
Make the collider extend below the vehicle so that when it rests on the ground the bike appears to be hovering at the desired height. The physics engine only does physics. It doesn't care about what those colliders are, it just wants them to behave in a physics-y way, and if that means falling until it reaches the ground, then let them fall until they reach the ground. Take advantage of the physics engine instead of going around it and then trying to solve the bugs created by going around the physics engine.
First, apply your dampening force scaled by the downward velocity, then apply an additional force scaled by how far it needs to travel back upwards. Keep track of how much force/acceleration you apply through this process and cap that amount at some constant.
readonly float yForce = 80f; // requires tuning
readonly float dampenFactor = 0.8f; // requires tuning
readonly float offsetFactor = 0.5f; // requires tuning
readonly float targetHeight = 10f
Physics.Raycast(hoverbike.transform.position, Vector3.down, out hit);
Debug.Log(hit.distance);
if (hit.distance < targetHeight && hit.distance > 0)
{
float availableForce = yForce;
// cancel out downward velocity
if (hoverbike.velocity.y < 0)
{
// Cap out upward force based on yForce
float cappedDampenForce = Mathf.Min(dampenFactor * -hoverbike.velocity.y,
availableForce);
// How much force is available for the offset?
availableForce -= cappedDampenForce;
hoverbike.AddForce(Vector3.up * cappedDampenForce, ForceMode.Acceleration);
Debug.Log("applied dampening force");
}
// Find upward force scaled by distance left to target height, and cap that amount
float cappedOffsetForce = Mathf.Min(offsetFactor * (targetHeight - hit.distance),
availableForce);
hoverbike.AddForce(Vector3.up * cappedOffsetForce, ForceMode.Acceleration);
Debug.Log("applied offset force");
}
I'm trying to add sound to my pushable object and just have a simple if statement for checking if the pushable object is moving. The concept is quite simple, if the object is moving the sound should play and when it's not moving it shouldn't. The problem however is, that when I debug the value there is a 0 every 5 frames or so. This causes the sound to work inconsistently. The script I have is really simple, and I have tried changing to fixedupdate, but it didn't work. I had read somewhere that physics calculations are done in fixedUpdate.
public class PushableObject : MonoBehaviour
{
Rigidbody rb;
AudioSource audioS;
bool rbIsMoving = false;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
audioS = GetComponent<AudioSource>();
}
// Update is called once per frame
void FixedUpdate()
{
if (rb.velocity == Vector3.zero)
{
Debug.Log("Not moving");
audioS.volume = 0f;
}
else
{
Debug.Log("Moving");
audioS.volume = 0.1f;
}
}
}
Edit: I have just figured out that if the player pushes the pushable object into the wall the sound is still playing, for this reason I think I have to change the way too determine if the object is moving or not.
This happens due to single precision floating point.
You never (should) compare two float values directly since they might logically be equal (e.g. 1 = 5*0.2) but for the computer they might be different by a small "epsilon"
So Unity decides that Vector3 simply uses only a precision of 0.00001 for equality for ==
Rather use Mathf.Approximately which uses a very small Epsilon instead.
Than easier than comparing each component of the rb.velocity against 0 what you actually want is the rb.velocity.magnitude which is actually the overall speed.
if (Mathf.Approximately(rb.velocity.magnitude, 0))
Update
Alternatively store the last position and compare it to the current one using Vector3.Distance either again with Mathf.Approximately
private Vector3 lastPosition;
private void LateUpdate()
{
if(Mathf.Approximately(Vector3.Distance(lastPosition, transform.position), 0))
{
//...
}
else
{
//...
lastPosition = transform.positiom;
}
}
Or with a custom threshold
if(Vector3.Distance(lastPosition, transform.position) <= someThreshold)
or this time you actually can use == if a threshold of 0.00001 is what you want
if(lastPosition == transform.position)
You can check if the RigidBody is sleeping:
if (!rb.IsSleeping()
{
//it's moving
}
else
{
//it's not
}
Or check if the transform position has moved since last frame:
Vector3 lastposition;
Gameobject go = somegameobject;
function Update()
{
if (lastposition == go.transform.position)
{
//not moving
}
else
{
//moving
}
lastposition = go.transform.position;
}
You can use Transform.hasChanged to check if the player position has changed on the last update
if (!this.transform.hasChanged)
{
print("Player is not moving");
}
transform.hasChanged = false;
I have been following a tutorial on 2D Player Controller in Unity (It's 'Live Training 16 Dec 2013 - 2D Character Controllers' video).
I was able to implement everything the tutorial showed successfully with some edits to make it work in Unity 5. Afterwards, I decided to play around with it so that I can gain a better understanding. One thing I tried to do was changing the jump height when pressing the Space key. Here is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RobotControllerScript : MonoBehaviour {
public float maxSpeed = 10f;
bool facingRight = true;
Animator anim;
bool grounded = false;
public Transform groundCheck;
float groundRadius = 0.2f;
public LayerMask whatIsGround;
public float jumpForce = 700f;
// Use this for initialization
void Start () {
anim = GetComponent<Animator>();
}
// Update is called once per frame
void FixedUpdate () {
grounded = Physics2D.OverlapCircle(groundCheck.position, groundRadius, whatIsGround);
anim.SetBool("Ground", grounded);
//vSpeed = vertical speed
anim.SetFloat("vSpeed", GetComponent<Rigidbody2D>().velocity.y);
float move = Input.GetAxis("Horizontal");
anim.SetFloat("Speed", Mathf.Abs(move));
GetComponent<Rigidbody2D>().velocity = new Vector2(move * maxSpeed,
GetComponent<Rigidbody2D>().velocity.y);
if (move > 0 && !facingRight)
{
Flip();
}
else if (move < 0 && facingRight)
{
Flip();
}
}
void Update()
{
if(grounded && Input.GetKeyDown(KeyCode.Space))
{
anim.SetBool("Ground", false);
GetComponent<Rigidbody2D>().AddForce(new Vector2(0, jumpForce));
}
}
void Flip()
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
Looking at the code and the tutorial explanations, jumpForce is the variable that controls how high the character will jump (force applied). So, I changed 700f to a 5f. I expected the character to make a really small jump, but this was not the case. It jumped at the same height as 700f.
public float jumpForce = 700f;
After playing around with the code, I was able to have the expected result by removing the 'public' next to jumpForce. Other ways to fix this was setting it to private or static. I remember I had a similar problem when making a temperature widget on QT Creator. I had to set a variable to static or else it would not return to the default value after C to F conversion, but I don't remember exactly why.
Can anyone explain why 'public' does not work and why a private/static/nothing might? What is the best/efficient solution for this problem? Thank you very much.
When a field is public (or serializable), it is shown in inspector. When it is shown in inspector, you tweak it's value, a you expect the tweaks you make to the value, in the inspector, to be preserved between play mode runs.
When you declare a variable in the code, you may specify an initial value, like this:
public float jumpForce = 700f;
So, the first time this object gets inspected, after created, it has a reasonable value. Then, you tweak and choose a better value (or back to the same, whatever). That's the value you preferred! You want the object to keep functioning like this while you go ahead and finish making your game.
So, once a field is tweaked in Inspector, it doesn't respect the initial value you hardcoded anymore, and that's usually what everyone want. Imagine how annoying would be if everytime you make a change in your class, all the tweaked fields got back to the initial hardcoded value...
If a field was previously public, and then you make it private (or hidden in inspector by any mean), then the hardcoded initial value will stand.
But, what if I really want my object to get back to it's initial/default configuration? That's why the Reset method and the reset button in inspector were made!