You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何替换/修复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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 09:52:33