unity一些记录杂项 2023

/

As I understand it, these apply to how textures are sampled when they are scaled down:

  • Point (no filter) aka “nearest neighbor” is by far the fastest. It just takes a single point on the texture with no attempt at smoothing. Pixel art looks better like this, almost everything else looks worse.

  • Bilinear takes 4 sample points close to the point and blends them together. Hard edges will look blurrier and generally much better.

  • Trilinear only matters if MipMaps are enabled. MipMaps are pre-downscaled textures made with a high quality algorithm (Box = blurry, Kaiser = sharp). Trilinear filtering takes bilinear samples from two different mipmaps and blends those together.

  • Aniso is the 4th option; it takes extra trilinear samples, particularly when textures are squished in one dimension, like textures displayed at an angle to the camera, or 9-slice sprites. Aniso level “1” (the default) is off, but can be overridden in quality settings (Anisotropic Textures = Forced On). Aniso level “0” means ALWAYS stay off.

///

prefab解释

根据官网的介绍我们可以得知,在Unity中Prefab和纹理,材质,音效一样都是一种资源格式,预制件(Prefab)其实是一个资源载体(e.g. GameObject包含层级以及多种Component脚本做成的重用实体对象),为了方便重复使用做好的资源模版。

老版本预制件系统问题:

  1. 不支持嵌套预制件
  2. 不支持变体预制件(后续会介绍)

新版本预制件很好的解决了上面说到的两个问题。

新版预制件在Unity 2018.3推出
新版预制件主要由以下三个部分组成:

  1. 嵌套预制件
  2. 预制件变体
  3. 预制件模式

接下来挨个学习上面三个部分。

Note:
新版Prefab是一种Asset,但打开处理的时候更多的是一种对Content的封装的概念,打开Prefab Mode是指打开Prefab浏览编辑Prefab Content

嵌套预制件

嵌套预制件顾名思义是指预制件之间是允许引用使用的关系而不是完全独立复制使用的概念。
老版本预制件系统就是采用复制实体引用的方式。
而新版嵌套预制件是采用真实预制件引用的方式。

嵌套预制件举例说明:
预制件A里用到了预制件B,如果我们修改预制件B,那么预制件A应该是自动更新变化。

嵌套预制件实战:
如下所示,我们制作了三个预制件,分别是Prefab_Cube_A,Prefab_Sphere_B以及Prefab_A_And_B,其中前两者是独立的预制件,最后一个是使用前两个预制件拼接而成的新的预制件。
 

NestedPrefabExample

NestedPrefabExample

可以看到嵌套的预制件在新的预制件下是蓝色的(意味着是关联了特定预制件的意思),为了验证嵌套预制件,我简单的修改Prefab_Cube_A颜色来达到实现我们预期的嵌套预制件是引用关系的验证:
 

NestedPrefabChangeMat

NestedPrefabChangeMat
可以看到我们只修改了预制件Prefab_Cube_A的材质,但嵌套使用该预制件的Prefab_A_And_B里的Prefab_A也跟着变化了,这就是前面所提到的嵌套预制件的概念(嵌套引用关系而非实例化新个体)

预制件变体

预制件变体的概念类似于编程里继承的概念,继承属性的同时,父预制件的修改也会影响到子预制件变体。

预制件变体不仅继承了父预制件的状态数据,还跟父预制件是强制关联的。
举例说明:
通过预制件A创建预制件变体A2,当我们改变A时,A2继承的相同属性会跟着变化
 

NestedPrefabPrefabVariant

NestedPrefabPrefabVariant
 

NestedPrefabChangeVariantParent

NestedPrefabChangeVariantParent

适用情况:

  1. 需要在个别Prefab基础上做少许做成预制件使用,同时需要同步父预制件修改的情况下。

Note:

  1. 预制件变体可以理解成封装了一层Prefab的Prefab Asset
  2. 预制件变体的Content根节点时Prefab而Prefab的Content根节点时GameObject

新版预制件工作流

Prefab有两种状态:

  1. Prefab Asset(Prefab资源)
  2. Prefab Instance(Prefab实例对象—场景里是蓝色的)
    Prefab Instance和Prefab Asset之间是关联着的,跟老版预制件里的概念是一致的。我们可以通过修改Prefab Instance然后把改变Apply到Prefab Asset上。新版预制件有区别的是支持Component级别的Apply和Revert。

    NestedPrefabComponentApply

    NestedPrefabComponentApply

还有一种独立于预制件的状态:实例对象
 

UnpackPrefabInstance

UnpackPrefabInstance
于预制件无关的实体对象是黑色的而非蓝色:
 

NestedPrefabUnpackPrefabColor

NestedPrefabUnpackPrefabColor
这里的Unpack相当于解除了Prefab Asset关联直接访问预制件的Content(Prefab Content后续会说到,在新版预制件里很重要的一个概念)

新版预制件有一个新的概念叫做预制件模式,修改并Apply修改到Prefab Asset上有两种方式:

  1. 直接打开Prefab Mode编辑
  2. 修改Prefab Instance后Apply到Prefab Asset上

这里重点讲解学习Prefab Mode:
Prefab Mode类似单独打开了一个修改预制件的场景,进入新的预制件场景才能看到预制件里的内容,这一点概念很重要
 

NestedPrefabOpenPrefabMode

NestedPrefabOpenPrefabMode
 

NestedPrefabMode

NestedPrefabMode
在Prefab Mode下的修改可以通过保存和Undo操作来实现保存到Prefab Asset和撤销操作的效果。

了解了新版Prefab的Prefab Mode概念后,让我们结合相关API来制作一些新版Prefab的处理操作工具来深入理解:
需求:
检查指定Prefab里是否有无效的脚本挂在(Missing Script)并清除所有无效挂在脚本后保存Prefab Asset
 

NestedPrefabMissingScriptMono

NestedPrefabMissingScriptMono

相关API:

  1. AssetDatabase(处理Asset的工具类接口)
  2. Selection(处理Asset资源选中工具类接口)

步骤:

  1. 选中Prefab Asset
  2. 判定是否是选中Prefab Asset
  3. 打开Prefab Mode
  4. 查找并删除所有Missing Script
  5. 保存Prefab Asset并退出Prefab Mode
    实现:
    RemovePrefabMissingScriptTool.cs
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEditor;
    using UnityEngine;
    
    /// <summary>
    /// 删除预制件无效脚本引用工具
    /// </summary>
    public class RemovePrefabMissingScriptTool
    {
        [MenuItem("Tools/Assets/移除Missing脚本 #&s", false)]
        public static void RemoveAllMissingScripts()
        {
            // 1. 选中Prefab Asset
            var selections = Selection.GetFiltered(typeof(Object), SelectionMode.Assets | SelectionMode.DeepAssets);
            Debug.Log($"选中有效数量:{selections.Length}");
            foreach (var selection in selections)
            {
                if(IsPrefabAsset(selection))
                {
                    RemovePrefabAllMissingScripts(selection as GameObject);
                }
                else
                {
                    Debug.LogError($"不符合预制件要求的Asset:{selection.name}");
                }
            }
            AssetDatabase.SaveAssets();
        }
    
        /// <summary>
        /// 是否是预制件资源
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        private static bool IsPrefabAsset(Object obj)
        {
            if(obj is GameObject)
            {
                var assetpath = AssetDatabase.GetAssetPath(obj);
                return !string.IsNullOrEmpty(assetpath);
            }
            else
            {
                return false;
            }
        }
    
        /// <summary>
        /// 移除预制件Asset所有无效的脚本
        /// </summary>
        private static void RemovePrefabAllMissingScripts(GameObject go)
        {
            // 3. 打开Prefab Mode
            // 4. 查找并删除所有Missing Script
            // 5. 保存Prefab Asset并推出Prefab Mode)
            var assetpath = AssetDatabase.GetAssetPath(go);
            var contentsRoot = PrefabUtility.LoadPrefabContents(assetpath);
            RemoveAllMissingScripts(contentsRoot);
            PrefabUtility.SaveAsPrefabAsset(contentsRoot, assetpath);
            PrefabUtility.UnloadPrefabContents(contentsRoot);
        }
    
        /// <summary>
        /// 移除无效的脚本
        /// </summary>
        private static void RemoveAllMissingScripts(GameObject go)
        {
            Component[] components = go.GetComponents<Component>();
            var r = 0;
            var serializedObject = new SerializedObject(go);
            var prop = serializedObject.FindProperty("m_Component");
            for (int i = 0; i < components.Length; i++)
            {
                if (components[i] == null)
                {
                    string s = go.name;
                    Transform t = go.transform;
                    while (t.parent != null) 
                    {
                        s = t.parent.name +"/"+s;
                        t = t.parent;
                    }
                    Debug.Log (s + " has an empty script attached in position: " + i, go);
    
                    prop.DeleteArrayElementAtIndex(i - r);
                    r++;
                }
            }
            serializedObject.ApplyModifiedProperties();
            foreach (Transform childT in go.transform)
            {
                RemoveAllMissingScripts(childT.gameObject);
            }
        }
    }
    

核心思想是因为Missing Component本来就为Null无法向常规Component那样DestroyImmediate,需要通过SerializedObject去获取Component的方式删除指定位置的Component。

此脚本只针对单个预制件内容进行修改,嵌套预制件需要通过批量多选的方式实现各自移除各自身上的Missing Script来实现移除所有Missing脚本。

了解了Prefab Mode的存在,那么如何监听Prefab Mode的打开关闭生命周期了?
这里我们需要用到PrefabStage API(新版Prefab Mode下打开保存相关生命周期工具类接口)
这里只是简单的做个API实验,看看PrefabStage是否起作用。
PrefabStageListener.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Experimental.SceneManagement;
using UnityEngine;

/// <summary>
/// 新版预制件生命周期监听
/// </summary>
[ExecuteInEditMode]
public class PrefabStageListener : MonoBehaviour
{
    private void Start()
    {
        PrefabStage.prefabSaved += OnPrefabSaved;
        PrefabStage.prefabSaving += OnPrefabSaving;
        PrefabStage.prefabStageOpened += OnPrefabOpened;
        PrefabStage.prefabStageClosing += OnPrefabClosing;
    }

    private void OnDestroy()
    {
        PrefabStage.prefabSaved -= OnPrefabSaved;
        PrefabStage.prefabSaving -= OnPrefabSaving;
        PrefabStage.prefabStageOpened -= OnPrefabOpened;
        PrefabStage.prefabStageClosing -= OnPrefabClosing;
    }

    /// <summary>
    /// 响应预制件内容保存完毕
    /// </summary>
    /// <param name="go"></param>
    private void OnPrefabSaved(GameObject go)
    {
        Debug.Log("预制件内容保存完毕!");
    }

    /// <summary>
    /// 响应预制件内容保存
    /// </summary>
    /// <param name="go"></param>
    private void OnPrefabSaving(GameObject go)
    {
        Debug.Log("预制件内容保存!");
    }

    /// <summary>
    /// 响应预制件内容打开
    /// </summary>
    /// <param name="prefabstage"></param>
    private void OnPrefabOpened(PrefabStage prefabstage)
    {
        Debug.Log("打开预制件内容!");
        Debug.Log($"预制件:{prefabstage.prefabAssetPath} 内容阶段:{prefabstage.stageHandle}");
    }
    
    /// <summary>
    /// 响应预制件内容关闭
    /// </summary>
    /// <param name="prefabstage"></param>
    private void OnPrefabClosing(PrefabStage prefabstage)
    {
        Debug.Log("关闭预制件内容!");
        Debug.Log($"预制件:{prefabstage.prefabAssetPath} 内容阶段:{prefabstage.stageHandle}");
    }
}

结果:

PrefabStageListener

PrefabStageListener
PrefabStage.prefabStageOpened全局监听不起作用(原因未知,忘知道的朋友告知)。PrefabStage.prefabStageClosing能正确工作。

Note:
Prefab Mode模式下修改叫做Save而非Apply,因为已经处于Prefab Content模式下了

///Recipe for linking files under a specific dir at once  /

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
using System.Linq;
#endif

public class GameImgHolder : MonoBehaviour {

    public List<Sprite> imgList;

#if UNITY_EDITOR
    [Button("SetupImg" , "Setup Image")]
    public int SetupImgButton;

    void SetupImg(){

        // path , imgdir
        string prefabPath = "Assets/Prefab/GameManager.prefab";
        string imgDir = "Assets/Images/Test";

        GameManager gm = AssetDatabase.LoadAssetAtPath(prefabPath , typeof(GameManager)) as GameManager;
        GameImgHolder imgHolder = gm.GetComponent<GameImgHolder>();

      
        var guids = AssetDatabase.FindAssets("" , new string[]{imgDir});
        var pathList = guids.ToList().Select(guid => AssetDatabase.GUIDToAssetPath(guid)).Distinct();

        // sprite load
        var sprites = pathList.Select(path => AssetDatabase.LoadAssetAtPath(path , typeof(Sprite)) as Sprite);
        imgHolder.imgList = sprites.ToList();

       
        EditorUtility.SetDirty(gm);
        AssetDatabase.SaveAssets();

       
        PrefabUtility.RevertPrefabInstance(GameManager.Instance.gameObject); 

#endif
}

prefab property //

Create your own PropertyDrawer that prevents you from attaching anything other than Prefab

using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour
{
    [PrefabField]
    public GameObject hoge;
}

using UnityEngine;

public class PrefabFieldAttribute : PropertyAttribute
{
}

using UnityEngine;
using UnityEditor;

[CustomPropertyDrawer(typeof(PrefabFieldAttribute))]
public class PrefabFieldDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        if (property.objectReferenceValue != null)
        {
            var prefabType = PrefabUtility.GetPrefabType(property.objectReferenceValue);
            switch (prefabType)
            {
                case PrefabType.Prefab:
                case PrefabType.ModelPrefab:
                    break;
                default:
                    // Prefab以外がアタッチされた場合アタッチを外す
                    property.objectReferenceValue = null;
                    break;
            }
        }

        label.text += " (Prefab Only)";
        EditorGUI.PropertyField(position, property, label);
    }
}
 

/// Prefab drop //

Create Prefab from multiple objects at onceCreate Prefab from multiple objects at once

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

/// <summary>
/// 複数のオブジェクトをまとめてprefab化するエディタ拡張
/// </summary>
public class MultiPrefabCreator : EditorWindow {

    // 出力先
    private string PATH_FOLDER = "Assets/Resources/Prefab/Temp/";

    private HashSet<GameObject> m_dropList = new HashSet<GameObject>();

    //メニューに項目追加
    [MenuItem("拡張/MultiPrefabCreator")]
    static void open()
    {
        EditorWindow.GetWindow<MultiPrefabCreator>("MultiPrefabCreator");
    }

    void OnGUI()
    {
        EditorGUILayout.LabelField("MultiPrefabCreator");
        var dropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUILayout.ExpandWidth(true));
        GUI.Box(dropArea, "ここにPrefab化したいものをまとめてをドラッグ&ドロップ");

        var evt = Event.current;
        switch (evt.type)
        {
            case EventType.DragPerform:
                if (!dropArea.Contains(evt.mousePosition))
                {
                    break;
                }
                DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
                DragAndDrop.AcceptDrag();
                break;

            case EventType.DragExited:
                foreach (GameObject go in DragAndDrop.objectReferences)
                {
                    Debug.Log(go);
                    m_dropList.Add(go);
                }

                CreatePrefabs();
                Event.current.Use();
                break;
        }
    }


    /// <summary>
    /// Prefab化
    /// </summary>
    void CreatePrefabs()
    {
        foreach (GameObject go in m_dropList)
        {
            if (null == m_dropList)
            {
                return;
            }
            string prefab = PATH_FOLDER + go.name + ".prefab";
            PrefabUtility.SaveAsPrefabAsset(go,prefab);

        }
        m_dropList.Clear();
    }
}

Prefab replace /

InvalidOperationException: Destroying a GameObject inside a Prefab instance is not allowed.

Then I have to go manual to open the prefab and remove the gameobject from in there. But is there a way to do it by script automatic ?

This is my script to replace gameobjects with prefab. The only change I did for the prefab is taking a gmeobject and added to it some colors and texture all the scripts and other stuff are the same.

I'm trying to replace all the doors in the scene with the prefab that is also a door just with some colors and texture

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class PrefabReplace : EditorWindow
{
    [SerializeField] private GameObject prefab;
    private bool selectionChanged;
    private string objectsToSearch = "";
    private List<GameObject> foundObjects = new List<GameObject>();
    private List<GameObject> duplicatedObjects = new List<GameObject>();
    private bool searched = false;
    private int count = 0;
    private int countChilds = 0;
    private bool countChildren = false;
    private GUIStyle guiStyle = new GUIStyle(); //create a new variable
    private Texture timage;

    [MenuItem("Tools/Prefab Replace")]
    static void CreateReplaceWithPrefab()
    {
        int width = 340;
        int height = 300;

        int x = (Screen.currentResolution.width - width) / 2;
        int y = (Screen.currentResolution.height - height) / 2;

        GetWindow<PrefabReplace>().position = new Rect(x, y, width, height);
    }

    private void OnGUI()
    {
        Texture oo = null;
        Texture texture = (Texture)oo;
        //EditorGUI.DrawTextureTransparent(new Rect(10, 10, 20, 20), timage);

        guiStyle.fontSize = 20; //change the font size
        Searching();
        GUILayout.Space(50);
        Replacing();
    }

    private void Searching()
    {
        GUI.Label(new Rect(10, 20, 150, 20), "Search by name", guiStyle);
        objectsToSearch = GUI.TextField(new Rect(90, 60, 150, 20), objectsToSearch, 25);

        if (objectsToSearch != "")
        {
            GUI.enabled = true;
        }
        else
        {
            GUI.enabled = false;
        }
        GUILayout.Space(40);
        if (GUILayout.Button("Search"))
        {
            foundObjects = new List<GameObject>();
            duplicatedObjects = new List<GameObject>();
            countChildren = true;
            countChilds = 0;
            count = 0;

            foreach (GameObject gameObj in GameObject.FindObjectsOfType<GameObject>())
            {
                if (gameObj.name == objectsToSearch)
                {
                    count += 1;
                    foundObjects.Add(gameObj);
                    Transform[] childs = gameObj.GetComponentsInChildren<Transform>();
                    foreach (Transform go in childs)
                    {
                        foundObjects.Add(go.gameObject);
                    }
                }
            }

            if (foundObjects.Count > 0)
            {
                searched = true;
            }
            else
            {
                searched = false;
            }
        }

        GUI.enabled = true;
        if (count > 0)
            GUI.TextField(new Rect(90, 85, 60, 15), count.ToString(), 25);

        if (foundObjects.Count > 0 && countChildren == true)
        {
            for (int i = 0; i < foundObjects.Count; i++)
            {
                if (foundObjects[i].transform.childCount > 0)
                {
                    countChilds += foundObjects[i].transform.childCount;
                }
            }

            countChildren = false;
        }
        GUI.enabled = true;
        if (countChilds > 0)
            GUI.TextField(new Rect(90, 105, 60, 15), countChilds.ToString(), 25);

        GUILayout.Space(100);

        if (foundObjects.Count > 0)
            EditorGUILayout.LabelField("Test");
    }

    private void Replacing()
    {
        GUILayout.Space(20);
        GUILayout.BeginVertical(GUI.skin.box);
        GUILayout.Label("Replacing");
        GUILayout.Space(20);

        prefab = (GameObject)EditorGUILayout.ObjectField("Prefab", prefab, typeof(GameObject), false);

        var selection = Selection.objects.OfType<GameObject>().ToList();
        if (selectionChanged)
        {
            if (selection.Count == 0)
                GUI.enabled = false;

            for (var i = selection.Count - 1; i >= 0; --i)
            {
                var selectedObject = selection[i];
                if (prefab != null && selection.Count > 0 &&
                    selectedObject.scene.name != null
                    && prefab != PrefabUtility
                    .GetCorrespondingObjectFromSource(selectedObject))
                {
                    GUI.enabled = true;
                }
                else
                {
                    GUI.enabled = false;
                }
            }
        }
        else
        {
            GUI.enabled = false;
        }

        if (GUILayout.Button("Replace"))
        {
            InstantiatePrefab(selection);
            selectionChanged = false;
        }

        GUILayout.Space(10);
        GUI.enabled = true;
        EditorGUILayout.LabelField("Selection count: " + Selection.objects.OfType<GameObject>().Count());

        GUILayout.EndVertical();
    }

    private void OnInspectorUpdate()
    {
        Repaint();
    }

    private void OnSelectionChange()
    {
        selectionChanged = true;
    }

    private void InstantiatePrefab(List<GameObject> selection)
    {
        if (prefab != null && selection.Count > 0)
        {
            for (var i = selection.Count - 1; i >= 0; --i)
            {
                var selected = selection[i];
                Component[] components = selected.GetComponents(typeof(MonoBehaviour));
                if (components.Length == 0)
                {
                    SceneManager.SetActiveScene(SceneManager.GetSceneByName(selected.scene.name));

                    var prefabType = PrefabUtility.GetPrefabType(prefab);
                    GameObject newObject;

                    if (prefabType == PrefabType.Prefab)
                    {
                        newObject = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
                    }
                    else
                    {
                        newObject = Instantiate(prefab);
                        newObject.name = prefab.name;
                    }
                    if (newObject == null)
                    {
                        Debug.LogError("Error instantiating prefab");
                        break;
                    }

                    Undo.RegisterCreatedObjectUndo(newObject, "Replace With Prefabs");
                    newObject.transform.parent = selected.transform.parent;
                    newObject.transform.localPosition = selected.transform.localPosition;
                    newObject.transform.localRotation = selected.transform.localRotation;
                    newObject.transform.localScale = selected.transform.localScale;
                    newObject.transform.SetSiblingIndex(selected.transform.GetSiblingIndex());
                    Undo.DestroyObjectImmediate(selected);
                }
            }
        }
    }
}

// unity 新版 prefab 所有函数记录 /

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;


public class PrefabViewer : EditorWindow
{

    [MenuItem("Window/PrefabViewer")]
    static void Show()
    {
        EditorWindow.GetWindow<PrefabViewer>();
    }

    void OnGUI()
    {
        if (Selection.activeGameObject != null &&
            PrefabUtility.GetPrefabType(Selection.activeGameObject) == PrefabType.Prefab)
        {
            var root = PrefabUtility.FindRootGameObjectWithSameParentPrefab(Selection.activeGameObject);
            // 对于Unity2017.2.1或更高版本,请删除注释并使用它。
            // var root = PrefabUtility.FindPrefabRoot(Selection.activeGameObject);
            FoldOutRecursive(root.transform);
        }
        Repaint();
    }

    void FoldOutRecursive(Transform t)
    {
        EditorGUI.indentLevel++;
        EditorGUILayout.Foldout(true, t.name);
        foreach (Transform c in t)
        {
            FoldOutRecursive(c);
        }
        EditorGUI.indentLevel--;
    }
}

public class all_Prefab_function_test : MonoBehaviour
{

    [MenuItem("Assets/Create/New Prefab With Components")]
    public static void CreatePrefabWithComponents()
    {
        string name = "target";
        string outputPath = "Assets/WithComponents.prefab";

        GameObject gameObject = EditorUtility.CreateGameObjectWithHideFlags(name, HideFlags.HideInHierarchy, typeof(Rigidbody), typeof(BoxCollider));

        gameObject.GetComponent<Rigidbody>().isKinematic = true;
        gameObject.GetComponent<BoxCollider>().size = 5.0F * Vector3.one;

        string assetPath = AssetDatabase.GetAssetPath(PrefabUtility.GetCorrespondingObjectFromSource(gameObject));

        PrefabUtility.SaveAsPrefabAssetAndConnect(gameObject, outputPath, InteractionMode.UserAction);

        Editor.DestroyImmediate(gameObject);
    }

    [MenuItem("Assets/Create/New Prefab")]
    public static void NewPrefab()
    {
        string name = "target";
        string outputPath = "Assets/Prefab.prefab";

        GameObject gameObject = EditorUtility.CreateGameObjectWithHideFlags(name, HideFlags.HideInHierarchy);

        bool bSaveSucess = false;
        PrefabUtility.SaveAsPrefabAsset(gameObject, outputPath, out bSaveSucess);

        Editor.DestroyImmediate(gameObject);
    }

    [MenuItem("Assets/Remove All Components")]
    public static void DoRemoveAllComponents()
    {
        GameObject[] selectedObjects = Selection.gameObjects;
        foreach (GameObject selectedObject in selectedObjects)
        {
            PrefabAssetType prefabType = PrefabUtility.GetPrefabAssetType(selectedObject);
            if (prefabType == PrefabAssetType.Regular || prefabType == PrefabAssetType.Variant)
            {
                string path = AssetDatabase.GetAssetPath(selectedObject);
                GameObject originalPrefab = PrefabUtility.LoadPrefabContents(path);

                Component[] components = originalPrefab.GetComponents<Component>();
                foreach (Component component in components)
                {
                    if (component is Transform) continue;
                    if (component is Animator) continue;
                    DestroyImmediate(component, true);
                }
                PrefabUtility.SaveAsPrefabAsset(originalPrefab, path);
                PrefabUtility.UnloadPrefabContents(originalPrefab);
                Debug.Log("[RemoveAllComponent] Success. paht=" + path);
            }
            else
            {
                Debug.Log("[RemoveAllComponent] Please select a Prefab to remove components from.");
            }
        }
    }

    /// <summary>
    /// 把Prefab中的Transform替换为RectTransform
    /// 有个不足的地方是prefab的继承关系会丢失(Variant关系丢失)
    /// 如果一个prefab中引用另一个prefab,这种关系也会丢失
    /// </summary>
    /// <typeparam name="T">RectTransform</typeparam>
    /// <param name="obj"></param>
    private void AddComponentFromPrefab<T>(GameObject obj) where T : UnityEngine.Component
    {
        PrefabAssetType prefabType = PrefabUtility.GetPrefabAssetType(obj);
        //Debug.Log($"AddComponentFromPrefab,name:{obj.name},PrefabAssetType:{prefabType}");
        switch (prefabType)
        {

            case PrefabAssetType.Regular:
            case PrefabAssetType.Variant:
                //获取prefab实例的根节点
                GameObject prefabRoot = PrefabUtility.GetOutermostPrefabInstanceRoot(obj);
                //获取prefab资源,在project中的
                GameObject prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(obj);
                //Object prefabAsset = PrefabUtility.GetCorrespondingObjectFromOriginalSource(obj);
                //获取资源的路径名字
                string assetPath = AssetDatabase.GetAssetPath(prefabAsset);
                //Debug.Log(assetPath);

                //断开Model的联系,如果是Model是禁止修改RectTransform的
                PrefabUtility.UnpackPrefabInstance(prefabRoot, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction); //有个不足的地方是prefab的继承关系会丢失,这里是完全断开连接,非完全的方式没有测试
                PrefabUtility.SaveAsPrefabAssetAndConnect(prefabRoot, assetPath, InteractionMode.AutomatedAction);

                //修改prefab的RectTransform,如果修改实例的RectTransfom是失败的(Model虽然断开连接,但是还是禁止直接修改prefab实例的RectTransfom)
                GameObject pobj = PrefabUtility.LoadPrefabContents(assetPath);
                var ts = pobj.GetComponentsInChildren<Transform>();
                foreach (var t in ts)
                    if (!(t is RectTransform))
                        t.gameObject.AddComponent<T>();
                PrefabUtility.SaveAsPrefabAsset(pobj, assetPath);
                PrefabUtility.UnloadPrefabContents(pobj);
                break;
            case PrefabAssetType.Model:
                PrefabUtility.UnpackPrefabInstanceAndReturnNewOutermostRoots(obj, PrefabUnpackMode.Completely);
                if (!(obj.transform is T))
                    obj.AddComponent<T>();
                break;
            default:
                break;
        }
    }

    private void ModifyPrefab()
    {
        //string prefabPath = "";
        //GameObject prefabInstance = PrefabUtility.LoadPrefabContents(prefabPath);
        //SerializedObject serializedObject = new SerializedObject(prefabInstance.GetComponent<myScript>());
        //serializedObject.FindProperty("str").stringValue = "xxxxx";//字符串类型
        //serializedObject.ApplyModifiedProperties();//应用修改
        //PrefabUtility.SaveAsPrefabAsset(prefabInstance, prefabPath);//保存
    }


    [MenuItem("Assets/Util/Prefragment/AssignMeshFromFilterToCollider", false, 2351)]
    private static void AssignMeshFromFilterToCollider()
    {
        UnityEngine.Object[] selectedObjects = Selection.GetFiltered(typeof(UnityEngine.GameObject), SelectionMode.Assets);

        foreach (GameObject selectedSceneOrPrefabObject in selectedObjects)
        {
            string assetPath = AssetDatabase.GetAssetPath(selectedSceneOrPrefabObject);
            //AssetDatabase.LoadMainAssetAtPath(path) as GameObject;

            //var scene = EditorSceneManager.NewPreviewScene();
            //var roots = new List<GameObject>();

            //foreach (var prefab in allPrefabsPaths)
            //{
            //    PrefabUtility.LoadPrefabContentsIntoPreviewScene(prefab, scene);
            //    scene.GetRootGameObjects(roots);
            //    var root = roots[0];
            //    IterateAllGameObjects(root, true);
            //}

            //EditorSceneManager.ClosePreviewScene(scene);

            var root = PrefabUtility.LoadPrefabContents(assetPath);

            Transform player = PrefabUtility.LoadPrefabContents(assetPath).transform;

            int count = player.childCount;

            for (int i = 0; i < count; i++)
            {
                Transform obj = player.GetChild(0);

                Transform childTrfm = obj.GetComponent<Transform>();
            }


            Transform[] trfms = root.GetComponentsInChildren<Transform>();
            foreach (Transform iter in trfms)
            {
                var pos = iter.position;
            }

            //PrefabUtility.UnpackPrefabInstance(root, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
            //var child = root.FindInChildren(childName);
            //if (child)
            //{
            //    UnityEngine.Object.DestroyImmediate(child.gameObject);
            //    PrefabUtility.SaveAsPrefabAsset(root, path);
            //}

            PrefabUtility.SaveAsPrefabAsset(root, assetPath);
            PrefabUtility.UnloadPrefabContents(root);

        }
    }

    [MenuItem("Examples/Add BoxCollider to Prefab Asset")]
    static void AddBoxColliderToPrefab()
    {
        // Get the Prefab Asset root GameObject and its asset path.
        GameObject assetRoot = Selection.activeObject as GameObject;
        string assetPath = AssetDatabase.GetAssetPath(assetRoot);

        // Load the contents of the Prefab Asset.
        GameObject contentsRoot = PrefabUtility.LoadPrefabContents(assetPath);

        // Modify Prefab contents.
        contentsRoot.AddComponent<BoxCollider>();

        // Save contents back to Prefab Asset and unload contents.
        PrefabUtility.SaveAsPrefabAsset(contentsRoot, assetPath);
        PrefabUtility.UnloadPrefabContents(contentsRoot);
    }

    [MenuItem("Examples/Add BoxCollider to Prefab Asset", true)]
    static bool ValidateAddBoxColliderToPrefab()
    {
        GameObject go = Selection.activeObject as GameObject;
        if (go == null)
            return false;

        return PrefabUtility.IsPartOfPrefabAsset(go);
    }

    [MenuItem("Examples/Test--LoadPrefabContents")]
    private static void a()
    {
        GameObject prefab = Selection.activeObject as GameObject;

        string prefabPath = AssetDatabase.GetAssetPath(prefab);
        // 将预制件加载到内部场景中
        var contentsRoot = PrefabUtility.LoadPrefabContents(prefabPath);

        var gameObject = new GameObject();
        gameObject.transform.SetParent(contentsRoot.transform);

        PrefabUtility.SaveAsPrefabAsset(contentsRoot, prefabPath);
        PrefabUtility.UnloadPrefabContents(contentsRoot);
    }

    [MenuItem("Examples/Test--GetNearestPrefabInstanceRoot")]
    private static void b()
    {
        Object componentOrGameObject = Selection.activeObject;

        // 来自一些预制件实例(组件和游戏对象), 获取Prefab实例的根GameObject, 如果嵌套,则返回子级
        Selection.activeObject = PrefabUtility.GetNearestPrefabInstanceRoot(componentOrGameObject);
    }

    [MenuItem("Examples/Test--UnpackPrefabInstance")]
    private static void c()
    {
        Object selectObj = Selection.activeObject;

        GameObject instanceGO = Instantiate(selectObj) as GameObject;

        // 传递实例根并断开与 Prefab 的连接,  UnpackPrefabInstance must be called with a Prefab instance.
        PrefabUtility.UnpackPrefabInstance(instanceGO, PrefabUnpackMode.OutermostRoot, InteractionMode.AutomatedAction);
    }

    [MenuItem("Examples/Test--GetCorrespondingObjectFromOriginalSource")]
    private static void d()
    {
        Object componentOrGameObject = Selection.activeObject;

        // 如果传递实例组件或GameObject,返回Prefab中对应的组件或者GameObject
        Selection.activeObject = PrefabUtility.GetCorrespondingObjectFromOriginalSource(componentOrGameObject);
    }

    [MenuItem("Examples/Test--GetOutermostPrefabInstanceRoot")]
    private static void e()
    {
        Object componentOrGameObject = Selection.activeObject;

        // 来自一些预制件实例(组件和游戏对象), 获取Prefab实例的根GameObject, 如果嵌套,则返回父级
        GameObject go = PrefabUtility.GetOutermostPrefabInstanceRoot(componentOrGameObject);
    }

    [MenuItem("Examples/Test--GetNearestPrefabInstanceRoot")]
    private static void f()
    {
        Object componentOrGameObject = Selection.activeObject;

        // 来自一些预制件实例(组件和游戏对象), 获取Prefab实例的根GameObject, 如果嵌套,则返回子级
        Selection.activeObject = PrefabUtility.GetNearestPrefabInstanceRoot(componentOrGameObject);
    }

    [MenuItem("Examples/Test--OpenAsset")]
    private static void g()
    {
        GameObject prefab = Selection.activeObject as GameObject;
        AssetDatabase.OpenAsset(prefab);
    }

    private static void FindTransformRecursive(Transform transform)
    {
        for (int i = 0; i < transform.childCount; ++i)
        {
            Transform child = transform.GetChild(i);
            if (child.name.StartsWith("test1_", System.StringComparison.CurrentCulture) || child.name.StartsWith("test2_", System.StringComparison.CurrentCulture))
                DestroyImmediate(child.gameObject); // -_-

            //if (i > 0)   // -_-
            {
                if (child.childCount > 0)
                    FindTransformRecursive(transform.GetChild(i));
            }
        }
    }


    // Creates a new menu item 'Examples > Create Prefab' in the main menu.
    [MenuItem("Examples/Create Prefab")]
    static void CreatePrefab()
    {
        // Keep track of the currently selected GameObject(s)
        GameObject[] objectArray = Selection.gameObjects;

        // Loop through every GameObject in the array above
        foreach (GameObject gameObject in objectArray)
        {
            // Create folder Prefabs and set the path as within the Prefabs folder,
            // and name it as the GameObject's name with the .Prefab format
            if (!Directory.Exists("Assets/Prefabs"))
                AssetDatabase.CreateFolder("Assets", "Prefabs");
            string localPath = "Assets/Prefabs/" + gameObject.name + ".prefab";

            // Make sure the file name is unique, in case an existing Prefab has the same name.
            localPath = AssetDatabase.GenerateUniqueAssetPath(localPath);

            // Create the new Prefab and log whether Prefab was saved successfully.
            bool prefabSuccess;
            PrefabUtility.SaveAsPrefabAssetAndConnect(gameObject, localPath, InteractionMode.UserAction, out prefabSuccess);
            if (prefabSuccess == true)
                Debug.Log("Prefab was saved successfully");
            else
                Debug.Log("Prefab failed to save" + prefabSuccess);
        }
    }

    // Disable the menu item if no selection is in place.
    [MenuItem("Examples/Create Prefab", true)]
    static bool ValidateCreatePrefab()
    {
        return Selection.activeGameObject != null && !EditorUtility.IsPersistent(Selection.activeGameObject);
    }

    [MenuItem("Examples/Apply Selected Prefab &[")]
    public static void ApplySelectedPrefab()
    {
        GameObject go = GetCurrentlySelectedObjectHierarchy();

        if (go == null)
        {
            Debug.LogWarning("Selection is not a GameObject");
            return;
        }

        GameObject rootGO = PrefabUtility.GetOutermostPrefabInstanceRoot(go);

        if (rootGO == null)
        {
            Debug.LogWarning(string.Format("Selected GameObject ({0}) is not part of a prefab", go), go);
            return;
        }

        PrefabInstanceStatus prefabInstanceStatus = PrefabUtility.GetPrefabInstanceStatus(rootGO);
        if (prefabInstanceStatus != PrefabInstanceStatus.Connected)
        {
            Debug.LogWarning(string.Format("Selected Prefab Root of {0} ({1}) has invalid status of {2}", go, rootGO, prefabInstanceStatus), rootGO);
            return;
        }

        if (!PrefabUtility.HasPrefabInstanceAnyOverrides(rootGO, false))
        {
            Debug.LogWarning(string.Format("Selected Prefab Root of {0} ({1}) doesn't have any overrides", go, rootGO), rootGO);
            return;
        }

        PrefabUtility.ApplyPrefabInstance(rootGO, InteractionMode.UserAction);
        AssetDatabase.SaveAssets();

        Debug.Log(string.Format("Changes on {0} applied to {1}", go, rootGO), rootGO);
    }

    private static GameObject GetCurrentlySelectedObjectHierarchy() => Selection.activeGameObject;


    [MenuItem("Examples/ApplyRemovedGameObject Example 1")]
    static void CreatePrefabAndApplyChanges()
    {
        //Ensure the existence of a Prefabs folder inside the Assets folder
        if (!Directory.Exists("Assets/Prefabs"))
            AssetDatabase.CreateFolder("Assets", "Prefabs");

        //Setup hierarchy with root and one child
        GameObject rootGameObject = new GameObject("Root");
        GameObject child = new GameObject("Child");
        child.transform.parent = rootGameObject.transform;

        //Create prefab based on the GameObject hierarchy we just created
        GameObject prefabAsset = PrefabUtility.SaveAsPrefabAssetAndConnect(rootGameObject, "Assets/Prefabs/" + rootGameObject.name + ".prefab", InteractionMode.AutomatedAction);

        //Get the corresponding object matching the Child GameObject that was destroyed
        GameObject correspondingChildGameObject = prefabAsset.transform.GetChild(0).gameObject;

        //Destroy child GameObject so we can apply the override to the Prefab
        Object.DestroyImmediate(child);

        //Use the variables from above to apply the removed GameObject override to the Prefab asset
        //PrefabUtility.ApplyRemovedGameObject(rootGameObject, correspondingChildGameObject, InteractionMode.AutomatedAction);

        if (prefabAsset.transform.childCount == 0)
            Debug.Log("'Child' GameObject was removed and the override was applied to the Prefab successfully.");
        else
            Debug.Log("The override was not applied successfully");
    }

    [MenuItem("Examples/ApplyRemovedGameObject Example 2")]
    static void CreatePrefabAndApplyChangesWithGetRemovedGameObjects()
    {
        //Ensure the existence of a Prefabs folder inside the Assets folder
        if (!Directory.Exists("Assets/Prefabs"))
            AssetDatabase.CreateFolder("Assets", "Prefabs");

        //Setup hierarchy with root and one child
        GameObject rootGameObject = new GameObject("Root");
        GameObject child = new GameObject("Child");
        child.transform.parent = rootGameObject.transform;

        //Create prefab based on the GameObject hierarchy we just created
        GameObject prefabAsset = PrefabUtility.SaveAsPrefabAssetAndConnect(rootGameObject, "Assets/Prefabs/" + rootGameObject.name + ".prefab", InteractionMode.AutomatedAction);

        //Destroy child GameObject so we can apply the override to the Prefab
        Object.DestroyImmediate(child);

        //Get the override and the information to apply the changes to the Prefab asset
        //List<RemovedGameObject> removedGameObjects = PrefabUtility.GetRemovedGameObjects(rootGameObject);
        //GameObject assetGameObject = removedGameObjects[0].assetGameObject;
        //GameObject parentOfRemovedGameObjectInInstance = removedGameObjects[0].parentOfRemovedGameObjectInInstance;

        //Use the variables from above to apply the removed GameObject override to the Prefab
        //PrefabUtility.ApplyRemovedGameObject(parentOfRemovedGameObjectInInstance, assetGameObject, InteractionMode.AutomatedAction);

        if (prefabAsset.transform.childCount == 0)
            Debug.Log("'Child' GameObject was removed and the override was applied to the Prefab successfully.");
        else
            Debug.Log("The override was not applied successfully");
    }

    [MenuItem("Prefabs/Test_EditPrefabContentsScope")]
    public static void Test()
    {
        // Create a simple test Prefab Asset. Looks like this:
        // Root
        //    A
        //    B
        //    C
        var assetPath = "Assets/MyTempPrefab.prefab";
        var source = new GameObject("Root");
        var childA = new GameObject("A");
        var childB = new GameObject("B");
        var childC = new GameObject("C");
        childA.transform.parent = source.transform;
        childB.transform.parent = source.transform;
        childC.transform.parent = source.transform;
        PrefabUtility.SaveAsPrefabAsset(source, assetPath);

        /*
        using (var editingScope = new PrefabUtility.EditPrefabContentsScope(assetPath))
        {
            var prefabRoot = editingScope.prefabContentsRoot;

            // Removing GameObjects is supported
            Object.DestroyImmediate(prefabRoot.transform.GetChild(2).gameObject);

            // Reordering and reparenting are supported
            prefabRoot.transform.GetChild(1).parent = prefabRoot.transform.GetChild(0);

            // Adding GameObjects is supported
            var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
            cube.transform.parent = prefabRoot.transform;
            cube.name = "D";

            // Adding and removing components are supported
            prefabRoot.AddComponent<AudioSource>();
        }
        */

        // Prefab Asset now looks like this:
        // Root
        //    A
        //       B
        //    D
    }
}
 

  检查脚本里的浮点数 //


using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Text.RegularExpressions;
using System;

public class ScriptFloatCheck
{
    [MenuItem("代码检查/代码浮点类型检查")]
    public static void StartCodeFloatCheck()
    {
        Debug.Log($"代码浮点类型检查开始: {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff")}");

        var path = "Assets/Scripts";
        var files = Directory.GetFiles(path, "*.cs", SearchOption.AllDirectories);

        var total = 0.0f;
        var totalFind = 0;
        var errLines = new List<string>(100);
        try
        {
            foreach (var item in files)
            {
                var allines = File.ReadAllLines(item);
                if (allines.Length > 0 && allines[0].Trim().StartsWith("//Float_Uncheck"))
                {//Float_Uncheck
                    continue;
                }

                for (int i = 0; i < allines.Length; ++i)
                {
                    var ltext = allines[i].Trim();
                    if (CheckLine(allines, i))
                    {
                        var info = $"发现浮点类型代码,文件:{item}, 行:{i + 1},  {ltext}";
                        //EditorUtility.DisplayDialog("逻辑层代码检查", info, "确定");
                        errLines.Add(info);
                        totalFind++;
                    }
                }
                if (EditorUtility.DisplayCancelableProgressBar("代码浮点类型检查", $"进度:", total++ / files.Length))
                    break;
            }

            if (totalFind == 0)
            {
                EditorUtility.DisplayDialog("代码浮点类型检查", "检查完成,没有发现问题", "确定");
            }
            else
            {
                var savePath = "Assets/CodeFloatCheck.txt";
                EditorUtility.DisplayDialog("代码浮点类型检查", $"检查完成,共发现{totalFind}处问题,已保存到文件:{savePath}", "确定");

                File.WriteAllLines(savePath, errLines);
            }
        }
        catch (Exception e)
        {
            UnityEngine.Debug.LogWarningFormat("[LogicScriptFloatCheck][StartCodeFloatCheck] {0}", e.Message);
        }
        finally
        {
            EditorUtility.ClearProgressBar();
        }

        Debug.Log($"代码浮点类型检查完成: {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff")}");

    }

    static string[] rules = new string[] {
            @"[\s(]float[\s)]", @"[\s(]double[\s)]", @"\d+\.\d+",  //float, double, 浮点数值
            @"\sMath\.[a-zA-Z]+\s*\(", @"\sMathf\.[a-zA-Z]+\s*\(", //Math或Mathf相关API
            @"new\s+Random\s*\(",@"new\s+System\s*\.\s*Random\s*\(", @"Random\s*\.\s*RandomRange\s*\(", @"Random\s*\.\s*Range\s*\(", @"Random\s*\.\s*ColorHSV\s*\(",//随机数
        };

    static string[] excludeRules = new string[] { "Math.Max", "Math.Min", "Math.Abs" }; //排除项,不检查这些
    private static bool CheckLine(string[] allines, int iline)
    {
        var codeStr = FilterComment(allines, iline).Trim();
        if (codeStr.Length <= 0) return false;

        foreach (var item in rules)
        {
            var matches = Regex.Matches(codeStr, item);
            foreach (Match match in matches)
            {
                var isok = true;
                foreach (var exc in excludeRules)
                {
                    if (match.Value.Contains(exc)) isok = false;
                }

                if (isok) return true;
            }

        }

        return false;
    }

    //过滤掉注释
    private static string FilterComment(string[] allines, int iline)
    {
        if (InBlockComment(allines, iline)) return "";

        var codeStr = allines[iline];
        if (codeStr.Contains("Float_Uncheck") || codeStr.StartsWith("//") || codeStr.StartsWith("/*") || codeStr.StartsWith("#region"))
        {
            return "";
        }

        //去除行中注释
        var cmtIdx = codeStr.IndexOf("//");
        if (cmtIdx < 0)
        {
            cmtIdx = codeStr.IndexOf("/*");
        }
        if (cmtIdx >= 0)
        {
            codeStr = codeStr.Substring(0, cmtIdx);
        }

        return codeStr;
    }

    //是否在块注释中
    private static bool InBlockComment(string[] allines, int iline)
    {
        for (int i = iline - 1; i >= 0; --i)
        {
            var line = allines[i];
            var lineLen = line.Length;
            if (lineLen >= 2)
            {
                if (line[lineLen - 2] == '*' && line[lineLen - 1] == '/')
                {//line.EndsWith("*/"),这个函数非常低效
                    return false;
                }
                else if (line[0] == '/' && line[1] == '*')
                {//line.StartsWith("*/"),这个函数非常低效
                    return true;
                }
            }
        }

        return false;
    }
}

///

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Unity的Terrain是一个非常有用的工具,可以用于快速创建景观和环境。然而,在某些情况下,将Terrain转换为网格(mesh)可能是更好的选择,因为网格具有更高的控制力和更大的灵活性。 要将Unity的Terrain转换为网格,可以使用Unity中的一些脚本或第三方插件。在转换Terrain时,需要考虑一些因素,如地形的分辨率、细节、大小和顶点数量等。这些因素决定了生成的网格的复杂性和质量。 转换Terrain后,可以使用网格的优点来扩展实现场景的能力,如创建更多的自定义对象、添加水、树木和建筑等。此外,如果需要大规模的场景或复杂的元素,网格还可以优化游戏性能。 但是,将Terrain转换为网格也需要考虑一些限制和注意事项。例如,过于复杂的网格可能导致游戏的性能下降,而在使用网格时需要考虑多个碰撞器、UV贴图和纹理等。 总之,将Unity的Terrain转换为网格可以为游戏开发者提供更多的自定义方式和更高的灵活性,但需要注意一些限制和注意事项。 ### 回答2: Unity是一款游戏引擎,它被广泛用于游戏开发中。Unity的Terrain(地形)是一个强大的工具,可以用来构建大量的自然景观。然而,它在物理效果和优化方面有一些限制。所以,有时需要将Terrain转换成Mesh(网格)来实现更高精度的效果,同时提高游戏的性能。 Unity Terrain转换成Mesh有两种方法。第一种是使用Unity的API,从Terrain中获取数据,并将其转换成Mesh。这个过程涉及到一些数学计算和算法优化,需要一定的编程技能。 第二种方法是使用第三方工具。这些工具可以直接将Unity Terrain转换成Mesh,省去了一些复杂的步骤。目前有一些比较流行的工具,例如"Terrain to Mesh 2023",它可以快速、高效地将Terrain转换成高质量的Mesh。使用这种工具可以节省开发时间,同时获得更好的游戏效果。 总之,Unity Terrain转换成Mesh可以使游戏更加逼真、优化,提高用户的体验,是游戏开发的重要环节之一。 ### 回答3: Unity Terrain to Mesh2023这个问题可能涉及到Unity引擎的一个未来的更新或新功能,但由于无法确定具体的背景和含义,所以我只能就此进行猜测和探讨。 Unity Terrain和Mesh都是Unity引擎中用于场景渲染的重要组件。Unity Terrain是一种用于快速创建和编辑复杂地形的工具,可以在不需要复杂建模技能的情况下制作出逼真的地形效果。而Mesh则是一种用于渲染三维模型的基本几何形状,它可以由多边形(或三角形)组成,通过调整点的位置、法线和纹理等属性来表现出复杂的形态和细节。 在这个问题中,Unity Terrain to Mesh2023可能意味着一种将Unity Terrain转换为Mesh的新功能或技术。这可能会使得开发者能够更加灵活地创建和编辑场景,通过将地形转换为Mesh,就可以对它进行更加精细的编辑和优化,从而达到更好的渲染效果。 此外,Unity Terrain to Mesh2023还可能涉及到未来版本的Unity引擎的改进和升级。随着Unity引擎的不断发展和完善,我们可以期待更多的创新和功能!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值