一、准备工作
在Unity中创建承载日期和时间的文本
二、设置游戏的时间戳
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//标识这个类可以被序列化
[System.Serializable]
public class GameTimestamp
{// 游戏时间戳的成员变量public int year;//季节的枚举类型public enum Season { Spring, Summer, Fall, Winter }//星期几的枚举类型public enum DayOfTheWeek { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }public Season season;public int day;public int hour;public int minute;// 构造函数1:根据给定的参数创建一个游戏时间戳实例public GameTimestamp(int year, Season season, int day, int hour, int minute){this.year = year;this.season = season;this.day = day;this.hour = hour;this.minute = minute;}// 构造函数2:复制给定的游戏时间戳实例public GameTimestamp(GameTimestamp timestamp){this.year = timestamp.year;this.season = timestamp.season;this.day = timestamp.day;this.hour = timestamp.hour;this.minute = timestamp.minute;}// 更新游戏时钟public void UpdateClock(){minute++;if (minute >= 60) { minute = 0; hour++; }if (hour >= 24) { hour = 0; day++; }if (day > 30){day = 1;if (season == Season.Winter) { season = Season.Spring; year++; }else { season++; }}}// 获得当天是星期几public DayOfTheWeek GetDayOfTheWeek(){int daysPassed = YearsToDays(year) + SeasonsToDays(season) + day;int dayIndex = daysPassed % 7;return (DayOfTheWeek)dayIndex;}// 将小时转换为分钟数public static int HoursToMinutes(int hour) { return hour * 60; }// 将天数转换为小时数public static int DaysToHours(int days) { return days * 24; }// 将季节转换为天数public static int SeasonsToDays(Season season){int seasonIndex = (int)season;return seasonIndex * 30;}// 将年份转化为天数public static int YearsToDays(int year) { return year * 4 * 30; }// 比较两个不同的时间戳的差异public static int CompareTimestamp(GameTimestamp timestamp1, GameTimestamp timestamp2){int timestamp1Hours = DaysToHours(YearsToDays(timestamp1.year)) + DaysToHours(SeasonsToDays(timestamp1.season) + DaysToHours(timestamp1.day) + timestamp1.hour);int timestamp2Hours = DaysToHours(YearsToDays(timestamp2.year)) + DaysToHours(SeasonsToDays(timestamp2.season) + DaysToHours(timestamp2.day) + timestamp2.hour);int difference = timestamp1Hours - timestamp2Hours;//返回 difference 的绝对值。//Mathf.Abs 是 Unity 引擎中的一个数学函数,用于计算一个数的绝对值。//在这个脚本中,它用于计算两个时间戳之间的差异,并返回一个非负值return Mathf.Abs(difference);}
}
三、设置时间流逝逻辑
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class TimeManager : MonoBehaviour
{// 创建一个单例对象,其他脚本可以通过Instance访问public static TimeManager Instance { get; private set; }// 当脚本被实例化时调用的方法,用于初始化实例private void Awake(){// 如果Instance已经存在并且不等于当前实例,则销毁当前实例if (Instance != null && Instance != this) { Destroy(this); }else { Instance = this; }}//序列化字段[SerializeField]// 游戏的时间戳GameTimestamp timestamp;// 游戏的时间缩放比例(时间流逝速度)public float timeScale = 1.0f;// 游戏开始时调用的方法void Start(){// 初始化时间戳为游戏开始时的时间timestamp = new GameTimestamp(0, GameTimestamp.Season.Spring, 1, 6, 0);// 开始一个协程来更新游戏时间StartCoroutine(TimeUpdate());}// 游戏时间更新的协程IEnumerator TimeUpdate(){while (true){// 每隔1 / timeScale秒执行一次Tick方法Tick();yield return new WaitForSeconds(1 / timeScale);}}// 每次游戏时间更新时调用的方法public void Tick(){// 更新时间戳timestamp.UpdateClock();}
}
四、日夜循环
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class TimeManager : MonoBehaviour
{public static TimeManager Instance { get; private set; }private void Awake(){if (Instance != null && Instance != this) { Destroy(this); }else { Instance = this; }}[SerializeField]GameTimestamp timestamp;public float timeScale = 1.0f;//将一个用于表示白天和黑夜循环的Transform赋值给sunTransform变量,并声明一个ITimeTracker接口类型的列表listeners,用于保存所有注册的时间追踪器。[Header("Day and Night cycle")]public Transform sunTransform;List<ITimeTracker> listeners = new List<ITimeTracker>();void Start(){timestamp = new GameTimestamp(0, GameTimestamp.Season.Spring, 1, 6, 0);StartCoroutine(TimeUpdate());}IEnumerator TimeUpdate(){while (true){Tick();yield return new WaitForSeconds(1 / timeScale);}}public void Tick(){//更新时间戳timestamp.UpdateClock();//通知所有注册的时间追踪器更新时间foreach (ITimeTracker listener in listeners){listener.ClockUpdate(timestamp);}//调用UpdateSunMovement方法更新太阳的位置UpdateSunMovement();}void UpdateSunMovement(){//当前时间int timeInMinutes = GameTimestamp.HoursToMinutes(timestamp.hour) + timestamp.minute;//计算太阳的角度float sunAngle = .25f * timeInMinutes - 90;//将太阳角度应用到sunTransform的欧拉角上,实现太阳的运动sunTransform.eulerAngles = new Vector3(sunAngle, 0, 0);}//获取新的时间戳public GameTimestamp GetGameTimestamp(){//返回当前的时间戳return new GameTimestamp(timestamp);}//将一个时间追踪器注册到列表中public void RegisterTracker(ITimeTracker listener){listeners.Add(listener);}//将一个时间追踪器从列表中移除public void UnregisterTracker(ITimeTracker listener){listeners.Remove(listener);}
}
五、测试方法
public class PlayerController : MonoBehaviourvoid Update()
{//测试光线逻辑if(Input.GetKey(KeyCode.RightBracket)){TimeManager.Instance.Tick();}
}
按住右方括号,加速时间流速
六、创建接口
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public interface ITimeTracker
{void ClockUpdate(GameTimestamp timestamp);
}
七、显示游戏时间
1、编辑UIManager.cs,游戏时间
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;//添加接口,管理时间
public class UIManager : MonoBehaviour, ITimeTracker
{public static UIManager Instance { get; private set; }private void Awake(){if (Instance != null && Instance != this) { Destroy(this); }else { Instance = this; }}[Header("Startas Bar")]//手持工具栏中的时间文本public Text timeText;public Text dateText;private void Start(){//注册时间跟踪器(将UIManager添加到时间跟踪器的列表中)TimeManager.Instance.RegisterTracker(this);}//处理UI时间回调(回调:在特定的时刻执行自己定义的代码,实现特定的功能)//更新游戏中的时钟public void ClockUpdate(GameTimestamp timestamp){//获取小时和分钟的值int hours = timestamp.hour;int minutes = timestamp.minute;string prefix = "AM ";//转换为下午的时间if (hours > 12){prefix = "PM ";hours -= 12;}timeText.text = prefix + hours + ":" + minutes.ToString("00");//获取日期\星期int day = timestamp.day;string season = timestamp.season.ToString();string dayOfTheWeek = timestamp.GetDayOfTheWeek().ToString();dateText.text = season + " " + day + " (" + dayOfTheWeek +")";}
}
2、赋值
八、把时间系统运用到耕地上
1、编辑TimeManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class TimeManager : MonoBehaviour
{public static TimeManager Instance { get; private set; }private void Awake(){if (Instance != null && Instance != this) { Destroy(this); }else { Instance = this; }}[SerializeField]GameTimestamp timestamp;public float timeScale = 1.0f;[Header("Day and Night cycle")]public Transform sunTransform;List<ITimeTracker> listeners = new List<ITimeTracker>();void Start(){timestamp = new GameTimestamp(0, GameTimestamp.Season.Spring, 1, 6, 0);StartCoroutine(TimeUpdate());}IEnumerator TimeUpdate(){while (true){Tick();yield return new WaitForSeconds(1 / timeScale);}}public void Tick(){timestamp.UpdateClock();foreach (ITimeTracker listener in listeners){listener.ClockUpdate(timestamp);}UpdateSunMovement();}void UpdateSunMovement(){int timeInMinutes = GameTimestamp.HoursToMinutes(timestamp.hour) + timestamp.minute;float sunAngle = .25f * timeInMinutes - 90;sunTransform.eulerAngles = new Vector3(sunAngle, 0, 0);}//获取新的时间戳public GameTimestamp GetGameTimestamp(){return new GameTimestamp(timestamp);}public void RegisterTracker(ITimeTracker listener){listeners.Add(listener);}public void UnregisterTracker(ITimeTracker listener){listeners.Remove(listener);}
}
2、编辑Land.cs,设置灌溉后土壤变化
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//添加接口
public class Land : MonoBehaviour, ITimeTracker
{public enum LandStatus { Soil, Farmland, Watered }public LandStatus landStatus;public Material soilMat, farmlandMat, wateredMat;new Renderer renderer;public GameObject select;//灌溉时间GameTimestamp timeWatered;void Start(){renderer = GetComponent<Renderer>();SwitchLandStatus(LandStatus.Soil);//select.SetActive(false);Select(false);//注册时间戳TimeManager.Instance.RegisterTracker(this);}public void SwitchLandStatus(LandStatus statusToSwitch){landStatus = statusToSwitch;Material materialToSwitch = soilMat;switch (statusToSwitch){case LandStatus.Soil: materialToSwitch = soilMat; break;case LandStatus.Farmland: materialToSwitch = farmlandMat; break;//灌溉后土壤状态case LandStatus.Watered:materialToSwitch = wateredMat;//灌溉后流逝的时间timeWatered = TimeManager.Instance.GetGameTimestamp();break;}renderer.material = materialToSwitch;}public void Select(bool toggle){select.SetActive(toggle);}public void Interact(){ItemData toolSlot = InventoryManager.Instance.equippedTool;EquipmentData equipmentTool = toolSlot as EquipmentData;if (equipmentTool != null){EquipmentData.ToolType toolType = equipmentTool.toolType;switch (toolType){case EquipmentData.ToolType.Hoe:SwitchLandStatus(LandStatus.Farmland); break;case EquipmentData.ToolType.WateringCan:SwitchLandStatus(LandStatus.Watered); break;}}}//设置灌溉后土壤的变化public void ClockUpdate(GameTimestamp timestamp){if (landStatus == LandStatus.Watered){int hoursElapsed = GameTimestamp.CompareTimestamp(timeWatered, timestamp);Debug.Log(hoursElapsed + "上次灌溉时间");if (hoursElapsed > 24){SwitchLandStatus(LandStatus.Farmland);}}}
}