一、准备工作
在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 : MonoBehaviour
void 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);
}
}
}
}