2014-11-26 17:32:51 jingjingcrystal 阅读数 2468
unity3d教程同步学习,要时常关注哦。今天我们来说一说继续和退出的语法和用法,在switch...case语句中我们经常用到break,其实,break不知是可以在switch...case中用,在其他的语句中也可以使用,相应的就会有继续continue,让我们一起来学习学习吧。
退出
       单层循环:一般用在循环里面,执行到break时退出循环;
       多层循环:如果是循环嵌套,只退出内层循环(列循环),退出语句后面的语句还是要执行的。
例:int  i,sum=0;                                                for(int i=1;i<=9;i++)      
       for(i=1;i<=10;i++)                                           {
         {                                                                             for(int j=1;j<=9;j++)
                sum=sum+i;                                                          {
                print(sum);                                                                   if(i==9)
                if(i==5)                                                                            {
                   {                                                                                         break;
                         break;                                                                       }
                    }                                                                                    print(i+"*"+j+"="+i*j);
         }                                                                                      }
                                                                                      }
继续:
        单层循环:一般用在循环里面,执行到continue语句时,后面的语句是不执行的,但是还会继续循环;
        多层循环:如果是循环嵌套,只退出内层循环(列循环)。
例:int  i,sum=0;                                                for(int i=1;i<=9;i++)      
       for(i=1;i<=10;i++)                                           {
         {                                                                             for(int j=1;j<=9;j++)
                sum=sum+i;                                                          {
                print(sum);                                                                   if(i==9)
                if(i==5)                                                                            {
                   {                                                                                         continue;
                         continue;                                                                 }
                    }                                                                                    print(i+"*"+j+"="+i*j);
         }                                                                                      }
                                                                                      }
更多精彩的内容请关注:http://www.gopedu.com/

2017-06-08 15:25:10 ComplicatedCc 阅读数 5719

本文介绍了Unity如何在退出运行模式后,保存对组件参数的数据修改。


之前因为策划有在Game运行模式时动态修改脚本参数,在退出Playmode后脚本参数保存的需求,用于在场景中动态地调整参数。研究了一下之前老外实现的一个插件PlayModePersist,也就是一个在运行模式下进行保存数据的插件。
PlayModePersist 这款插件不知道什么原因已经从Appstore下架了。有需要的同学可以自行下载。
通过阅读这款插件的源码,学到了两个比较有趣的实现:
1. 通过反射获取所有应用类,做成编辑器以供筛选。
2. 退出Playmode后,保存修改过的数据。

今天讨论的就是No.2的实现方式


实现的核心还是通过反射。
通过脚本控制添加所需要修改的组件列表,在 EditorApplication.playmodeStateChanged 类似于OnApplicationQuit 的时候把保存的数据提交修改。

我发现以前写云笔记的时候还是比较轻松,毕竟只是写给自己看,写博客的时候要让读者读懂,说清楚思路还是好难。
不废话了,先上代码
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Reflection;
using System;

public class PlayModeEditHelper {

    private static PlayModeEditHelper _instance;
    public static PlayModeEditHelper Instance
    {
        get
        {
            if (_instance == null)
                _instance = new PlayModeEditHelper();
            return _instance;
        }
    }

    //在构造函数中 注册修改事件
    public PlayModeEditHelper()
    {
        EditorApplication.playmodeStateChanged = Application_PlaymodeStateChanged;
        SavingDatas = new Dictionary<int, SettingData>();
    }

    List<Component> SavingGroups = new List<Component>(); //需要保存的组件列表
    List<int> SavingIDs = new List<int>();                //需要保存组件的InstanceID列表
    Dictionary<int, SettingData> SavingDatas;             //缓存数据字典

    void Application_PlaymodeStateChanged()
    {
        if (EditorApplication.isPlaying || EditorApplication.isPaused)
        {
            //window repaint or do sth...
        }
        else
        {
            RestoreAllSavingSetting();
            EditorApplication.playmodeStateChanged = null;
        }
    }

    //还原所有修改数据
    void RestoreAllSavingSetting()
    {
        for (int i = 0; i < SavingIDs.Count; i++)
        {
            RestoreSetting(SavingIDs[i]);
        }
    }

    //通过实例ID修改组件参数
    void RestoreSetting(int id)
    {
        Component ComponentObject = EditorUtility.InstanceIDToObject(id) as Component;

        Dictionary<string, object> values = SavingDatas[id].values;

        foreach (string name in values.Keys)
        {
            object newValue = values[name];

            PropertyInfo property = ComponentObject.GetType().GetProperty(name);

            if (null != property)
            {
                object currentValue = property.GetValue(ComponentObject, null);
                property.SetValue(ComponentObject, newValue, null);
            }
            else
            {
                FieldInfo field = ComponentObject.GetType().GetField(name, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
                object currentValue = field.GetValue(ComponentObject);
                field.SetValue(ComponentObject, newValue);
            }
        }

        if (ComponentObject != null)
        {
            SetPrefabDirty(ComponentObject);
        }
    }

    //如果是预设体,通知引擎修改 不能省略,修正了修改目标是预设体时,每次运行参数重置的bug
    void SetPrefabDirty(Component ComponentObject)
    {
        PrefabType prefabType = PrefabUtility.GetPrefabType(ComponentObject.gameObject);
        if (prefabType == PrefabType.DisconnectedPrefabInstance || prefabType == PrefabType.PrefabInstance)
        {
            EditorUtility.SetDirty(ComponentObject);
        }
    }
    
    //缓存修改信息  包括属性、字段
    public void SaveValues(Component ComponentObject)
    {
        if (SavingGroups.Contains(ComponentObject))
        {
            RefreshValues(ComponentObject);
            return;
        }

        Dictionary<string, object> values = new Dictionary<string, object>();

        List<PropertyInfo> properties = GetProperties(ComponentObject);
        List<FieldInfo> fields = GetFields(ComponentObject);

        foreach (PropertyInfo property in properties)
        {
            values.Add(property.Name, property.GetValue(ComponentObject, null));
        }

        foreach (FieldInfo field in fields)
        {
            values.Add(field.Name, field.GetValue(ComponentObject));
        }

        //添加需要保存的组件
        SavingGroups.Add(ComponentObject);
        SavingIDs.Add(ComponentObject.GetInstanceID());
        SavingDatas.Add(ComponentObject.GetInstanceID(), new SettingData(values));
    }

    //修正多次点击修改不能更新数据的bug
    private void RefreshValues(Component ComponentObject)
    {
        Dictionary<string, object> values = SavingDatas[ComponentObject.GetInstanceID()].values;

        List<PropertyInfo> properties = GetProperties(ComponentObject);
        List<FieldInfo> fields = GetFields(ComponentObject);

        foreach (PropertyInfo property in properties)
        {
            values[property.Name] = property.GetValue(ComponentObject, null);
        }

        foreach (FieldInfo field in fields)
        {
            values[field.Name] = field.GetValue(ComponentObject);
        }
    }

    //获取私有字段+公有字段列表
    private List<FieldInfo> GetFields(Component ComponentObject)
    {
        List<FieldInfo> fields = new List<FieldInfo>();
        //获取字段包括私有字段、共有字段  修正了私有字段不能正常修改的bug
        FieldInfo[] infos = ComponentObject.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);

        //foreach (FieldInfo fieldInfo in ComponentObject.GetType().GetFields())
        foreach (FieldInfo fieldInfo in infos)
        {
            if (!Attribute.IsDefined(fieldInfo, typeof(HideInInspector)))
            {
                fields.Add(fieldInfo);
            }
        }
        return fields;
    }

    //获取属性列表
    private List<PropertyInfo> GetProperties(Component ComponentObject)
    {
        List<PropertyInfo> properties = new List<PropertyInfo>();

        foreach (PropertyInfo propertyInfo in ComponentObject.GetType().GetProperties())
        {
            if (!Attribute.IsDefined(propertyInfo, typeof(HideInInspector)))
            {
                MethodInfo setMethod = propertyInfo.GetSetMethod();
                if (null != setMethod && setMethod.IsPublic)
                {
                    properties.Add(propertyInfo);
                }
            }
        }
        return properties;
    }
}

//简易的设置数据 自行根据需求扩充
public class SettingData
{
    public Dictionary<string, object> values;

    public SettingData(Dictionary<string, object> datas)
    {
        values = datas;
    }

    public void AddData(string name, object value)
    {
        if (!values.ContainsKey(name))
        {
            values.Add(name, value);
        }
    }
}

其中测试脚本如下:
using UnityEngine;

public class TestEditParam : MonoBehaviour {

    public enum ENUM_TEST
    {
        ENUM1,
        ENUM2,
        ENUM3
    }

    public int mIntValue;               //测试整形字段

    public float mFloatValue;           //测试Float型字段

    public Vector3 mVector3Value;       //测试Vector型字段

    public string mStringValue;         //测试String型字段

    public ENUM_TEST mEnumType;         //测试自定义枚举型字段

    //测试私有字段
    [SerializeField]
    private int mPrivateIntValue;
    [SerializeField]
    private float mPrivateFloatValue;
}

测试Editor脚本:置于Editor文件夹下
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(TestEditParam), true)]
public class TestEditParamEditor : Editor {

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        GUILayout.Space(10);
        if (GUILayout.Button("Save"))
        {
            SaveParam();
        }
    }

    //将TestEditParam 和 Transform 注册到修改队列
    void SaveParam()
    {
        //将target(即Editor脚本的目标组件TestEditParam)注册到修改队列
        Component mTarget = target as Component;
        PlayModeEditHelper.Instance.SaveValues(mTarget);
        //将Transform 注册到修改队列
        Transform rectCom = mTarget.transform.GetComponent<Transform>();
        PlayModeEditHelper.Instance.SaveValues(rectCom);
        //ApplyParam();
    }

    //提交预设体修改  直接保存prefab的暴力方法 只需更改prefab时直接用此接口即可
    void ApplyParam()
    {
        Object prefabParent = PrefabUtility.GetPrefabParent(target);
        TestEditParam mScript = target as TestEditParam;
        PrefabUtility.ReplacePrefab(mScript.gameObject, prefabParent);
    }
}

使用方法很简单,在Editor脚本的按钮事件中添加需要修改的组件mTarget 即可
PlayModeEditHelper.Instance.SaveValues(mTarget);
支持添加多个组件,支持多次保存更新

其实prefab在保存时就是一系列序列化信息。可以通过文本工具查看,如:

主要实现的思路就是,通过反射方法,把Component的 属性(PropertyInfo),和字段(FieldInfo)缓存下来。在结束运行模式后,系统首先会把组件数值还原。然后调用Application_PlaymodeStateChanged 方法,在系统默认还原后重新通过InstanceID获取组件,并对其字段属性的数值进行覆盖。
由于这个时序性,具体应用时会直观地看到一个数值跳动的过程。

接下来我们具体看一下 测试脚本中 PropertyInfo 和FieldInfo具体包含的信息

PropertyInfo :

FieldInfo:

我们测试时用到的是字段值,而工作中也经常会用到属性索引器等等。所以都需要处理。

我们可以看到在PropertyInfo 中有些无用的基类的属性比如tag值,hideflag等。
这些在插件源码中单独处理成了一个IgnoreList 忽略列表,但是代码基类千变万化,出于简化代码的目的(不想人力维护这个列表),而且插件中的属性有多余的情况,比如包含了Transform类的基础属性等等,最终没有定义这个筛选列表。如果有需要可以参照插件源码。

这里在获取所有字段中用到了反射的筛选条件,支持了获取私有字段
ComponentObject.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
具体反射类可自行查阅相关文章,这里不做详细说明。

在playmodeStateChanged 状态切换后,检查状态如果是退出playmode时,调用脚本的保存修改方法。
通过property.SetValue,field.SetValue设置参数。

这里需要说明的是,如果修改的目标被设置成prefab(通常都是这样),要通过EditorUtility.SetDirty(ComponentObject)方法在Editor中将其标记为脏(变化过的)才能保存修改。否则会出现退出时虽然属性被修改,但是下一次运行时,系统会自动还原成prefab的参数。

如果大家不明白最好的方法就是断点走一遍。呃。程序猿们最简单的上手方法。

补充:
1. 之前只支持了单组件的保存。后来发现实际过程中可能会想保存Transform的属性等等,所以添加了对多个组件保存的支持。为了不影响之前的代码结构维护了一个Dictionary<int, SettingData> SavingDatas; //缓存数据字典
临时添加SavingDatas用作处理缓存数据,方法也比较粗暴。大家用到时可以扩充数据类型,优化查找方法。
2. 还是写了这篇文章自己测试后发现,多次Save只能保存第一次的数据。忘了Save也应该有更新数据的功能,所以添加了RefreshValues的方法。也很粗暴,直接进行的所有数据全更新。请同学们自行修改优化。
3. 本文描述时比较啰嗦,主要是想简单记录一下实现时遇到的坑。
4. 在测试Editor脚本中有个方法。其实是脚本做提交预设体修改的方法。即:
        Object prefabParent = PrefabUtility.GetPrefabParent(target);
        TestEditParam mScript = target as TestEditParam;
        PrefabUtility.ReplacePrefab(mScript.gameObject, prefabParent);
如果仅是保存prefab需求直接应用即可



由于这是个辅助脚本,用于配合企划的修改需求。不会在正常的项目中实际运行到。所以代码并不优雅,只是给同学们一个思路,也许有更佳的方法

有建议和想法的还望不吝赐教~谢谢




2014-10-30 20:48:57 book_longssl 阅读数 3941


 

Unity3D C#打开外部应用程序,并检测应用程序是否关闭退出实现代码。

using UnityEngine;

using System.Collections;

using System.Diagnostics;

using System;

 

public class StartOtherApp : MonoBehaviour {

 

         private Process pc;

         void Start () {

                   StartCoroutine(StartApp());

         }

 

         IEnumerator StartApp()

         {

                   yield return new WaitForSeconds(2);

                   pc = Process.Start("C:/Users/Lee/Desktop/Flash VideoPlayer/VRPlayer/VRPlayer.app/VRPlayer.exe");

 

                   pc.EnableRaisingEvents = true;

                   pc.Exited += new EventHandler(myProcess_Exited);

         }

 

         void myProcess_Exited(object sender, EventArgs e)

         {

                   Application.Quit();

         }

}


Unity3D 关于运动的代码


运动相关一般要写在update中,实际开发中,需要分清楚哪些是需要每frame都检测的,哪些是触发的。

input下面的类一般要写在update中,因为每个frame都需要检测。

OnTriggerEnter();OnTriggerStay;OnTriggerExit;与之对应的是OnCollisionEnter....,如果勾选了IsTrigger则需要Trigger类。

可以用于运动的函数。我们应该更具具体的情况选择合适的函数。

 

rigidbody(2D)velocity,

//向某个方向移动

velocity=transform.TransformDirection(Vector3.forward*100);

 

transform.translate,

//向某个方向移动

playerTransform.Translate(Vector3.right*Time.deltaTime*(-moveSpeed.x));

 

vector3.movetowards,

//移动到目标点

playerTransform.position=Vector3.MoveTowards(playerTransform.position,tagerpoint.position,200*Time.deltaTime);

 

也可以直接操作transform.position



2018-03-18 16:04:35 MadBam_boo 阅读数 1328

Unity3d实战之Unity3d网络游戏实战篇(10):玩家类Player

学习书籍《Unity3d网络游戏实战》 罗培羽著 机械工业出版社
本文是作者在学习过程中遇到的认为值得记录的点,因此引用的代码等资源基本出资罗培羽老师的书籍,如有侵权请联系,必删。

 玩家在登录游戏到退出游戏时,有以下流程:
这里写图片描述
 我们给Player类设计如下方法:
 Send: 提供一个Player的Send方法,该方法调用ServNet的Send方法;
 KickOff:将当前角色踢下线;
 Logout:该方法将触发HandlePlayerEvent中的OnLogout事件。

 Player类的Property:

public string id;       // user_name
public Conn conn;       // the conn connect with user[id]'s client
public PlayerData data; // player's data
public PlayerTempData tempData; // player's tempdata

 KickOff:
 当用户在两个Client上登录同一账号时,把前一个角色踢下线。

public static bool KickOff(string id, ProtocolBase protoBase)
{
    Conn[] conns = ServNet.instance.conns;      // get client list.
    for (int i = 0; i < conns.Length; i++) {
        Conn conn = conns [i];
        if (conn == null || conn.isUse == false)
            continue;
        if (conn.player == null)
            continue;

        if (conn.player.id == id) {             // find the user who named [id].
            lock (conn.player) {
                if (protoBase != null) {        // send the logout protocol to user.
                    ProtocolBytes protocol = (ProtocolBytes)protoBase;
                    protocol.AddInt32 (-1);
                    conn.player.Send ((ProtocolBase)protocol);
                }

                return conn.player.Logout ();   // execute logout function
            }
        }
    }
    return true;
}

 Logout:
 Player类的Logout方法,调用Logout方法将会触发HandlePlayerEvent中的OnLogout事件处理相关逻辑。

public bool Logout()
{       
    ServNet.instance.handlePlayerEvent.OnLogout (this);     // trigger OnLogout event

    if (!DataMgr.instance.SavePlayer (this)) {              // save player's data before logout
        return false;
    }

    conn.player = null;
    conn.Close ();
    return true;
}
没有更多推荐了,返回首页