void Fire(float firingRate)
{
TimePerFrame += Time.deltaTime;
if(TimePerFrame >= firingRate)
{
Vector3 ProjectileDistance = new Vector3(0, 30, 0); //distance between center of the campion and it's head
GameObject beam = Instantiate(projectile, transform.position + ProjectileDistance, Quaternion.identity) as GameObject;
beam.GetComponent<Rigidbody2D>().velocity = new Vector3(0, projectileSpeed, 0);
// AudioSource.PlayClipAtPoint(fireSound, transform.position);
TimePerFrame = 0;
}
}
void Update ()
{
if (freezePosition == false)
{
Fire(firingRate);
PositionChaning();
firingRate = Mathf.Lerp(minFiringRate, maxFiringRate, 0.1f);
Debug.Log(firingRate);
}
}
I want my firerate to be flexible, i want it to start by shooting fast and let it automatically lower it's fire rate. (the bigger the firingRate float is the slower the speed is)
The problem is that firingRate = Mathf.Lerp(minFiringRate, maxFiringRate, 0.1f);
triggers once and only once. It doesn't seem to change it's value every frame.
Debug.Log(firingRate); tells the value every frame but it seems to remain a constant.
Why does this happen?
The update triggers every Frame, and so does your Mathf.Lerp However you are not changing the interpolation, which in your case is defined as 0.1f.
By changing this interpolation, you will be able to achieve the 'shifting' of fire rate.
In your case you could define a variable t outside the scope of your update, and update it inside the Update() through t += 0.5f * Time.deltaTime;
The Mathf.Lerp documentation has a pretty good sample of how todo so as well
void Update()
{
// animate the position of the game object...
transform.position = new Vector3(Mathf.Lerp(minimum, maximum, t), 0, 0);
// .. and increate the t interpolater
t += 0.5f * Time.deltaTime;
// now check if the interpolator has reached 1.0
// and swap maximum and minimum so game object moves
// in the opposite direction.
if (t > 1.0f)
{
float temp = maximum;
maximum = minimum;
minimum = temp;
t = 0.0f;
}
}
Your problem is here:
firingRate = Mathf.Lerp(minFiringRate, maxFiringRate, 0.1f);
As you can see here your t has to be calculated every frame.
public static float Lerp(float a, float b, float t);
You can change it like this:
private float fireTimer = 1.0f;
public float fireLimiter = 0.05f;
void Fire(float firingRate)
{
TimePerFrame += Time.deltaTime;
if(TimePerFrame >= firingRate)
{
Vector3 ProjectileDistance = new Vector3(0, 30, 0); //distance between center of the campion and it's head
GameObject beam = Instantiate(projectile, transform.position + ProjectileDistance, Quaternion.identity) as GameObject;
beam.GetComponent<Rigidbody2D>().velocity = new Vector3(0, projectileSpeed, 0);
// AudioSource.PlayClipAtPoint(fireSound, transform.position);
TimePerFrame = 0;
}
}
void Update ()
{
if (freezePosition == false)
{
if(fireTimer > 0.0f){
fireTimer -= Time.deltaTime * fireLimiter;
}
Fire(firingRate);
PositionChaning();
firingRate = Mathf.Lerp(minFiringRate, maxFiringRate, fireTimer);
Debug.Log(firingRate);
}
}
Do not forget to reset fireTimer to 1.0f after shooting!
The delay of the firerate can be controlled by fireLimiter
Related
I know this has been answered like 1000 times, but I just don't know how exactly should I code.
All I want is when the platform changes its position on the x or the z axis, then rotate the whole platform by 90 degrees.
I tried it with a platform.transform.Rotate(0, 90, 0), so I think there is more to do.
The code itself:
public GameObject platform;
public Transform lastPlatform;
Vector3 lastPosition;
Vector3 newPos;
bool stop;
private Quaternion rotationQuaternion;
void Start()
{
lastPosition = lastPlatform.position;
StartCoroutine(SpawnPlatforms());
rotationQuaternion = transform.rotation;
}
void Update()
{
}
IEnumerator SpawnPlatforms()
{
while (!stop)
{
GeneratePosition();
Instantiate(platform, newPos, rotationQuaternion * Quaternion.identity);
lastPosition = newPos;
yield return new WaitForSeconds(0.1f);
}
}
void GeneratePosition()
{
newPos = lastPosition;
int rand = Random.Range(0, 2);
if (rand > 0)
{
newPos.x += 1.5f;
transform.rotation = rotationQuaternion * Quaternion.Euler(0, 90, 0); //one way i tried
}
else
{
newPos.z += 1.5f;
platform.transform.Rotate(0, 90, 0) //another way I tried
}
}
I appreciate all the help!
So as I see it currently there are multiple issues / unclear things
you are rotating about 90° each 0.1 seconds, so within 1 second you rotate about 900°. That sounds to me like it is simply way to fast.
You also rotate transform and not the platform
platform seems to be a Prefab in the assets so it makes no sense to rotate it
You probably want to do
void GeneratePosition()
{
newPos = lastPosition;
int rand = Random.Range(0, 2);
if (rand > 0)
{
newPos.x += 1.5f;
}
else
{
newPos.z += 1.5f;
}
// rather directly rotate the quaternion parameter
// if you are going to do the same thing in both cases anyway I would rather extract it right away
rotationQuaternion *= Quaternion.Euler(0, 90, 0);
}
and then also
// Rotating by "Quaternion.idendity" has no effect
Instantiate(platform, newPos, rotationQuaternion);
Still that sounds like a lot of spawning to me, maybe you would want to look into Object Pooling
I managed to find a solution for the problem. Maybe could be helpful for someone, so here you go:
public struct SpawnPoint
{
public Vector3 position;
public Quaternion orientation;
public void Step(float distance)
{
if (Random.value < 0.5)
{
position.x += distance;
orientation = Quaternion.Euler(0, 90, 0);
}
else
{
position.z += distance;
orientation = Quaternion.Euler(0, 0, 0);
}
//orientation = Quaternion.Euler(0, 90, 0) * orientation;
}
}
void Start()
{
_spawn.position = lastPlatform.position;
_spawn.orientation = transform.rotation;
}
From now on, all what's left to do is Instantiate:
var newPlatform = Instantiate(platform, _spawn.position, _spawn.orientation);
Huge thanks to derHugo for giving me a start on how to do that!
I'm trying to move and rotate a gameobject inside a coroutine to smoothly reach a target position. To do this I tried using Mathf.SmoothDamp() to calculate a factor, which I use in a lerping function. This is my method:
private IEnumerator ReachTarget()
{
_speed = 0f;
var lerpFactor = 0f;
var startingPosition = transform.position;
var startingRotation = transform.rotation;
while (lerpFactor < 0.99f)
{
lerpFactor = Mathf.SmoothDamp(lerpFactor, 1f, ref _speed, 1f);
transform.position = Vector3.Lerp(startingPosition, _targetTransform.position, lerpFactor);
transform.rotation = Quaternion.Lerp(startingRotation, _targetTransform.rotation, lerpFactor);
yield return null;
}
transform.position = _targetTransform.position;
transform.rotation = _targetTransform.rotation;
}
Based on the documentation for Mathf.SmoothDamp() it should change my lerpFactor from 0 to 1 in one second, which in turn should move and rotate my object to to it's target position in one second. This however simply doesn't happen and it takes much longer (approximately 3s) for the lerpFactor to reach 1 (I used 0.99 to because it will never actually reach 1, just get very close).
I thought the reason for this might be that the Mathf.SmoothDamp() uses Time.deltaTime by default, which maybe doesn't work inside coroutines. So I tried supplying my own value:
private IEnumerator ReachTarget()
{
_speed = 0f;
var lerpFactor = 0f;
var startingPosition = transform.position;
var startingRotation = transform.rotation;
var prevTime = Time.realtimeSinceStartup;
while (lerpFactor < 0.99f)
{
var time = Time.realtimeSinceStartup - prevTime;
prevTime = Time.realtimeSinceStartup;
lerpFactor = Mathf.SmoothDamp(lerpFactor, 1f, ref _speed, 1f, maxSpeed: 1000f, time); // using 1000f for max speed
transform.position = Vector3.Lerp(startingPosition, _targetTransform.position, lerpFactor);
transform.rotation = Quaternion.Lerp(startingRotation, _targetTransform.rotation, lerpFactor);
yield return null;
}
transform.position = _targetTransform.position;
transform.rotation = _targetTransform.rotation;
}
This didn't change anything and it takes the same amount of time for the lerpFactor to reach 1.
How can I make it work as it's supposed to?
SmoothDamp's smoothTime is only an approximation and won't be precise as more effort is put into not overshooting the target.
I suggest you switch to either SmoothStep or an Animation curve where you can customize the interpolation as you like (e.g. add overshooting and bouncing effects).
Here is an example where smoothTime is much more precise
using System.Collections;
using UnityEngine;
public class LerpTest : MonoBehaviour
{
[SerializeField] private Transform _targetTransform;
[SerializeField] private AnimationCurve _animationCurve;
private Vector3 startingPosition;
private Quaternion startingRotation;
void Start()
{
startingPosition = transform.position;
startingRotation = transform.rotation;
}
private void Update()
{
if (Input.anyKeyDown)
{
StopAllCoroutines();
transform.position = startingPosition;
transform.rotation = startingRotation;
StartCoroutine(ReachTarget(1f));
}
}
private IEnumerator ReachTarget(float smoothTime)
{
var elapsedTime = 0f;
while (elapsedTime < smoothTime)
{
float lerpFactor = Mathf.SmoothStep(0f, 1f, elapsedTime / smoothTime);
// Or use an animation curve to customize the smoothing curve:
// float lerpFactor = _animationCurve.Evaluate(elapsedTime / smoothTime);
elapsedTime += Time.deltaTime;
transform.position = Vector3.Lerp(startingPosition, _targetTransform.position, lerpFactor);
transform.rotation = Quaternion.Slerp(startingRotation, _targetTransform.rotation, lerpFactor);
yield return null;
}
Debug.Log($"Elapsed time: {elapsedTime} seconds");
transform.position = _targetTransform.position;
transform.rotation = _targetTransform.rotation;
}
}
As a side note, I suggest that you use Quaternion.Slerp instead of lerp to get a nicer looking interpolation and feel free to use Time.deltaTime in corountines - it works just fine.
You don't have to use smooth damp to calculate lerpFactor. Lerp factor is simply the ratio of elapsed time and duration. So if you want your animations in one second;
private IEnumerator ReachTarget()
{
float timeElapsed = 0;
float lerpDuration = 1f;
float lerpFactor;
while (timeElapsed < lerpDuration)
{
lerpFactor = timeElapsed / lerpDuration;
// Use lerpFactor in your lerp functions
timeElapsed += Time.deltaTime;
yield return null;
}
}
Basically I'm trying to create a rolling system for my top down RPG style game, but when I do the Vector3.Lerp it doesn't move the entire way
The code is:
public void CheckSpaceKey()
{
if (exhausted == false)
{
if (Input.GetKey(KeyCode.Space) && canRoll)
{
if (rollDir == RollDir.Up)
{
Vector3 rollStartPos = new Vector3(transform.position.x, transform.position.y, -1.0f);
Vector3 rollEndPos = new Vector3(transform.position.x, transform.position.y + 3, -1.0f);
transform.position = Vector3.Lerp(rollStartPos, rollEndPos, Time.deltaTime);
canRoll = false;
playerStats.stamina -= 10;
Invoke("RollCooldown", 1.5f);
}
}
}
}
public void RollCooldown()
{
canRoll = true;
}
This should be making my player move upwards 3 units, but it instead is moving a random number of around 0.35 to 0.45.
Also CheckSpaceKey is being called in update
Lerp doesn't do what you think it does. Assuming Time.deltaTime is about 1/60, it's going to move the unit 1/60th of the way to the destination, and that's it.
Consider using a coroutine that updates the t parameter for Lerp with each frame:
public void CheckSpaceKey()
{
// by the way, this could just be `if (!exhausted)`
// although some coding guidelines require this format below
if (exhausted == false)
{
if (Input.GetKey(KeyCode.Space) && canRoll)
{
if (rollDir == RollDir.Up)
{
StartCoroutine(DoRoll(Vector3.up * 3));
}
}
}
}
IEnumerator DoRoll(Vector3 offset)
{
float duration = 1f;
canRoll = false;
playerStats.stamina -= 10;
Invoke("RollCooldown", 1.5f);
Vector3 rollStartPos = new Vector3(transform.position.x, transform.position.y, -1.0f);
Vector3 rollEndPos = rollStartPos + offset;
float t = 0;
while (t < 1f)
{
t = Mathf.Min(1f, t + Time.deltaTime/duration);
transform.position = Vector3.Lerp(rollStartPos, rollEndPos, t);
yield return null;
}
}
public void RollCooldown()
{
canRoll = true;
}
This will not move 3 Units, you call
transform.position only inside the if case and access the if case only once every 1.5 seconds.
Lerp will not update your position every frame but only the frame it is called, if you the object to move constantly you have to update your position constantly you should crate rollStartPos and rollEndPos inside the first if and create add
else{
time += Time.deltaTime;
transform.position = Vector3.Lerp(rollStartPos, rollEndPos, time);
}
under the 2nd if. This way it will lerp til rollEndPos unless the ball(?) is exhausted, if it should roll even when it's exhausted you need to update the position outside the first if
I am trying to "squeeze" a gameObject. When distance starts to be 6.98, execute this code: "The smaller the distance between gameObject1 and gameObject2, the thinner and longer this gameObject is". Somehow, this code has no effect. Why?
public Transform gameObject1;
public Transform gameObject2;
void Update ()
{
float distance_squeeze = Vector3.Distance(gameObject1.position, gameObject2.position);
for (distance_squeeze = 6.98f; distance_squeeze > 0; distance_squeeze -= 0.1f)
{
transform.localScale += new Vector3(-0.5F, 0.5F, 0);
}
}
You are calculating the distance between gameObject1 and gameObject2 and then setting it equal to 6.98.
Try something like:
void Update ()
{
float distance_squeeze = Vector3.Distance(gameObject1.position, gameObject2.position);
if(distance_squeeze < 6.98F){
transform.localScale = new Vector3(-distance_squeeze, distance_squeeze, 1);
}
}
I have a Controller which moves my object diagonally. The left arrow should move the player forward and to the left 45 degrees, and the right arrow the same to the right. I would like to move the player relatively to its current position. Right now it moves relatively to the point(0,0,0).
My code:
public class JollyJumper : MonoBehaviour {
protected CharacterController control;
public float fTime = 1.5f; // Hop time
public float fRange = 5.0f; // Max dist from origin
public float fHopHeight = 2.0f; // Height of hop
private Vector3 v3Dest;
private Vector3 v3Last;
private float fTimer = 0.0f;
private bool moving = false;
private int number= 0;
private Vector2 direction;
public virtual void Start () {
control = GetComponent<CharacterController>();
if(!control){
Debug.LogError("No Character Controller");
enabled=false;
}
}
void Update () {
if (fTimer >= fTime&& moving) {
var playerObject = GameObject.Find("Player");
v3Last = playerObject.transform.position;
Debug.Log(v3Last);
v3Dest = direction *fRange;
//v3Dest = newVector* fRange + v3Last;
v3Dest.z = v3Dest.y;
v3Dest.y = 0.0f;
fTimer = 0.0f;
moving = false;
}
if(Input.GetKeyDown(KeyCode.LeftArrow)){
moving = true;
direction = new Vector2(1.0f, 1.0f);
number++;
}
if(Input.GetKeyDown(KeyCode.RightArrow)){
moving = true;
direction = new Vector2(-1.0f, 1.0f);
number++;
}
if(moving){
Vector3 v3T = Vector3.Lerp (v3Last, v3Dest, fTimer / fTime);
v3T.y = Mathf.Sin (fTimer/fTime * Mathf.PI) * fHopHeight;
control.transform.position = v3T;
fTimer += Time.deltaTime;
}
}
}
How can resolve this? Any ideas? Thanks a lot!
The short answer is: you hard-coded two locations you want to jump to: points (1, 1) and (-1, 1). You should create new Vector each time you start jumping. Replace each
direction = new Vector2(1.0f, 1.0f);
with this line:
v3Dest = transform.position + new Vector3(1.0f, 0, 1) * fRange;
and it should work.
While I'm on it, there are some other things I want to point:
There is a lot of floating point error after each jump. Notice that in your code v3T will never be equal to v3Dest (you never actually reach your destination), because you switch the moving flag earlier. You should explicitly set your position to v3Dest when the jump is over.
You are checking jump timers etc. every frame. A more elegent solution is to start a coroutine.
You use a sinusoid as your jump curve, which looks ok, but using a parabola would be conceptually more correct.
Right now it is possible to start next jump mid-air (I'm not sure whether it is intended or not)
Here is some code you may use that avoids those problems:
using System.Collections;
using UnityEngine;
public class Jumper : MonoBehaviour
{
#region Set in editor;
public float jumpDuration = 0.5f;
public float jumpDistance = 3;
#endregion Set in editor;
private bool jumping = false;
private float jumpStartVelocityY;
private void Start()
{
// For a given distance and jump duration
// there is only one possible movement curve.
// We are executing Y axis movement separately,
// so we need to know a starting velocity.
jumpStartVelocityY = -jumpDuration * Physics.gravity.y / 2;
}
private void Update()
{
if (jumping)
{
return;
}
else if (Input.GetKeyDown(KeyCode.LeftArrow))
{
// Warning: this will actually move jumpDistance forward
// and jumpDistance to the side.
// If you want to move jumpDistance diagonally, use:
// Vector3 forwardAndLeft = (transform.forward - transform.right).normalized * jumpDistance;
Vector3 forwardAndLeft = (transform.forward - transform.right) * jumpDistance;
StartCoroutine(Jump(forwardAndLeft));
}
else if (Input.GetKeyDown(KeyCode.RightArrow))
{
Vector3 forwardAndRight = (transform.forward + transform.right) * jumpDistance;
StartCoroutine(Jump(forwardAndRight));
}
}
private IEnumerator Jump(Vector3 direction)
{
jumping = true;
Vector3 startPoint = transform.position;
Vector3 targetPoint = startPoint + direction;
float time = 0;
float jumpProgress = 0;
float velocityY = jumpStartVelocityY;
float height = startPoint.y;
while (jumping)
{
jumpProgress = time / jumpDuration;
if (jumpProgress > 1)
{
jumping = false;
jumpProgress = 1;
}
Vector3 currentPos = Vector3.Lerp(startPoint, targetPoint, jumpProgress);
currentPos.y = height;
transform.position = currentPos;
//Wait until next frame.
yield return null;
height += velocityY * Time.deltaTime;
velocityY += Time.deltaTime * Physics.gravity.y;
time += Time.deltaTime;
}
transform.position = targetPoint;
yield break;
}
}