Spawned objects won't stop moving after instantiating. 2D Top-Down
void SpawnCoins()
{
for (int i = 0; i < coinDrops; ++i) //Spawns an amount of coins
{
GameObject coin = Instantiate(coinPrefab, transform.position, Quaternion.identity) as GameObject;
Vector3 targetPosition = new Vector3(Random.Range(-1.0f, 1.0f), Random.Range(-1.0f, 1.0f), 0);
Rigidbody2D rb = coin.GetComponent<Rigidbody2D>();
rb.AddForce(targetPosition * speed);
Debug.Log("Adding force");
if (Vector2.Distance(coin.transform.position, targetPosition < 1f) //this part isn't working
{
rb.velocity = Vector2.zero;
Debug.Log("It's going through.");
}
}
}
Also, tried:
if (coin.transform.position == targetPosition)
{
rb.velocity = Vector2.zero;
Debug.Log("It's going through.");
}
So, I'm spawning coins after an enemy dies. After the coins instantiate, I want them to burst out in random directions and move a small distance away, and then stop. I can get a set amount of coins to spawn and they do move in random directions...but they won't stop moving. I would like help to figure out how to stop the coins.
I've used a debug.log to check if the "if" statement was going through, and it wasn't.
I've also tried moveTowards, but the coins just teleport to the target position. I'm guessing this is because it's not being called in the Update function. But I'm not sure how to reference the instantiated coin gameobject outside of it's own function. When I made it public, I got errors.
Any help/suggestions would be very much appreciated!
Answer by Jack-Mariani · May 04, 2019 at 01:04 PM
I see 2 issues:
THE CHECK MUST RUN OVER TIME
This is happening because you call the method once and not over time.
Basically this is what happening:
Spawn
Addforce to rigidbody
Check the distance just in the same frame
End
This means the rigidbody had not the time to move away.
To solve this in the same script you can use a coroutine (otherwise you can add a monobehaviour to the spawned object and use its update loop).
THE CHECK MUST BE DONE AGAINST A POSITION
This:
Vector3 targetPosition = new Vector3(Random.Range(-1.0f, 1.0f), Random.Range(-1.0f, 1.0f), 0);
Gives you a direction between (-1f,-1f) and (1f,1f). While I think you should check against a position that should be something like:
PositionToCheck = targetPosition + coin.transform.position.
SOLUTION
You may implement the coin as monobehaviour like this:
1 - Create the monobehaviour for the coin
[RequireComponent(typeof(Rigidbody2D))]
public class CoinStopper : MonoBehaviour
{
// --------------- DATA TO CHECK THE SPEED --------------- //
//the position we want the coin to reach
private Vector2 _positionToReach;
//the rigidbody
private Rigidbody2D _rb;
public void SpawnCoin(Vector2 direction, float speed)
{
//first thing we calculate the position to reach
_positionToReach = (Vector2) transform.position + direction;
//caching the rigibopdy
_rb = GetComponent<Rigidbody2D>();
//add the force
_rb.AddForce(direction * speed);
}
//we use fixed update because this is related to a physics logic
private void FixedUpdate()
{
//we keep checking in the fixed update
if(Vector2.Distance(transform.position, _positionToReach) > 1f)
_rb.velocity = Vector2.zero;
}
}
2 - Add this monobehaviour to your Coin prefab.
3 - Update the spawner code with this
void SpawnCoins()
{
for (int i = 0; i < coinDrops; ++i) //Spawns an amount of coins
{
//instantiate the coin
GameObject coin = Instantiate(coinPrefab, transform.position, Quaternion.identity) as GameObject;
//calculate the position
Vector3 targetPosition = new Vector3(Random.Range(-1.0f, 1.0f), Random.Range(-1.0f, 1.0f), 0);
//send the logic directly to the coin
var coinStopper = coin.GetComponent<CoinStopper>();
coinStopper.SpawnCoin(targetPosition, speed);
}
}
------EDIT Changed from coroutine to update in a specific monobehaviour.
Thanks so much for the help. I've adjusted the code, but only one of the coins stops sometimes. I also tried adding PositionToCheck replacing the targetPosition, but the same thing happens, and none of the coins manage to stop.
Here's what I tried, very tiny differences compared to the code you provided, there might be something I messed up with the code: void SpawnCoins() { for (int i = 0; i < coinDrops; ++i) {
GameObject coin = Instantiate(coinPrefab, transform.position, Quaternion.identity) as GameObject;
Vector2 targetPosition = new Vector2(Random.Range(-1f, 1f), Random.Range(-1f, 1f));
Vector2 PositionToCheck = targetPosition + (Vector2)coin.transform.position;
Rigidbody2D rb = coin.GetComponent<Rigidbody2D>();
rb.AddForce(targetPosition * speed);
StartCoroutine(CheckDistance(rb, coin, targetPosition));
}
}
private IEnumerator CheckDistance(Rigidbody2D rb, GameObject coin, Vector2 targetPosition)
{
Vector2 finalPosition = (Vector2)coin.transform.position + targetPosition;
while (Vector2.Distance(coin.transform.position, finalPosition) < 1f)
yield return null;
rb.velocity = Vector2.zero;
}
And here's with the PositionToCheck added, which I may have also hecked up:
void SpawnCoins()
{
for (int i = 0; i < coinDrops; ++i)
{
GameObject coin = Instantiate(coinPrefab, transform.position, Quaternion.identity) as GameObject;
Vector2 targetPosition = new Vector2(Random.Range(-1f, 1f), Random.Range(-1f, 1f));
Vector2 PositionToCheck = targetPosition + (Vector2)coin.transform.position;
Rigidbody2D rb = coin.GetComponent<Rigidbody2D>();
rb.AddForce(targetPosition * speed);
StartCoroutine(CheckDistance(rb, coin, PositionToCheck));
}
}
private IEnumerator CheckDistance(Rigidbody2D rb, GameObject coin, Vector2 PositionToCheck)
{
Vector2 finalPosition = PositionToCheck;
while (Vector2.Distance(coin.transform.position, finalPosition) < 1f)
yield return null;
rb.velocity = Vector2.zero;
}
@Triggerzz I've just updated the answer using a monobehaviour, please let me know if that works.
It works, thank you so much for the help! The only thing I changed was ins$$anonymous$$d of adding force:` _rb.AddForce(direction speed);` I set the velocity:`_rb.velocity = direction speed;` I felt like I learned a lot from this, so thanks again. It was such a big help.