1

Important: I took parts of the code from here but the OP did not get a helpful answer yet

Make an AI move to a collectable, call a method and fly back in Unity


I have a follower which is following the player. This follower has a trigger attached. This trigger scans the area and looks for gems to collect. When collecting a gem, the follower should move to it, activate some code of the gem and move back to the player.

The default state

Default

The collectable entered the trigger zone

Collecting

So I tried to achieve it by using Enumerators. When the follower is not collecting gems, it should follow the player.

When I start the game and move to a gem, the follower is not moving to the gem but the gem does fine. It moves to the player. But the follower does not move.

So my improved code looks this

private Transform player;
private List<Collectable> collectablesToCollect = new List<Collectable>(); // all items found in the area
private bool isCollecting = false;
private float movementSpeed = 10;

private void Start()
{
    player = Globals.GetPlayerObject().transform; // find the player object in the scene
}

private void Update()
{
    if (CollectablesInRange()) // any gems in the area?
    {
        if (!isCollecting) 
            MoveToCollectable(); // Collect the gem
    }
    else
    {
        FollowPlayer(); 
    }
}

private void OnTriggerEnter(Collider col)
{
    Collectable collectable = col.GetComponent<Collectable>();

    if (collectable != null) // is the found object a gem?
    {
        if (!collectable.GetMovementState()) // got it collected?
            collectablesToCollect.Add(collectable);
    }
}

private void OnTriggerExit(Collider col)
{
    Collectable collectable = col.GetComponent<Collectable>();

    if (collectable != null)
        collectablesToCollect.Remove(collectable); // remove it from the list of items to collect
}

private void MoveToCollectable()
{
    isCollecting = true;

    Collectable collectable = collectablesToCollect.First(); // get the first gem in the list
    Vector3 defaultPosition = transform.position;

    CollectionMovement(transform.position, collectable.GetCollectablePosition()); // move to the gem

    collectable.StartMovingToPlayer(); // activate the gem

    CollectionMovement(transform.position, defaultPosition); // move back to the default position

    collectablesToCollect.Remove(collectable); // remove the gem from the list

    isCollecting = false;

    if (CollectablesInRange()) // are there more gems to collect?
        MoveToCollectable();
}

private IEnumerator CollectionMovement(Vector3 startPos, Vector3 endPos)
{
    transform.position = Vector3.MoveTowards(startPos, endPos, movementSpeed * Time.deltaTime);
    yield return null;
}

void FollowPlayer()
{
    Vector3 playerPos = player.position;
    transform.position = new Vector3(playerPos.x, playerPos.y + 2, playerPos.z);
}

private bool CollectablesInRange() // any gems in the area?
{
    return collectablesToCollect.Count > 0;
}

Really important should be the part when it comes to IEnumerator CollectionMovement() and the call in MoveToCollectable()

peterHasemann
  • 1,550
  • 4
  • 24
  • 56
  • 1
    [This](https://stackoverflow.com/questions/36850253/move-gameobject-over-time/36851965#36851965) post describes how to move GameObject over time. That function from that answer should solve your problem. – Programmer Sep 13 '17 at 09:45
  • yes it almost does, but I will try it on my own – peterHasemann Sep 13 '17 at 11:22

2 Answers2

1

There are a couple issues with your CollectionMovement() IEnumerator.

1) When you call a Coroutine, you need to run it using StartCoroutine(function());. For example, you call CollectionMovement() like this:

CollectionMovement(transform.position, collectable.GetCollectablePosition())

but you should be doing this:

StartCoroutine(CollectionMovement(transform.position, collectable.GetCollectablePosition()));

2) I don't think your CollectionMovement does what you think it does. In it's current state it just moves towards the target position a little (movementSpeed * Time.deltaTime), and then stops. You should change the function to something like this:

private IEnumerator CollectionMovement(Vector3 startPos, Vector3 endPos)
{
    while(transform.position != endPos)
    {
        transform.position = Vector3.MoveTowards(startPos, endPos, movementSpeed * Time.deltaTime);
        yield return null;
    }
}

This function will make the follower move to the gem.

3) Because of how Coroutines work, when you call CollectionMovement(), the follower will start to move to the gem (if you use the function in the previous fix), but at the same time the collectable.StartMovingToPlayer(); will be called and the gem will start moving to the player. This is because Coroutines do not stop code from running. They kind of run in the background.

One way to fix this is to make an IEnumerator that waits for CollectionMovement() to finish. You could do something like this:

bool reachedPlayer;

private IEnumerator CollectionMovement(Vector3 startPos, Vector3 endPos)
{
    reachedPlayer = false;
    while(transform.position != endPos)
    {
        transform.position = Vector3.MoveTowards(startPos, endPos, movementSpeed * Time.deltaTime);
        yield return null;
    }
    reachedPlayer = true;
}

private IEnumerator WaitForFollower(Collectable collectable){
    while(!reachedPlayer){
        yield return null;
    }
    collectable.StartMovingToPlayer();
}

With this setup, everything should work as you want. You just need to call those two functions like this:

StartCoroutine(CollectionMovement(transform.position, collectable.GetCollectablePosition())); // move to the gem

StartCoroutine(WaitForFollower(collectable)); // activate the gem

StartCoroutine(CollectionMovement(transform.position, defaultPosition)); // move back to the default position

Please tell me if you have issues with any of this.

Bhaskar
  • 680
  • 7
  • 26
0

So I finally got it, you can copy and use the code here

https://hastebin.com/besutetazi.cs

I modified the method MoveToCollectable()

 private void MoveToCollectable()
    {
        Collectable collectable = collectablesToCollect.First();
        StartCoroutine(CollectionMovement(collectable)); // collect it
    }

    private IEnumerator CollectionMovement(Collectable collectable)
    {
        if (isCollecting)
            yield break; // Already in use? return

        isCollecting = true;

        yield return StartCoroutine(MoveToPoint(collectable.GetCollectablePosition())); // Move to it

        if (collectable != null)
            collectable.StartMovingToPlayer(); // make it move to the player

        yield return StartCoroutine(MoveToPoint(currentPosition)); // move back

        collectablesToCollect.Remove(collectable); // remove it from the list

        if (CollectablesInRange()) // any other collectables in range?
            MoveToCollectable(); // do it again

        isCollecting = false;
    }

    private IEnumerator MoveToPoint(Vector3 targetPosition) // move to a point
    {
        while (transform.position != targetPosition)
        {
            transform.position = Vector3.MoveTowards(transform.position, targetPosition, movementSpeed * Time.deltaTime);
            yield return null;
        }
    }

this should do the magic.

peterHasemann
  • 1,550
  • 4
  • 24
  • 56