Unity中动态修改游戏中任意参数的框架

发表于2017-05-04
评论1 2.8k浏览

一、 引言

心血来潮,根据之前做的一个控制台的输入,就想根据控制台的输入,来控制和修改游戏中的某些参数。 

目前大部分简单游戏修改参数有数据库,XML,json等各式各样,现在做的是一个可以在游戏过程中,实时修改任意变量的一个东西。 

用法很简单,tab按键来打开控制台,输入需要修改的参数就可以,看到参数实时被修改。也可以把它理解成简单的修改器,用于调节参数和可能的命令修改游戏中的参数。 

当然,也需要稍微修改一个项目中原有的代码。这个功能已经使用代码一键实现了,并且使用unity中不加入编译的方式,在代码文件名字前加“.”的方式保留了原来的代码。 

先看下整体的思维导图

 
思维导图

二、输入部分


输入部分有控制台来实现输入。

图0 

在ConsoleInput类中添加了OnInputText时间,这样就可以根据输入不同进行处理了。 

关于具体控制台程序的一些用法和使用事项,请参考后面给出的github和博客地址,里面进行了说明和更新,也非常期待感兴趣的可以自己提交更改。

1
2
3
4
5
6
7
8
void OnInputText( string obj )
{
    this.ConsolePrint(obj);       
    if (!NotifyToChangeVarialbe(obj))
    {
        this.ConsolePrint("not correct input to change variable");
    }
}


顺便说下,this.ConsolePrint是对MonoBehaviour类进行了扩展。 
具体代码:

1
2
3
4
5
6
7
8
public static class ExtendDebugClass
{
    public static void ConsolePrint(this MonoBehaviour mono, string message)
    {
        if (message.Length < 0) return;
        System.Console.WriteLine(message);
    }
}

三、输入转换


输入的字符串作为参数,需要不同进行处理。 
输入的参数有三部分组成,类名称,变量名,变量新值。 

目前只可以处理整形,浮点型,字符串和布尔型四种常用类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static bool IsVarialbeInList(string classname,string classname_variableName, out string valuetype)
   {
       bool bresult = false;
       string tmpclassName = classname;
       Debug.Assert(!string.IsNullOrEmpty(tmpclassName));
       Debug.Assert(!string.IsNullOrEmpty(classname_variableName));
       valuetype = null;
       if (CollectAttributeUtil.myFuctionList.ContainsKey(tmpclassName))
       {
           List mlist = (List)myFuctionList[tmpclassName];
           for (int i = 0; i < mlist.Count; i++)
           {
               if (mlist[i] != null &&
                   string.Compare( mlist[i].class_variable ,classname_variableName,true) == 0)
               {  
                   valuetype = CheckVariableType(mlist[i].variable_type);
                   bresult = true;
                   break;
               }
           }
       }
       return bresult;
   }

然后进行消息触发,就搞定了修改参数,并且可以实时修改。

四、收集可修改变量


这里自定义类一个字段属性ModifyAttribute 
ModifyAttribute.cs

1
2
3
4
5
6
7
8
9
10
11
using System;
 
[System.AttributeUsage(AttributeTargets.All)]
public class ModifyAttribute : Attribute
{
    //public ModifyAttribute()
    //{
 
    //}
 
}


若使用这个框架,只需在需要改变的变量代码前添加这个属性描述。 
例如:

1
2
3
4
5
[ModifyAttribute]
    public int clolor;
 
    [ModifyAttribute]
    public string attack;

下面这个函数负责收集所有的可修改变量,在ServerConsole.cs中的Awake中被调用。

1
CollectAttributeUtil.InitialCollectModifyAttribute();


收集变量的主要代码在CollectAttributeUtil.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
///
    /// get attribute type variable in each monobehaviour class.
    ///
    ///
    ///
    private static void CollectAttributeProperty(MonoBehaviour p) where T : ModifyAttribute
    {
        var flags = BindingFlags.Instance  | BindingFlags.Public | BindingFlags.Static;
        var type = p.GetType();
        FieldInfo[] fieldInfor = type.GetFields(flags);
        foreach (var field in fieldInfor)
        {
            var objs = field.GetCustomAttributes(typeof(T), false);
            if (objs.Length > 0)
            {
                string keyTmp = type.FullName;
                AttributeForClass attri4Class = new AttributeForClass();
                attri4Class.class_variable = (type.FullName + "#" + field.Name);
                attri4Class.variable_type = field.FieldType;
                if (!myFuctionList.Contains(keyTmp))
                {
                    List AttriClassList = new List();
                    AttriClassList.Add(attri4Class);
                    myFuctionList.Add(keyTmp, AttriClassList);
                }
                else
                {
                    List mlist = (List)myFuctionList[keyTmp];
                    mlist.Add(attri4Class);
                }
                //Debug.Log(type.ToString() + " current varible is  " + field.Name + " type is " + field.FieldType);
            }
        }
    }


这个函数把所有收集对象按类的名字为基础,然后形成列表,存放到myFuctionList中,等待被使用。

五、编辑器模式下的自动生成代码


由于使用了消息机制,需要自动来把需要修改的变量,写为函数,然后在注册为消息。 
不用担心,这些工作已经做为了。只需要按下一个按键,一键搞定。 

在Editor文件夹下,有个CreateCode.cs文件,且只可以在编辑器模式下运行。

图3 

这里的代码还是蛮多,说下主要的, 

第一,得到所有需要注册的类名和变量名,这个工作其实代码已经写过了就是前面的收集工作。这里只需要调用一下就可以了。 

第二,创建partial文件夹,根据类创建每个类的文件.cs。 

第三,给每个类文件,根据类中所需要修改的变量,自动添加变量消息和赋值函数。 

第四,把消息注册自动的编写到原有的代码的start函数中,这就结束了。 

这里有个小的约束: 
函数必须有

1
2
3
void Start ()
    {
    }

必须有,并且最好大括号不能在同一行,也就是说不能写成

1
void Start ()    {

因为需要便是这个函数,在里面添加代码;

1
2
3
4
5
6
7
8
void Start ()
    {  
           // auto regist code. @cz
           Start4AutoSubscribe();
        transf = this.transform;
        //string messName = GETNAME(new { this.movespeed });
        //NotificationManager.Instance.Subscribe(messName, ControlMove_movespeed);
    }


start后面的两行是代码自动添加的。代码中已经做了判断,若添加过

1
2
// auto regist code. @cz
           Start4AutoSubscribe();

就不会再次添加。 

这里还有个保存原有代码的功能呢,就是最原始的你写的项目中代码。根据unity的特性,对点开头的.cs文件视而不见,默认不加入编译中。

图2 

若有需要可以把原代码给还原了。但是,还是自动svn提交的好。 

为什么需要修改源代码呢? 
有两个原因:一个是需要注册消息。一个是需要扩展这个需要修改的类,这里会把原来的类前面添加一个关键字partial。

1
2
public partial class ControlMove : MonoBehaviour
{

代码中没有加,没有关系,代码会自动运行修改。 

你需要做的就是按下AutoModifyClass按钮,等待编译修改。

六、具体使用


消息的处理,也就是这个消息机制之前说过多次。主要用于解耦对象。 
这里不过多说明了。 

说怎么使用呢? 

第一,把带导入的项目中,把prefab文件中的TestConsole拖到场景中即可。

图5 

第二,把项目中所需要在游戏中动态修改的变量添加属性

1
[ModifyAttribute]

修类型不限于public。当前对变量的类型还是做了限制的,只有public。 
限制的代码在这里:

1
var flags = BindingFlags.Instance  | BindingFlags.Public | BindingFlags.Static;


第三,点击生成按钮 

添加完毕后,点击生成按钮。

图3 

第四,所有代码代码就会自动生成,也会修改源工程中的部分需要修改的类的代码。


第五,运行

图4 

运行,使用tab键或~键都可以调出和关闭控制台窗口。 
在输入类,变量,修改值,就可以看到了。 

这适用于基础版本的打包测试,编辑器测试,或其他项目,不用为了一个变量来回的打包测试,当然也可以与其他比方说json或xml,数据库等配合使用。

七、下一步需要处理


目前的还需要做的事情: 

1.命令行不区分大小写,输入参数现在必须区分大小写,有空格隔开。可能需要处理为不区分大小写的模式。 

2.命令行需要丰富,现在控制台命名只有两个,一个是清屏clear,一个是退出控制台exit。 

3.控制台命令行输入提示,根据需要提示和回滚之前的输入信息。 

4.项目中源代码的回滚功能,防止错误的替换。

八、源码分享


项目地址:

https://github.com/cartzhang/dynamic_parameter_frame


相关图片可下载地址: 
https://github.com/cartzhang/dynamic_parameter_frame/tree/master/image


更多控制台的输入项目工程地址和说明,见参考。

九、参考

【1】 https://github.com/cartzhang/UnityConsoleWindow

【2】 http://blog.csdn.net/cartzhang/article/details/49818953

【3】 http://blog.csdn.net/cartzhang/article/details/49884507

作者:cartzhang

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论