I'm following a tutorial on Unity 5 to create a simple stealth game.
I followed the instructions to create the script that controls the movement of the player. When I tested the game I noticed that the player takes a few seconds after pressing the button before moving.
It's as if before moving should await the conclusion of the rotation that is performed by Quaternion.Lerp.
Also pressing the x button should scream to attract attention and take proper animation.. It runs the sound but the animation is not done.. Was performed only once in multiple tests I did.
public class PlayerMovement : MonoBehaviour
{
public AudioClip shoutingClip; // Audio clip of the player shouting.
public float turnSmoothing = 15f; // A smoothing value for turning the player.
public float speedDampTime = 0.1f; // The damping for the speed parameter
private Animator anim; // Reference to the animator component.
private HashIDs hash; // Reference to the HashIDs.
void Awake ()
{
// Setting up the references.
anim = GetComponent<Animator>();
hash = GameObject.FindGameObjectWithTag(Tags.gameController).GetComponent<HashIDs>();
// Set the weight of the shouting layer to 1.
anim.SetLayerWeight(1, 1f);
}
void FixedUpdate ()
{
// Cache the inputs.
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
bool sneak = Input.GetButton("Sneak");
MovementManagement(h, v, sneak);
}
void Update ()
{
// Cache the attention attracting input.
bool shout = Input.GetButtonDown("Attract");
// Set the animator shouting parameter.
anim.SetBool(hash.shoutingBool, shout);
AudioManagement(shout);
}
void MovementManagement (float horizontal, float vertical, bool sneaking)
{
// Set the sneaking parameter to the sneak input.
anim.SetBool(hash.sneakingBool, sneaking);
// If there is some axis input...
if(horizontal != 0f || vertical != 0f)
{
// ... set the players rotation and set the speed parameter to 5.5f.
Rotating(horizontal, vertical);
anim.SetFloat(hash.speedFloat, 5.5f, speedDampTime, Time.deltaTime);
}
else
// Otherwise set the speed parameter to 0.
anim.SetFloat(hash.speedFloat, 0);
}
void Rotating (float horizontal, float vertical)
{
// Create a new vector of the horizontal and vertical inputs.
Vector3 targetDirection = new Vector3(horizontal, 0f, vertical);
// Create a rotation based on this new vector assuming that up is the global y axis.
Quaternion targetRotation = Quaternion.LookRotation(targetDirection, Vector3.up);
// Create a rotation that is an increment closer to the target rotation from the player's rotation.
Quaternion newRotation = Quaternion.Lerp(GetComponent<Rigidbody>().rotation, targetRotation, turnSmoothing * Time.deltaTime);
// Change the players rotation to this new rotation.
GetComponent<Rigidbody>().MoveRotation(newRotation);
}
void AudioManagement (bool shout)
{
// If the player is currently in the run state...
if(anim.GetCurrentAnimatorStateInfo(0).nameHash == hash.locomotionState)
{
// ... and if the footsteps are not playing...
if(!GetComponent<AudioSource>().isPlaying)
// ... play them.
GetComponent<AudioSource>().Play();
}
else
// Otherwise stop the footsteps.
GetComponent<AudioSource>().Stop();
// If the shout input has been pressed...
if(shout)
// ... play the shouting clip where we are.
AudioSource.PlayClipAtPoint(shoutingClip, transform.position);
}
}
I'm new in unity so I might need some more explanation. Thanks to everyone!
The first issue, delaying your player movement, may be caused by the line
anim.SetFloat(hash.speedFloat, 5.5f, speedDampTime, Time.deltaTime);
The potential issue is that you are calling Time.deltaTime, which is intended to be use inside an update() call. Your function is called inside of fixedUpdate() which means you should be using Time.fixedDeltaTime.
For your second issue, shouting, the code seems fine and the issue is likely in your animation tree. Check that the state you are in before shouting can transition to shout, and checks for the correct trigger.
Related
I am creating a game that is set in Zero-gravity. I am using a script that can be found here https://github.com/brihernandez/ArcadeSpaceFlightExample
The movement is controlled by a throttle that can be increased/decreased using the mouse wheel or 'w' and 's' keys.
This code controls the mouse wheel and is the same for the 'w' and 's' keys.
private void UpdateMouseWheelThrottle()
{
throttle += Input.GetAxis("Mouse ScrollWheel");
throttle = Mathf.Clamp(throttle, 0.0f, 1.0f);
}
The object that is accelerated by the throttle stops moving when I decrease the throttle and does not drift or continue moving as it should in zero gravity.
This is the code that controls the physics.
public class PlayerPhysics: MonoBehaviour
{
[Tooltip("X: Lateral thrust\nY: Vertical thrust\nZ: Longitudinal Thrust")]
public Vector3 linearForce = new Vector3(100.0f, 100.0f, 100.0f);
[Tooltip("X: Pitch\nY: Yaw\nZ: Roll")]
public Vector3 angularForce = new Vector3(100.0f, 100.0f, 100.0f);
[Range(0.0f, 1.0f)]
[Tooltip("Multiplier for longitudinal thrust when reverse thrust is requested.")]
public float reverseMultiplier = 1.0f;
[Tooltip("Multiplier for all forces. Can be used to keep force numbers smaller and more readable.")]
public float forceMultiplier = 100.0f;
public Rigidbody Rigidbody { get { return rbody; } }
private Vector3 appliedLinearForce = Vector3.zero;
private Vector3 appliedAngularForce = Vector3.zero;
private Rigidbody rbody;
private Player player;
void Awake()
{
rbody = GetComponent<Rigidbody>();
if (rbody == null)
{
Debug.LogWarning(name + ": ShipPhysics has no rigidbody.");
}
player = GetComponent<Player>();
}
void FixedUpdate()
{
if (rbody != null)
{
rbody.AddRelativeForce(appliedLinearForce * forceMultiplier, ForceMode.Force);
rbody.AddRelativeTorque(appliedAngularForce * forceMultiplier, ForceMode.Force);
}
}
public void SetPhysicsInput(Vector3 linearInput, Vector3 angularInput)
{
appliedLinearForce = MultiplyByComponent(linearInput, linearForce);
appliedAngularForce = MultiplyByComponent(angularInput, angularForce);
}
private Vector3 MultiplyByComponent(Vector3 a, Vector3 b)
{
Vector3 ret;
ret.x = a.x * b.x;
ret.y = a.y * b.y;
ret.z = a.z * b.z;
return ret;
}
}
And here is the code that instantiates both.
public class Player: MonoBehaviour
{
public static Player Playerdrone { get { return player; } }
private static Player player;
private PlayerInput input;
private PlayerPhysics physics;
public Vector3 Velocity { get { return physics.Rigidbody.velocity; } }
public float Throttle { get { return input.throttle; } }
// Start is called before the first frame update
void Awake()
{
input = GetComponent<PlayerInput>();
physics = GetComponent<PlayerPhysics>();
}
// Update is called once per frame
void Update()
{
physics.SetPhysicsInput(new Vector3(input.strafe, 0.0f, input.throttle), new Vector3(input.pitch, input.yaw, input.roll));
}
}
I cannot get the Rigidboy to keep drifting when I decrease the throttle, my option is to either abandon the throttle concept and use a simple "Addforce" but I would prefer to keep it.
I have edited the script to have a similar function to the 'Roll' that used 'w' and 's' but moves me forwards and backward. I still find myself decelerating when I let go of the input.
Lowering drag and angular causes the object to spin out of control due to the mouse following function. The mouse does not center the camera and instead causes the camera to keep rotating and tracking the mouse. Changing the mouse script will cause it's own issues since it controls the pitch and yaw of the object.
private void SetStickCommandsUsingMouse()
{
Vector3 mousePos = Input.mousePosition;
// Figure out most position relative to center of screen.
// (0, 0) is center, (-1, -1) is bottom left, (1, 1) is top right.
pitch = (mousePos.y - (Screen.height * 0.5f)) / (Screen.height * 0.5f);
yaw = (mousePos.x - (Screen.width * 0.5f)) / (Screen.width * 0.5f);
// Make sure the values don't exceed limits.
pitch = -Mathf.Clamp(pitch, -1.0f, 1.0f);
yaw = Mathf.Clamp(yaw, -1.0f, 1.0f);
}
The object that is accelerated by the throttle stops moving when I decrease the throttle and does not drift or continue moving as it should in zero gravity.
The Rigidbody component has properties that can be changed in the Inspector (documented here) (and also via script (documented here)):
Mass: The mass of the object (in kilograms by default).
Drag: How much air resistance affects the object when moving from forces. 0 means no air resistance, and infinity makes the object
stop moving immediately.
It sounds like your Drag value might be too large (?).
Although this example project uses "space mechanics" (effectively with no air resistance), the Drag property can still be used to tune the movement and "drift behavior" of the rigidbody, and to control how long it will keep decelerating after the throttle value went down to 0.
Alternatively, you could try to experiment with other ForceMode parameters for the Add*Force() calls: ForceMode.Acceleration or ForceMode.VelocityChange (which is ignoring the mass of the rigidbody).
To make your game more "realistic", you could instead set the Drag property to 0 (no air resistance like in real space -> infinite drift), and try to actively decelerate the ship by experimenting with retrograde rockets/thrusters, by adding a force in backward direction. However, that's probably quite impractical for an arcade game, I guess.
There are also the global Physics settings of the project, which can be found under Edit -> Project settings -> Physics (documented here). There, you'll also find many important physics settings (like Gravity, for example).
(It can also be interesting to have a look at the official docs of the PhysX physics engine, which Unity uses internally for its 3D physics, to understand how things actually work under the hood...)
So I am making a game in unity where an object will dash in the direction (unit vector) of the player's mouse click. Based on dash speed and dash time variables I made and set in the inspector it will usually work, as in the distance dashed by the object and its velocity are usually constant. However, sometimes the dash distance of the object will be off. I attempted to troubleshoot the problem by logging the dash distance and the object's velocity and what I found was that sometimes either or both variables with be some random value for some reason I am missing. On most clicks though the variables are the same number. Here is my code:
[RequireComponent(typeof(Rigidbody2D))]
public class DashController : MonoBehaviour
{
Rigidbody2D rigidbody;
public float dashSpeed;
public float dashTime; //dash time for inspector use
float codeDashTime; //dash time for code use
bool dashing;
Vector2 direction;
Vector2 startPosition;
Vector2 endPosition;
float distance;
private void Start()
{
rigidbody = GetComponent<Rigidbody2D>();
codeDashTime = dashTime;
dashing = false;
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
//need to subtract the current postion from the target postion
direction = (Camera.main.ScreenToWorldPoint(Input.mousePosition) - this.transform.localPosition);
startPosition = this.transform.localPosition; // used to see the distance player dashes
dashing = true;
}
if (dashing)
{
Dash();
}
}
void Dash()
{
if (codeDashTime > 0)
{
rigidbody.velocity = direction.normalized * dashSpeed;
codeDashTime -= Time.deltaTime;
}
else
{
Debug.Log(rigidbody.velocity.magnitude);// used to see the velocity the player travels at
Debug.Log(direction.normalized.magnitude);
rigidbody.velocity = Vector2.zero;
endPosition = this.transform.localPosition; // used to see the distance player dashes
distance = Vector2.Distance(startPosition, endPosition);// used to see the distance player dashes
Debug.Log(distance); // used to see the distance player dashes
dashing = false;
codeDashTime = dashTime;
}
}
}
Sorry if my code is a bit jumbled. I have been moving things around a lot testing different solutions but nothing has worked. My best guess is that maybe something with the frames is messing up codeDashTime, but I am too new to unity and C# to know. Any help is appreciated, thanks.
All Physics should run on FixedUpdate and the inputs are normally used on Update, try separate them to get a better Rigidbody behaviour.
I'm new to Unity and rigidbodies, and I thought I'd learn by trying to make a 3D Tron Light-cycle game. I've made my player vehicle using a combination of cylinders, spheres, and rectangles, seen below:
I used a rigid body on the elongated sphere, and used the following code:
public float accel = 1.0f;
// Use this for initialization
void Start () {
cycleSphere = GetComponent<Rigidbody>();
}
void FixedUpdate () {
cycleSphere.velocity = Vector3.forward * accel;
}
That moves the vehicle forwards. I'm not sure if there's a better way to do this, but if there is, please do say so.
I've attached the Main camera to the vehicle, and disabled X rotation to prevent it and the camera from rolling.
Now I would like to get it to turn by pressing the A and D buttons. Unlike the 90 degree turning of the original Tron light-cycles, I wanted it to turn like a regular vehicle.
So I tried this:
void Update () {
if (Input.GetKey (KeyCode.A)) {
turning = true;
turnAnglePerFixedUpdate -= turnRateAngle;
} else if (Input.GetKey (KeyCode.D)) {
turning = true;
turnAnglePerFixedUpdate += turnRateAngle;
} else {
turning = false;
}
}
void FixedUpdate () {
float mag = cycleSphere.velocity.magnitude;
if (!turning) {
Quaternion quat = Quaternion.AngleAxis (turnAnglePerFixedUpdate, transform.up);// * transform.rotation;
cycleSphere.MoveRotation (quat);
}
cycleSphere.velocity = Vector3.forward * accel;
}
While the above code does rotate the vehicle, it still moves in the last direction it was in - it behaves more like a tank turret. Worse, pressing either A or D too much would cause it to rotate in the desired direction and, after a short while, go nuts, rotating this way and that, taking the camera with it.
What did I do wrong and how can I fix it?
First of all I would recommend you to change from Input.GetKey to Input.GetAxis which will gracefully increase or decrease it's value when the key is pressed. This will give you the option to normalize the force vector applied as your velocity. Then based on that vector you have to adapt your force input so that the "front wheel" will "drag" the rest of the body to some other direction ( left or right ). This is not the ideal "real world physics behavior" because the forward force is slightly bigger than the side ( left or right ) force.
code example :
// member fields
float sideForceMultiplier = 1.0f;
float frontForceMultiplier = 2.0f;
Vector3 currentVeloticy = Vector3.zero;
void Update()
{
Vector3 sideForce = (sideForceMultiplier * Input.GetAxis("horizontal")) * Vector3.right;
Vector3 frontForce = frontForceMultiplier * Vector3.forward;
currentVelocity = (sideForce + fronForce).Normalize;
}
void FxedUpdate()
{
cycleSphere.velocity = currentVelocity * accel;
}
I'm following the Player Character tutorial on the Unity site. There say that the player can move, idle or can be dead. Here is the situation in the 'Animator' panel:
The parameters are:
IsWalking type of bool
Die type of trigger
The normal situation is when the IsWalking parameters is true, he must begin whit walking. When false, he must stop. Here are some screenshots.
And here is the C# script I've made:
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float speed = 6f; // The speed that the player will move at.
Vector3 movement; // The vector to store the direction of the player's movement.
Animator anim; // Reference to the animator component.
Rigidbody playerRigidbody; // Reference to the player's rigidbody.
int floorMask; // A layer mask so that a ray can be cast just at gameobjects on the floor layer.
float camRayLength = 100f; // The length of the ray from the camera into the scene.
void Awake()
{
// Create a layer mask for the floor layer.
floorMask = LayerMask.GetMask("Floor");
// Set up references.
anim = GetComponent<Animator>();
playerRigidbody = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
// Store the input axes.
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
// Move the player around the scene.
Move(h, v);
// Turn the player to face the mouse cursor.
Turning();
// Animate the player.
Animating(h, v);
}
void Move(float h, float v)
{
// Set the movement vector based on the axis input.
movement.Set(h, 0f, v);
// Normalise the movement vector and make it proportional to the speed per second.
movement = movement.normalized * speed * Time.deltaTime;
// Move the player to it's current position plus the movement.
playerRigidbody.MovePosition(transform.position + movement);
}
void Turning()
{
// Create a ray from the mouse cursor on screen in the direction of the camera.
Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
// Create a RaycastHit variable to store information about what was hit by the ray.
RaycastHit floorHit;
// Perform the raycast and if it hits something on the floor layer...
if (Physics.Raycast(camRay, out floorHit, camRayLength, floorMask))
{
// Create a vector from the player to the point on the floor the raycast from the mouse hit.
Vector3 playerToMouse = floorHit.point - transform.position;
// Ensure the vector is entirely along the floor plane.
playerToMouse.y = 0f;
// Create a quaternion (rotation) based on looking down the vector from the player to the mouse.
Quaternion newRotation = Quaternion.LookRotation(playerToMouse);
// Set the player's rotation to this new rotation.
playerRigidbody.MoveRotation(newRotation);
}
}
void Animating(float h, float v)
{
// Create a boolean that is true if either of the input axes is non-zero.
bool walking = h != 0f || v != 0f;
// Tell the animator whether or not the player is walking.
anim.SetBool("IsWalking", walking);
}
}
The problem is now, when I'll walk whit the player he stays in the idle state. It's sometimes that he goes to the move state, but not always. What is the problem here I've got? I've exactly the same code and animations I've made in the animator and inspector tabs.
You should try setting ExitTime to false.
When on, it means the animation will still be played by the ExitTime value. So for instance if ExitTime is 1, the whole idle animation has to be finished before it transits. If you stop moving within that time, then the boolean is back to idle and you won't see the animation for walking. Setting it to false means you want transition to happen instantly.
Currently I'm making my first video game using unity written in C#, but now I'm facing a trouble and I don't know what to do anymore. First I already made my character move but, I want to put all the attributes in one script, like it's moveSpeed, attackSpeed, etc. but once I access it to another script, my character just stands and do nothing. Here's my code
public class ClickToMove : MonoBehaviour {
public CharacterController controller; // Use to move the player
private Vector3 position; // Store the position at which the player clicked;
public Attributes attribute;
// Animation variables
public AnimationClip idleClip;
public AnimationClip runClip;
// Use this for initialization
void Start () {
position = transform.position; // Set the position to player's current position
}
// Update is called once per frame
void Update () {
// Execute the code below once the player press left click
if(Input.GetMouseButton(0)) {
locatePosition();
}
moveToPosition();
}
// Locate at which the player clicked on the terrain
private void locatePosition() {
RaycastHit hit; // Get information from ray
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); // A line in 3D space
// Cast a ray that start from the camera with a distance of 1000
if(Physics.Raycast(ray, out hit, 1000)) {
// Store the position if the casted ray is not pointing to the player or enemy's position
if(hit.collider.tag != "Player" && hit.collider.tag != "Enemy") {
position = new Vector3(hit.point.x, hit.point.y, hit.point.z);
}
}
}
// Move to the located position
private void moveToPosition() {
// Check if the player position and the destination is greater than one
if(Vector3.Distance(transform.position, position) > 1) {
// Subtract the clicked position to the player position
Quaternion newRotation = Quaternion.LookRotation (position - transform.position);
// Disable the rotation from x and z angle
newRotation.x = 0f;
newRotation.z = 0f;
// Rotate the player to a new rotation then move forward
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, Time.deltaTime * attribute.moveSpeed);
controller.SimpleMove(transform.forward * attribute.moveSpeed);
animation.CrossFade(runClip.name);
} else {
animation.CrossFade(idleClip.name);
}
}
}
and here is the script I'm accessing
public class Attributes : MonoBehaviour {
public float moveSpeed;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
I don't know what to do anymore. Any help will be appreciated. Thank you.
There are a couple of things that you need to double check, the first being that you are actually assigning a value to attribute.moveSpeed in the inspector. Otherwise moveSpeed will always be 0 and therefore all of your multiplication operations will result in 0, resulting in no movement at all.
Second, make sure that something is actually being hit by your raycast. You can do this a couple ways, first by printing a message to the console saying that you hit something
// Cast a ray that start from the camera with a distance of 1000
if(Physics.Raycast(ray, out hit, 1000)) {
// Send a debug message to Unity's console
Debug.Log("I hit Something!");
// Store the position if the casted ray is not pointing to the player or enemy's position
if(hit.collider.tag != "Player" && hit.collider.tag != "Enemy") {
position = new Vector3(hit.point.x, hit.point.y, hit.point.z);
}
}
and second by checking that the position variable changed values in the inspector. (Make the position variable public and look at your character's inspector)
Note that you've assigned a value to your attribute variable in the inspector because you're not getting NullReferenceExceptions