Hi, I’m making 2.5d game where player and enemies are sprite and can move in 3d space .
(also I’m new to a* ). I’m using grid graph and I would like to set destination to closest grid which is on the side the player depend on the main camera’s direction instead of on any direction of the target position. You can have a look at my pictures for the reference. Is it possible and what is the first solution you came up with?, Thank you!
Hi
I’m not quite sure what you are asking for. Would you mind clarifying a bit?
If I understand collect, If I set Seeker(script) End Point Snapping to “Node Center (Snap To Node)”
and set player as the target in AI Destination Setter(script)
AI will walk to center of the node which is the closest one to player’s position right?
Instead I want to set the destination to be any nodes on the blue area instead of nodes in red area, so the AI will be on the same z-index (next to the player) before strike the player
Yes
What you ask for is not quite possible with the default code. You could use a MultiTargetPath and specify the targets as one node on the left of the player, and one node on the right of the player.
Thanks! I’m having a look at the Class document and the example right now. I’ll come back to this forum, if I encounter any problems.
Hi, its been a while ,I hoped, I can still contact you through this forum.
this might not be very easy to read, but just in case.
public class ChaseBehavior : UnitBehavior{
//--------------------------------------------------------------------------------------------------------------
// INSPECTOR
//--------------------------------------------------------------------------------------------------------------
#if ODIN_INSPECTOR && UNITY_EDITOR
[TabGroup("Status")]
[ReadOnly]
#endif
public Actor LastTarget;
#if ODIN_INSPECTOR && UNITY_EDITOR
[TabGroup("Options")]
#endif
public Actor Target;
#if ODIN_INSPECTOR && UNITY_EDITOR
[TabGroup("Options")]
[MinValue(0)]
#endif
public float ChaseDistance;
//--------------------------------------------------------------------------------------------------------------
#region MEMBERS
private Seeker seeker;
private IAstarAI ai;
private Vector3 targetPos;
#endregion
//--------------------------------------------------------------------------------------------------------------
// SubState INTERFACE
//--------------------------------------------------------------------------------------------------------------
public override void OnStateValidation(){
base.OnStateValidation();
Behavior = EUnitBehavior.Chasing;
}
public override void OnStateEnter(){
base.OnStateEnter();
seeker = Owner.GetComponent<Seeker>();
ai = Owner.GetComponent<IAstarAI>();
ai.onSearchPath += OnSearchingPath;
if (!Owner.CanWalk && !Owner.CanSprint)
{ BackToDefaultBehavior($"{UnitOwner.UnitType}", "has entered ChaseBehavior but can't walk or sprint"); }
if (Target == null){
if (UnitOwner.Detecter != null){
if (UnitOwner.Detecter.ClosestTarget != null){
Target = UnitOwner.Detecter.ClosestTarget;
if (Target == null)
BackToDefaultBehavior($"{UnitOwner.UnitType}", "has entered ChaseBehavior but can't find any target");
}
else
{ BackToDefaultBehavior($"{UnitOwner.UnitType}", "has entered ChaseBehavior but detecter module can't find any target"); }
}
else
{ BackToDefaultBehavior($"{UnitOwner.UnitType}", "has no target and detecter module"); }
}
}
public override void OnStateUpdate(){
base.OnStateUpdate();
Target = UnitOwner.Detecter.ClosestTarget;
if (Target == null)
BackToDefaultBehavior($"{UnitOwner.UnitType}", "has lost target while chasing");
FindPathToTarget();
}
public override void OnStateExit(){
base.OnStateExit();
ai.onSearchPath -= OnSearchingPath;
if (Target != null){
LastTarget = Target;
Target = null;
}
}
//--------------------------------------------------------------------------------------------------------------
// VIRTUAL METHODS
//--------------------------------------------------------------------------------------------------------------
public void OnSearchingPath(){
ai.destination = targetPos;
}
public virtual void OnChasingPathComplete (Path path){
Debug.Log("[Callback] OnChasingPathComplete");
if (path.error) {
Debug.Log("Ouch, the path returned an error\nError: " + path.errorLog);
return;
}
if (path is not MultiTargetPath mp) {
Debug.LogError("The Path was no MultiTargetPath");
return;
}
// All paths
var paths = mp.vectorPaths;
for (var i = 0; i < paths.Length; i++) {
var p = paths[i];
if (p == null) {
Debug.Log("Path number "+i+" could not be found");
continue;
}
var color = AstarMath.IntToColor(i, 0.5F);
for (var j = 0; j < p.Count-1; j++)
Debug.DrawLine(p[j], p[j+1], color, 10);
}
}
//--------------------------------------------------------------------------------------------------------------
// METHODS
//--------------------------------------------------------------------------------------------------------------
public void SetTarget(Actor target)
=> Target = target;
private void FindPathToTarget(){
if (Target == null)
return;
var cameraBackwardDir = Vector3.ProjectOnPlane(CameraHandler.ActiveVirtualCamera.VirtualCameraGameObject.transform.forward, Vector3.up).normalized;
var sides = new Vector3[2];
var right = Vector3.Cross(Vector3.up, cameraBackwardDir);
var left = Vector3.Cross(cameraBackwardDir, Vector3.up);
sides[0] = Target.transform.position + right * ChaseDistance;
sides[1] = Target.transform.position + left * ChaseDistance;
targetPos = seeker.StartMultiTargetPath(transform.position, sides, true, OnChasingPathComplete).endPoint;
}
}
This is what I’ve done so far, I’ll chunk down some part of the code, so you don’t have to read it all.
ai.onSearchPath += OnSearchingPath; // WhenEnterState
ai.onSearchPath -= OnSearchingPath; // WhenExitState
public void OnSearchingPath(){
ai.destination = targetPos;
}
// Called in Update
private void FindPathToTarget(){
if (Target == null)
return;
var cameraBackwardDir = Vector3.ProjectOnPlane(CameraHandler.ActiveVirtualCamera.VirtualCameraGameObject.transform.forward, Vector3.up).normalized;
var sides = new Vector3[2];
var right = Vector3.Cross(Vector3.up, cameraBackwardDir);
var left = Vector3.Cross(cameraBackwardDir, Vector3.up);
sides[0] = Target.transform.position + right * ChaseDistance;
sides[1] = Target.transform.position + left * ChaseDistance;
targetPos = seeker.StartMultiTargetPath(transform.position, sides, true, OnChasingPathComplete).endPoint;
}
public virtual void OnChasingPathComplete (Path path){
Debug.Log("[Callback] OnChasingPathComplete");
if (path.error) {
Debug.Log("Ouch, the path returned an error\nError: " + path.errorLog);
return;
}
if (path is not MultiTargetPath mp) {
Debug.LogError("The Path was no MultiTargetPath");
return;
}
// All paths
var paths = mp.vectorPaths;
for (var i = 0; i < paths.Length; i++) {
var p = paths[i];
if (p == null) {
Debug.Log("Path number "+i+" could not be found");
continue;
}
var color = AstarMath.IntToColor(i, 0.5F);
for (var j = 0; j < p.Count-1; j++)
Debug.DrawLine(p[j], p[j+1], color, 10);
}
}
[Short Explain] This class is a state in my statemachine which Unit will chase down player till it reach player which are why I put
FindPathToTarget()
inside Update Function
[QUESTION#1] Is it bad to put
FindPathToTarget()
inside an update function?
My goal is to make it chase down player and change to another behavior on reached, So what I’m thinking is
[QUESTION#2] Is How I Call a function on reached Endpoint in
ai.onSearchPath
the right call?
The unit still don’t move along the path and there is a bit of problem
This is how it’s look like now : 2024-04-09 22-52-36.mp4 - Google Drive
Yes. You should at least wait until the previous path calculation is done using seeker.IsDone().
Maybe you want ai.reachedDestination
instead?
Wait could it be that my unit doesn’t walk to endpoint because it has rigidbody instead of charactercontroller?
I couldn’t upload my 8mb .gif for some reason, please take a look at the drive link instead, if you don’t mind.
Edit: I set Recalculate Path Automatically on AIPath and the problem in the video went away. Now my only problem is it still doesn’t move even I set the destination somehow.
To make the agents move, you shouldn’t use seeker.StartMultiTargetPath. That’s for when you are writing your own movement script. Instead use ai.SetPath(MultiTargetPath.Construct(...))
, to make the agent aware of the path. You will want to disable the agent’s own path recalculations for this.