在之前的编辑脚本中,我们讨论了编辑脚本的概述和重要性。我们在第1部分和第2部分中讨论了Gizmos、自定义检查器和编辑器窗口。现在,我们将给您一个关于脚本对象的洞察和概述
脚本化的对象是什么?
根据Unity:
一个类,来自于Unity的对象类,它的引用和字段可以被序列化。
脚本对象是什么样的
脚本对象是一种特殊类型的对象,它不需要出现在场景中,也不需要附加在一个游戏对象上,因为它可以作为项目中的资产保存。脚本对象的主要用途是以持久的方式保存数据。它只是一种单行为的数据版本,只能保存数据变量。
他们能做什么?
可以将脚本对象构建在Unity,并可以作为项目中的资产保存。它们可以在运行时保存,这意味着如果您必须在运行时或播放模式上改变一些东西,您可以使用可脚本对象来保存这些更改。您不必担心在运行时解析/序列化数据,他们会为您这样做。因为它只能存储数据,几乎和单行为一样,您不必担心它与类之间的交互。
局限性是什么?
在使用脚本时,需要记住的是脚本对象的一些限制。它们需要编辑脚本,因为它们只能在编辑器中创建。另外,您不能使用第三方工具编辑它们的外部。因此,它们只能用于保存和管理开发生命周期的数据;也就是说,它们不能被修改或保存一次。简而言之,脚本对象最适合存储游戏开发/游戏设计数据和优化数据加载。
可能的使用场景
1.你想在播放模式下调整值,并想保留它们。
2.你想要改变某些类型的游戏对象。
3.你不想改变游戏设计师或者艺术家来扰乱你的游戏对象的不相关价值。
在上述所有场景中,可脚本对象都是有用的,并且总是可以保存到它们自己独特的资产文件中。在游戏模式中,你可以轻松编辑脚本对象实例,让游戏设计者/艺术家迭代不用担心实际的游戏数据。此外,你的场景和前言将会更快地保存和加载。此外,它还实现了可靠的关注点分离(SoC)模式。
例子
让我们动手玩可脚本的对象。我们现在有一个简单的类 'Player.cs':
[C#]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
public
class
Player : MonoBehaviour
{
//Player data
public
string
_name;
public
int
_health;
public
Color _color;
private
MeshRenderer _renderer;
void
Awake()
{
_renderer = GetComponent<MeshRenderer>();
_name =
"John"
;
_health = 100;
_renderer.material.color = Color.green;
}
|
我们的玩家是一个占位的立方体(为了便于理解),有名字,健康和颜色。在Awake()回调中,我们将数据分配给播放器。我们来做一个脚本对象。它只是一个简单的c#类' PlayerData ',它扩展了脚本对象:
[C#]
纯文本查看
复制代码
1
2
3
4
5
|
public
class
PlayerData : ScriptableObject
{
public
string
m_name;
public
int
m_health;
public
Color m_color;
}
|
将可脚本对象保存为资产
为了在项目中保存我们的资产,创建一个简单的c#类,包括UnityEditor名称空间来访问编辑器函数,它不需要扩展任何类:
[C#]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
using
UnityEditor;
using
UnityEngine;
public
class
MyEditorUtils
{
}
Now create a
static
generic method CreateAsset<T>() inside the
class
. Generic methods have type parameters and they provide a way to use types
as
parameter
in
a method:
public
class
MyEditorUtils
{
public
static
void
CreateAsset<T>() where T : ScriptableObject
{
}
}
|
在方法中添加此代码:
[C#]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
|
public
static
void
CreateAsset<T>() where T : ScriptableObject
{
T asset = ScriptableObject.CreateInstance<T>();
string
path = AssetDatabase.GetAssetPath(Selection.activeObject);
string
assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(path +
"/New "
+
typeof
(T).ToString() +
".asset"
);
AssetDatabase.CreateAsset(asset, assetPathAndName);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.FocusProjectWindow();
Selection.activeObject = asset;
}
|
现在让我们逐行分解代码:
第3行:我们正在创建一个T类型的脚本对象的实例,我们正在获取我们的方法的参数。
第5、6条:我们在项目中获取所选文件夹的路径,然后为我们的资产构建路径和名称字符串。
第8行:我们正在创建一个。资产的文件和我们的资产名称和我们想要的路径。
第9行:为我们的项目节省我们的资产。
第10行:刷新和更新我们的资产视图和数据。
第11行,第12行:将项目窗口集中起来,我们的保存因此被选中。
现在让我们把它封装在一个静态方法中,这样它就可以被触发在一个[MenuItem]属性操作上,并在参数中传递PlayerData:
[C#]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
public
class
MyEditorUtils
{
[MenuItem(
"Assets/Create/Create Player Data Object"
)]
public
static
void
CreateAsset()
{
CreateAsset<PlayerData>();
}
public
static
void
CreateAsset<T>() where T : ScriptableObject
{
T asset = ScriptableObject.CreateInstance<T>();
string
path = AssetDatabase.GetAssetPath(Selection.activeObject);
string
assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(path +
"/New "
+
typeof
(T).ToString() +
".asset"
);
AssetDatabase.CreateAsset(asset, assetPathAndName);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.FocusProjectWindow();
Selection.activeObject = asset;
}
}
|
现在去看Unity编辑器,看看它的作用:
这就是我们的PlayerData在检查器中看起来的样子:
[CreateAssetMenu]属性
从Unity 5.1开始,您可以使用[CreateAssetMenu]属性在一行代码中创建资产。该属性允许您在资产/创建上下文菜单中创建菜单项。这是非常快的创建资产的方法,并且不需要上面的类来做这个。你所要做的就是,用一个[CreateAssetMenu]来标记我们的PlayerData类:
[C#]
纯文本查看
复制代码
1
2
3
4
5
6
|
[CreateAssetMenu(fileName =
"My Scriptable Object"
, menuName =
"My Content/Create Player Scriptable Object"
, order = 0)]
public
class
PlayerData : ScriptableObject
{
public
string
m_name;
public
int
m_health;
public
Color m_color;
}
|
fileName:将在资产中创建的资产文件的名称。
menuName: 在资产/创建菜单中菜单的层次结构。
order:是您的菜单项在资产/创建菜单中的顺序。我将它设置为0,显示在每个菜单的顶部。
结果如下:
链接和引用可脚本对象
引用和使用来自脚本对象的数据相当容易。你所要做的只是创建一个变量的PlayerData:
[C#]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public
class
Player : MonoBehaviour
{
public
PlayerData data;
//Player data
public
string
_name;
public
int
_health;
public
Color _color;
private
MeshRenderer _renderer;
void
Awake()
{
_renderer = GetComponent<MeshRenderer>();
_name =
"John"
;
_health = 100;
_renderer.material.color = Color.green;
}
|
在Awake()中,将数据分配给类变量:
[C#]
纯文本查看
复制代码
1
2
3
4
5
6
|
void
Awake()
{
_renderer = GetComponent<MeshRenderer>();
_name = data.m_name;
_health = data.m_health;
_renderer.material.color = data.m_color;
}
|
在检视编辑器中分配引用:
让我们从检查器中给我们的PlayerData脚本对象提供一些值:
我还添加了一个标签,可以快速地在游戏视图中看到玩家的信息,在我的播放器类中获取它的引用,并在Awake()方法中更新它:
[C#]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
|
void
Awake()
{
_renderer = GetComponent<MeshRenderer>();
_name = data.m_name;
_health = data.m_health;
_renderer.material.color = data.m_color;
UpdateUI();
}
void
UpdateUI()
{
_playerInfo.text =
"Name: "
+ _name +
"\n"
+
"Health: "
+ _health +
"\n"
+
"Color: "
+ _color;
}
|
结果:
让我们再复制两个播放器数据对象,并给它们分配不同的数据:
取一组PlayerData并在索引上进行切换,这是我们的完整代码(用数组替换单个数据变量):
[C#]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
public
class
Player : MonoBehaviour
{
public
PlayerData data;
public
string
_name;
public
int
_health;
public
Color _color;
public
PlayerData[] dataArray;
[Range(0, 2)]
public
int
dataIndex;
public
Text _playerInfo;
private
MeshRenderer _renderer;
void
Update()
{
_renderer = GetComponent<MeshRenderer>();
_name = dataArray[dataIndex].m_name;
_health = dataArray[dataIndex].m_health;
_renderer.material.color = dataArray[dataIndex].m_color;
UpdateUI();
}
void
UpdateUI()
{
_playerInfo.text =
"Name: "
+ _name +
"\n"
+
"Health: "
+ _health;
}
}
|
这是我们最后的结果: