?
public class 00PTest : MonoBehaviour
{
class Animal
{
public string name;
public virtual string shout()
//virtual代表虚方法可重写,此父类方法已经被子类重写,所以在此父类方法中返回什么没有意义
{
return this.name;
}
class Cat : Animal//继承 Animal类,父类里的方法属性等 子类可以用
{
public override string shout()//override重写父类方法
{
return this.name +",喵喵";
}
}
class Dog : Animal
{
public override string shout()
{
return this.name +",汪汪";
}
}
void Start()
{
Cat cat1 = new Cat();
cat1.name ="咪咪";
Debug.Log(cat1.shout());
Dog dog1 = new Dog();
dog1.name ="旺财";
Debug.Log(dog1.shout());
}
}
public class 00PTest : MonoBehaviour
{
abstract class Animal
//abstract代表抽象类,抽象类不一定非要有抽象方法,抽象类不能用,不能new
//当需要一个类不能实例化时,用到抽象类
{
public string name;
public abstract string shout();//抽象方法没有方法体
//定义为抽象方法,所在的类必须也为抽象类,此方法需被重写才有意义
//只要有一个抽象方法 所在类必须是抽象类
class Cat : Animal//继承 Animal类,父类里的方法属性等 子类可以用
{
public override string shout()//override重写父类方法
{
return this.name +",喵喵";
}
}
class Dog : Animal
{
public override string shout()
{
return this.name +",汪汪";
}
}
void Start()
{
Cat cat1 = new Cat();
cat1.name ="咪咪";
Debug.Log(cat1.shout());
Dog dog1 = new Dog();
dog1.name ="旺财";
Debug.Log(dog1.shout());
//多态:多种形态,声明变量时使用的是父类,new变量时使用的是子类
Animal a1=new Cat();
//如果定义时用的父类定义
//a1调用的方法也只能是父类有的方法调用不出来子类Cat的自己的方法(父类没有的方法),
//当我们要实现多个子类来回切换时(需要new不同的子类),需要用父类定义,
//防止调用了子类自己独特的方法,导致出错
Debug.Log(a1.shout);//喵喵
a1=new Dog();
Debug.Log(a1.shout);//汪汪
// IList l1=new ArrayList();
// l1.Add(11);
// l1.Add(22);
}
}
重写是将父类方法重写,重载是方法相同参数不同(virtual)?
pubulic class Rabbit:MonoBehaviour
{
//防止被而已更改并且也能在检查器中显示
[SerializaField]//序列化 暴露私有属性到inspector中,
private Color furColor;
public static Rabbit Instance{get;};//虽然是公有变量,但其他地方只能得到不能修改
//{get;set;}外部能修改能得到 {get;private set;}只有内部能修改 外部能得到不能修改
}
public float m_ProductionSpeed = 0.5f;
public float ProductionSpeed//一旦被修改就会触发set里的内容,无论在内部还是在外部,这时会进入死循环
{//此时需要再定义一个变量值m_ProductionSpeed,赋值0.5,外部获取时将m_ProductionSpeed返回,一旦内外设置ProductionSpeed变量时即触发set更改m_ProductionSpeed值
//如果依然用ProductionSpeed进行get中的返回和set中的改值,set中的赋值操作会一直触发进入死循环
get { return m_ProductionSpeed; }
set {
if (value < 0f)//value为要赋的值
{
Debug.Log("报错");
}
else
{
m_ProductionSpeed = value;
}
}
}
源码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
// Base class for all Unit. It will handle movement order given through the UserControl script.
// It require a NavMeshAgent to navigate the scene.
[RequireComponent(typeof(NavMeshAgent))]
public abstract class Unit : MonoBehaviour,
UIMainScene.IUIInfoContent
{
public float Speed = 3;
protected NavMeshAgent m_Agent;
protected Building m_Target;
protected void Awake()
{
m_Agent = GetComponent<NavMeshAgent>();
m_Agent.speed = Speed;
m_Agent.acceleration = 999;
m_Agent.angularSpeed = 999;
}
private void Start()
{
if (MainManager.Instance != null)
{
SetColor(MainManager.Instance.TeamColor);
}
}
void SetColor(Color c)
{
var colorHandler = GetComponentInChildren<ColorHandler>();
if (colorHandler != null)
{
colorHandler.SetColor(c);
}
}
private void Update()
{
if (m_Target != null)
{
float distance = Vector3.Distance(m_Target.transform.position, transform.position);
if (distance < 2.0f)
{
m_Agent.isStopped = true;
BuildingInRange();
}
}
}
public virtual void GoTo(Building target)
{
m_Target = target;
if (m_Target != null)
{
m_Agent.SetDestination(m_Target.transform.position);
m_Agent.isStopped = false;
}
}
public virtual void GoTo(Vector3 position)
{
//we don't have a target anymore if we order to go to a random point.
m_Target = null;
m_Agent.SetDestination(position);
m_Agent.isStopped = false;
}
/// <summary>
/// Override this function to implement what should happen when in range of its target.
/// Note that this is called every frame the current target is in range, not only the first time we get in range!
/// </summary>
protected abstract void BuildingInRange();//子类可以访问,其他不可
//Implementing the IUIInfoContent interface so the UI know it should display the UI when this is clicked on.
//Implementation of all the functions are empty as default, but they are set as virtual so subclass units can
//override them to offer their own data to it.
public virtual string GetName()
{
return "Unit";
}
public virtual string GetData()
{
return "";
}
public virtual void GetContent(ref List<Building.InventoryEntry> content)
{
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class MainManager : MonoBehaviour
{
//单例模式 设置为静态后整个程序Instance只有一个,实现传数据,防止从Main回到Menu的时候Menu会再加载一遍,在生成一个是不对的,因为需要来回传数据
public static MainManager Instance { get; private set; }//静态变量名字首字母大写,属性
public Color TeamColor;//字段
private void Awake()//Awake-》Start->Update
{
if (Instance != null)
{//gameObject省略了this.,this.gameObject
Destroy(this.gameObject);//销毁从Main回到Menu后在一次加载Menu生成的MainManager游戏对象
return;
}
Instance = this;//游戏启动,组件等都会加载,将当前的MainManager对象赋值给Instance
DontDestroyOnLoad(gameObject);//this和gameObject不是一个对象,gameObject场景中MainManager游戏对象,而this是MainManager类对象
//MainManager将被缓存不会被销毁,为了传给Main场景
LoadColor();//不点击Load按钮无所谓,因为直接自动调用了,如果没有此句则需要运行后再点击才能取出磁盘文件的内容
}
//private void Start()
//{
// if (MainManager.Instance != null)
// {
// SetColor(MainManager.Instance.TeamColor);
// }
//}
[System.Serializable]//SaveData可被序列化
class SaveData//未加public默认为private
{//如果不用这个内部类,直接在MainManager主类前面加[System.Serializable],
//运行后也会生成savefile.json文件,但选择了颜色但文件内的颜色值全是0,未赋值存储成功
//是因为如果是上述情况会需要新new MainManager(),和之前的MainManager(游戏运行时刚加载时编辑器帮new的)不是一个
//如果不new MainManager(),而是string json = JsonUtility.ToJson(this);将当前的MainManager类传进去,这种方法是可以将颜色值存储的,
//但如果MainManager中含有其他属性和对应的值也会一同存储进磁盘文件,而这些不一定是我们需要的,所以要创建内部类来实现
public Color TeamColor;
}
public void SaveColor()//将数据存入磁盘文件中(序列化)
{
SaveData data = new SaveData();
data.TeamColor = TeamColor;
string json = JsonUtility.ToJson(data);//将data转换成json形式(磁盘文件 文本格式就是字符串形式)
//File.WriteAllText(Application.persistentDataPath + "/savefile.json", json);//将文件存储到C盘某目录
File.WriteAllText(Application.dataPath + "/savefile.json", json);//将文件存储到当前文件根目录某目录
//存入savefile.json文件
}
public void LoadColor()//将磁盘文件中的数据拿出(反序列化)
{
//string path = Application.persistentDataPath + "/savefile.json";
string path = Application.dataPath + "/savefile.json";
if (File.Exists(path))//如果文件存在
{
string json = File.ReadAllText(path);//读出
SaveData data = JsonUtility.FromJson<SaveData>(json);//反序列化取出数据
TeamColor = data.TeamColor;
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
#if UNITY_EDITOR
using UnityEditor;
#endif
// Sets the script to be executed later than all default scripts
// This is helpful for UI, since other things may need to be initialized before setting the UI
[DefaultExecutionOrder(1000)]//给其关联的游戏对象的脚本排序
public class MenuUIHandler : MonoBehaviour
{
public ColorPicker ColorPicker;
public void NewColorSelected(Color color)
{
// add code here to handle when a color is selected
MainManager.Instance.TeamColor = color;
//TeamColor不是静态变量(属性),所以不能直接用MainManager.TeamColor
}
private void Start()
{
ColorPicker.Init();
//this will call the NewColorSelected function when the color picker have a color button clicked.
ColorPicker.onColorChanged += NewColorSelected;//将NewColorSelected此函数行为赋值给ColorPicker.onColorChanged
ColorPicker.SelectColor(MainManager.Instance.TeamColor);
}
public void StartNew()
{
SceneManager.LoadScene(1);//生成设置 1 是里面的场景序号
}
public void Exit()
{
MainManager.Instance.SaveColor();
#if UNITY_EDITOR//如果是Unity编辑器
EditorApplication.ExitPlaymode();//编辑器运行环境的终止
#else//不是Unity编辑器
Application.Quit();//程序退出,适用于打包完后程序文件,再运行时会成功退出
#endif
}
public void SaveColorClicked()
{
MainManager.Instance.SaveColor();
}
public void LoadColorClicked()
{
MainManager.Instance.LoadColor();
ColorPicker.SelectColor(MainManager.Instance.TeamColor);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ProductivityUnit : Unit
{
//定义m_CurrentPile为ResourcePile(物品堆)类型,本项目中一共有两种类型的物品堆,初值为null
private ResourcePile m_CurrentPile = null;//点中哪个物品堆
//定义资源生产速度应该提高多少
public float ProductivityMultiplier = 2;
protected override void BuildingInRange()//重写父类方法
{
//判断是否选择物品堆 防止生产速度会一直提高
if(m_CurrentPile == null)
{
ResourcePile pile = m_Target as ResourcePile;//m_Target是Building类型,看你点击的是否是那两个物品堆中的某一个,并将其赋值,每点击赋值null
if (pile != null)
{
m_CurrentPile = pile;
m_CurrentPile.ProductionSpeed *= ProductivityMultiplier;
//提高生产速度加倍
}
}
}
void ResetProductivity()//当小人离开生产速度恢复原值
{
if (m_CurrentPile != null)
{
m_CurrentPile.ProductionSpeed /= ProductivityMultiplier;
m_CurrentPile = null;
}
}
public override void GoTo(Building target)
{
ResetProductivity();//调用你的新方法 重置速度
base.GoTo(target);//调用父类中的GoTo方法
}
public override void GoTo(Vector3 position)
{
ResetProductivity();
base.GoTo(position);
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// A subclass of Building that produce resource at a constant rate.
/// </summary>
public class ResourcePile : Building
{
public ResourceItem Item;
public float m_ProductionSpeed = 0.5f;
public float ProductionSpeed//一旦被修改就会触发set里的内容,无论在内部还是在外部,这时会进入死循环
{//此时需要再定义一个变量值m_ProductionSpeed,赋值0.5,外部获取时将m_ProductionSpeed返回,一旦内外设置ProductionSpeed变量时即触发set更改m_ProductionSpeed值
//如果依然用ProductionSpeed进行get中的返回和set中的改值,set中的赋值操作会一直触发进入死循环
get { return m_ProductionSpeed; }
set {
if (value < 0f)//value为要赋的值
{
Debug.Log("报错");
}
else
{
m_ProductionSpeed = value;
}
}
}
private float m_CurrentProduction = 0.0f;
private void Update()
{
if (m_CurrentProduction > 1.0f)
{
int amountToAdd = Mathf.FloorToInt(m_CurrentProduction);
int leftOver = AddItem(Item.Id, amountToAdd);
m_CurrentProduction = m_CurrentProduction - amountToAdd + leftOver;
}
if (m_CurrentProduction < 1.0f)
{
m_CurrentProduction += m_ProductionSpeed * Time.deltaTime;
}
}
public override string GetData()
{
return $"Producing at the speed of {m_ProductionSpeed}/s";
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Subclass of Unit that will transport resource from a Resource Pile back to Base.
/// </summary>
public class TransporterUnit : Unit
{
public int MaxAmountTransported = 1;
private Building m_CurrentTransportTarget;
private Building.InventoryEntry m_Transporting = new Building.InventoryEntry();
// We override the GoTo function to remove the current transport target, as any go to order will cancel the transport
public override void GoTo(Vector3 position)
{
base.GoTo(position);
m_CurrentTransportTarget = null;
}
protected override void BuildingInRange()
{
if (m_Target == Base.Instance)
{
//we arrive at the base, unload!
if (m_Transporting.Count > 0)
m_Target.AddItem(m_Transporting.ResourceId, m_Transporting.Count);
//we go back to the building we came from
GoTo(m_CurrentTransportTarget);
m_Transporting.Count = 0;
m_Transporting.ResourceId = "";
}
else
{
if (m_Target.Inventory.Count > 0)
{
m_Transporting.ResourceId = m_Target.Inventory[0].ResourceId;
m_Transporting.Count = m_Target.GetItem(m_Transporting.ResourceId, MaxAmountTransported);
m_CurrentTransportTarget = m_Target;
GoTo(Base.Instance);
}
}
}
//Override all the UI function to give a new name and display what it is currently transporting
public override string GetName()
{
return "Transporter";
}
public override string GetData()
{
return $"Can transport up to {MaxAmountTransported}";
}
public override void GetContent(ref List<Building.InventoryEntry> content)
{
if (m_Transporting.Count > 0)
content.Add(m_Transporting);
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// This script handle all the control code, so detecting when the users click on a unit or building and selecting those
/// If a unit is selected it will give the order to go to the clicked point or building when right clicking.
/// </summary>
public class UserControl : MonoBehaviour
{
public Camera GameCamera;
public float PanSpeed = 10.0f;
public GameObject Marker;
private Unit m_Selected = null;//声明的是Unit类
private void Start()
{
Marker.SetActive(false);
}
private void Update()
{
Vector2 move = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
GameCamera.transform.position = GameCamera.transform.position + new Vector3(move.y, 0, -move.x) * PanSpeed * Time.deltaTime;
if (Input.GetMouseButtonDown(0))
{//GameCamera.ScreenPointToRay(Input.mousePosition)鼠标点击位置与照相机之间形成一条射线,返回Ray射线类型
var ray = GameCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;//得到射线碰上的物体
if (Physics.Raycast(ray, out hit))// out hit,hit为引用,这里会给hit赋值,检测射线ray命中某个物体
{
//the collider could be children of the unit, so we make sure to check in the parent
var unit = hit.collider.GetComponentInParent<Unit>();//利用多态,找射线命中的物体,找组件Unit,从本身开始一直往上找,自己没有Unit就继续找父亲,找离它最近的有Unit的节点
m_Selected = unit;//如果找到的是继承Unit类的子类组件对象,则返回的是子类,多态
//check if the hit object have a IUIInfoContent to display in the UI
//if there is none, this will be null, so this will hid the panel if it was displayed
var uiInfo = hit.collider.GetComponentInParent<UIMainScene.IUIInfoContent>();//命中的物体找含有此接口UIMainScene.IUIInfoContent这个组件(类)取出其中的信息,因为IUIInfoContent是UIMainScene内部接口
UIMainScene.Instance.SetNewInfoContent(uiInfo);//把信息uiInfo传入
}
}
else if (m_Selected != null && Input.GetMouseButtonDown(1))
{//right click give order to the unit
var ray = GameCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
var building = hit.collider.GetComponentInParent<Building>();
if (building != null)
{
m_Selected.GoTo(building);
}
else
{
m_Selected.GoTo(hit.point);
}
}
}
MarkerHandling();
}
// Handle displaying the marker above the unit that is currently selected (or hiding it if no unit is selected)
void MarkerHandling()
{
if (m_Selected == null && Marker.activeInHierarchy)
{
Marker.SetActive(false);
Marker.transform.SetParent(null);
}
else if (m_Selected != null && Marker.transform.parent != m_Selected.transform)
{
Marker.SetActive(true);
Marker.transform.SetParent(m_Selected.transform, false);
Marker.transform.localPosition = Vector3.zero;
}
}
}
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Base class for building on the map that hold a Resource inventory and that can be interacted with by Unit.
/// This Base class handle modifying the inventory of resources.
/// </summary>
public abstract class Building : MonoBehaviour,
UIMainScene.IUIInfoContent
{
//need to be serializable for the save system, so maybe added the attribute just when doing the save system
[System.Serializable]
public class InventoryEntry
{
public string ResourceId;
public int Count;
}
[Tooltip("-1 is infinite")]
public int InventorySpace = -1;
protected List<InventoryEntry> m_Inventory = new List<InventoryEntry>();
public List<InventoryEntry> Inventory => m_Inventory;
protected int m_CurrentAmount = 0;
//return 0 if everything fit in the inventory, otherwise return the left over amount
public int AddItem(string resourceId, int amount)
{
//as we use the shortcut -1 = infinite amount, we need to actually set it to max value for computation following
int maxInventorySpace = InventorySpace == -1 ? Int32.MaxValue : InventorySpace;
if (m_CurrentAmount == maxInventorySpace)
return amount;
int found = m_Inventory.FindIndex(item => item.ResourceId == resourceId);
int addedAmount = Mathf.Min(maxInventorySpace - m_CurrentAmount, amount);
//couldn't find an entry for that resource id so we add a new one.
if (found == -1)
{
m_Inventory.Add(new InventoryEntry()
{
Count = addedAmount,
ResourceId = resourceId
});
}
else
{
m_Inventory[found].Count += addedAmount;
}
m_CurrentAmount += addedAmount;
return amount - addedAmount;
}
//return how much was actually removed, will be 0 if couldn't get any.
public int GetItem(string resourceId, int requestAmount)
{
int found = m_Inventory.FindIndex(item => item.ResourceId == resourceId);
//couldn't find an entry for that resource id so we add a new one.
if (found != -1)
{
int amount = Mathf.Min(requestAmount, m_Inventory[found].Count);
m_Inventory[found].Count -= amount;
if (m_Inventory[found].Count == 0)
{//no more of that resources, so we remove it
m_Inventory.RemoveAt(found);
}
m_CurrentAmount -= amount;
return amount;
}
return 0;
}
public virtual string GetName()
{
return gameObject.name;
}
public virtual string GetData()
{
return "";
}
public void GetContent(ref List<InventoryEntry> content)
{
content.AddRange(m_Inventory);
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// A special building that hold a static reference so it can be found by other script easily (e.g. for Unit to go back
/// to it)
/// </summary>
public class Base : Building
{
public static Base Instance { get; private set; }
private void Awake()
{
Instance = this;
}
}
?UI源码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ContentEntry : MonoBehaviour
{
public Image Icone;
public Text Count;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class InfoPopup : MonoBehaviour
{
public Text Name;
public Text Data;
public RectTransform ContentTransform;
public ContentEntry EntryPrefab;
public void ClearContent()
{
foreach (Transform child in ContentTransform)
{
Destroy(child.gameObject);
}
}
public void AddToContent(int count, Sprite Icone)
{
var newEntry = Instantiate(EntryPrefab, ContentTransform);
newEntry.Count.text = count.ToString();
newEntry.Icone.sprite = Icone;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class UIMainScene : MonoBehaviour
{
public static UIMainScene Instance { get; private set; }
public interface IUIInfoContent//interface接口,不能new
{//接口是用来实现的,里面的方法都是抽象的,需要被子类重写来实现
//接口的主要作用是用来约束行为(方法)一个需要的方法定义一个接口,需要的类就实现它,低耦合高聚合,自顶而下
//继承属于强耦合,自底而上,很多子类有公共部分就可以抽象一个父类去继承
/*例如 c#中一个类只能继承一个父类,单继承多实现(可实现多个接口)
class Aa : SomeInterface,ISomeInterface{}//c#中,和继承没办法区分所以一般接口开头有I
interface SomeInterface{void Methhod1();void Methhod1();}
class Bb extends Cc implements SomeInterface{}//Java中
*/
string GetName();//获取Main场景中点击物体的信息
string GetData();
void GetContent(ref List<Building.InventoryEntry> content);
}
public InfoPopup InfoPopup;
public ResourceDatabase ResourceDB;
protected IUIInfoContent m_CurrentContent;
protected List<Building.InventoryEntry> m_ContentBuffer = new List<Building.InventoryEntry>();
private void Awake()
{
Instance = this;
InfoPopup.gameObject.SetActive(false);
ResourceDB.Init();
}
private void OnDestroy()
{
Instance = null;
}
private void Update()
{
if (m_CurrentContent == null)
return;
//This is not the most efficient, as we reconstruct everything every time. A more efficient way would check if
//there was some change since last time (could be made through a IsDirty function in the interface) or smarter
//update (match an entry content ta type and just update the count) but simplicity in this tutorial we do that
//every time, this won't be a bottleneck here.
InfoPopup.Data.text = m_CurrentContent.GetData();
InfoPopup.ClearContent();//先将左上角信息表清空
m_ContentBuffer.Clear();
m_CurrentContent.GetContent(ref m_ContentBuffer);
foreach (var entry in m_ContentBuffer)//取图标
{
Sprite icon = null;
if (ResourceDB != null)
icon = ResourceDB.GetItem(entry.ResourceId)?.Icone;
InfoPopup.AddToContent(entry.Count, icon);
}
}
public void SetNewInfoContent(IUIInfoContent content)//多态,传过来的是实现接口的类,把点击的物体显示的信息到左上角
{
if (content == null)//此类未实现此接口
{
InfoPopup.gameObject.SetActive(false);//左上角不显示信息
}
else
{
InfoPopup.gameObject.SetActive(true);
m_CurrentContent = content;
InfoPopup.Name.text = content.GetName();//打开的是点击物体对应组件(类)中实现接口的方法,如果没重写接口中的方法就去继承的父类(实现接口方法)实现的方法
}
}
public void BackToMenuScene()
{
SceneManager.LoadScene(0);//0为Menu场景,回到Menu场景,将Menu场景在一次加载一遍
}
}
Helpers文档
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class AnimatorHandler : MonoBehaviour
{
private Animator m_Animator;
private NavMeshAgent m_Agent;
// Start is called before the first frame update
void Start()
{
m_Agent = GetComponentInParent<NavMeshAgent>();
m_Animator = GetComponentInChildren<Animator>();
}
// Update is called once per frame
void Update()
{
if (m_Agent != null && m_Animator != null)
{
m_Animator.SetFloat("Speed", m_Agent.velocity.magnitude / m_Agent.speed);
}
}
}
using UnityEngine;
// Handles setting a color to a given renderer and material slot. Used to simplify coloring our Unit.
// (This can be added on the visual prefab and the Unit code can just query oif that component exists to set color)
public class ColorHandler : MonoBehaviour
{
public Renderer TintRenderer;
public int TintMaterialSlot;
public void SetColor(Color c)
{
var prop = new MaterialPropertyBlock();
prop.SetColor("_BaseColor", c);
TintRenderer.SetPropertyBlock(prop, TintMaterialSlot);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ColorPicker : MonoBehaviour
{
public Color[] AvailableColors;
public Button ColorButtonPrefab;
public Color SelectedColor { get; private set; }
public System.Action<Color> onColorChanged;//添加某种行为,在MenuUIHandler中添加过来的行为
List<Button> m_ColorButtons = new List<Button>();
// Start is called before the first frame update
public void Init()
{
foreach (var color in AvailableColors)//var不确定AvailableColors是什么类型用var,自己会推断
{
var newButton = Instantiate(ColorButtonPrefab, transform);//克隆出来一个新的按钮放在自己的下边
newButton.GetComponent<Image>().color = color;//按钮赋值颜色
newButton.onClick.AddListener(() =>//给按钮绑定点击事件,只有点击时才会执行里面的东西
{
SelectedColor = color;
foreach (var button in m_ColorButtons)
{
button.interactable = true;//按钮交互,当鼠标划到颜色按钮身上,颜色会加深
}
newButton.interactable = false;//点击后的按钮,关闭交互
onColorChanged.Invoke(SelectedColor);//触发NewColorSelected行为,并将SelectedColor传到NewColorSelected中
//等同于MainManager.Instance.TeamColor=SelectedColor,但上述代码是将你用的颜色给你,但具体干什么可以写在调用的函数中
});
//回调,整体生成按钮 绑定事件 赋值按钮 瞬间完成,只有点击时才会回到上面代码进入里面执行foreach
m_ColorButtons.Add(newButton);//按钮扔到按钮列表中
}
}
public void SelectColor(Color color)
{
for (int i = 0; i < AvailableColors.Length; ++i)
{
if (AvailableColors[i] == color)
{
m_ColorButtons[i].onClick.Invoke();//设置的颜色作为下一次运行时菜单默认的颜色,自动触发点击函数
}
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "ResourcesDatabase", menuName = "Tutorial/Resources Database")]
public class ResourceDatabase : ScriptableObject
{
public List<ResourceItem> ResourceTypes = new List<ResourceItem>();
private Dictionary<string, ResourceItem> m_Database;
public void Init()
{
m_Database = new Dictionary<string, ResourceItem>();
foreach (var resourceItem in ResourceTypes)
{
m_Database.Add(resourceItem.Id, resourceItem);
}
}
public ResourceItem GetItem(string uniqueId)
{
m_Database.TryGetValue(uniqueId, out ResourceItem type);
return type;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "ResourceItem", menuName = "Tutorial/Resource Item")]
public class ResourceItem : ScriptableObject
{
public string Name;
public string Id;
public Sprite Icone;
}
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?