Skip to content

Instantly share code, notes, and snippets.

@SorraTheOrc
Last active April 3, 2023 17:11
Show Gist options
  • Save SorraTheOrc/8706e8cfa122d57e509272bf163d6125 to your computer and use it in GitHub Desktop.
Save SorraTheOrc/8706e8cfa122d57e509272bf163d6125 to your computer and use it in GitHub Desktop.
Extension to CiDy that will allow you to create a NavMesh to guide pedestrians around the city. Any NavMesh driven character can be used. I provide some basic open source pedestrian code (see https://www.patreon.com/posts/64257587 and https://youtu.be/nzzS40XHWA8) but it's not required. If you don't use my Character controller code you will need…
/*
Extension to CiDy that will allow you to create a NavMesh to guide pedestrians around the city.
Any NavMesh driven character can be used. I provide some basic open source pedestrian code
(see https://www.patreon.com/posts/64257587 and https://youtu.be/nzzS40XHWA8) but it's not
required. If you don't use my Character controller code you will need to make a couple of
small changes to the scripts (see comments) and build your own Custom Editor base on my code
below.
Add CiDyPedestrians to a convenient object in your scene, setup a couple of parameters and
click the "Build NavMesh" button.
*/
#if CiDy
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using WizardsCode.BackgroundAI; // Remove if not using my character controller code
using CiDy;
namespace WizardsCode.CiDYExtension
{
/// <summary>
/// Extends the Wizards Code Spawner with features to automatically configure the NavMesh Areas in a
/// CiDy generated city.
/// </summary>
public class CiDyPedestrians : Spawner // If not using my character controller code use MonoBehaviour rather than Spawner
{
[SerializeField, Tooltip("The CiDy graph object that manages the generation of the city.")]
CiDyGraph cidyGraph;
[SerializeField, Tooltip("The name of the navmesh area to use for roads and junctions.")]
public string roadAreaName = "Road";
[SerializeField, Tooltip("The name of the navmesh area to use for road crossings.")]
public string crossingAreaName = "Crossing";
[SerializeField, Tooltip("The name of the navmesh area to use for pavement/sidewalk.")]
public string pavementAreaName = "Pavement";
}
}
#endif
#if CiDy
using CiDy;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.AI;
using WizardsCode.Character;
namespace WizardsCode.CiDYExtension
{
[CustomEditor(typeof(CiDyPedestrians), true)]
public class CiDyPedestriansEditor : SpawnerEditor // If not using my character controller code use CustomEditor rather than SpawnerEditor, but you are on your own implementing that editor ;-)
{
private CiDyGraph graph;
private CiDyPedestrians peds;
protected override void OnEnable()
{
base.OnEnable();
SerializedProperty serializedGraph = serializedObject.FindProperty("cidyGraph");
graph = (CiDyGraph)serializedGraph.objectReferenceValue;
peds = (CiDyPedestrians)target;
}
public bool IsNavMeshSetup
{
get
{
bool isValid = true;
isValid &= NavMesh.GetAreaFromName(peds.roadAreaName) >= 0;
isValid &= NavMesh.GetAreaFromName(peds.crossingAreaName) >= 0;
isValid &= NavMesh.GetAreaFromName(peds.pavementAreaName) >= 0;
return isValid;
}
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
if (!IsNavMeshSetup)
{
EditorGUILayout.LabelField("Please add the three NavMesh Areas (" + peds.roadAreaName + ", " + peds.crossingAreaName + ", " + peds.pavementAreaName + ") as defined above.");
}
else
{
if (graph != null)
{
if (GUILayout.Button("Configure CiDy"))
{
// NOTE this assumes that you have configured NavMesh Areas for Road, Pedestrian Crossing and Pavement
// Road should have a higher cost than Pedestrian crossing, with Pavement having the lowest
// If you don't know how to do this see https://youtu.be/nzzS40XHWA8
//TODO Setup necessary NavMeshAreas if not already present
//as can be seen here (CiDY calls it a Sidewalk, I call it a Pavement)
ConfigureRoads();
ConfigureIntersections();
ConfigurePavements();
UnityEditor.AI.NavMeshBuilder.BuildNavMeshAsync();
}
}
}
serializedObject.ApplyModifiedProperties();
}
private void ConfigurePavements()
{
List<CiDyCell> cells = graph.cells;
for (int i = 0; i < cells.Count; i++)
{
//TODO is there a more robust way of getting the sidewalks?
Transform sidewalk = cells[i].transform.Find("SideWalk");
if (sidewalk != null)
{
SetNavMeshArea(sidewalk.gameObject, peds.pavementAreaName);
}
}
}
private void ConfigureIntersections()
{
List<CiDyEdge> edges = graph.graphEdges;
for (int i = 0; i < edges.Count; i++)
{
SetNavMeshArea(edges[i].v1.intersection, peds.roadAreaName);
SetNavMeshArea(edges[i].v2.intersection, peds.roadAreaName);
}
}
private void ConfigureRoads()
{
List<GameObject> roads = graph.roads;
for (int i = 0; i < roads.Count; i++)
{
SetNavMeshArea(roads[i], peds.roadAreaName);
// Configure crossings
List<Transform> decals = roads[i].GetComponent<CiDyRoad>().decals;
for (int idx = 0; idx < decals.Count; idx++)
{
SetNavMeshArea(decals[idx].gameObject, peds.crossingAreaName);
}
}
}
/// <summary>
/// Set the navmesh area on all MeshRenderer children of an object.
/// Also set isStatic to true.
/// </summary>
/// <param name="obj"></param>
/// <param name="nameOfNavMeshArea"></param>
private static void SetNavMeshArea(GameObject obj, string nameOfNavMeshArea)
{
MeshRenderer[] renderers = obj.GetComponentsInChildren<MeshRenderer>();
for (int idx = 0; idx < renderers.Length; idx++)
{
GameObjectUtility.SetNavMeshArea(renderers[idx].gameObject, NavMesh.GetAreaFromName(nameOfNavMeshArea));
renderers[idx].gameObject.isStatic = true;
}
}
}
}
#endif
@Maximus58
Copy link

type of namespace utility not defined, character not defined and many other error like that

@SorraTheOrc
Copy link
Author

SorraTheOrc commented Feb 11, 2022

It sounds like you are not using my character controller code as described in the video linked in the description. That's fine, but it will require a couple of changes to the scripts.

I'm not on a machine at which I can test this, but looking at the code I see no reason for having namespace WizardsCode.Utility (line 5 of CiDyPedestrians.cs).

It looks like it is an unused namespace from an earlier version. Can you try removing that line and let me know if it fixes it, I'll remove from the Gist if it does.

@SorraTheOrc
Copy link
Author

SorraTheOrc commented Feb 11, 2022

Ditto for the WizardsCode.Character using clause in both scripts. As for "many others" without knowing what they are I can't help.

If you do remove the WizardsCode stuff, be sure to take note of the comment at the top of the second script.

@SorraTheOrc
Copy link
Author

I've updated the comments to make this clearer but have NOT tested, so please let me know if it works for you.

@superlele88
Copy link

Guys to make CidyPedestrian.cs compile with last version of WizardCode Character Controller:

  • comment this line: using WizardsCode.Utility (it is no more necessary)
  • add this line: using WizardsCode.BackgroundAI (because Spawner is now on this path)

@SorraTheOrc
Copy link
Author

Thank you @superlele88 I've updated the Gist

@chris52580
Copy link

So, I think I am setting up the editor file wrong, but figure I would ask for assistance in case it is something with the code. I have it copied to Assets/Scripts/Editor/CiDY, with the file name CiDyPedestriansEditor. I am seeing 9 errors. Any suggests?
Also, not sure why, but the Editor folder isn't showing up in VS, but it shows in Unity.

errorPedEditor

@superlele88
Copy link

@chris52580 I strongly suggest you to inherit from SpawnerEditor. CustomEditor is a type you have to define somewhere in your code and your class CustomEditor must inheirt from UnityEditor otherwise you can't override all the methods the compiler underline as error.

@chris52580
Copy link

@superlele88 Apparently I don't have the needed packages to do that and I am not sure what I have skipped. Where do I get the character controller needed for it? I was following the Youtube video and thought I had followed all the steps.

@SorraTheOrc
Copy link
Author

SorraTheOrc commented Jul 12, 2022

@chris52580 Yeah Superlelee88 is correct. You need to inherit from SpawnerEditor. I know the comment says you can do it without, but clearly you are new to Unity Editor coding. All those errors are "obvious" once you have a little experience. To help get you going you can use my Spawner Editor. Don't worry that code is open source too. See https://www.patreon.com/posts/64257587 (and again, don't worry about the Patreon link, it's not behind a paywall).

Once you have it up and running you can use that code to start your learning journey and figure out how to built your own CustomEditor that doesn't use my controller, if that's what you want.

@chris52580
Copy link

chris52580 commented Jul 12, 2022

@SorraTheOrc I have tried setting it to SpawnerEditor, but I receive the the error that it could not be found. I figure I am missing a step. I have went through the patreon link and am just stuck for that file. I added https://github.com/TheWizardsCode/Character-Unity-Package.git and it show it in the package manager. I figure I am also missing a step or code cause I do receive an error on cidypedestrians if I leave it set to Spawner for the class. I had set that to Monobehavior and from a compile stand point, it seemed fine. I am guessing it would have failed when trying to use it lol.

I assume the folders are created at the root Asset folder, Assets/Scripts/Editor/CiDY and Assets/Scripts/Runtime/CiDY, with the respective filenames added there.

I have also posted this question in Discord, under Wardancer, as to try and not pollute this Git any further as I doubt my issue is code related.

@SorraTheOrc
Copy link
Author

@chris52580 I assume you are the same person asking for help on my Discord. If not then hop over to http://bit.ly/WizardsCodeDiscord and we'll get you going.

For those coming in the future....

We've identified some issue with the currently documented approach (at the time of this message). These seem to be caused in newer version of Unity. By the time you read this we will hopefully have resolved it and updated the instructions. If not, checking out the source from GitHub rather than importing using the package manager works fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment