I'm Making a movement script with vector 2 and Rigidbody.velocity. I want my gameobject to move until a certain x pos and then return to the initial pos, all of that while moving continiously. I achieved the first part, it goes backwards when it reaches a certain point but then the backwards movement never stops:
public float spawnPos;
public float currentPos;
public float constantSpeed = 3;
public float speed = 0;
KeyInput scriptBeholderKI;
Rigidbody2D squadRigidBody;
//Comprovation of spawnposition and KeyInput
//Script adquisition
void Start () {
spawnPos = transform.position.x;
scriptBeholderKI = gameObject.GetComponent <KeyInput> ();
squadRigidBody = gameObject.GetComponent <Rigidbody2D> ();
}
void FixedUpdate () {
//Movement
squadRigidBody.velocity = new Vector2 (constantSpeed + speed , 0);
//key inputs
if (Input.GetKeyDown (scriptBeholderKI.forward)) {
StopAllCoroutines ();
StartCoroutine (RightMovement(0f));
}
if (Input.GetKeyDown (scriptBeholderKI.backwards)) {
StopAllCoroutines ();
StartCoroutine (LeftMovement (0f));
}
}
//Speed values (Right, Left)
IEnumerator RightMovement (float Rloop) {
while (transform.position.x < constantSpeed * Time.time + spawnPos + 14) {
speed = 10f;
yield return new WaitForSeconds (Rloop);
}
if (transform.position.x> constantSpeed * Time.time + spawnPos + 14) {
StopAllCoroutines ();
StartCoroutine (LeftMovement (0f));
}
}
IEnumerator LeftMovement (float Lloop) {
while (transform.position.x > constantSpeed * Time.time + spawnPos) {
speed = -7f;
yield return new WaitForSeconds (Lloop);
}
}
If you also see anything that could be improved pls make me note c:
edit:
when I press the forward input my gameobject moves until it reaches a point, when I press the bacwards one it moves to the left nonstop.
also the thing is that I want my target to move at the constantspeed of the gameobject, thats why I use Time.time. the movements I do with the inputs are more properly said accelerations. I want my target to be always at the same relative distance from the gameobject, since the game starts. At least the maxium reach to the right is the one I want...
And I also tried to remove the stop all coroutines block and nothing, I but my intention was to be able to cancel the movement whenever I want (I removed the one in the RightMovement coroutine, however. It wasn't neecesary after all)
one last thing: I changed "FixedUpdate" for "Update" because it gave me problems with my inputs response, I don't know if it's relevent but just in case.
I think you are mistaking the Time.time variable. Just like yes mentions in his comment
Time.time is the time in seconds since the start of the game.
This means, as time moves by, so will your target, which will slowly but surely move away.
Instead of Time.time you could however use Time.deltaTime
Time.deltaTime is the time in seconds it took to complete the last frame
This is most often used for movement.
When you multiply with Time.deltaTime you essentially express: I want to move this object 10 meters per second instead of 10 meters per frame, if the multiplication is 10.
Unity docs: Time.deltaTime
Related
I am moving a rigidbody using rb.AddForce(force,ForceMode.Impulse) where force is the target position the rigidbody have to reach.
Now the speed it goes directly depends on the distance it has to cover.
Let's say the time taken to reach the target position is 3sec. I need the rigidbody to cover the same target pos in 5sec.
I dont want to change the timescale as it affects my gameflow
On Changing the velocity of rigidbody it fails to reach the target position
Some basic physics/math:
velocity = change-in-position / travel-time
force = mass * change-in-velocity / acceleration-time
For ease, we're going to call change-in-position as distance, and change-in-velocity/acceleration-time as acceleration
Now, since the acceleration-time component is effectively zero because you're using Impulse, we're going to remove it from the equation (in math terms, we set it at '1')
force = mass * change-in-velocity
Assuming your object starts at zero velocity, we can simplify change-in-velocity to just velocity
force = mass * velocity
force = mass * distance / travel-time
To bring that back into Unity code:
var mass = rb.mass;
var distance = destination.position - transform.position;
var travelTime = 5f; // seconds
var force = mass * distance / travelTime;
rb.AddForce(force, ForceMode.Impulse);
Note that this assumes a frictionless transfer and constant velocity.
If you ignore gravity, this code solves the problem, here I changed the drag according to weight and distance, it may be a little bit away from the destination at the end, the reason should be higher drag friction.
public void ForceToTarget(Transform target, float time = 1f)
{
var rb = GetComponent<Rigidbody>();
var vector = target.position - transform.position;
var distance = vector.magnitude;
rb.drag = distance/time;
rb.AddForce(vector*rb.mass*distance/time, ForceMode.Impulse);
}
If you want precise control over your speed, then stop using ForceMode.Impulse because other physics effects like drag will make your answers wrong. Instead, just set the speed yourself. You can do this with a Coroutine to control timing and ForceMode.VelocityChange to control the speed. Basically, just look at where you are, where the target is, how much time is left, and apply the speed directly.
private bool canMove = true;
public void MoveTo(Vector3 targetPosition, float targetTime)
{
if(canMove)
{
StartCoroutine(MoveToCoroutine(targetPosition,targetTime));
}
}
private IEnumerator MoveToCoroutine(Vector3 targetPosition, float time)
{
canMove = false;
while(time > 0)
{
var positionDelta = transform.position - targetPosition;
var targetSpeed = positionDelta / time;
var speedDelta = targetSpeed - rb.velocity;
rb.AddForce(speedDelta , ForceMode.VelocityChange);
yield return null;
time -= Time.deltaTime;
}
// Bring the object to a stop before fully releasing the coroutine
rb.AddForce(-rb.velocity, ForceMode.VelocityChange);
canMove = true;
}
I wrote this here into the text editor, no IDE and haven't tested it, but I'm pretty sure this'll do what you want.
Assuming you're using the target position as-is then larger vectors will cause larger force to be applied than smaller vectors. Similarly, if using a direction vector as-is then as the rb gets closer to the target the magnitute of the vector gets smaller and thus less force is applied.
To get a constant speed use the direction to the target and Normalise it instead. Regardless of the distance the direction vector will always have a magnitude of 1 so you can multiply it by any value to accurately control the speed of the object:
Rigidbody rb;
public Transform target;
public float dist;
public float speed = 2f; // or whatever
public float targetDistance = 40f; // or whatever
private void Start()
{
rb = GetComponent<Rigidbody>();
StartCoroutine("IMove");
}
IEnumerator IMove()
{
dist = Vector3.Distance(transform.position, target.position);
while (dist > targetDistance)
{
dist = Vector3.Distance(transform.position, target.position);
rb.AddForce(Vector3.Normalize(target.position - transform.position) * speed, ForceMode.Impulse);
yield return new WaitForFixedUpdate();
}
}
Without getting too much into the physics and maths, if you want it to travel slower but the same distance you need to reduce the gravity on it and the initial force.
Note in this example I am assuming the weight is 1 to make the calculation a bit easier for force.
public class TrevelSpeedAdjusted
{
public float speedFactor = 1;
void FixedUpdate()
{
// Reduce the gravity on the object
rb.AddForce(-Physics.gravity * rigidbody.mass * (1 - speedFactor));
}
public float AddAdjustedForce(Vector3 force, ForceMode forceMode)
{
rb.AddForce(force * speedFactor, forceMode);
}
}
So you can try DoTween package to do this pretty easily and its very convenient to use a package instead of using Unity's inbuilt system.
With doTween use this:
DOMove(Vector3 to, float duration, bool snapping) condition to tween your physics Gameobject to a given target position in the duration you require.
Here's documentation you can refer to if you want: http://dotween.demigiant.com/documentation.php
Let me give you an example:
Install the doTween Package. http://dotween.demigiant.com/download
Import it to unity.
Go to your script where you want to achieve the functionality you mentioned on your question and add this header "using DG.Tweening".
Now get access of your RigidBody.
For Example Lets say: I have a cube gameobject with rigidbidy and this script attached.
The Cube Initial Position is at 0,0,0.
And I want it to move to 5,5,5 in 3 seconds or 5 seconds as per your questions request. And lets say I want this to happen when I click SpaceBar on keyboard.
So I would simply do.
Rigidbody rb;
void Start()
{
rb= GetComponent<Rigibody>();
}
void Update()
{
if(Input.GetButtonDown(Keycode.Space))
{
MoveCube(new Vector3(5,5,5),5);
}
}
void MoveCube(Vector3 inTargetPosition , float durationToReachTheTarget)
{
//What this line does is first take in the target position you want your physics object to reach as it first parameter, Then takes in the duration in which you want it to reach there.
rb.DoMove(inTargetPosition,durationToReachTheTarget);
}
This should help you. But remember this is only if you okay with adding an extra package. Personally this package is very good and I would recommend you this.
I'm new to unity3d and my problem is that the following code doesn't work and I'm looking for help to find out what I'm doing wrong.
if (enemy.transform.position.x < 1 & enemy.transform.position.z > 1)
{
transform.position = Vector3.MoveTowards(transform.position, moveSpots2[randomSpot2].position, speed * Time.deltaTime);
if (Vector3.Distance(transform.position, moveSpots2[randomSpot2].position) < 0.1f)
{
if (waitTime <= 0)
{
randomSpot2 = Random.Range(0, moveSpots2.Length);
waitTime = startWaitTime;
}
else
{
waitTime -= Time.deltaTime;
}
What I'm trying to do:
When the Enemy position is within the range defined by: enemy.transform.position.x < 1 & enemy.transform.position.z > 1
The player doesn't move. But, if the Enemy's position is outside of range then the player moves.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Patrol : MonoBehaviour
{
public float speed;
public Transform[] moveSpots2;
public Transform[] moveSpotsP;
private float waitTime;
private int randomSpot2;
private int randomSpotP;
public float startWaitTime;
public Transform ball;
public GameObject enemy;
void Start()
{
waitTime = startWaitTime;
randomSpot2 = Random.Range(0, moveSpots2.Length);
randomSpotP = Random.Range(0, moveSpotsP.Length);
GameObject enemy = this.GetComponent<GameObject>();
}
void Update()
{
transform.LookAt(ball);
if (enemy.transform.position.x > 0 & enemy.transform.position.z < 0)
{
transform.position = Vector3.MoveTowards(transform.position, enemy.transform.position, speed * Time.deltaTime);
}
if (enemy.transform.position.x < 0 & enemy.transform.position.z < 0 | enemy.transform.position.x > 0 & enemy.transform.position.z > 0)enemy.transform.position.x < 0 & enemy.transform.position.z > 0)
{
transform.position = Vector3.MoveTowards(transform.position, moveSpotsP[randomSpotP].position, speed * Time.deltaTime);
if (Vector3.Distance(transform.position, moveSpotsP[randomSpotP].position) < 0.1f)
{
if (waitTime <= 0)
{
randomSpotP = Random.Range(0, moveSpotsP.Length);
waitTime = startWaitTime;
}
else
{
waitTime -= Time.deltaTime;
}
}
if (enemy.transform.position.x < 1 & enemy.transform.position.z > 1)
{
transform.position = Vector3.MoveTowards(transform.position, moveSpots2[randomSpot2].position, speed * Time.deltaTime);
if (Vector3.Distance(transform.position, moveSpots2[randomSpot2].position) < 0.1f)
{
if (waitTime <= 0)
{
randomSpot2 = Random.Range(0, moveSpots2.Length);
waitTime = startWaitTime;
}
else
{
waitTime -= Time.deltaTime;
}
}
}
}
}
In Update you put the game handling code. There you progress the game state. Note that realtime games do not really exist. Instead we just use very, very, very small turns wich do not wait for user input. We call those "game ticks". A large part (at least 3/4) of the Update function are those game ticks.
You have a counter function wich will draw the game state. These two functions play ping-pong in the processor. They are called interchangeably. Together creating what we call the game loop.
Input handling. (non-particle) Physics. The game menu logic. That is what belongs here. There are cases where you want to handle stuff but do not want advance the gamestate - stuff like the game being paused. Those belong into update, but usually a bool will lock out progressing the game tick.
The biggest mistakes are usually:
putting the gametick logic into the Draw function - seriously guys it is 2019. The mistake as bad in teh last millenium and it did not go any better
accidetally locking the UI updates/input handling behind the gameticks.
The update function is called every frame. Essentially the way all games work is they perform calculations (the calculations depend on what the game is doing) and then based on those calculations they "draw" a frame and display it on the monitor. The faster the computer can perform the calculations and draw the frame is the frame rate, or FPS (Frames per seconds).
Basically, put everything in Update that you need to be calculated every frame of the game. Don't use it if you don't need it though, generally the less calculations you have to perform each frame, the higher the frame rate is.
Usually you want to check stuff like input (so like if a players pressing the shift key etc.) in Update because you want to constantly be checking if the player is pressing shift (or whatever key/input you're checking for)
For stuff that you only need performed on or before the first frame is drawn, use Start or Awake (Awake is like Start, but it's called even earlier).
There's also other methods you sometimes use like OnTriggerEnter (see the documentation).
There's also other variations of Update for different tasks, such as FixedUpdate which you use for physics calculations and LateUpdate which you use for camera movement, but most of the time you'll use update.
Documentation:
https://docs.unity3d.com/2017.3/Documentation/ScriptReference/MonoBehaviour.Update.html
https://docs.unity3d.com/ScriptReference/MonoBehaviour.FixedUpdate.html
https://docs.unity3d.com/ScriptReference/MonoBehaviour.LateUpdate.html
https://docs.unity3d.com/ScriptReference/MonoBehaviour.Start.html
https://docs.unity3d.com/ScriptReference/MonoBehaviour.Awake.html
https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnTriggerEnter.html
I'm currently making a runner type game that is not randomly generated in Unity. The player, camera and background stay in the same place (the player movement is clamped) and the hazards come towards the player along the negative Z-Axis with this simple lines of code:
public float speed;
private Rigidbody rb;
void Start () {
rb = GetComponent<Rigidbody> ();
rb.velocity = transform.forward * -speed;
}
And it works fine. Now I want to add asteroids that not only come towards the player, but they move up and down (and repeat) and I tried doing this with Lerp(). I'm not a pro about Lerp so I used a code that I found online that works:
public Transform startPos, endPos;
public bool repeatable = true;
public float speed = 1.0f;
public float duration = 3.0f;
float startTime, totalDistance;
IEnumerator Start () {
startTime = Time.time;
while(repeatable) {
yield return RepeatLerp(startPos.position, endPos.position, duration);
yield return RepeatLerp(endPos.position, startPos.position, duration);
}
}
void Update () {
totalDistance = Vector3.Distance(startPos.position, endPos.position);
if(!repeatable) {
float currentDuration = (Time.time - startTime) * speed;
float journeyFraction = currentDuration / totalDistance;
this.transform.position = Vector3.Lerp(startPos.position, endPos.position, journeyFraction);
}
}
public IEnumerator RepeatLerp(Vector3 a, Vector3 b, float time) {
float i = 0.0f;
float rate = (1.0f / time) * speed;
while (i < 1.0f) {
i += Time.deltaTime * rate;
this.transform.position = Vector3.Lerp(a, b, i);
yield return null;
}
}
What I did is create 2 Empty Objects for the start and end positions and then I put the hazard and two lerping points under another Empty Object, let's call it Parent (So Parent > -Hazard -Start - End). Then I added the moving script along the Z-Axis to the parent empty and it kinda works but is like jumping. So the lerping works but it does not move along the Z-Axis until the lerping function is done, the the object jumps to the new Position in the Z-Axis. So it looks very jumpy and glitchy.
How can I do this so is a smooth movement along the Z-Axis and the lerp still works along the Y?
Thanks!!
PS: I know I could just move the character and camera etc, but for now I would like to keep it this way.
The coroutine RepeatLerp acts across several frames, but receives input of type Vector3. The actual position of a reference point will change over time, but the argument values a and b do not change until the coroutine terminates.
So the immediate solution is simple: instead of passing Vector3, pass reference to Transform, and grab its new position each frame. Change your function to this:
public IEnumerator RepeatLerp(Transform a, Transform b, float time)
{
float i = 0.0f;
float rate = (1.0f / time) * speed;
while (i < 1.0f)
{
i += Time.deltaTime * rate;
this.transform.position = Vector3.Lerp(a.position, b.position, i);
yield return null;
}
}
And in Start funtion the invocation will now be:
yield return RepeatLerp(startPos, endPos, duration);
yield return RepeatLerp(endPos, startPos, duration);
This will solve your problem, however, having said that, I strongly suggest you reconsider the way you move your object. You have a mix of Rigidbody semantics with moving objects using transform, which is a really bad practice. The engine is not designed to do that and it may cause great performance issues in the future.
In general, if you have colliders on your objects (and it looks like you have) you should move your objects around using only Rigidbody and applying velocity / forces to it. Maybe have a look at Rigidbody.MovePosition if you want to use Lerp, but even better use Rigidbody.velocity, like for example (the code below is just schematic):
if (transform.position.x > highLimit) rigidbody.velocity -= 2*pulsingSpeed;
if (transform.position.x < lowLimit) rigidbody.velocity += 2*pulsingSpeed;
Good day. I am trying to achieve simple thing but nothing just works....
The desired output:
• Player touches a place in my world
• Character starting to smoothly move with walking animation towards that location
The actual result:
• Player touches a place in my world
• Character just jumps into the final point, no smooth movement nothing
Things tried:
• Vector2 finalPosition = Camera.main.ScreenToWorldPoint(position);
transform.position = finalPosition;
In this scenario the character just jumps into the final point
• Vector2 finalPosition = Camera.main.ScreenToWorldPoint(position);
transform.Translate(finalPosition);
In this case character just dissappears from the screen.
Any solution?
You can use Vector2.Lerp() to move smoothly between two points.
Some pseudo code:
bool move;
float t;
Update()
{
if () // insert condition to begin movement
{
move = true;
t = 0; // reset timer
startPos = transform.position; // store current position for lerp
finalPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
if (move)
MovePlayer();
}
void MovePlayer()
{
t += Time.deltaTime / 3f; // 3 is seconds to take to move from start to end
transform.position = Vector2.Lerp(startPos, finalPosition, t);
if (t > 3)
{
move = false; // exit function
}
}
in update:
transform.position += (final_pos - transform.position).normalized * Time.deltaTime;
this is adding to your current position the direction ... use delta time to scale the movement and you can increase or decrease the speed by multiplying it all by some scalar value, ie any float value. note that it’s best to normalize once rather than every frame but this is the general idea.
I'm having this problem with my enemy shooting, you see I'm using raycasting to detected where my player is and once detected I want the enemy to shoot, so far I have accomplished that but however there's not delay between each Instantiated bullet!So the bullet is being constantly spawn rather than a delay in between each spawn. I've tried a whole lot of different solutions to fix this problem but nothing worked! I've tried IEnumerator, object pooling, creating a count down timer and invoke & invokerepeating but still my bullets are still being instantiated instantly without no delay. Does any one knows how to have a delay between each instantiated bullet?? Thank you!
This is my script:
public GameObject bulletPrefab;
public Transform bulletSpawn;
public Transform sightStart, sightEnd;
public bool spotted = false;
void Update()
{
RayCasting ();
Behaviours ();
}
void RayCasting()
{
Debug.DrawLine (sightStart.position, sightEnd.position, Color.red);
spotted = Physics2D.Linecast (sightStart.position, sightEnd.position, 1 << LayerMask.NameToLayer("Player"));
}
void Behaviours()
{
if (spotted == true) {
//Invoke("Fire", cooldownTimer);
Fire ();
} else if (spotted == false) {
//CancelInvoke ();
}
}
void Fire()
{
GameObject bullet = (GameObject)Instantiate (bulletPrefab);
//GameObject bullet = objectPool.GetPooledObject ();
bullet.transform.position = transform.position;
bullet.GetComponent<Rigidbody2D> ().velocity = bullet.transform.up * 14;
Destroy (bullet, 2.0f);
}
You need to throttle the rate at which bullets are spawned. You could base it on number of frames but that's a bad approach as fire rate will vary with game performance.
A better approach is to use a variable to track how much time must elapse before we can fire again. I'll call this "Time To Next Shot" (TTNS)
Decrement TTNS by the time that has elasped.
Check if TTNS is 0 or less
If so:
Set TTNS as appropriate for our desired rate of fire.
Fire a shot
Something like..
private float fireRate = 3f; // Bullets/second
private float timeToNextShot; // How much longer we have to wait.
// [Starts at zero so our first shot is instant]
void Fire() {
// Subtract the time elapsed (since the last frame) from TTNS .
timeToNextShot -= time.deltaTime;
if(timeToNextShot <= 0) {
// Reset the timer to next shot
timeToNextShot = 1/fireRate;
//.... Your code
GameObject bullet = (GameObject)Instantiate (bulletPrefab);
//GameObject bullet = objectPool.GetPooledObject ();
bullet.transform.position = transform.position;
bullet.GetComponent<Rigidbody2D> ().velocity = bullet.transform.up * 14;
Destroy (bullet, 2.0f);
}
}
This does assume that you only call Fire() once per frame (Otherwise the elapsed time will be subtracted from nextShot multiple times).
I opted to do it this way (subtracting slices from a time span) rather than defining an absolute time (wait until time.elapsedTime > x) as the resolution of elapsedTime decreases over time and that approach will result in glitches after a few hours of gameplay.