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

时间:2015-01-06 15:49:06

标签: c# unity3d unity3d-editor

我正在尝试在Unity Editor中创建一个易于使用的按钮,用于创建角色和项目。

我会在这里抛出一些额外的信息来帮助解释我的问题。 我的游戏结构如下; 游戏控制器>>字符脚本>> (PlayerName)脚本 字符对象同时包含字符脚本和以其命名的脚本。

我希望能够点击"创建新角色"在Unity编辑器中,它执行以下操作; 1)提示使用名称。 2)从用户输入的内容中创建名为Name的空游戏对象。 3)创建一个名为相同的新C#脚本,并将其添加到对象中。    - 我希望生成的脚本有一些预先确定的"字符模板"代码在里面。 4)将新脚本附加到新的空游戏对象,并附加一个"字符脚本"它也是。

提前致谢。

最后一个子问题。 通过角色脚本上的公共单一行为从GameController访问PlayerNamedScript会更好吗?

或者CharacterScript可以动态扩展PlayerNamedScript兄弟。

我希望这很清楚。再次感谢。

2 个答案:

答案 0 :(得分:4)

试试这个

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;
    }

}



将以下文本文件放入路径&#34; YOUR_PROJECT / Assets / CharacterTemplate.txt&#34; 如果您不知道,代码WON&#39; T WORK!< / p>

<强> 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 () {

    }
}



代码说明

首先,编辑器脚本获取所有输入变量(应该相当明显它们是什么) 单击完成按钮后,会发生以下情况

  1. 新的GameObject实例化
  2. 实例化的GameObject与编辑器中的角色名称相同(例如John Doe)
  3. 附加了CharacterScript(您的通用脚本)
  4. 读取模板文本文件(&#34; CharacterTemplate.txt&#34;),并将所有数据替换为您在编辑器窗口中输入的数据
  5. 然后将其写入新的脚本文件
  6. 我们刷新资产数据库,等待新编译的脚本编译(例如JohnDoe.cs)
  7. 最后将脚本附加到在步骤1中实例化的GameObject


  8. 对于第二个问题,您需要做的是让所有PlayerNamedClass扩展相同的基类。这样,您可以键入您在CharacterScript

    中公开的变量

    因此,例如,如果你调用基类&#34; NamedCharacterScripts&#34;

    JohnDoe.cs

    public class JohnDoe : NamedCharacterScripts
    

    JaneDoe.cs

    public class JaneDoe : NamedCharacterScripts
    

    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>();
    }
    

    希望这能回答你的问题。如果您遇到问题,请发表评论

答案 1 :(得分:0)

我的剧本不像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;
    }
}

用法:

  1. 将此脚本添加到场景中的任何对象。
  2. 右键单击检查器中添加的组件。
  3. 选择“创建角色”。
  4. 等几秒钟。