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;
}
Related
The player goes on top of some objects when he walks toward them, how can I prevent that from happening? Here is an example image of that:
I did not jump to be on the couch but yet it still goes on top of it when I walk to it. Here is my player information:
I don't want to change the player's movement, but I don't want it to go on top of objects when I'm walking.
Make the Step Offset in character controller 0. More info on it here
The character controller Step Offset solves this problem by increasing it value, but you may find that after adding the value, the character controller does not generate any gravitational force on its own.
To solve the problem of gravity, it is enough to first get the component.
private CharacterController controller;
public void Start()
{
controller = GetComponent<CharacterController>();
}
And then apply gravity to the object with the following instructions, you have already obtained the moveInput axis with the Input.GetAxis method.
private Vector3 velocity;
private void Update()
{
var moveInput = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
// === AFTER CALCULATING MOVE INPUT
controller.Move(moveInput*Time.deltaTime);
velocity += Physics.gravity * Time.deltaTime;
controller.Move(velocity); // Apply Gravity
if (controller.isGrounded) velocity = Vector3.zero;
}
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.
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);
}
}
Hi so I am making a recoil script for my First Person Shooter. I have the gun working pretty well. Here is my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunRecoil : MonoBehaviour
{
public Vector3 beforeRecoilRotation, rightBeforeRecoilRotation, leftBeforeRecoilRotation;
//I have a separate script controlling maxRecoilX and recoil
public float maxRecoilX = -0, recoilSpeed = 10, recoil = 0;
[SerializeField]
GameObject leftControl, rightControl, weapon;
public bool once = false;
// Start is called before the first frame update
void Recoiling()
{
//if recoil is present
if (recoil > 0)
{
//make amount of recoil
var maxRecoil = Quaternion.Euler(maxRecoilX, 0, 0);
//move gun's rotation, according to recoil
weapon.transform.localRotation = Quaternion.Slerp(weapon.transform.localRotation, maxRecoil, Time.deltaTime * recoilSpeed);
//subtract recoil by time.
recoil -= Time.deltaTime;
}
else
{
//make sure recoil is now zero
recoil = 0;
//make min recoil, based on starting position
Quaternion minRecoil = Quaternion.Euler(beforeRecoilRotation);
Quaternion minRecoilHandRight = Quaternion.Euler(0, 0, 0);
weapon.transform.localRotation = Quaternion.Slerp(weapon.transform.localRotation, minRecoil, Time.deltaTime * recoilSpeed / 2);
// rightControl.transform.localRotation = Quaternion.Slerp(rightControl.transform.localRotation, minRecoilHandRight, Time.deltaTime * recoilSpeed / 2);
maxRecoilX = 0;
}
}
// Update is called once per frame
void Update()
{
//constantly run Recoiling
Recoiling();
}
}
I also have hands for my gun. I want them to follow the rotation of the gun, to the rotation of my gun.
First I tried just applied the recoil to my hand. The results were close-ish, but not close enough. I used something like
var maxRecoilRight = Quaternion.Euler(maxRecoilX *5f, 0, 0);
rightControl.transform.localRotation = Quaternion.Slerp(weapon.transform.localRotation, maxRecoilRight, Time.deltaTime * recoilSpeed);
Here are the results:
recoil One
I also tried using Fast IK: https://assetstore.unity.com/packages/tools/animation/fast-ik-139972#reviews .
It didn't work, since I am using the parts of the arm in an animation
I also cannot parent the hands to the gun, since I am going to have to replace animations, and it might break my generic rig.
What kind of math could I use, to reposition my hands, to my gun in Unity? Please leave any suggestions for me. Thank you and have a good day/evening.
You could turn off the animation of the hands while the recoil is ongoing, and use an IK solution for that time. Then when the recoil is finished you can turn it back on.
My character is a car and I try to rotate it the direction it move, so far so good I succeeded to do that but once I stop moving the character flips back to the direction it was on the start.
Also how can I make my turns from side to the opposite site smooth ?
Here is my code so far:
[SerializeField] float driveSpeed = 5f;
//state
Rigidbody2D myRigidbody;
// Start is called before the first frame update
void Start()
{
myRigidbody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
Move();
}
private void Move()
{
//Control of velocity of the car
float HorizontalcontrolThrow = CrossPlatformInputManager.GetAxis("Horizontal"); // Value between -1 to 1
float VerticalcontrolThrow = CrossPlatformInputManager.GetAxis("Vertical"); // Value between -1 to 1
Vector2 playerVelocity = new Vector2(HorizontalcontrolThrow * driveSpeed, VerticalcontrolThrow * driveSpeed);
myRigidbody.velocity =playerVelocity;
**//Direction of the car**
Vector2 direction = new Vector2(HorizontalcontrolThrow, VerticalcontrolThrow);
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
myRigidbody.rotation = angle;
}
I'm not sure about this, but maybe that last line "myRigidbody.rotation = angle" being called every frame is what is making your car reset its rotation.
Maybe change it to "myRigidbody.rotation *= angle" or "myRigidbody.rotation += angle".
It looks like it may be because HorizontalcontrolThrow and VerticalcontrolThrow are going to be reset when you release the controls. If it's resetting to its original orientation, then what's happening is that until you move, those two values are going to be at their default value. You then move and it affects the rotation. But when you release the controls, those values are back to the starting values again, and so is your rotation.
What you therefore need to do is try to separate the HorizontalcontrolThrow and VerticalcontrolThrow from the rest of the code, which should only be activated when at least one of these two variables are not at their default setting (I can't remember what the axis functions return at the moment).
Edit:
An IF statement should suffice (some rough pseudo code):
if (horizontalAxis != default || verticalAxis != default)
{
Rotate/Move
}
I solved the snap rotation using Quaternion at rotation, the issiu I had with it was to convert it from 3d to 2d, through the guide of this clip: youtube.com/watch?v=mKLp-2iseDc and made my adjustments it works just fine !