创建C#脚本,包括如何手动控制智能体,智能体的观测空间、动作空间,强化学习的奖励函数等。
注:本文所有代码均来自于官方入门教程,仅作学习使用。
一、FlowerArea.cs
双击FlowerArea.cs脚本文件,在代码编辑器中打开脚本文件。清除掉FlowerArea类里原本的内容。
1.属性
在定义之前,首先应明确,在该任务场景中,花以一簇一簇的形式(FlowerCluster)随机分布在智能体可活动区域内,每簇中包含多株花(FlowerPlant),每株又包含多个FlowerBud,每个FlowerBud包含一个Flower(与上节中定义的Flower.cs绑定),每个Flower中包含一个花朵碰撞体和花蜜碰撞体和一些花蕊。如图所示。蜂鸟的喙与花蜜碰撞体发生碰撞时视为在采蜜。
该类代表蜂鸟的活动范围,有三个属性:区域的半径;包含区域内所有FlowerPlant的列表;包含区域内所有FlowerNectarCollider及其所属的Flower的字典;包含区域内所有Flower的列表。
// The diameter of the area where the agent and flowers can be
// used for observing relative distance from agent to flower
public const float AreaDiameter = 20f;
// The list of all flower plants in this flower area (flower plants have multiple flowers)
private List<GameObject> flowerPlants;
// A lookup dictionary for looking up a flower from a nectar collider
private Dictionary<Collider, Flower> nectarFlowerDictionary;
/// <summary>
/// The list of all flowers in the flower area
/// </summary>
public List<Flower> Flowers { get; private set; }
2.方法
下面的方法实现重置智能体活动范围内的所有花。
/// <summary>
/// Reset the flowers and flower plants
/// </summary>
public void ResetFlowers()
{
// Rotate each flower plant around the Y axis and subtly around X and Z
foreach (GameObject flowerPlant in flowerPlants)
{
float xRotation = UnityEngine.Random.Range(-5f, 5f);
float yRotation = UnityEngine.Random.Range(-180f, 180f);
float zRotation = UnityEngine.Random.Range(-5f, 5f);
flowerPlant.transform.localRotation = Quaternion.Euler(xRotation, yRotation, zRotation);
}
// Reset each flower
foreach (Flower flower in Flowers)
{
flower.ResetFlower();
}
}
下面的方法实现查找FlowerNectarCollider所属的Flower。
/// <summary>
/// Gets the <see cref="Flower"/> that a nectar collider belongs to
/// </summary>
/// <param name="collider">The nectar collider</param>
/// <returns>The matching flower</returns>
public Flower GetFlowerFromNectar(Collider collider)
{
return nectarFlowerDictionary[collider];
}
Awake 方法是 MonoBehaviour 类提供的一个回调方法,它在脚本实例被加载时执行,用于完成一些初始化的操作。
/// <summary>
/// Called when the area wakes up
/// </summary>
private void Awake()
{
// Initialize variables
flowerPlants = new List<GameObject>();
nectarFlowerDictionary = new Dictionary<Collider, Flower>();
Flowers = new List<Flower>();
// Find all flowers that are children of this GameObject/Transform
FindChildFlowers(transform);
}
下面的方法被上面的Awake方法调用,递归查找所有的FlowerPlant和Flower。同时在Assets/Hummingbird/Prefabs目录下双击FlowerPlant,在属性检查器里修改其标签为flower_plant
。
/// <summary>
/// Recursively finds all flowers and flower plants that are children of a parent transform
/// </summary>
/// <param name="parent">The parent of the children to check</param>
private void FindChildFlowers(Transform parent)
{
for (int i = 0; i < parent.childCount; i++)
{
Transform child = parent.GetChild(i);
if (child.CompareTag("flower_plant"))
{
// Found a flower plant, add it to the flowerPlants list
flowerPlants.Add(child.gameObject);
// Look for flowers within the flower plant
FindChildFlowers(child);
}
else
{
// Not a flower plant, look for a Flower component
Flower flower = child.GetComponent<Flower>();
if (flower != null)
{
// Found a flower, add it to the Flowers list
Flowers.Add(flower);
// Add the nectar collider to the lookup dictionary
nectarFlowerDictionary.Add(flower.nectarCollider, flower);
// Note: there are no flowers that are children of other flowers
}
else
{
// Flower component not found, so check children
FindChildFlowers(child);
}
}
}
}
3.绑定脚本
在Unity编辑器中将该脚本绑定到FloatingIsland
上。