如何替换/修复PrefabUtility.SaveAsPrefabAsset实现打包后皮肤选择功能?
问题分析
PrefabUtility.SaveAsPrefabAsset属于UnityEditor命名空间下的API,仅在Unity编辑器环境中可用,打包后的游戏运行时完全不包含该命名空间的代码,因此不可能让它在打包后生效。所谓的“添加Editor文件”只是将编辑器相关代码放到Editor文件夹,让Unity打包时自动排除这些代码避免编译错误,但无法解决运行时的皮肤保存需求。
以下是无需依赖编辑器的重构方案,核心逻辑是通过保存皮肤索引而非Prefab来实现持久化。
重构方案
1. 定义皮肤数据结构
创建SkinData可脚本化对象(或普通类),整合所有方向的Sprite,简化多列表维护:
using UnityEngine; [CreateAssetMenu(fileName = "SkinData", menuName = "Game/Skin Data")] public class SkinData : ScriptableObject { public Sprite idle; public Sprite left; public Sprite leftDown; public Sprite down; public Sprite rightDown; public Sprite right; public Sprite rightUp; public Sprite up; public Sprite leftUp; }
2. 修改皮肤管理器(SkinManager)
移除编辑器依赖,改用PlayerPrefs保存选中皮肤的索引:
using System.Collections.Generic; using UnityEngine; public class SkinManager : MonoBehaviour { public SpriteRenderer sr; public SpriteRenderer sLeft; public SpriteRenderer sLeftD; public SpriteRenderer sDown; public SpriteRenderer sRightD; public SpriteRenderer sRight; public SpriteRenderer sRightU; public SpriteRenderer sUp; public SpriteRenderer sLeftU; public List<SkinData> skins = new List<SkinData>(); private int selectedSkin = 0; void Start() { // 读取上次保存的皮肤索引 if (PlayerPrefs.HasKey("SelectedSkinIndex")) { selectedSkin = PlayerPrefs.GetInt("SelectedSkinIndex"); UpdateSkinDisplay(); } } public void NextOption() { selectedSkin = (selectedSkin + 1) % skins.Count; UpdateSkinDisplay(); } public void BackOption() { selectedSkin = (selectedSkin - 1 + skins.Count) % skins.Count; UpdateSkinDisplay(); } public void Select() { // 保存选中的皮肤索引到本地 PlayerPrefs.SetInt("SelectedSkinIndex", selectedSkin); PlayerPrefs.Save(); } private void UpdateSkinDisplay() { var currentSkin = skins[selectedSkin]; sr.sprite = currentSkin.idle; sLeft.sprite = currentSkin.left; sLeftD.sprite = currentSkin.leftDown; sDown.sprite = currentSkin.down; sRightD.sprite = currentSkin.rightDown; sRight.sprite = currentSkin.right; sRightU.sprite = currentSkin.rightUp; sUp.sprite = currentSkin.up; sLeftU.sprite = currentSkin.leftUp; } // 静态方法供其他脚本获取选中皮肤 public static SkinData GetSelectedSkin(List<SkinData> allSkins) { int index = PlayerPrefs.GetInt("SelectedSkinIndex", 0); if (index < 0 || index >= allSkins.Count) index = 0; return allSkins[index]; } }
3. 修改玩家控制脚本(Movement)
读取保存的皮肤索引,加载对应Sprite:
using UnityEngine; using UnityEngine.SceneManagement; public class Movement : MonoBehaviour { public bool gameStarted = false; private bool noReset = false; public List<SkinData> allSkins; // 在Inspector中拖入所有皮肤数据 private SpriteRenderer playerRenderer; private Sprite left; private Sprite right; private Sprite down; private Sprite up; private Sprite leftDown; private Sprite rightDown; private Sprite leftUp; private Sprite rightUp; private Sprite stare; private int sceneID = 0; void Start() { playerRenderer = GetComponent<SpriteRenderer>(); LoadSelectedSkin(); } private void LoadSelectedSkin() { SkinData selectedSkin = SkinManager.GetSelectedSkin(allSkins); stare = selectedSkin.idle; left = selectedSkin.left; right = selectedSkin.right; down = selectedSkin.down; up = selectedSkin.up; leftDown = selectedSkin.leftDown; rightDown = selectedSkin.rightDown; leftUp = selectedSkin.leftUp; rightUp = selectedSkin.rightUp; playerRenderer.sprite = stare; } void Update() { if (gameStarted) { float moveX = 0f; float moveY = 0f; float moveSpeed = 6f; bool isMoving = false; if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) { moveY = +1f; playerRenderer.sprite = up; isMoving = true; } if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) { moveX = -1f; playerRenderer.sprite = left; isMoving = true; } if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) { moveY = -1f; playerRenderer.sprite = down; isMoving = true; } if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow)) { moveX = +1f; playerRenderer.sprite = right; isMoving = true; } // 斜向移动Sprite处理 if ((Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) && (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))) playerRenderer.sprite = leftUp; if ((Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) && (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))) playerRenderer.sprite = rightUp; if ((Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) && (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))) playerRenderer.sprite = leftDown; if ((Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) && (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))) playerRenderer.sprite = rightDown; // 无输入时回到 idle 状态 if (!isMoving) playerRenderer.sprite = stare; Vector3 moveDir = new Vector3(moveX, moveY).normalized; transform.position += moveDir * moveSpeed * Time.deltaTime; } if (Input.GetKeyDown(KeyCode.Space) && !noReset) GameStart(); if (Input.GetKeyDown(KeyCode.R) && noReset && !gameStarted) RestartScene(); if (Input.GetKeyDown(KeyCode.Escape)) HomeScreen(); } public void HomeScreen() => SceneManager.LoadScene(sceneID); void GameStart() { gameStarted = true; noReset = true; } void GameEnd() => gameStarted = false; void OnTriggerEnter2D(Collider2D other) { if (other.gameObject.CompareTag("enemy")) GameEnd(); } public void RestartScene() => SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); }
额外优化建议
- 将
SkinManager改为单例模式,避免在Movement中手动传递皮肤列表。 - 若皮肤数量较多,可改用
Addressable或AssetBundle加载资源,减少初始打包体积。 - 可扩展皮肤解锁逻辑,用
PlayerPrefs保存已解锁皮肤的ID列表。
内容的提问来源于stack exchange,提问作者Matt Sou
相关产品推荐
相关产品推荐

