问题描述
让我先解释一下我要做什么的场景。 我正在尝试创建一个类似 VN 的游戏,其中角色会根据一天中的时间改变他们的形象。例如,在白天 8:00AM 到 11:00AM 之间,名为 Benjamin 的 npc 角色将站在场景中的池塘边,而在上午 11:00 之后,他将坐在场景中的篝火旁。这意味着每个 npc 角色都有许多不同的精灵/图像,但根据一天中的时间,只能激活其中一个。
在编码和构建脚本的工作方式时,我想遵循最佳实践。所以我不想要一些有效但以凌乱的方式完成的东西,我觉得它可以改进。所以我就是这样做的:
public class Benjamin : MonoBehavIoUr
{
public GameObject BenjaminStanding;
public GameObject BenjaminSitting;
void Update()
{
//If Time logic Between 8:00AM - 11:00AM
ActivateBenjaminStanding();
//if Time logic is After 11:00AM
ActivateBenjamindSitting();
}
//Activate Benjamin standing image
public void ActivateBenjaminStanding()
{
BenjaminSitting.SetActive(false);
BenjaminStanding.SetActive(true);
}
//Activate Benjamin sitting image
public void ActivateBenjamindSitting()
{
BenjaminStanding.SetActive(false);
BenjaminSitting.SetActive(true);
}
}
在我的层次结构中,我有一个空的游戏对象 Benjamin,然后是该父对象下的所有图像,如下所示:
本杰明
- 本杰明·斯坦丁
- BenjaminSitting
我将脚本附加到我的 Benjamin 对象并执行所有必要的图像引用。
现在上面的代码可以工作了,但我觉得这是一种非常混乱的做事方式,因为如果我有更多的角色,我必须为每个角色制作单独的脚本,有些可能只有很少的图像,有些可能有很多。我必须在激活它们时为每个图像创建一个方法。那么有没有更好的方法呢?
我正在寻找更快、更清洁、更高效的方法。我将不胜感激任何帮助或建议。谢谢!
解决方法
您可以为您的角色创建一个基类。使其成为抽象类,并添加所有字符应具有的变量和方法。
喜欢
public abstract class Character : MonoBehaviour
{
public GameObject characterStanding;
public GameObject characterSitting;
internal virtual void Update()
{
//If Time logic Between 8:00AM - 11:00AM
ActivateCharacterStanding();
//if Time logic is After 11:00AM
ActivateCharacterSitting();
}
//Activate Benjamin standing image
public void ActivateCharacterStanding()
{
characterSitting.SetActive(false);
characterStanding.SetActive(true);
}
//Activate Benjamin sitting image
public void ActivateSitting()
{
characterStanding.SetActive(false);
characterSitting.SetActive(true);
}
}
然后您可以创建子类并从 Character 类继承。
public class Benjamin : Character
{
}
public class Marting: Character
{
}
public class John: Character
{
// You can change main class function and override it
override Update(){
// If you remove this doesn't apply base class function.
base.ActivateCharacter();
} }
但是如果每个角色都有相似的属性并且您创建了许多属性,则这种方法根本没有用。您可以使用 Scriptable Object 为每个角色创建资产并将可编写脚本的对象分配给场景对象或预制件,选择哪种方式适合您的设置。
,您可以通过将您的游戏对象组织成一个数据结构并使用单个函数在图像之间切换来减少您的工作量。
通过这样做,您可以将您的 Benjamin
类概括为任何给定角色的更通用的类,从而使您的组件具有可重用性。
此示例使用 Dictionary
按名称组织每个游戏对象,并假设每个图像与游戏对象具有相同的名称,
以便可以按名称引用预期的 GameObject。这样,而不是为每个图像编写变量并为每个图像编写方法,
我们可以调用单个函数(例如:Activate("BenjaminStanding")
)。
public class ClassNameHere : MonoBehaviour
{
public GameObject[] images;
private Dictionary<string,GameObject> imageDict;
private GameObject currentImage;
void Awake()
{
imageDict = new Dictionary<string,GameObject>();
foreach (GameObject image in images)
{
imageDict.Add(image.name,image);
}
}
void Activate(string name)
{
if (currentImage) currentImage.SetActive(false);
var image = imageDict[name];
image.SetActive(true);
currentImage = image;
}
void Update()
{
//If Time logic Between 8:00AM - 11:00AM
Activate("BenjaminStanding");
//if Time logic is After 11:00AM
Activate("BenjaminSitting");
}
}
,
如果你有很多图像,一个常见的做法就是拥有一个数组变量,
public ImageOrSomething[] avatars;
请注意,然后在检查器中,您可以放入任意数量的图像,而无需更改代码。
请注意,我所说的“ImageOrSomething”可以是任何东西......一个Texture
、一个Image
、一个完整的动画角色、一些精灵表......无论你的情况是什么。
进一步...
实际上,您可以组织资源文件夹中的图像,然后按名称加载它们。
这样,当您的设计部门制作或更改图像,或添加不同的时间时,您无需更改代码。
这是一种将纹理加载到卡片上的随机示例(比如扑克牌),
private int _Cardnumber = 1;
public int Cardnumber
{
get { return _Cardnumber; }
set
{
.. obviously check for valid numbers,etc
string f = "bonuscards/bonus_" + Cardnumber.ToString();
Texture t = Resources.Load(f,typeof(Texture)) as Texture;
ri.texture = t;
}
}
在示例中,图像将由您的艺术部门命名为“bonus_3.png”“bonus_4.png”等。
您还可以自动确定该文件夹中包含哪些项目并随机选择一项(或无论如何)。
关于您的“一天中的时间”示例...
聪明的做法就是在图像名称中添加实际时间。
有点像
cat_03.png
cat_09.png
cat_13.png
表示您分别更改为 0300、0900 和 1300 处的图像。
假设你想为睡猫添加一个新的,并且你希望它在晚上 10 点发生。 您只需添加新图片
cat_22.png
只需将其放在您的资源文件夹中,它就会自动从那里开始。