[FreedomAI] Week 3 - InfluenceMap

Influence diagrams or influence diagrams are considered to be a good way for AI to obtain information from the outside world when making decisions. These external information may include friendly information, enemy information, terrain and prop information. If we let each AI use GameObject.Find....Withtag, it is conceivable that the complexity is N AI*M information providers.

But if we change our way of thinking, these item information is fixed in each frame (even some games are fixed at the beginning), why don't we set a picture to preprocess this information? Like the baking we often do in graphics?

So the influence diagram is such an algorithm.

First of all, we will grid this map, let's think of this as a 2D or 2.5D situation. Then each information source will calculate a value (quantify itself), and then have a radiation radius, which affects the surrounding grid, and the values ​​on the grid are continuously superimposed.

Of course, this information does not make the AI ​​happen immediately. To be precise, it only makes sense when we collect this information and then use it further for pathfinding and decision-making logic.

Code first:

public Vector2 center;
		public float width;
		public float height;
		public int wtileCount;
		public int htileCount;
		public float[][][] IMData;
		public int allCount = 0;
		public Dictionary<string,int> mDictionary = new Dictionary<string, int>();
		private static InfluenceMap mInfluenceMapsingleton;
		public Dictionary<int,bool> mDicStatic = new Dictionary<int, bool>();
		public bool Debugging;
		private Vector2[] NineGrid = new Vector2[]{new Vector2(0,1),new Vector2(0,-1),
			new Vector2(1,1),new Vector2(1,0),new Vector2(1,-1),
			new Vector2(-1,1),new Vector2(-1,0),new Vector2(-1,-1)};

		public Vector2 deltaTileSize
		{
			get
			{
				return new Vector2 (width/wtileCount,height/htileCount);
			}
		}

		public static InfluenceMap getInstance()
		{
			if (mInfluenceMapsingleton == null)
				mInfluenceMapsingleton = new InfluenceMap ();
			return mInfluenceMapsingleton;
		}

		public override void Init ()
		{
			base.Init ();
			IMData = new float[11][][];
			for (int i = 0; i < 11; i++)
			{
				IMData[i] = new float[wtileCount][];
				for (int j = 0; j < wtileCount; j++)
				{
					IMData[i][j] = new float[htileCount];
					for (int k = 0; k < htileCount; k++)
					{
						IMData [i] [j] [k] = 0.0f;
					}
				}
			}
			AddConverage (0.0f,"Collision",false);
		}

		public void AddConverage(float defaultValue,string name,bool isStatic)
		{
			if (allCount > 10)
				return;
			mDictionary.Add (name,allCount);
			mDicStatic.Add (allCount,isStatic);
			allCount++;
		}

		public Vector2 getTilefromPosition(Vector2 pcenter)
		{
			Vector2 pfir = center - new Vector2 (width/2,height/2);
			Vector2 delta = new Vector2 (width/wtileCount,height/htileCount);
			Vector2 size = new Vector2 ((pcenter-pfir).x/delta.x,(pcenter-pfir).y/delta.y);
			return size;
		}

This is part of the definition in InfluenceMap. You can see that the most important attribute is a three-dimensional array such as IMData. Why set a three-dimensional array, because we can set multiple layers, such as 0 layer to store obstacle information, 1 layer to store obstacle information Friendly information, 2 layer setting danger level information, etc.

public class InfluenceMapTrigger:UComponent
	{
		public IMComputer mIMComputer;
		public float maxInfluence;
		public string mWhere;
		public GameObject mGameObject;

		public override void Init ()
		{
			base.Init ();
			string tWhere = mWhere;
			InfluenceMap tInfluenceMap = InfluenceMap.getInstance ();
			int index = tInfluenceMap.mDictionary [tWhere];
			bool isStatic = tInfluenceMap.mDicStatic [index];
			if (isStatic)
			{
				Vector3 position = mGameObject.transform.position;
				Vector2 center = new Vector2 (position.x,position.z);
				Vector2 tile = InfluenceMap.getInstance ().getTilefromPosition (center);
				float IMdata = mIMComputer (mUEntity);
				for (int i = (int)(tile.x - maxInfluence / 2); i <= (int)(tile.x + maxInfluence / 2); i++)
				{
					for(int j = (int)(tile.y - maxInfluence / 2); j <= (int)(tile.y + maxInfluence / 2); j++)
					{
						i = Mathf.Clamp (i,0,InfluenceMap.getInstance().wtileCount-1);
						j = Mathf.Clamp (i,0,InfluenceMap.getInstance().htileCount-1);
						Vector2 v = new Vector2 ((i-tile.x)/maxInfluence,(j-tile.y)/maxInfluence);
						InfluenceMap.getInstance ().IMData [index] [i] [j] += IMdata * Gauss (v);
					}
				}
			}
		}

		private float Gauss(Vector2 v)
		{
			return Mathf.Exp(-(v.x*v.x/2.0f+v.y*v.y/2.0f));		
		}

	};

Here is the component of the information source. You can see that if the influence layer filled in by the information source is considered to be Static, it can be processed at the beginning of the game, and no calculation will be done for each frame after that.

And we use the diffusion method of Gaussian distribution.

public class InfluenceMapUpdateSystem:USystem
	{
		
		public override void Init ()
		{
			base.Init ();
			this.AddRequestComponent (typeof(InfluenceMapTrigger));
			power = 90;
		}	

		public override void Update (UEntity uEntity)
		{
			base.Update (uEntity);
			string tWhere = uEntity.GetComponent<InfluenceMapTrigger>().mWhere;
			InfluenceMap tInfluenceMap = InfluenceMap.getInstance ();
			int index = tInfluenceMap.mDictionary [tWhere];
			bool isStatic = tInfluenceMap.mDicStatic [index];
			if (!isStatic)
			{
				Vector3 position = uEntity.GetComponent<InfluenceMapTrigger> ().mGameObject.transform.position;
				Vector2 center = new Vector2 (position.x,position.z);
				Vector2 tile = InfluenceMap.getInstance ().getTilefromPosition (center);
				float IMdata = uEntity.GetComponent<InfluenceMapTrigger>().mIMComputer(uEntity);
				for (int i = (int)(tile.x - uEntity.GetComponent<InfluenceMapTrigger> ().maxInfluence / 2); i <= (int)(tile.x + uEntity.GetComponent<InfluenceMapTrigger> ().maxInfluence / 2 i++)
				{
					for(int j = (int)(tile.y - uEntity.GetComponent<InfluenceMapTrigger> ().maxInfluence / 2); j <= (int)(tile.y + uEntity.GetComponent<InfluenceMapTrigger> ().maxInfluence / 2 j++)
					{
						i = Mathf.Clamp (i,0,InfluenceMap.getInstance().wtileCount-1);
						j = Mathf.Clamp (i,0,InfluenceMap.getInstance().htileCount-1);
						Vector2 v = new Vector2 ((i-tile.x)/uEntity.GetComponent<InfluenceMapTrigger>().maxInfluence,(j-tile.y)/uEntity.GetComponent<InfluenceMapTrigger>().maxInfluence);
						InfluenceMap.getInstance ().IMData [index] [i] [j] += IMdata * Gauss (v);
					}
				}
			}
		}

		private float Gauss(Vector2 v)
		{
			return Mathf.Exp(-(v.x*v.x/2.0f+v.y*v.y/2.0f));		
		}

	};

	public class InfluenceMapFlushSystem:USystem
	{
		public override void Init ()
		{
			base.Init ();
			this.AddRequestComponent (typeof(InfluenceMap));
			power = 89;
		}

		public override void Update (UEntity uEntity)
		{
			base.Update (uEntity);
			for (int i = 0; i < uEntity.GetComponent<InfluenceMap> ().allCount; i++)
			{
				if (!uEntity.GetComponent<InfluenceMap>().mDicStatic[i])
				{
					for (int j = 0; j < uEntity.GetComponent<InfluenceMap> ().wtileCount; j++)
					{
						for (int k = 0; k < uEntity.GetComponent<InfluenceMap> ().htileCount; k++)
						{
							uEntity.GetComponent<InfluenceMap>().IMData[i][j][k] = 0.0f;
						}
					}
				}
			}
		}

	};


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324610239&siteId=291194637