I want to control and animation using legacy system, must be able to forward / backward while specific keys are pressed.
Currentyl tried to control current frame on Start Method:
ani.GetComponent<Animation>()["myAnim"].time = .3f;
ani.speed=0;
However it doesn't seem to do anything (nor it prints error / warnings).
If I do check "Play automatically" in animation component, it begins animation on given time (or so it seems), but doesn't pause.
An idea on how to properly achieve this?
You can use AnimationClip.SampleAnimation
E.g. something like
[SerializeField] private AnimationClip _clip;
[SerializeField] private float speed = 1f;
private float sampleTime;
private void Update()
{
if(Input.GetKey(KeyCode.RightArrow))
{
sampleTime = Mathf.Min(_clip.Length, sampleTime + Time.deltaTime * speed);
_clip.SampleAnimation(gameObject, sampleTime);
}
else if(Input.GetKey(KeyCode.LeftArrow))
{
sampleTime = Mathf.Max(0, sampleTime - Time.deltaTime * speed);
_clip.SampleAnimation(gameObject, sampleTime);
}
}
Related
Im trying to code so that my Character dashes to the right when pressing the Left mouse button, but instead of dashing it just starts slowly glieding or lets say floating.
This is the code i´ve used;
if (Input.GetMouseButton(0))
{
rb.velocity = Vector2.right * DashSpeed;
}
Im not sure but a other part of my code might be the reason for this problem but if so i would like to know how i could solve it. Thats the part im talking about
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
thats the code im using for movement.
void Start()
{
cam = Camera.main;
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
horizontal = Input.GetAxisRaw("Horizontal");
animator.SetFloat("Horizontal", Input.GetAxis("Horizontal"));
if (Input.GetKeyDown(KeyCode.Space) && isGrounded == true)
{
float jumpVelocity = 7f;
rb.velocity = Vector2.up * jumpVelocity;
jumpsound.Play();
}
Vector3 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
if (Input.GetKey(KeyCode.RightAlt))
{
Dashing();
}
}
void FixedUpdate()
{
isGrounded = Physics2D.OverlapCircle(groundCheck.position, CheckRadius, whatisGround);
moveInput = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
if (Input.GetKeyDown(KeyCode.Escape))
{
SceneManager.LoadScene("Main menu");
}
}
void Dashing()
{
rb.AddForce(Vector2.right * DashSpeed, ForceMode2D.Impulse);
}
The issue with your current code is you are directly changing velocity in a few places. Some basic physics, the integral of position vs. time graph is velocity, and the integral of velocity is acceleration vs. time. To get a more realistic movement, it is better to apply a Force to objects. When doing this, the physics engine Unity uses can add a new force at a given time, then using acceleration can accelerate the object in that direction over time, then can change the velocity over time which will result in the position changing over time.
The example code you posted, you are directly setting velocity in a few places.
rb.velocity = Vector2.up * jumpVelocity; (Jump)
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y); (Movement)
rb.velocity = Vector2.right * DashSpeed; (Dash)
When directly setting these values, the new values overwrite the old ones. As your movement code is not in any sort of if conditional it will continually write to the velocity causing the dash to never change anything regardless if you use add-force or change velocity directly.
I would consider making both your jump and dash use AddForce, and if you like the feel of your movement by applying velocity directly, then add the velocity do not set it.
Your previous line rb.velocity = new Vector2(moveInput * speed, rb.velocity.y); would then become rb.AddForce(new Vector2(moveInput * speed, 0), ForceMode2D.Impulse);. Similarly you can update your jump and dash to match this. Let me know if you get this working or have more questions.
It could be a problem with your animation. Link to a thread on unity answers:
https://answers.unity.com/questions/674516/rigidbody-character-problems-constant-floating-jum.html
You should go over to the animation place and hit bake into pose.
You should use the Rigidbody2D.AddForce(Vector2, ForceMode2D). What this does is moving the GameObject in the direction of the Vector2, with the force mode of ForceMode2D. What is different about this from just translating it, is that it interacts with physics and improves the quality of your game. Here is a link, and the script:
https://docs.unity3d.com/ScriptReference/Rigidbody2D.AddForce.html
Rigidbody2D rb;
float dashSpeed;
void Update()
{
if (Input.GetMouseButton(0))
{
rb.AddForce(Vector2.right * dashSpeed);
}
}
And if the other part of the code you were talking about, if that is glitching, then do the same trick.
The Unity 3D movement has this built in feature where it glides when you stop while holding the key. How do I remove this glide?
Fair warning, I haven't tried anything yet. I am very new to Unity and assume its most likely just some button I need to click so please help.
Here's the code that I'm using:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class movement : MonoBehaviour
{
float moveSpeed;
float walkSpeed = 3;
float sprintSpeed = 6;
public CharacterController controller;
public float gravity = -9.81f;
public Transform groundCheck;
public float groundDistance = 8.4f;
public LayerMask groundMask;
Vector3 velocity;
bool isGrounded;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.LeftShift))
{
moveSpeed = sprintSpeed;
}
else
{
moveSpeed = walkSpeed;
}
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * moveSpeed * Time.deltaTime);
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}
I'm more than happy to answer your question but I can't leave out this tidbit. I can't stress this enough. If you are new to Unity, you should avoid posting questions like this as much as possible. I'm not discouraging you from asking questions, but since you're new, really give it a go before you post anything. You'll learn soooo much more by removing variables and lines of code to figure out a solution on your own instead of getting the answer right away.
Regardless I believe the answer is in the last two lines of your code:
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
Let's think about this line by line. Dissect each line and try to really understand what they mean. As you run through your code, you are receiving three main inputs from the user:
if (Input.GetKey(KeyCode.LeftShift))
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
if (Input.GetKey(KeyCode.LeftShift)) is just a check to see if your user wants to run or not. The other two are receiving what is likely mouse movement input. These are primarily used to define the users movement. We know this because they are directly tied to these next two lines:
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * moveSpeed * Time.deltaTime);
Now when you look closer at this, it tells us that if the user isn't moving the mouse, then the Vector3 move will be 0. That means that our player will stop moving and there should be no reason they glide. However, take a look at the next two.
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
These are not necessarily driven by user input. This means that considering they are tied to your controller and the other move calls aren't running without user input, these two lines must be the culprits for your glide problem.
So remove these and you should be good. However, I must make it clear, asking questions is good, but breaking the code to learn more about it is soo much better. As a challenge I think you should look into what drives velocity.y... or maybe isGrounded... and that will tell you more about why the glide is happening!
Hope this helps!
Check the "Gravity" field on your "Horizontal" and "Vertical" axes in the input manager (Edit > Project Settings, then select the Input category). If not 0, it will smooth out keyboard inputs.
I am working on a Unity project. There is a car wiper and i want to rotate it in a way that when I pres "wipers toggle on" wipers start rotating from 0,0,0 to 0,0,-45 and start lerping. But when I press "wipers toggle off" the wipers must rotate back to 0,0,0. For example if current wipers rotation are 0,0,-20 and I press the "wipers toggle off" key they wipers must be rotateTowards 0,0,0. Also, If I press "wipers toggle on" again the wipers must start rotating from 0,0,0 to 0,0,-45. Now, the situation is that Wipers are rotating but when I press "wipers toggle off" the wipers stop at exactly the same current rotation point where they are for example (0,0,-30). And when I press "wipers toggle on" again the wipers starts from weird different rotation point. Here is my code:
using UnityEngine;
using System.Collections;
public class Wipers : MonoBehaviour
{
[SerializeField] protected Vector3 m_from = new Vector3(0.0F, 0.0F, 0.0F);
[SerializeField] protected Vector3 m_to = new Vector3(0.0F, -45.0F, 0.0F);
[SerializeField] protected float m_frequency = 1.0F;
protected virtual void Update()
{
if (ControlFreak2.CF2Input.GetAxis ("wipers") != 0)
{
Quaternion from = Quaternion.Euler(this.m_from);
Quaternion to = Quaternion.Euler(this.m_to);
float lerp = 0.5F * (1.0F + Mathf.Sin(Mathf.PI * Time.realtimeSinceStartup * this.m_frequency));
this.transform.localRotation = Quaternion.Lerp(from, to, lerp);
}
}
}
Your issue when turning on is that you are directly using Time.realtimeSinceStartup which also continues running while the routine is turned off of course! So you never know at which point the wipers will be when turned on.
→ You rather want to use the time since the wipers where started instead.
Your issue when turning off is of course that you immediately stop moving.
→ You rather want to always complete a full cycle after the wipers have been turned off.
Instead of doing this in Update I would suggest to use a Coroutine. Coroutines are way easier to control and maintain than doing stuff in Update directly. In fact they are like small temporary Update like routines and execute right after the real Update has finished.
A Coroutine you can let continue and complete a full cycle until it is actually done.
In Update you would only check the user input and start a routine (if none is running already) and the routine can run and terminate a complete cycle independently:
[SerializeField] protected Vector3 m_from = new Vector3(0.0F, 0.0F, 0.0F);
[SerializeField] protected Vector3 m_to = new Vector3(0.0F, -45.0F, 0.0F);
[SerializeField] protected float m_frequency = 1.0F;
// TODO only for debugging
[SerializeField] private bool ControlFreak2Dummy;
private bool wipersOn;
private Quaternion from;
private Quaternion to;
private void Awake()
{
// Store these once -> more efficient then recalculate them everytime
from = Quaternion.Euler(m_from);
to = Quaternion.Euler(m_to);
}
protected virtual void Update()
{
// TODO switch these back
//if (ControlFreak2.CF2Input.GetAxis ("wipers") != 0)
if (ControlFreak2Dummy)
{
if(!wipersOn)
{
StartCoroutine(Wipers());
}
}
}
// To make things easier for us this in itself closed routine is exactly ONE FULL wipers cycle
// so we can determine exactly when a full cycle is done or not
private IEnumerator Wipers()
{
// block concurrent routines
wipersOn = true;
var duration = 1f / m_frequency;
var timePassed = 0f;
while (timePassed <= duration)
{
// Note: Your sinus factor calculation was a bit strange
// (or maybe this early I'm way too stupid for maths lol ^^)
// It was always minimum 0.5 and maximum 1, you rather want to pingpong between 0 and 1
//
// This now moves forth and back exactly once between 0 and 1
var factor = Mathf.Sin(Mathf.PI * timePassed / duration);
transform.localRotation = Quaternion.Lerp(from, to, factor);
// increase timePassed by the time passed since last frame
timePassed += Time.deltaTime;
// allows Unity to "pause" here, render this frame and
// continue from here in the next frame
yield return null;
}
// make sur it really terminates in 0
transform.localRotation = from;
// tell others that this routine is done
wipersOn = false;
}
I use rigidbody.MovePosition to move around my character , and have a camera following it. The problem is when i switch directions suddenly the player will teleport a bit instead of smoothly moving in the opposite direction of motion. The scripts for the player and Camera are set to FixedUpdate , if I try moving camera to a LateUpdate then the whole thing jitters a lot.
Player Script :
private void Start()
{
m_Rb = GetComponent<Rigidbody>();
m_InputAxisName = "Vertical" + m_playerNumber;
m_StrafeAxisName = "Horizontal" + m_playerNumber;
}
private void FixedUpdate()
{
m_InputAxisValue = Input.GetAxis(m_InputAxisName);
m_StrafeAxisValue = Input.GetAxis(m_StrafeAxisName);
//Movement
Vector3 movement = (transform.forward * m_InputAxisValue * m_Speed * Time.deltaTime) + (transform.right * m_StrafeAxisValue * m_Speed * Time.deltaTime);
m_Rb.MovePosition(m_Rb.position + movement);
}
Camera Script
void FixedUpdate () {
m_NewPos = m_player.transform.position + m_offset;
if (m_Rotate)
{
Quaternion newRot = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * m_rotSpeed, Vector3.up);
m_offset = newRot * m_offset;
}
transform.position = Vector3.SmoothDamp(transform.position, m_NewPos, ref m_MoveSpeed, m_DampTime); ;
if (m_Rotate)
transform.LookAt(m_player);
}
the problem is that your motion logic is implemented within FixedUpdate but you take the time.deltaTime. You have to use the fixedDeltaTime version.
Also do not try to get the Input within the FixedUpdate that can also cause jitter and stutter/jumping of objects.
Instead get the Input from Update and use variables to pass it inside the FixedUpdate.
A last one, look at your RigidBody for the Interpolation/Extrapolation depending on your Scene Setup and Camera this could also help.
I finally figured out what was wrong with it, something about the rotation of the camera in the fixed update. I moved it to update and it works fine now
For control NammeshAgent and stop after reach to destination i used OnAnimatorMove(). but when it reached to destination, run animation won't stop.
I used BlendTree for control animations. so this is my code:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
// private vriables
private Animator anim;
private NavMeshAgent navAgent;
public Transform target;
private float speed;
void Start()
{
anim = GetComponent<Animator>();
navAgent = GetComponent<NavMeshAgent>();
}
void Update()
{
MoveToDestination();
}
private void MoveToDestination()
{
// Move to Target
if (Vector3.Distance(transform.position, target.position) > navAgent.stoppingDistance)
{
speed = 1f;
navAgent.SetDestination(target.position);
}
else
{
speed = 0;
}
anim.SetFloat("Speed", speed, 0.1f, Time.deltaTime);
}
private void OnAnimatorMove()
{
navAgent.speed = (anim.deltaPosition / Time.deltaTime).magnitude;
}
NOTE: when NavmeshAgent is reached to destination, speed value in NavmeshAgent Component is greater than 1. i seen this with print below line.
Vector3.Distance(transform.position, target.position)
NOTE: I set stoppingDistance to 0 in NavMesh Component.
So i need calculate stoppingdistance dinamically between player and target.
Some minor problems with your code:
you set speed = 0; but I don't see speed anywhere defined, except for navAgent.speed - make sure you use the correct/intended variable!
Vector3.Distance(transform.position, target.position) > navAgent.stoppingDistance is basically distance > 0 - replace stoppingDistance with 0.2f or some other small value. The distance will almost never be exactly zero, therefore (almost) always > 0.
Some thoughts:
You could set the speed of your animations to the agent speed, so the animation would "run" but not actually move if the player won't move. Also it would scale with acceleration automatically. Maybe your blendTrees would work better then, too.
Why did you put stopping distance to 0?
problem was in this part and i removed it.
private void OnAnimatorMove()
{
navAgent.speed = (anim.deltaPosition / Time.deltaTime).magnitude;
}
and also i set stoppingDistance to none zero.