由于官方教程是全英版,本文为根据个人理解做的中文版
(能力有限,有问题的地方还望指出,同时欢迎志同道合的朋友参与讨论,谢谢!)
官方教程:https://learn.unity.com/course/ml-agents-hummingbirds?uv=2019.3
系列文章:unity learn—— ML_Agent:Hummingbirds中文教程 自译(一 ——配置Unity)
unity learn—— ML_Agent:Hummingbirds中文教程 自译(二——代码Flowers.cs、FlowerArea.cs)
unity learn—— ML_Agent:Hummingbirds中文教程 自译(三——代码HummingbirdAgent.cs)
unity learn—— ML_Agent:Hummingbirds中文教程 自译(四——Ray Perception Sensor)
unity learn—— ML_Agent:Hummingbirds中文教程 自译(五 ——安装Anaconda)
unity learn—— ML_Agent:Hummingbirds中文教程 自译(六——准备训练)
unity learn—— ML_Agent:Hummingbirds中文教程 自译(七——测试模型)
本次主要写Flower.cs和FlowerArea.cs两个脚本和相关配置
-
代码
Flower.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 控制一朵花 /// </summary> public class Flower : MonoBehaviour { [Tooltip("被采粉前的花色")] [SerializeField] private Color fullFlowerColor = new Color(1f, 0, .3f); [Tooltip("被采粉后的花色")] [SerializeField] private Color emptyFlowerColor = new Color(.5f, 0, 1f); [HideInInspector] public Collider nectarCollider; //花蕊的碰撞体 private Collider flowerCollider; //花瓣的碰撞体 private Material flowerMaterial; //花的材质 public Vector3 FlowerUpVector //从花蕊指向外面的方向 { get { return nectarCollider.transform.up; } } public Vector3 FlowerCenterPosition //花蕊的中心位置 { get { return nectarCollider.transform.position; } } public float NectarAmount { get; private set; } //目前余下的花蕊个数 public bool HasNectar { get { return NectarAmount > 0; } } /// <summary> /// 尝试采粉 /// </summary> /// <param name="amount">尝试带粉数量</param> /// <returns>实际成功采粉数量</returns> public float Feed(float amount) { //保证采集数量不大于现有花粉数 float nectarTaken = Mathf.Clamp(amount, 0, NectarAmount); //更新花粉数 NectarAmount -= amount; if (NectarAmount <= 0) { NectarAmount = 0; //没有花粉了 //屏蔽花瓣和花蕊的碰撞体 flowerCollider.gameObject.SetActive(false); nectarCollider.gameObject.SetActive(false); //改变花瓣颜色 flowerMaterial.SetColor("_BaseColor", emptyFlowerColor); } //返回实际被采集的花粉数 return nectarTaken; } public void ResetFlower() { //花粉数量 NectarAmount = 1f; //花瓣和花蕊的碰撞体 flowerCollider.gameObject.SetActive(true); nectarCollider.gameObject.SetActive(true); //花瓣颜色 flowerMaterial.SetColor("_BaseColor", fullFlowerColor); } private void Awake() { //获取meshRenderer组件,并找到其material MeshRenderer meshRenderer = GetComponent<MeshRenderer>(); flowerMaterial = meshRenderer.material; //获取花瓣和花蕊的碰撞体(通过命名查找) flowerCollider = transform.Find("FlowerCollider").GetComponent<Collider>(); nectarCollider = transform.Find("FlowerNectarCollider").GetComponent<Collider>(); } }
FlowerArea.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 管理一片区域的几 簇花 /// </summary> public class FlowerArea: MonoBehaviour { //区域半径 (用于训练的小岛区域半径最大为20) public const float AreaDiameter = 20f; //区域内的花 private List<GameObject> flowerPlants; //花蕊碰撞体与花的字典 private Dictionary<Collider, Flower> nectarFlowerDic; /// <summary> /// 区域内所有花的列表 /// </summary> public List<Flower> Flowers { get; private set; } private void Awake() { //给变量赋空间 flowerPlants = new List<GameObject>(); nectarFlowerDic = new Dictionary<Collider, Flower>(); Flowers = new List<Flower>(); } private void Start() { //找到子物体中的所有花 FindChildFloweers(transform); } /// <summary> /// 重置每朵花和花簇 /// </summary> public void ResetFlower() { //随机摆放每簇花 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); } //重置每朵花 foreach(Flower flower in Flowers) { flower.ResetFlower(); } } /// <summary> /// 通过字典找到花蕊碰撞体属于哪朵花 /// </summary> /// <param name="collider">花蕊碰撞体</param> /// <returns>对应的花</returns> public Flower GetFlowerFromNectar(Collider collider) //写成函数是为了当字典里找不到时丢出错误 { return nectarFlowerDic[collider]; } /// <summary> /// 递归查找父物体下的所有是花的子物体 /// </summary> /// <param name="Parent">父物体的Transform</param> private void FindChildFloweers(Transform Parent) { //遍历所有子物体(当迭代到没有子物体时,即Parent.childCount==0,该for循环会被跳过) for (int i = 0; i < Parent.childCount; i++) { Transform child = Parent.GetChild(i); if (child.CompareTag("flower_plant")) //如果tag是flower_plant { //找到的花簇加到花簇列表里 flowerPlants.Add(child.gameObject); //找该簇花里的每朵花 FindChildFloweers(child); } else { //不是花簇,那就找花(也可能是别的) Flower flower = child.GetComponent<Flower>(); if (flower != null) //不为空就证明是花 { //找到了花,加入花的列表和字典 Flowers.Add(flower); nectarFlowerDic.Add(flower.nectarCollider, flower); } else //为空可能是花簇,也可能是别的子物体有花的父物体 { //所以接着迭代 FindChildFloweers(child); } } } } }
-
完成了以上两代码的编写后,回到unity,找到FlowerPlant这一预制体(Prefab),然后添加tag
点击加号输入“flower_plant”
最后返回Inspector界面,为FlowerPlant预制体设置tag(之所以设置预制体是因为场景中的花簇太多了,一个个设置很麻烦……)
将两个脚本分别挂载在FlowerBud的Flower子物体和FloatingIsland
上