简体   繁体   English

Unity 4.6编辑器,使用预定义数据创建脚本

[英]Unity 4.6 Editor, Create a Script with Predefined Data

I am trying to make an easy to use button inside the Unity Editor for Character and Item creation. 我想在Unity Editor中创建一个易于使用的按钮,用于创建角色和项目。

I will throw out a little extra info here to help explain my issue. 我会在这里抛出一些额外的信息来帮助解释我的问题。 My game is structured like this; 我的游戏结构如下; Game Controller >> Character Script >> (PlayerName)Script A character object has both the character script and a script named after it, on it. 游戏控制器>>角色脚本>>(PlayerName)脚本角色对象具有角色脚本和以其命名的脚本。

I want to be able to click "Create New Character" in the Unity editor and it do the following; 我希望能够在Unity编辑器中单击“创建新角色”,它会执行以下操作: 1) Prompt for a Name to use. 1)提示使用名称。 2) Create Empty Game Object named Name from whatever the user typed in. 3) Create a new C# Script named the same, and add it to the object. 2)从用户输入的内容中创建名为Name的空游戏对象.3)创建一个名为相同的新C#脚本,并将其添加到对象中。 -I want the generated script to have some pre-determined "Character Template" code in it. - 我希望生成的脚本中包含一些预先确定的“字符模板”代码。 4) Attach the new Script to the new empty game object, and attach a "Character Script" to it as well. 4)将新脚本附加到新的空游戏对象,并附加“角色脚本”。

Thanks in advance. 提前致谢。

One last sub-question. 最后一个子问题。 Would it be better to Access the PlayerNamedScript from the GameController by a public monobehaviour on the Character Script? 通过角色脚本上的公共单一行为从GameController访问PlayerNamedScript会更好吗?

Or can the CharacterScript Dynamically extend the PlayerNamedScript, sibling. 或者,CharacterScript可以动态扩展PlayerNamedScript兄弟姐妹。

I hope that is clear. 我希望这很清楚。 Thanks again. 再次感谢。

Try this out 试试吧

Put the CharacterCreatorEditor.cs in a folder named Editor somewhere in your project. CharacterCreatorEditor.cs放在项目中某处名为Editor的文件夹中。

CharacterCreatorEditor.cs CharacterCreatorEditor.cs

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

public class CharacterCreatorEditor : EditorWindow {

    #region Character Fields
    //Add as many character specific fields / variables you want here.
    //Remember to update the same thing in the "CharacterTemplate.txt"!
    public string characterName = "John Doe";

    public float characterHealth = 10;

    public int characterCost = 1000;

    public bool isBadGuy = false;
    #endregion


    private bool needToAttach = false;      //A boolean that checks whether a newly created script has to be attached
    private float waitForCompile = 1;       //Counter for compile
    GameObject tempCharacter;               //A temporary GameObject that we assign the new chracter to.


    //A Menu Item when clicked will bring up the Editor Window
    [MenuItem ("AxS/Create New Character")]
    public static void CreateNewChar () {
         EditorWindow.GetWindow(typeof(CharacterCreatorEditor));
    }


    void OnGUI () {

        GUILayout.Label("Here's a sample Editor Window. Put in more variables as you need below.");
        GUILayout.Space(10);

        //Note on adding more fields
        //The code below is broken into groups, one group per variable
        //While it's relatively long, it keeps the Editor Window clean
        //Most of the code should be fairly obvious

        GUILayout.BeginHorizontal();
        GUILayout.Label("Character Name", new GUILayoutOption[0]);
        characterName = EditorGUILayout.TextField(characterName, new GUILayoutOption[0]);
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

        GUILayout.BeginHorizontal();
        GUILayout.Label("Character Health", new GUILayoutOption[0]);
        characterHealth = EditorGUILayout.FloatField(characterHealth, new GUILayoutOption[0]);
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

        GUILayout.BeginHorizontal();
        GUILayout.Label("Character Cost", new GUILayoutOption[0]);
        characterCost = EditorGUILayout.IntField(characterCost, new GUILayoutOption[0]);
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

        GUILayout.BeginHorizontal();
        GUILayout.Label(string.Format("Is {0} a Bad Guy?", new object[] { characterName }), new GUILayoutOption[0]);
        isBadGuy = EditorGUILayout.Toggle(isBadGuy, new GUILayoutOption[0]);
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

        GUI.color = Color.green;

        //If we click on the "Done!" button, let's create a new character
        if(GUILayout.Button("Done!", new GUILayoutOption[0]))
            CreateANewCharacter();

    }

    void Update () {
        //We created a new script below (See the last few lines of CreateANewCharacter() )
        if(needToAttach) {

            //Some counter we just keep reducing, so we can give the
            //EditorApplication.isCompiling to kick in
            waitForCompile -= 0.01f;

            //So a few frames later, we can assume that the Editor has enough
            //time to "catch up" and EditorApplication.isCompiling will now be true
            //so, we wait for the newly created script to compile
            if(waitForCompile <= 0) {

                 //The newly created script is done compiling
                if(!EditorApplication.isCompiling) {

                    //Lets add the script
                    //Here we add the script using the name as a string rather than
                    //it's type in Angled braces (As done below)
                    tempCharacter.AddComponent(characterName.Replace(" ", ""));

                    //Reset the control variables for attaching these scripts.
                    needToAttach = false;
                    waitForCompile = 1;
                }
            }
         }
    }

    private void CreateANewCharacter () {

        //Instantiate a new GameObject
        tempCharacter = new GameObject();

        //Name it the same as the Character Name 
        tempCharacter.name = characterName;

        //Add the ChracterScript component. Note the use of angle braces over quotes
        tempCharacter.AddComponent<CharacterScript>();

        //Loading the template text file which has some code already in it.
        //Note that the text file is stored in the path PROJECT_NAME/Assets/CharacterTemplate.txt
        TextAsset templateTextFile = AssetDatabase.LoadAssetAtPath("Assets/CharacterTemplate.txt", 
                                                               typeof(TextAsset)) as TextAsset;
        string contents = "";
        //If the text file is available, lets get the text in it
        //And start replacing the place holder data in it with the 
        //options we created in the editor window
        if(templateTextFile != null) {
            contents = templateTextFile.text;
            contents = contents.Replace("CHARACTERCLASS_NAME_HERE", characterName.Replace(" ", ""));
             contents = contents.Replace("CHARACTER_NAME_HERE", characterName);
            contents = contents.Replace("CHARACTER_HEALTH_HERE", characterHealth.ToString());
            contents = contents.Replace("CHARACTER_COST_HERE", characterCost.ToString());
            contents = contents.Replace("CHARACTER_BAD_GUY_HERE", isBadGuy.ToString().ToLower());
        }
        else {
            Debug.LogError("Can't find the CharacterTemplate.txt file! Is it at the path YOUR_PROJECT/Assets/CharacterTemplate.txt?");
        }

        //Let's create a new Script named "CHARACTERNAME.cs"
        using(StreamWriter sw = new StreamWriter(string.Format(Application.dataPath + "/{0}.cs", 
                                                           new object[] { characterName.Replace(" ", "") }))) {
            sw.Write(contents);
        }
        //Refresh the Asset Database
        AssetDatabase.Refresh();

        //Now we need to attach the newly created script
        //We can use EditorApplication.isCompiling, but it doesn't seem to kick in
        //after a few frames after creating the script. So, I've created a roundabout way
        //to do so. Please see the Update function
        needToAttach = true;
    }

}



Put the below text file into the path "YOUR_PROJECT/Assets/CharacterTemplate.txt" If you don't, the code WON'T WORK! 将下面的文本文件放入路径“YOUR_PROJECT / Assets / CharacterTemplate.txt”如果不这样,代码将无法正常工作!

CharacterTemplate.txt CharacterTemplate.txt

using UnityEngine;
using System.Collections;


public class CHARACTERCLASS_NAME_HERE : MonoBehaviour {

    public string characterName = "CHARACTER_NAME_HERE";

    public float characterHealth = CHARACTER_HEALTH_HERE;

    public int characterCost = CHARACTER_COST_HERE;

    public bool isBadGuy = CHARACTER_BAD_GUY_HERE;


    public void SomeMethod () {

    }
}



Explanation of the code 代码说明

First, the editor script takes all the input variables (should be fairly obvious what they are) Once you click the done button, the following happen 首先,编辑器脚本获取所有输入变量(应该很明显它们是什么)一旦单击完成按钮,就会发生以下情况

  1. A new GameObject is Instantiated 一个新的GameObject被实例化
  2. The instantiated GameObject is named the same as the Character Name in the Editor (eg. John Doe) 实例化的GameObject与编辑器中的字符名称相同(例如John Doe)
  3. The CharacterScript (your common script) is attached 附加了CharacterScript(您的通用脚本)
  4. The template text file ("CharacterTemplate.txt") is read, and all the data is replaced with the data you entered in the Editor Window 读取模板文本文件(“CharacterTemplate.txt”),并将所有数据替换为您在编辑器窗口中输入的数据
  5. This is then written to a new script file 然后将其写入新的脚本文件
  6. We refresh the Asset Database, and wait until the newly created script is compiled (eg. JohnDoe.cs) 我们刷新资产数据库,并等待新编译的脚本编译(例如JohnDoe.cs)
  7. Lastly attach the script to the GameObject instantiated in Step 1 最后将脚本附加到在步骤1中实例化的GameObject



For your second question, what you'll need to do is to have all your PlayerNamedClass extend the same base class. 对于第二个问题,您需要做的是让所有PlayerNamedClass扩展相同的基类。 This way, you can type the variable you'll expose in CharacterScript 这样,您可以键入将在CharacterScript中公开的变量

So, for example, if you call the base class "NamedCharacterScripts" 因此,例如,如果您调用基类“NamedCharacterScripts”

In JohnDoe.cs JohnDoe.cs

public class JohnDoe : NamedCharacterScripts

In JaneDoe.cs JaneDoe.cs

public class JaneDoe : NamedCharacterScripts

In CharacterScript.cs CharacterScript.cs中

public NamedCharacterScripts namedCharacterScript;

void Awake () {
    //This will assign JohnDoe.cs for the GameObject named "John Doe" &
    //JaneDoe.cs to the GameObject named "Jane Doe"
    namedCharacterScript = GetComponent<NamedCharacterScripts>();
}

Hope this answers your questions. 希望这能回答你的问题。 If you have trouble, just leave a comment 如果您遇到问题,请发表评论

My script is not production-ready like Venkat's answer, but it should be easier to understand for educational purposes. 我的脚本不像Venkat的答案那样是生产就绪的,但出于教育目的应该更容易理解。

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

[ExecuteInEditMode]
public class CharacterTools : MonoBehaviour
{
    [SerializeField, HideInInspector]
    private string className;

    private bool waitForCompile = false;

    private void Update()
    {
        if (string.IsNullOrEmpty(className))
            return;

        if (waitForCompile && EditorApplication.isCompiling)
            waitForCompile = false;

        if (!waitForCompile && !EditorApplication.isCompiling)
        {
            var gameObject = new GameObject(className);
            Debug.Log("Attempting to add " + className);
            var c = gameObject.AddComponent(className);

            className = null;
        }
    }

    [ContextMenu("Create character")]
    private void CreateCharacter()
    {
        string name = "Number" + Random.Range(0, 100).ToString();

        string nameTemplate = "{0}Character";
        string contentTemplate = @"using UnityEngine;

public class {0} : MonoBehaviour
{{
}}
";

        var className = string.Format(nameTemplate, name);
        var path = Application.dataPath + "/" + className + ".cs";

        var scriptFile = new StreamWriter(path);
        scriptFile.Write(string.Format(contentTemplate, className));
        scriptFile.Close();

        AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceSynchronousImport);
        AssetDatabase.Refresh();

        this.className = className;
        this.waitForCompile = true;
    }
}

Usage: 用法:

  1. Add this script to any object on scene. 将此脚本添加到场景中的任何对象。
  2. Right click on just added component in inspector. 右键单击检查器中刚刚添加的组件。
  3. Choose “Create character”. 选择“创建角色”。
  4. Wait few seconds. 等几秒钟。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM