在进行场景切换的时候,经常会遇到需求保存上一个场景里所有的物品信息和他们的位置。再次切换回场景的时候场景里物品的布局就如自己离开时一样,避免了穿帮和玩家体验优化。通常是需要在不被关闭的主场景添加一个物品管理器,在物品管理器里创建字典<string,List<Item>>字典的key为激活的场景名字,值为物品列表。在卸载当前场景之前发送消息提醒管理器让其收集物品信息。需要注意的是vector3无法序列化存储,这样在创建物品信息时需要修改坐标的存储方法。可以用一个类来存储,在类里创建x,y,z三个参数并创建其构造方法,内部做vector3的返回方法。
public class SerializableVector3
{
public float x,y,z;
public SerializableVector3(Vector3 pos)
{
this.x = pos.x;
this.y = pos.y;
this.z = pos.z;
}
public Vector3 ToVector3()
{
return new Vector3(x,y,z);
}
public Vector2Int ToVecotr2Int()
{
return new Vector2Int((int)x,(int)y);
}
}
public class SceneItem
{
public int itemId;
public SerializableVector3 position;
}
物品信息中坐标参数用以上类作为参数,在循环场景中物品的时候只需要把游戏物体position赋值给物体信息的position即可实现存储。使用时再使用ToVector3进行转换即可变为拿来直接用于坐标的赋值。场景切换时会把显示的场景设为激活场景 而SceneManager.GetActiveScene()方法可以直接获取当前激活的场景,可以用场景.name直接赋值字典的key,而Item列表就只需要遍历场景中挂载了物品脚本的游戏物体即可。最后在加载场景的时候再根据当前激活场景的名字获取场景的物品列表再进行遍历然后实例化物品。
// 卸载场景前的信息存储
private void GetAllSceneItems()
{
List<SceneItem> currentSceneItems = new List<SceneItem>();
foreach (var item in FindObjectsOfType<Item>())
{
SceneItem sceneItem = new SceneItem();
sceneItem.itemId = item.itemId;
sceneItem.position = new SerializableVector3(item.transform.position);
currentSceneItems.Add(sceneItem);
}
if(sceneItemData.ContainsKey(SceneManager.GetActiveScene().name))
{
sceneItemData[SceneManager.GetActiveScene().name] = currentSceneItems;
}
else
{
sceneItemData.Add(SceneManager.GetActiveScene().name,currentSceneItems);
}
}
// 加载场景时的实例化
private void RecreateAllItems()
{
List<SceneItem> currentSceneItems = new List<SceneItem>();
if(sceneItemData.TryGetValue(SceneManager.GetActiveScene().name,out currentSceneItems))
{
if(currentSceneItems != null)
{
// 先清空场景中所有的物品
foreach (var item in FindObjectsOfType<Item>())
{
Destroy(item.gameObject);
}
foreach (var item in currentSceneItems)
{
Item newItem = Instantiate(itemPrefab,item.position.ToVector3(),Quaternion.identity,ItemParent);
newItem.Init(item.itemId);// 根据id修改展示图片或模型
}
}
}
}