2014-12-03 14:48:34 asd237241291 阅读数 4410

原创文章如需转载请注明:转载自 脱莫柔Unity3D学习之旅 QQ群:【Unity3D(AR/VR) 334163814】【Unity3D(游戏) 119706192】 本文链接地址:Unity3D 编辑器功能之MonoBehaviour属性

using System;
using UnityEngine;

public class Test : MonoBehaviour
{
    #region 编辑Inspector视图
    /// <summary>
    /// 只能输入 0-1的值
    /// </summary>
    [Range(0f, 1f)]
    public float tRange = 1f;

    /// <summary>
    /// 输入时的提示
    /// </summary>
    [Tooltip("Tooltip_test")]
    public float tTooltip = 1f;

    /// <summary>
    /// 标头
    /// </summary>
    [Header("Header_test")]
    public float tHeader = 1f;

    /// <summary>
    /// 距离上一行50px
    /// </summary>
    [Space(50)]
    public float tSpace = 1f;

    /// <summary>
    /// 隐藏该属性(依然会被实例化)
    /// </summary>
    [HideInInspector]
    public float tHideInInspector = 1f;

    #endregion

    #region 编辑MonoBehaviour功能
    /// <summary>
    /// 在标题栏Component中添加("Duan/AddComponentMenu_test")层级。
    /// 点击将(Test)脚本绑定到当前选中的gameobject上。
    ///(Test)脚本名必须与文件名一致,(单独的Class文件)。
    /// </summary>
    //[AddComponentMenu("Duan/AddComponentMenu_test")]
    //public class Test : MonoBehaviour{}

    /// <summary>
    /// 在辑模式运行Update、FixedUpdate和OnGUI。
    /// </summary>
    //[ExecuteInEditMode]
    //public class Test : MonoBehaviour{}

    /// <summary>
    /// 强制要求该脚本的gameobject必须同时绑定了Rigidbody组件,如果没有则立即添加。
    /// </summary>
    //[RequireComponent(typeof(Rigidbody))]
    //public class Test : MonoBehaviour{}

    /// <summary>
    /// 给当前脚本添加右键(或小齿轮)选项
    /// 点击调用该方法。
    /// </summary>
    [ContextMenu("ContextMenu Test")]
    public void mContextMenu()
    {
        Debug.Log("ContextMenu Test Log");
    }

    /// <summary>
    /// 在标题栏中添加("Duan/MenuItem")层级。
    /// 点击调用该方法。
    /// 该方法必须是static的。
    /// </summary>
    [MenuItem("Duan/MenuItem")]
    public static void tMenuItem()
    {
        Debug.Log("MenuItem Test Log");
    }
    #endregion 

    #region 编辑属性
    /// <summary>
    /// 标记一个变量或方法不会被序列化
    /// </summary>
    [NonSerialized]
    public float tNonSerialized = 1f;

    /// <summary>
    /// 该类可以被序列化 (序列化就是把内存中对象以一种可以保存的形式保存起来。 )
    /// </summary>
    [Serializable]
    public class Serializable{ }

    /// <summary>
    /// 强制序列化属性(Unity只序列化Public属性。序列化Private添加[SerializeField]。)
    /// </summary>
    [SerializeField]
    private bool tSerializeField = true;
    #endregion
}


2019-08-03 22:29:08 piai9568 阅读数 126

做编辑器扩展开发时,假如我们生成一个文件到Assets之外的目录,要查看它的时候,需要从系统路径一步步打开到那个目录。
Unity给我们提供了一个API可以直接打开指定目录,类似Project窗口右键文件选择“Reveal in Finder” 从系统窗口显示文件。

EditorUtility.RevealInFinder(string outputPath);

利用这个API我们可以做诸如编译成功后直接跳转到文件所在目录的操作,方便直接查看。

假设我们用编辑器生成了一个level1.txt文件 位于 configfiles目录中,点击对话框中的确定,就可以直接打开显示它。

先看下效果

代码如下:

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

public class ShowSystemFinder {

   [MenuItem("UnityAsk/DoTest")]
   private static void DoTest()
   {
      var outputPath = "configfiles";
      if (!Directory.Exists(outputPath))
      {
         Directory.CreateDirectory(outputPath);
      }

      File.WriteAllText(Path.Combine(outputPath,"level1.txt"),"this is level one");
      
      
      if (EditorUtility.DisplayDialog("UnityAsk的Unity3D小技巧","我完成了","确定"))
      {
         EditorUtility.RevealInFinder(outputPath);
      }
   }
}

创建ShowSystemFinder.cs文件,并放到Editor目录下。

Unity技术交流 微信公众号 UnityAsk,QQ群:891920228

2018-01-06 17:11:32 qq_34469717 阅读数 1249

前言

这几天琢磨着开发个个人作品的时候,发现原来Unity3D官方没有提供圆锥体的创建功能,就自己做了个编辑器扩展。鉴于之前搜索Mesh编程的时候很少有博客把自己的算法讲清楚,这里我抛砖引玉,尽我所能为一些初学者提供参考,当然,算法未必优,如有更好的算法并乐意知会我则不胜感激,我是大龄转行Unity3D开发,一路行来都是自己琢磨,比较辛苦,先行谢过。

软件环境

Win10 + Unity3D 2017.3.0f3

正文

基本思路是以原点为圆锥体底部圆的中心点,以其正上方1单元处的点为圆锥体锥尖顶点,其他点参照Cylinder为分布在半径为0.5单元的圆上,每20度一个点,这样总共加起来的顶点数量是38个,三角形索引数组数量是108个(锥体可以看作底部圆心上移,所以这两部分的三角形数量是相等的,而底部每20度一个点,那么就有18个三角形,所以结果就是1832=108)。
下面开始逐步分解实现。

编辑器扩展

首先,扩展编辑器,在GameObject/3D Object下新建一个Cone菜单,为了假装是亲生的,就和Cube等原生菜单放在一起好了。

[MenuItem("GameObject/3D Object/Cone",false,priority = 7)]
public static void CreateCone()
{
   SpawnConeInHierarchy();
}

这里主要就是利用MenuItem特性来实现的,其中false表示该菜单不需要有效性验证,priority=7控制菜单显示的位置,可以参考这里:
链接:http://www.cnblogs.com/yangrouchuan/p/6690689.html
方便起见,我把图贴下面:
这里写图片描述

这里写图片描述

接下来实现SpawnConeInHierarchy方法:

private static void SpawnConeInHierarchy()
    {
        Transform[] selections = Selection.GetTransforms(SelectionMode.TopLevel | SelectionMode.ExcludePrefab);

        if (selections.Length <= 0)
        {
            GameObject cone = new GameObject("Cone");
            cone.transform.position = Vector3.zero;
            cone.transform.rotation = Quaternion.identity;
            cone.transform.localScale = Vector3.one;
            //SetMesh(cone);
            return;
        }

        foreach (Transform selection in selections)
        {
            GameObject cone = new GameObject("Cone");
            cone.transform.SetParent(selection);
            cone.transform.localPosition = Vector3.zero;
            cone.transform.localRotation = Quaternion.identity;
            cone.transform.localScale = Vector3.one;
            //SetMesh(cone);
        }
    }

这里分两种情况,如果没有在Hierarchy面板选中任何物体,那么就在根目录下生成一个名字为”Cone”的GameObject,如果有选中物体,则生成的”Cone”会变为选中项的子物体。
PS:这里有个Bug,如果同时选中了多个物体,又是采用的Hierarchy面板右键菜单的方式,那么会在每个选中物体下都生成与选中物体数量相同的子物体,见下图。这个Bug应该不仅限于版本2017.3,因为我在网上有搜到一个同情况的帖子,时间是2016年8月。
这里写图片描述
目前这个Bug我已经提交给官方确认了,他们已转交给QA,不过不影响使用,避免办法就是不用右键菜单,而是点击菜单栏”GameObject”下的菜单。

到此为止,我们已经扩展了编辑器菜单,但是生成出来的是空物体,接下来我们实现SetMesh方法以创建Mesh,让圆锥体显示出来。

创建Mesh

分两部,首先绘出底部的圆。

绘制圆形底部

圆心已经确定为原点,半径为0.5f,圆上分布共20个点,那么每个点的坐标就可以用三角函数算出。

private static void SetMesh(GameObject go)
    {
        if (null == go)
            return;
        //仿Cylinder参数
        float myRadius = 0.5f;
        int myAngleStep = 20;
        Vector3 myTopCenter = new Vector3(0, 1, 0);
        Vector3 myBottomCenter = Vector3.zero;
        //构建顶点数组和UV数组
        //每20度一个顶点,再加上圆心,得出顶点数组长度
        Vector3[] myVertices = new Vector3[360 / myAngleStep + 1];
        //因为uv数组和顶点数组是一一对应的,所以这里同时计算uv数组
        Vector2[] myUV = new Vector2[myVertices.Length];
        //将圆心作为第一个顶点,对应的uv设置为贴图正中
        myVertices[0] = myBottomCenter;
        myUV[0] =  new Vector2(0.5f, 0.5f);
        //循环计算其他顶点坐标
        for (int i = 1; i <= myVertices.Length / 2; i++)
        {
            float curAngle = i * myAngleStep * Mathf.Deg2Rad;
            float curX = myRadius * Mathf.Cos(curAngle);
            float curZ = myRadius * Mathf.Sin(curAngle);
            myVertices[i] = new Vector3(curX, 0, curZ);
            //顶点坐标范围是[-0.5,0.5],而uv坐标范围是[0,1],所以要进行转换
            myUV[i] = new Vector2(curX + 0.5f, curZ + 0.5f);
        }

接下来,构建三角形索引数组,19个顶点,共18个三角形,所以数组长度是18 * 3 = 54。

int[] myTriangle = new int[(myVertices.Length - 1) * 3];       
        //每三个索引(即顶点数组中的顶点索引值)为一个三角形索引组
        for (int i = 0; i <= myTriangle.Length - 3; i = i+3)
        {
            //每组都以圆心起始
            myTriangle[i] = 0;
            //为能从圆锥底部看见物体,这里按逆时针顺序排列,也就是(0 1 2 0 2 3...)
            myTriangle[i + 1] = i / 3 + 1;
            //最后一个三角形时终点索引应为1
            myTriangle[i + 2] = i + 2 == myTriangle.Length / 2 - 1 ? 1 : i / 3 + 2;
            }
        }

最后,分配mesh,赋值材质后就可以看到一个圆形物体了。

//构建mesh
        Mesh myMesh = new Mesh();
        myMesh.name = "Cone";
        myMesh.vertices = myVertices;
        myMesh.triangles = myTriangle;
        myMesh.uv = myUV;
        myMesh.RecalculateBounds();
        myMesh.RecalculateNormals();
        myMesh.RecalculateTangents();
        //分配mesh
        MeshFilter mf = go.AddComponent<MeshFilter>();
        mf.mesh = myMesh;
        //分配材质
        MeshRenderer mr = go.AddComponent<MeshRenderer>();
        Material myMat = new Material(Shader.Find("Standard"));
        mr.sharedMaterial = myMat;

这里写图片描述

因为底部没光照,所以看起来是黑的,另外,上面的代码是我从最终代码中手动修改得到的,可能有错误,只是用于理解思路,完整代码会在最后给出。

完善锥体

底部圆既然已经绘制成功,锥体可以理解为将圆心上移即可,在顶点数量上,三角形索引数组上都相当于double了一份即可。
这里有个情况说明一下,我本来是想共用圆上顶点的,这样整个锥体的顶点数就是20,但经过测试是不可以的,我参考了Cube,顶点数是24,说明不同面的顶点是不能共用的,可能是因为法线方向等因素吧。
修改后的完整代码如下,

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

public class ConeCreatorEditor
{

    [MenuItem("GameObject/3D Object/Cone",false,priority = 7)]
    public static void CreateCone()
    {
        SpawnConeInHierarchy();
    }


    private static void SetMesh(GameObject go)
    {
        if (null == go)
            return;
        //仿Cylinder参数
        float myRadius = 0.5f;
        int myAngleStep = 20;
        Vector3 myTopCenter = new Vector3(0, 1, 0);
        Vector3 myBottomCenter = Vector3.zero;
        //构建顶点数组和UV数组
        Vector3[] myVertices = new Vector3[360 / myAngleStep * 2 + 2];
        //
        Vector2[] myUV = new Vector2[myVertices.Length];
        //这里我把锥尖顶点放在了顶点数组最后一个
        myVertices[0] = myBottomCenter;
        myVertices[myVertices.Length - 1] = myTopCenter;
        myUV[0] =  new Vector2(0.5f, 0.5f);
        myUV[myVertices.Length - 1] = new Vector2(0.5f,0.5f);
        //因为圆上顶点坐标相同,只是索引不同,所以这里循环一般长度即可
        for (int i = 1; i <= (myVertices.Length -2) / 2; i++)
        {
            float curAngle = i * myAngleStep * Mathf.Deg2Rad;
            float curX = myRadius * Mathf.Cos(curAngle);
            float curZ = myRadius * Mathf.Sin(curAngle);
            myVertices[i] = myVertices[i + (myVertices.Length - 2) / 2] = new Vector3(curX, 0, curZ);
            myUV[i] = myUV[i + (myVertices.Length - 2) / 2] = new Vector2(curX + 0.5f, curZ + 0.5f);

        }
        //构建三角形数组
        int[] myTriangle = new int[(myVertices.Length - 2) * 3];       
        for (int i = 0; i <= myTriangle.Length - 3; i = i+3)
        {
            if (i + 2 < myTriangle.Length / 2)
            {
                myTriangle[i] = 0;
                myTriangle[i + 1] = i / 3 + 1;
                myTriangle[i + 2] = i + 2 == myTriangle.Length / 2 - 1 ? 1 : i / 3 + 2;
            }
            else
            {
                //绘制锥体部分,索引组起始点都为锥尖
                myTriangle[i] = myVertices.Length - 1;
                //锥体最后一个三角形的中间顶点索引值为19
                myTriangle[i + 1] = i == myTriangle.Length - 3 ? 19 : i / 3 + 2;
                myTriangle[i + 2] = i / 3 + 1;
            }
        }

        //构建mesh
        Mesh myMesh = new Mesh();
        myMesh.name = "Cone";
        myMesh.vertices = myVertices;
        myMesh.triangles = myTriangle;
        myMesh.uv = myUV;
        myMesh.RecalculateBounds();
        myMesh.RecalculateNormals();
        myMesh.RecalculateTangents();
        //分配mesh
        MeshFilter mf = go.AddComponent<MeshFilter>();
        mf.mesh = myMesh;
        //分配材质
        MeshRenderer mr = go.AddComponent<MeshRenderer>();
        Material myMat = new Material(Shader.Find("Standard"));
        mr.sharedMaterial = myMat;
    }

    private static void SpawnConeInHierarchy()
    {
        Transform[] selections = Selection.GetTransforms(SelectionMode.TopLevel | SelectionMode.ExcludePrefab);

        if (selections.Length <= 0)
        {
            GameObject cone = new GameObject("Cone");
            cone.transform.position = Vector3.zero;
            cone.transform.rotation = Quaternion.identity;
            cone.transform.localScale = Vector3.one;
            //设置创建操作可撤销
            Undo.RegisterCreatedObjectUndo(cone, "Undo Creating Cone");
            SetMesh(cone);
            return;
        }

        foreach (Transform selection in selections)
        {
            GameObject cone = new GameObject("Cone");
            cone.transform.SetParent(selection);
            cone.transform.localPosition = Vector3.zero;
            cone.transform.localRotation = Quaternion.identity;
            cone.transform.localScale = Vector3.one;
            //设置创建操作可撤销
            Undo.RegisterCreatedObjectUndo(cone, "Undo Creating Cone");
            SetMesh(cone);
        }
    }
}

PS:这里的uv设置比较简单,所以对贴图也特定要求,不然图片会比较扭曲,需要的朋友可以自行修改。

结果

这里写图片描述

2019-01-17 10:34:20 ZFSR05255134 阅读数 164
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(Camera))]
public class CameraEditor : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        EditorGUILayout.LabelField(" ********** 以下是拓展功能 ********** ");

        if(GUILayout.Button("拓展功能"))
        {
            Debug.LogError("点击了拓展功能");
        }
    }
}

摄像机编辑功能拓展,可以参考此脚本拓展其它组件编辑器

2014-05-23 17:37:35 hanfeng0564 阅读数 3556

unity3D Socket与C#服务器第一次连接时通讯正常,客服端段关闭后,unity3D编辑器再次启动连接 unity3D编辑器立即卡死 原因是Socket处于异步状态,而异步线程是不受Unity管理的。这样会导致Unity一直与Socket服务器保持着握手状态,而再次启动时无法再次于服务器建立握手。

       我的解决方案是,在脚本内启用退出函数OnApplicationQuit(),因为我的Socket是写在单例里面 所以我的代码如下,道理就是这个道理。

  void OnApplicationQuit()
    {
        MySocket.GetInstance().Closed();
    }


没有更多推荐了,返回首页