Support Forum

Add Penalty To Nodes within Mesh Collider

I’m working on a project where I’m creating “roads” using a spline tool. When I make a spline, I want to alter the penalty of the gridgraph nodes along the spline to make them the easier pathway.

I’ve got the grid set up and working correctly and have verified that the penalty works when using a GraphUpdateScene component with arbitrarily placed points (noted just to say that everything up to this point is working great as intended).

The following are my two attempted solutions:

  1. I converted the spline into a list of points and inserted those into a GraphUpdateScene. I can’t make it convex as that doesn’t follow the spline and even when adding those points back in reverse order to create a “whole” shape it never overlaps with the center of a node and so doesn’t mark any nodes.
  2. Convert the spline into a 3D mesh collider. Doing this I can set the collider’s layer and in the grid graph settings I can make it detect those overlapping nodes as not walkable. However, there doesn’t seem to be a way to do that and make them add a penalty or tag instead.

Option 2 is really the easiest solution for me but I cannot figure out how to make the nodes set their penalty based on a collider, only their walkability. If there is a way to do this it would be super helpful. Thank you!

Hi

In the beta version you can add a grid graph rule to set the node tag based on the layer of the ground object. Then on the seeker you can make that tag have a penalty.

@aron_granberg Thank you for the reply! That looks like a really powerful feature. I’m on a team where I may not currently be able to get us on to the beta to take advantage of it (hopefully eventually!). I did come up with a solution to my problem late yesterday though. I’ll post it here once I’ve had a chance to clean up the code a bit. The tl;dr is I wrote a custom graph update script that takes advantage of the existing graph collision settings to check for any collisions with the road mesh and then adjusts that node’s penalty. Might actually go back and just extend the existing GraphUpdateScene so I can take advantage of the collider bounds logic for determining the scope of the update.

1 Like

Can you post your solution? I am trying to make an agent prefer walking on roads also.

@huenemeca Yes! I’ve been working on cleaning up the code. I’ll try and get this posted today.

@huenemeca Ok, here’s some code. It’s not perfect (and won’t work in its entirety by just copy/paste) but hopefully it can get you started in the right direction. Please look at the GraphUpdateScene class in the A* project files for more ways on how you can improve this. Note that it will take pretty big penalty numbers to get the seeker to use the paths. For context, this is designed to test each grid node for collisions with certain layers and alter that node’s Penalty if a collision is found.

public class MyGraphUpdater : GraphUpdateObject
{
    public MyGraphUpdater(int penaltyDelta, LayerMask layerMask)
    {
        _penaltyDelta = penaltyDelta;
        _layerMask = layerMask;
    }

    private int _penaltyDelta;
    private LayerMask _layerMask;

    public override void Apply(GraphNode node)
    {
        base.Apply(node);

        GridGraph grid = (GridGraph)node.Graph;
        LayerMask originalLayerMask = grid.collision.mask;
		// Changes grid collision to just the desired layers while running this op
        grid.collision.mask = _layerMask;
        if (grid.collision.Check((Vector3)node.position))
        {
		    // Fancy convert int to uint
			// As always be careful here because penalty mods will flip from 0 to max if you go below zero with uint.
            uint absPenaltyDelta = (uint)Mathf.Abs(_penaltyDelta);
            if (_penaltyDelta < 0)
            {
                node.Penalty -= absPenaltyDelta;
            }
            else
            {
                node.Penalty += absPenaltyDelta;
            }
        }

        // Changes grid collision back to normal
        grid.collision.mask = originalLayerMask;
    }
}
// put this one in your scene
public class MyGraphUpdaterForTheScene : GraphModifier
{
    [SerializeField] private int _penaltyDelta = 0;
    [SerializeField] private LayerMask _detectionLayers = default;

	//...

    public void Apply()
    {
        if (AstarPath.active == null)
        {
            Debug.LogError("There is no AstarPath object in the scene", this);
            return;
        }

        MyGraphUpdater mgu = new MyGraphUpdater(_penaltyDelta, _detectionLayers);
        mgu.bounds = GetBounds();

        AstarPath.active.UpdateGraphs(mgu);
    }
	
	// _penaltyDelta is how much the node penalty will be changed
	// _detectionLayers are those layers which collisions will be checked for
	// GetBounds is a function that gives you a Bounds object that defines the area
	// in which you want to update the graph, you can get that however you want.
	
	//...
}

// Reference GraphUpdateScene for more ideas on how to modify this, especially useful are the bounds logic and the gizmos.
// You can also change nodes to specific tags instead of direct penalties.

Awesome, thanks I will take a look.