Reputation: 3762
I want to create a follower AI like Sparx from Spyro. You can see the mechanics in this video
https://youtu.be/2DicrivJ2xc?t=5
Sparx looks for gems on the ground, flies to them and flies back to Spyro. When flying to them, the gems start to fly to the player too. So Sparx is collecting the gems.
I already created the functionality making the gem fly to the player. Let's say I call this method
StartMovingToPlayer();
Sparx will call this method from the gem in his script.
public class PlayerFollower : Ai
{
private void OnTriggerEnter(Collider col) // something entered the trigger
{
Collectable collectable = col.GetComponent<Collectable>(); // try to get the collectable script
if (collectable != null) // is it a collectable?
collectable.StartMovingToPlayer(); // make it move to the player
}
}
So my question is, how can I make the AI fly to the gem, call the method, fly back and have all the gems in the trigger stored in a queue because when there is more than one gem in the trigger, the AI has to queue it.
So here's an update, I tried to make the follower collecting the gems when having them stored in my list
public class PlayerFollower : Ai { private List collectablesToCollect = new List(); // have all the gems in range stored here private bool isCollecting = false; // is the follower currently collecting gems? private float movementSpeed = 10; // collecting speed
private void Update()
{
if (CollectablesInRange()) // any gems in range?
{
if (!isCollecting) // collecting queued?
MoveToCollectable(); // collect it
}
else
{
// follow the player
}
}
private void OnTriggerEnter(Collider col)
{
Collectable collectable = col.GetComponent<Collectable>(); // get the gem
if (collectable != null) // is the object a gem?
{
if (!collectable.GetMovementState()) // got the gem already collected?
collectablesToCollect.Add(collectable); // add it to the queue
}
}
private void OnTriggerExit(Collider col)
{
Collectable collectable = col.GetComponent<Collectable>();
if (collectable != null)
collectablesToCollect.Remove(collectable); // remove it from the queue
}
private void MoveToCollectable() // start collecting
{
isCollecting = true;
if (CollectablesInRange())
{
Collectable collectable = collectablesToCollect.First(); just look for one gem
Vector3 defaultPosition = transform.position;
transform.position = Vector3.MoveTowards(transform.position, collectable.GetCollectablePosition(), movementSpeed * Time.deltaTime); // move to the gem
collectable.StartMovingToPlayer(); // call the gems movement
transform.position = Vector3.MoveTowards(transform.position, defaultPosition, movementSpeed * Time.deltaTime); // move back to the player
collectablesToCollect.Remove(collectable); // remove it from the queue
isCollecting = false;
if (CollectablesInRange()) // collect again, when the list is not empty
MoveToCollectable();
}
}
private bool CollectablesInRange()
{
return collectablesToCollect.Count > 0; // are there any gems in range?
}
}
Upvotes: 1
Views: 574
Reputation: 4037
Here's what I would do:
You have your code on the Gem (Collectable
) and your code on your Collecting-AI (PlayerFollower
).
PlayerFollower
needs a Trigger
that represents its search-radius and it needs a collider that represents its physical position. Also put the AI on its own physical layer. Your collectables require the same setup, but I would advise you to set the Trigger
on the same layer as the AI, while the collision should be on he same as the world (so you can touch it, but the AI passes through).
Whenever an object enters the Trigger
of PlayerFollower
that has a component of type Collectable
, you store it in a List<Collectable>
. Whenever an object exits the Trigger
of PlayerFollower
that has a component of type Collectable
, you remove it from the list. That's how you track what can be collected and what not.
Now when an object enters the Trigger
and gets added, you check if you have a current target to move to and if not, you set the added object as target.
When your AI enters the trigger of another object, this object gets its OnTriggerEnter
fired and recognizes the AI. It calls a method on the AI that states that it is now collected and probably sets a boolean that can be read from other objects to true
, stating that it is now collected and not part of the world anymore.
This method removes the object from the list and searches for the next target (in the list). When nothing can be found, it just goes back to the player and waits for the next object to collect.
Possible (not tested) implementation for the AI:
public class PlayerFollower : AI
{
List<Collectable> possibleTargets = new List<Collectable>();
Collectable target;
void OnTriggerEnter(Collider other)
{
Collectable collectable = other.GetComponent<Collectable>();
if (collectable != null && !collectable.Collected)
{
possibleTargets.Add(collectable);
SetNextTarget();
}
}
public void Collect(Collectable collectable)
{
possibleTargets.Remove(collectable);
SetNextTarget();
}
void SetNextTarget()
{
target = null;
for(int i = 0; i < possibleTargets.Count; i++)
if(!possibleTargets[i].Collected)
{
target = possibleTargets[i];
return;
}
}
}
Possible (not tested) implementation for the Gem:
public class Collectable : MonoBehaviour
{
private bool collected;
public bool Collected { get { return collected; } }
void OnTriggerEnter(Collider other)
{
PlayerFollower ai = other.GetComponent<PlayerFollower>();
if (ai != null)
{
collected = true;
ai.Collect(this);
}
}
}
Upvotes: 2