精华内容
下载资源
问答
  • unity游戏序列化编辑器插件Odin - Inspector and Serializer Odin - Inspector and Serializer 是一个可将任何您想序列化的内容进行序列化编辑的工具。强大而灵活的API允许您轻松地扩展和修改...Unity编辑器拓展插件
  • Unity编辑器拓展基础知识,内有四个小Demo,具体可以参考我的文章配合使用。。。。。。。。。。。。。。。。。。。。。。
  • Unity编辑器拓展【编辑器简介】

    千次阅读 2018-01-31 20:44:48
    为什么要写Unity编辑器Unity本身的功能已经很强大了,但是由于某些项目的特殊需求,需要拓展编辑器来提高工作效率,让程序去代替人工进行一些比较繁琐的操作,减少一些出错的可能性,比如我们可以写一些工具来定制...

    为什么要写Unity编辑器

    Unity本身的功能已经很强大了,但是由于某些项目的特殊需求,需要拓展编辑器来提高工作效率,让程序去代替人工进行一些比较繁琐的操作,减少一些出错的可能性,比如我们可以写一些工具来定制更改动画曲线,也可以写工具来一键打包资源,压缩资源,检测资源冗余,统计资源信息等等。拓展编辑器也可以展示一些效果,比如人物模型的碰撞框可视化,地图网格AI的可视化等等,拓展编辑器也可以送入AssetStore进行售卖,获取经济收益。

    Unity脚本的编译顺序

    我们首先创建一个Unity项目,目录如下


    然后用VS打开,如下图,可以看到生成了四个工程文件,他们的编译顺序为①②③④的顺序


    • ①所有在Standard Assets、Pro Standard Assets 或者 Plugins文件夹中的脚本会产生一个Assembly-CShap-firstpass-vs.csproj文件,并且先编译
    • ②所有在Standard Assets/Editor、Pro Standard Assets/Editor 或者 Plugins/Editor文件夹中的脚本会产生一个Assembly-CShap-Editor-firstpass-vs.csproj文件,并且先编译
    • ③所有在Assets/Editor外面的、并且不在上面两个文件夹中的脚本文件(一般这些脚本就是我们自己写的非编辑器拓展脚本),会产生Assembly-CShap-vs.csproj工程文件,被编译
    • ④所有在Assets/Editor中的脚本会产生一个Assembly-CShap-Editor-vs-csproj工程文件,被编译。

    工程的引用关系

    如果我们用Unity自带的MonoDevelop打开后,还可以发现上述4个工程之间的引用关系是这样的:








    引用关系图:


    由此我们可以得出,我们写的一些Library库可以根据规则编译到Assembly-CSharp-fiistpass工程中,这样可以保证这个库是纯净的,不依赖于任何其他的工程。另外我们在Edito下写的代码不会被我们运行时候的代码使用到,因而在打包应用程序时,Assembly-CSharp-Editor和Assembly-CSharp-Editor-firstpass是不会被打包到应用程序中的。


    Unity常用特殊目录

    Resources

    一般用来放置游戏运行时动态的资源,比如我们创建的一些特效,UI会放到这个目录下,运行时通过Resource.Load来加载这些资源。


    StreamingAsset

    这部分的内容在游戏发布的时候,会不经过任何处理地放到安装包里,一般放置视频,音频或者一些AssetBundle,这个目录在运行时是只读的,通过Application.streamingAssetsPath获取到。


    Plugins

    一般放置和平台相关的Native库,比如游戏中需要android里面的native弹框,那么我们可以用andriod导出 jar 包放到Plugins/Android目录下,也可以在Plugin下面创建Ios目录来放置一些ios的库.另外在Unity编译安装包的时候,会按照不同平台把不同目录下的内容放到不同的平台下,比如Android包里不会包含ios目录里面的内容。


    Editor

    一般放置和编辑器相关的脚本和资源,这些脚本只能在编辑器下使用,不会被打包到安装包里面。


    Editor Default Resources

    一般放置编辑器里面用到的动态资源,我们可以用EditorGUIUtility.Load来读取相关资源,这个目录要放置在Assets这个根目录下。


    Gizmos

    一般放置图片,图标icon,用于在Scene场景中给特定的gameObject显示特定的icon。

    展开全文
  • 文章目录一、菜单栏或工具栏中创建子项二、菜单栏拓展项创建下拉菜单三、菜单拓展项...//加载LevelEditor模块,LevelEditor模块负责的就是打开UE4编辑器首先映入眼帘的各个编辑窗口。其中包括菜单栏、工具栏、视窗、...

    一、菜单栏或工具栏中创建拓展项

    首先区分几个概念:MenuBar、Menu、Toolbar。
    MenuBar:横向的可交互菜单项
    ToolBar
    Menu:纵向菜单
    Menu
    Toolbar:工具栏
    Toolbar

    在StartModule中创建拓展项
    通过FExtender对象的三个方法可以分别绑定上述三种方法的拓展项。然后通过LevelEditorModule模块的GetMenuExtensibilityManager和GetToolBarExtensibilityManager方法获取并绑定拓展项以实现对菜单及Toolbar的拓展功能。

    //加载LevelEditor模块,LevelEditor模块负责的就是打开UE4编辑器首先映入眼帘的各个编辑窗口。其中包括菜单栏、工具栏、视窗、详情面板、Modes面板、世界大纲面板等GUI功能。
    FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
    {	//创建菜单栏项
    	//创建拓展
    	TSharedPtr<FExtender> MenuBarExtender = MakeShareable(new FExtender());
    	/**
    	@Param FName ExtensionHook. 项所在位置
    	@Param EExtensionHook::Position HookPosition. 更具体的表示在ExtensionHook的相对位置:Before、After、First
    	@Param const TSharedPtr< FUICommandList >& CommandList. 触发命令
    	@Param const FMenuBarExtensionDelegate& MenuBarExtensionDelegate. 单播委托。绑定一个方法,方法是关于返回拓展项的信息。
    	*/
    	MenuBarExtender ->AddMenuBarExtension("Help", EExtensionHook::After, PluginCommands, FMenuBarEtensionDelegate::CreateRaw(this, &FStandaloneWinModule::AddMenuBarExtension));
    	//添加拓展项到关卡编辑器
    	LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuBarExtender);
    }
    {	//创建菜单拓展项,就是菜单栏内的子项
    	TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());
    	MenuExtender->AddMenuExtension("WindowLayout", EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateRaw(this, &FStandaloneWinModule::AddMenuExtension));
    	LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
    }
    {	//创建工具栏拓展项
    	TSharedPtr<FExtender> ToolBarExtender = MakeShareable(new FExtender());
    	ToolBarExtender->AddToolBarExtension("Settings", EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FStandaloneWinModule::AddToolBarExtension));
    	LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolBarExtender);
    }
    

    拓展想项委托方法
    当编辑器启动后,会调用我们绑定到拓展项的委托方法以实现功能拓展,而调用方法的Builder则可以用于构建UI。三种常用的Builder分别是FMenuBarBuilder、FMenuBuilder、FToolBarBuilder分别对应开头讲的三种拓展菜单。

    //此方法是创建一个菜单栏拓展项的委托方法,其它两个方法与此类似。
    void AddMenuBarExtension(FMenuBarBuilder& Builder)
    {
    	//AddMenuEntry有多个重载方法。可以通过其设置显示文本、提示文本、图标等参数。
    	Builder.AddMenuEntry(FStandaloneWinCommands::Get().OpenPluginWindow);
    }
    
    void AddMenuExtension(FMenuBuilder& Builder)
    {
    	Builder.AddMenuEntry(FStandaloneWinCommands::Get().OpenPluginWindow);
    }
    
    void AddToolbarExtension(FToolBarBuilder& Builder)
    {
    	//添加一个工具栏按钮
    	Builder.AddToolBarButton(FStandaloneWinCommands::Get().OpenPluginWindow);
    	//添加一个下拉菜单
    	Builder.AddComboButton(
    		FUIAction(), FOnGetContent::CreateRaw(this, &FAssetCheckToolModule::ToolbarPulldownExtension),
    		LOCTEXT("PullDown", "Pull-down"),
    		LOCTEXT("PullDownTip", "Pull-down Tip"),
    		FSlateIcon(), true);
    }
    
    

    菜单栏拓展项:
    创建菜单栏拓展项
    创建的菜单拓展项:
    菜单子项
    工具栏拓展项:
    工具栏子项
    上面构建菜单栏、菜单和工具栏拓展项的显示参数分别使用FMenuBarBuilder、FMenuBuilder和FToolBarBuilder。他们的继承关系为:

    FMultiBoxBuilder
    FBaseMenuBuilder
    FMenuBarBuilder
    FMenuBuilder
    FToolBarBuilder

    二、菜单栏拓展项(Menu Bar Exntender)

    创建下拉菜单

    我们刚刚通过AddMenuEntry创建的是一个按钮项,下面我们创建一个下拉菜单:

    void AddMenuBarExtension(FMenuBarBuilder& Builder)
    {
    	/**
    	在MenuBar中添加下拉菜单是调用AddPullDownMenu,而在ToolBarBuilder中则是AddComboButton。
    		@Param const FText& InMenuLabel. 第一个参数是菜单栏拓展项的显示名称。
    		@Param const FText& InToolTip.	第二参数是菜单栏拓展项的提示名称。
    		@Param const FNewMenuDelegate& InPullDownMenu.	第三个是下拉菜单显示的委托绑定,我们这里绑定了刚刚的菜单拓展项委托方法。
    		@Param FName InExtensionHook.	第四个参数是菜单栏拓展项的Hook名称。
    	*/
    	Builder.AddPullDownMenu(
    		LOCTEXT("PullMenu", "PullMenu"),
    		LOCTEXT("This_Is_Pull_Menu", "This is Pull Menu"),
    		FNewMenuDelegate::CreateRaw(this, &FStandaloneWinModule::AddMenuExtension),
    		"AfterHelp"
    	);
    }
    

    菜单栏拓展项:
    菜单栏拓展项
    菜单栏拓展项,菜单项我们绑定了下面的菜单委托方法,所以会显示一个分割栏和两个菜单项:
    菜单栏拓展项

    三、菜单拓展项(Menu Extender)

    1. 创建分隔栏

    
    void AddMenuExtension(FMenuBuilder& Builder)
    {
    	//第一个参数是分隔栏的Hook名,第二参数是分隔栏的显示名称
    	Builder.BeginSection("CustomSection", LOCTEXT("CustomArea", "CustomArea"));
    	//AddMenuEntry执行两遍,将在分隔栏添加两次拓展项
    	Builder.AddMenuEntry(FStandaloneWinCommands::Get().OpenPluginWindow);
    	Builder.AddMenuEntry(FStandaloneWinCommands::Get().OpenPluginWindow);
    	Bilder.EndSection();
    }
    
    

    这个是最上面绑定的此方法,所以在Window菜单显示:
    下拉菜单分隔栏
    这个是上面创建的菜单拓展项绑定此委托方法后,显示的菜单项:
    菜单栏拓展项

    2. 创建分割线

    void AddMenuExtension(FMenuBuilder& Builder)
    {
    	//第一个参数是分隔栏的Hook名,第二参数是分隔栏的显示名称
    	Builder.BeginSection("CustomSection", LOCTEXT("CustomArea", "CustomArea"));
    	//AddMenuEntry执行两遍,将在分隔栏添加两次拓展项
    	Builder.AddMenuEntry(FStandaloneWinCommands::Get().OpenPluginWindow);
    	//创建简单的分割线
    	Builder.AddMenuSeparator("SeparatorHook");
    	Builder.AddMenuEntry(FStandaloneWinCommands::Get().OpenPluginWindow);
    	Bilder.EndSection();
    }
    

    带Hook的分割线:
    带Hook的分割线

    3. 创建子下拉菜单

    菜单栏下拉菜单内还可以创建子下拉菜单

    void FStandaloneWinModule::AddMenuExtension(FMenuBuilder& Builder)
    {
    	Builder.BeginSection("CustomSection", LOCTEXT("CustomArea", "CustomArea"));
    
    	Builder.AddMenuEntry(FStandaloneWinCommands::Get().OpenPluginWindow);
    	Builder.AddMenuEntry(FStandaloneWinCommands::Get().OpenPluginWindow);
    	Builder.AddMenuSeparator("SubMenuSp");
    	/**
    	创建子下拉菜单,与创建下拉菜单相似,该方法有多个重载方法
    	@Param 子下拉菜单的父拓展项的显示名称
    	@Param 提示名称
    	@Param 子下拉菜单项的委托
    	@Param true-点击显示下拉菜单,false-鼠标移上去即显示下拉菜单
    	@Param 显示图标,这里没有设置
    	@Param 当一个子项被选择后,是否关闭子下拉菜单
    	@Param Hook名称
    	*/
    	Builder.AddSubMenu(
    		LOCTEXT("SubMenu", "SubMenu"),
    		LOCTEXT("This_is_Sub_Menu", "This is Sub Menu!"),
    		FNewMenuDelegate::CreateRaw(this, &FStandaloneWinModule::AddSubMenuExtension),
    		false,
    		FSlateIcon(),
    		true,
    		"SubMenuHook"
    	);
    	Builder.EndSection();
    }
    
    void FStandaloneWinModule::AddSubMenuExtension(FMenuBuilder& Builder)
    {
    	Builder.AddMenuEntry(FStandaloneWinCommands::Get().OpenPluginWindow);
    }
    

    子下拉菜单:
    子下拉菜单

    4. 添加其它拓展项

    自定义UI

    /**
    FMenuBuilder方法,添加自定义Slate控件
    @param	InWidget			要显示在下拉菜单的UI控件
    @param	InLabel				控件前面显示的标签文本
    @param	bNoIndent			如果为true,移除UI控件左边的边距,默认为false
    @param	bSearchable			如果为true,则Widget组件可被搜索到,默认为true
    */
    void AddWidget( TSharedRef<SWidget> InWidget, const FText& Label, bool bNoIndent = false, bool bSearchable = true );
    

    输入框

    /**
    添加一个可编辑文本框(输入框)
    @param	InLabel				显示文本
    @param	InToolTip			提示文本
    @param	InIcon				显示图标
    @param	InTextAttribute		编辑的文本内容
    @param	InOnTextCommitted	文本提交触发委托
    @param	InOnTextChanged		文本修改触发委托
    @param	bInReadOnly			文本是否只允许可读,默认为false,表示文本框可编辑
    */
    void AddEditableText( const FText& InLabel, const FText& InToolTip, const FSlateIcon& InIcon, const TAttribute< FText >& InTextAttribute, const FOnTextCommitted& InOnTextCommitted = FOnTextCommitted(), const FOnTextChanged& InOnTextChanged = FOnTextChanged(), bool bInReadOnly = false );
    

    四、工具栏拓展项

    1. 添加分隔栏和分割线

    //添加分隔栏,参数为Hook名称
    ToolBarBuilder.BeginSection("CustomHook");
    ToolBarBuilder.AddToolBarButton(FStandaloneWinCommands::Get().OpenPluginWindow);
    //添加分割线,参数为Hook名称
    ToolBarBuilder.AddSeparator("ToolBarSep");
    ToolBarBuilder.AddToolBarButton(FStandaloneWinCommands::Get().OpenPluginWindow);
    ToolBarBuilder.EndSection();
    

    分隔栏和分割线效果:
    分隔栏和分割线

    2. 创建下拉菜单

    /**
     * 创建下拉菜单按钮,其实这个方法不止可以创建下拉菜单,主要是可以创建一个带有下拉图标的按钮
     *
     * @param	InAction				FUIAction类型,可以先通过其设置拓展项参数然后传给此方法调用,也可以直接设置下面的参数
     * @param	InMenuContentGenerator	当拓展项被触发,将会调用此委托,生成一个菜单
     * @param	InLabelOverride			标签显示名称,参数如果省略,将会使用Action中的参数
     * @param	InToolTipOverride		按钮提示名称,参数如果省略,将会使用Action中的参数
     * @param	InIconOverride			按钮图标,参数如果省略,将会使用Action中的参数
     * @param	bInSimpleComboBox		如果为true,标签和图标将不会被显示,默认为false
     * @param	InTutorialHighlightName	这个组件的标识名称,在教程说明中将会高亮显示,这个参数一般不用
     */
    
    ToolBarBuilder.AddComboButton(
    	FUIAction(),		//拓展项的设置我们使用下面的参数去设置
    	FOnGetContent::CreateRaw(this, &FStandaloneWinModule::ToolBarCombox),	//绑定下面的UI委托方法
    	LOCTEXT("ComboBox", "ComboBox"),					//标签内容
    	LOCTEXT("This_Is_ComboBox", "This is Combo Box!"),	//提示信息
    	FSlateIcon(),			//不使用图标
    	false, 	//true-不显示图标和标签,false-显示图标和标签
    );
    
    //一个带显示文本框和输入文本框的UI,将其绑定到工具栏的菜单内容生成委托上
    TSharedRef<SWidget> FStandaloneWinModule::ToolBarCombox()
    {
    	TSharedPtr<SWidget> Combox = SNew(SBox).WidthOverride(150.f).HeightOverride(200.f)
    		[
    			SNew(SVerticalBox)+SVerticalBox::Slot().HAlign(HAlign_Fill).VAlign(VAlign_Top).AutoHeight()
    			[
    				SNew(STextBlock).Text(LOCTEXT("ThisComboBox", "This is Combo Box!"))
    			]+SVerticalBox::Slot().HAlign(HAlign_Fill).VAlign(VAlign_Top).AutoHeight()
    			[
    				SNew(SEditableTextBox)
    			]
    		];
    	return Combox.ToSharedRef();
    }
    

    工具栏下拉菜单是否显示标签和图标的对比(第六个参数):
    工具栏下拉菜单对比
    创建的工具栏菜单效果:
    工具栏菜单

    五、 Viewport选中Actor的菜单拓展

    //获取Level Editor模块
    FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
    //获取Viewport菜单上下文拓展项(Viewport中选中物体的右击菜单拓展项),是一个委托数组
    auto& LVCMExtenders = LevelEditorModule.GetAllLevelViewportContextMenuExtenders();
    //在数组中添加一个新的拓展项,当右击选中Actor并弹出菜单时,将会遍历此数组并执行绑定的委托方法
    LVCMExtenders.Add(FLevelEditorModule::FLevelViewportMenuExtender_SelectedActors::CreateRaw(this, &FStandaloneWinModule::LVCMExtender));
    
    //委托方法,第一个参数是命令列表,第二个是选中的Actor数组,返回菜单拓展项(即显示内容)
    TSharedRef<FExtender> FStandaloneWinModule::LVCMExtender(const TSharedRef<FUICommandList> CommandList, const TArray<AActor*> Actors)
    {
    	//加载LevelEditor模块
    	FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
    	//创建一个拓展项,与上一章中用法相似,用其拓展菜单项
    	TSharedPtr<FExtender> Extender = MakeShareable(new FExtender());
    	//设置菜单拓展项显示内容,绑定的委托是上章创建的委托方法
    	Extender->AddMenuExtension("ActorAsset", EExtensionHook::After, CommandList, FMenuExtensionDelegate::CreateRaw(this, &FStandaloneWinModule::AddMenuExtension));
    	//auto& LVCMExtenders = LevelEditorModule.GetAllLevelViewportContextMenuExtenders();
    	//LVCMExtenders.Pop();			//如果将最后一个(我们刚刚创建的)Extender移除,则将会在第一显示菜单后,下次不会再显示此项
    	//返回拓展项,将会被显示在菜单栏
    	return Extender.ToSharedRef();
    }
    

    Viewport中选中物体的菜单效果:
    场景中显示菜单
    World Outline中选中Actor的菜单效果:
    世界大纲中选中Actor的菜单效果

    六、其它常用可拓展编辑器模块

    类型菜单栏拓展工具栏拓展
    AnimationBlueprintEditor
    AnimationEditor
    BehaviorTreeEditor
    Cascade
    CurveAssetEditor×
    CurveTableEditor×
    DataTableEditor×
    DestructibleMeshEditor
    EnvironmentQueryEditor
    FontEditor
    Kismet×
    LevelEditor
    MaterialEditor
    Matinee
    NiagaraEditor
    Persona×
    PhysicsAssetEditor
    SkeletalMeshEditor
    StaticMeshEditor
    StringTableEditor×
    TextureEditor
    TranslationEditor×
    UMGEditor

    加载模块时,注意有些模块的注册名和类名不一致,比如Kistmet和FBlueprintEditorModule等。还要注意某些模块的加载顺序和时间。

    展开全文
  • Unity5.6 编辑器拓展demo

    2019-05-03 14:59:20
    学习unity编辑器拓展的时候做的一个demo: 1.对脚本组件的拓展,对某个变量的编辑器拓展。 2.使用Wizard创建窗口实现了批量修改选择的GameObject。 3. 创建了Editor Window,使用自定义窗口创建对象。
  • 经过平时的项目积累,使用以及自己编写的编辑器工具越来越多,有些工具在别的项目中同样用的上,但是不希望每次都重新拷贝新代码到工具中,于是就希望能够拓展Unity模块,实现自己的工具库,方便导入工具到项目中。...

    博客迁移

    个人博客站点,欢迎访问,www.jiingfengji.tech

    拓展Unity模块,打造私人工具库(一)

    功能介绍

    经过平时的项目积累,使用以及自己编写的编辑器工具越来越多,有些工具在别的项目中同样用的上,但是不希望每次都重新拷贝新代码到项目中,于是就希望能够拓展Unity模块,实现自己的工具库,方便导入工具到项目中。

    通过查阅资料发现,在Unity的安装目录 Unity2017.3.1f1\Editor\Data\UnityExtensions\Unity (我的版本是2017.3.1f1)下,有一些Unity自带的模块,可以通过在该路径下放入自己的模块,就可以实现Unity新建的项目都拥有该拓展模块的功能。那么,接下来就写一个工具导入模块放入该文件夹中。

    UnityExtensions文件结构基本介绍

    1、ivy.xml文件

    其中:version、unityVersion均为当前Unity的版本号;artifact name 标签为我们的DLL文件的相对路径。

    <?xml version="1.0" encoding="utf-8"?>
    <ivy-module version="2.0">
      <info version="2017.3.1" organisation="Unity" module="UnityToolCenter" e:packageType="UnityExtension" e:unityVersion="2017.3.1f1" xmlns:e="http://ant.apache.org/ivy/extra" />
      <publications xmlns:e="http://ant.apache.org/ivy/extra">
        <artifact name="Editor/UnityToolCenter" type="dll" ext="dll" e:guid="adebbd281f1a4ef3a30be7f21937e02f" />
      </publications>
    </ivy-module>
    

    2、Editor文件夹
    放入dll 代码中属于编辑器拓展的代码

    逻辑说明

    工具初版示意图:

    这里写图片描述

    通过勾选UnityPackage,直接导入到项目中,简单快捷。

    为了不直接将UnityPackage文件放入到Unity安装目录下,因此新增一个config.ini文件,里面配置UnityPackage文件的路径(绝对路径),例如下图:
    这里写图片描述

    这里写图片描述

    通过读取该配置文件,加载配置下所有路径的所有后缀名为unitypackage文件,然后显示在编辑器中。

    使用介绍

    将脚本打成dll后,将UnityToolCenter.dll和UnityToolCenter.pdb文件放入安装目录下

    这里写图片描述

    修改ivy.xml文件为

    <?xml version="1.0" encoding="utf-8"?>
    <ivy-module version="2.0">
      <info version="2017.3.1" organisation="Unity" module="UnityToolCenter" e:packageType="UnityExtension" e:unityVersion="2017.3.1f1" xmlns:e="http://ant.apache.org/ivy/extra" />
      <publications xmlns:e="http://ant.apache.org/ivy/extra">
        <artifact name="Editor/UnityToolCenter" type="dll" ext="dll" e:guid="adebbd281f1a4ef3a30be7f21937e02f" />
      </publications>
    </ivy-module>
    

    【注意】版本号,以及dll的路径

    此时,新建另一个Unity工程(同版本),就会发现菜单栏也带有Tools/UnityToolCenter,具体导入UnityPackage逻辑见下期

    参考文献

    https://539go.com/2017/10/20/UnityEditor-Unity%E7%9A%84%E6%A8%A1%E5%9D%97/

    工具终版见下期

    以上知识分享,如有错误,欢迎指出,共同学习,共同进步。

    展开全文
  • 其中EditorGUILayout、EditorGUI感觉是专门为编辑器拓展的,因为需要using UnityEditor;而GUILayout、GUI则需要using UnityEngine;但是使用过程中发现这两种控件很矛盾呀,比如GUILayout没有intpopup而EditorGUI竟然...
    效果图

    这里写图片描述

    导出配置文件
    rollMatrixPool =
    {
        [1] =
        {
            {
                { "E","E,i_2","E","E","E,i_2","E","E",},
                { "E","E","E,i_2","E","E,i_2","E","E",},
                { "E","E","E","E,i_2","E","E","E",},
            },
        },
        [2] =
        {
            {
                { "E","E,i_1","E","E","E","E","E",},
                { "E","E,i_1","E","E","E","E","E",},
                { "E","E","E,i_1","E,i_1","E","E","E",},
            },
            {
                { "E","E","E,i_2","p","p","p","E",},
                { "E","E","E,i_2","p","p,i_2","p","E",},
                { "E","E,i_2","E","E","p","p","E",},
            },
        },
        [3] =
        {
            {
                { "E","E","E","E","E","E","E",},
                { "E","E","ceb","E","E","E","E",},
                { "E","E","ceb","ceb","E","E","E",},
            },
            {
                { "E","E","E","E","E","E","E",},
                { "E","E","E","E","E","E","E",},
                { "E","E","E","E","E","E","E",},
            },
            {
                { "E","E","ceb,i_2","E","E","E,i_2","E",},
                { "ceb","E","ceb,i_2","ceb","E,i_2","E","E",},
                { "ceb","E","E","ceb,i_2","E","E","E",},
            },
            {
                { "E","p","E","p","p","p_3","p_3",},
                { "E,i_1","E,i_1","p,i_1","p","E,i_1","p_3,i_1","p_3",},
                { "E","E","p","p,i_1","E,i_1","p_3,i_1","p_3",},
            },
        },
    }
    

    由于导出的配置内容需要提供给lua用作表读取,但是c#中没有数据结构能够与lua的表进行转换。起初想通过lua与c#结合操作,然后通过lua读取存储数据,c#用来操作编辑器。但是项目使用的slua,在lua调用c#的时候不能像ulua那样直接把c#的函数注册进中间栈,需要把c#导出成库才可以调用。只能用采用字符串操作。贼麻烦。

    编辑器拓展控件分类

    主要就是GUILayout、EditorGUILayout、GUI、EditorGUI。
    其中EditorGUILayout、EditorGUI感觉是专门为编辑器拓展的,因为需要using UnityEditor;而GUILayout、GUI则需要using UnityEngine;但是使用过程中发现这两种控件很矛盾呀,比如GUILayout没有intpopup而EditorGUI竟然没有button。因此我感觉混合使用还行。

    图片叠加、按钮图片叠加

    如果使用layout是没有办法这样叠加的。吧?反正我没找到。。
    解决办法就是通过利用GUI类设置Rect。GUI类没有使用自动布局,因此在使用这个类库创建控件的时候需要指定Rect。如果你需要一个按钮使用layout布局,然后在上面叠加图片。这时候上面叠加的图片可以使用GUI.DrawTexture(rect,texture),怎么获取按钮的矩形呢?Rect r = GUILayoutUtility.GetLastRect ();这个函数可以获取到最后一次使用layout的控件的矩形。

    #GUILayout与GUI控件混合使用。

    这里写图片描述
    图中的很多button其实是没有使用layout布局的,坐标都是算出来的。实现过程是

    GUILayout.Box("",GUILayout.Width(col*gridWidth),GUILayout.Height(row*gridWidth));
                    Rect r = GUILayoutUtility.GetLastRect ();
                    var item = spanList [m];
                    for (int i = 0; i < row; i++) {
                        float y = r.y + i * gridWidth;
                        for (int j = 0; j < col; j++) {
                            float x = r.x + j * gridWidth;
                            var grid = item [i, j];
                            grid.setPos (x, y);
    各种配置文件处理

    因为编辑器是给策划用的,因此降低各种实用配置。
    比如,地图元素的种类,获取配置目录里面的全部png都当做贴图

    DirectoryInfo direction = new DirectoryInfo(rootPath); 
            FileInfo[] files = direction.GetFiles("*.png",SearchOption.AllDirectories); 
            Debug.Log ("地图贴图个数: " + files.Length);
    
            foreach (var file in files) {
                var tex = AssetDatabase.LoadAssetAtPath<Texture> (rootPath + file.Name);
                var chunk = new GUIContent (tex);
                if (chunk != null) {
                    if (IsMantle (chunk.image.name)) {
                        contentMantle.Add (chunk);
                    } else {
                        contentGround.Add (chunk);
                    }
                }
            }
    全部代码
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    using System.IO; 
    
    using System;
    
    public class MapChunk{
        public GUIContent con;
        public Rect rect;
        public Texture mantleTexture;
        public MapChunk(){
            var width = BattleMatrixEditor.gridWidth;
            rect = new Rect (0, 0, width, width);
        }
        public MapChunk(GUIContent con):this(){
            this.con = con;
        }
        public void setPos(float x, float y){
            rect.x = x;
            rect.y = y;
        }
        public void setGround(string name){
            if (con == null) {
                con = new GUIContent ();
            }
            con.image = AssetDatabase.LoadAssetAtPath<Texture> (BattleMatrixEditor.rootPath + name + ".png");
        }
        public void setMatrix(string name){
            mantleTexture = AssetDatabase.LoadAssetAtPath<Texture> (BattleMatrixEditor.rootPath + name + ".png");
        }
    }
    
    public class BattleMatrixEditor : EditorWindow {
    
        [MenuItem("Edit/BattleMatrixEditor", false, 1)]
        static void OpenWindow()
        {
            var window = GetWindow<BattleMatrixEditor>(false, "BattleMatrixEditor");
            if (window.position.position == Vector2.zero)
            {
                Resolution res = Screen.currentResolution;
            }
    
            window.position = new Rect(100, 20, 400, 700);
            window.Show();
        }
        //------------------------------配置
        int row=3;
        int col = 7;
        public static string[] isMantle = {"i_1","i_2","i_3"};
        public static float gridWidth = 40;
        public static string rootPath = "Assets/Editor/BattleMatrixEditor/";
    
        List<GUIContent> contentGround;
        List<GUIContent> contentMantle;
    
        public bool IsMantle(string name){
            foreach (var con  in isMantle) {
                if (name.Equals (con)) {
                    return true;
                }
            }
            return false;
        } 
    
        private void LoadBattleFlag(){
            contentGround = new List<GUIContent> ();
            contentMantle = new List<GUIContent> ();
    
            DirectoryInfo direction = new DirectoryInfo(rootPath); 
            FileInfo[] files = direction.GetFiles("*.png",SearchOption.AllDirectories); 
            Debug.Log ("地图贴图个数: " + files.Length);
    
            foreach (var file in files) {
                var tex = AssetDatabase.LoadAssetAtPath<Texture> (rootPath + file.Name);
                var chunk = new GUIContent (tex);
                if (chunk != null) {
                    if (IsMantle (chunk.image.name)) {
                        contentMantle.Add (chunk);
                    } else {
                        contentGround.Add (chunk);
                    }
                }
            }
    
            dufaultTexture = AssetDatabase.LoadAssetAtPath<Texture> (rootPath + defaultName +".png");
        }
    
        List<List<MapChunk[,]>> diffiList = new List<List<MapChunk[,]>>();
    
        private void AddSpanConfig(List<MapChunk[,]> spanList){
            MapChunk[,] spanItemList = new MapChunk[row,col];
            for (int i = 0; i < row; i++) {
                for (int j = 0; j < col; j++) {
                    spanItemList [i, j] = new MapChunk(new GUIContent (dufaultTexture));
                }
            }
            spanList.Add (spanItemList);
        }
    
        private void AddDiffiConfig(){
            List<MapChunk[,]> spanList = new List<MapChunk[,]>();
            diffiList.Add (spanList);
        }
    
        string defaultName = "E";
        Texture dufaultTexture;
        public void Awake(){
            LoadBattleFlag ();
        }
    
        private string align( int count){
            string s = "";
            for (int i = 0; i < count; i++) {
                s += "\t";
            }
            return s;
        }
    
        private void Save(){
            using (FileStream fs = File.Open (rootPath + "map.txt", FileMode.Create))
            {
                StreamWriter sw = new StreamWriter(fs);
                sw.WriteLine("rollMatrixPool =");
                sw.WriteLine("{");
                for (int i = 1; i <= diffiList.Count; i++) {
                    sw.WriteLine(align(1) + "["+ i +"] =");
                    sw.WriteLine(align(1) + "{");
                    var cons = diffiList [i-1];
                    foreach (var con in cons) {
                        sw.WriteLine(align(2) + "{");
                        for (int m = 0; m < row; m++) {
                            string s = align(3) + "{ ";
                            for (int n = 0; n < col; n++) {
                                var chunk = con[m, n];
                                s += "\"";
                                if (chunk.con != null && chunk.con.image && chunk.con.image)
                                    s += chunk.con.image.name;
                                if (chunk.mantleTexture != null)
                                    s += "," + chunk.mantleTexture.name;
                                s += "\",";
    
                            }
                            s += "},";
                            sw.WriteLine(s);
                        }
                        sw.WriteLine(align(2) + "},");
                    }
                    sw.WriteLine(align(1) + "},");
                }
                sw.WriteLine("}");
                sw.Close();
                fs.Close();
                Debug.Log ("SAVE FINISH ");
            }
        }
    
        private void Load(){
            diffiList.Clear ();
            using (FileStream fs = File.Open (rootPath + "map.txt", FileMode.Open)) {
                StreamReader sr = new StreamReader (fs);
                string szMap = sr.ReadToEnd ();
    
                List<string> grid = new List<string> ();
    
                int diffiIndex = 0;
                while (diffiIndex >= 0) {
                    int diffiIndexB = szMap.IndexOf ('[', diffiIndex);
                    int diffiIndexE = -1;
                    string work = "";
                    if (diffiIndexB >= 0) {
                        diffiIndexE = szMap.IndexOf ('[', diffiIndexB+1);
                        if (diffiIndexE >= 0) {
                            work = szMap.Substring (diffiIndexB, diffiIndexE - diffiIndexB);
                        } else {
                            work = szMap.Substring (diffiIndexB);
                        }
                    }
    
                    string[] info =  work.Split ('\"');
                    for (int i = 0; i < info.Length; i++) {
                        if (i % 2 == 1) {
                            grid.Add (info [i]);
                        }
                    }
                    LoadConfigToDiffic (grid);
    //              int gridIndex = 1;
    //              while (gridIndex >= 1) {
    //                  int gridIndexB = work.IndexOf ('\"', gridIndex);
    //                  int gridIndexE = -1;
    //                  string szGrid = "";
    //                  if (gridIndexB >= 0) {
    //                      gridIndexB++;
    //                      gridIndexE = work.IndexOf ('\"', gridIndexB);
    //                      if (gridIndexE >= 0) {
    //                          count++;
    //                          szGrid = work.Substring (gridIndexB, gridIndexE - gridIndexB);
    //                      }
    //                  }
    //                  Debug.Log (szGrid);
    //                  gridIndex = gridIndexE + 1;
    //              }
                    grid.Clear();
                    diffiIndex = diffiIndexE;
                }
                Debug.Log ("LOAD FINISH");
                sr.Close ();
                fs.Close ();
            }
        }
    
        private void LoadConfigToDiffic(List<string> confs){
            if (confs == null) {return;}
    
            int confCount = confs.Count / (row * col);
            int index = 0;
            List<MapChunk[,]> spanList = new List<MapChunk[,]>();
            for (int i = 0; i < confCount; i++) {
                MapChunk[,] spanItemList = new MapChunk[row,col];
                for (int m = 0; m < row; m++) {
                    for (int n = 0; n < col; n++) {
                        spanItemList [m, n] = madeMapGrid(confs[index++]) ;//new MapChunk(new GUIContent (dufaultTexture));
                    }
                }
                spanList.Add (spanItemList);
            }
            diffiList.Add (spanList);
        }
    
        private MapChunk madeMapGrid(string name){
            var chunk = new MapChunk ();
            string[] gI = name.Split (',');
            if (gI.Length >= 0) {
                chunk.setGround (gI [0]);
            }
            if (gI.Length > 1) {
                chunk.setMatrix (gI [1]);
            }
            return chunk;
        }
    
        int currentGroundIndex = -1;
        int currentMantleIndex = -1;
        Vector2 scrollPos;
    
        private void OnGUI(){
            EditorGUILayout.BeginHorizontal();
            if (GUILayout.Button("保存", GUILayout.Width(100))){
                Save ();
            } 
            if (GUILayout.Button("读取", GUILayout.Width(100))){
                Load ();
            } 
            EditorGUILayout.EndHorizontal ();
            EditorGUILayout.BeginHorizontal();
            for (int i = 0; i < contentGround.Count; i++) {
                var con = contentGround [i];
                if (GUILayout.Button(con, GUILayout.Width(40), GUILayout.Height(40))){
                    currentGroundIndex = i;
                    currentMantleIndex = -1;
                }
            }
            EditorGUILayout.EndHorizontal ();
            EditorGUILayout.BeginHorizontal();
            for (int i = 0; i < contentMantle.Count; i++) {
                var con = contentMantle [i];
                if (GUILayout.Button(con, GUILayout.Width(40), GUILayout.Height(40))){
                    currentMantleIndex = i;
                    currentGroundIndex = -1;
                }
            }
            EditorGUILayout.EndHorizontal ();
    
            if (GUILayout.Button("添加难度", GUILayout.Width(100))){
                AddDiffiConfig ();
            } 
    
            scrollPos = EditorGUILayout.BeginScrollView (scrollPos);
            for (int n = 0; n < diffiList.Count; n++) {
                var spanList = diffiList [n];
    
                EditorGUILayout.BeginHorizontal();
                GUILayout.Space(30);
                EditorGUILayout.LabelField ("难度:"+n.ToString(), GUILayout.Width(50));
                if (GUILayout.Button("添加配置", GUILayout.Width(100))){
                    AddSpanConfig (spanList);
                } 
                if (GUILayout.Button("删除该难度", GUILayout.Width(100))){
                    diffiList.Remove(spanList);
                } 
                EditorGUILayout.EndHorizontal ();
    
                for (int m = 0; m < spanList.Count; m++) {
    
                    EditorGUILayout.BeginHorizontal();
                    GUILayout.Space(60);
    
                    EditorGUILayout.LabelField ("配置:"+m.ToString(),GUILayout.Width(50));
                    if (GUILayout.Button("删除配置", GUILayout.Width(100))){
                        spanList.RemoveAt (m);
                        return;
                    } 
                    EditorGUILayout.EndHorizontal ();
                    GUILayout.BeginHorizontal();
                    GUILayout.Space(60);
                    GUILayout.Box("",GUILayout.Width(col*gridWidth),GUILayout.Height(row*gridWidth));
                    Rect r = GUILayoutUtility.GetLastRect ();
                    var item = spanList [m];
                    for (int i = 0; i < row; i++) {
                        float y = r.y + i * gridWidth;
                        for (int j = 0; j < col; j++) {
                            float x = r.x + j * gridWidth;
                            var grid = item [i, j];
                            grid.setPos (x, y);
                            var con = grid.con;
                            if (GUI.Button(grid.rect, con)){
                                if (currentGroundIndex >= 0) {
                                    con.image = contentGround [currentGroundIndex].image;
                                }
                                if (currentMantleIndex >= 0) {
                                    var tex = grid.mantleTexture;
                                    var curTex = contentMantle [currentMantleIndex].image;
                                    if (grid.mantleTexture == null) {
                                        grid.mantleTexture = curTex;
                                    } else {
                                        if (tex.Equals (curTex))
                                            grid.mantleTexture = null;
                                        else
                                            grid.mantleTexture = curTex;
                                    }
                                }
                            }
                            if (grid.mantleTexture != null) {
                                GUI.DrawTexture (grid.rect, grid.mantleTexture);
                            }
                        }
                    }
                    GUILayout.EndHorizontal ();
                }
            }
            EditorGUILayout.EndScrollView ();
        }
    }
    
    展开全文
  • Unity 编辑器拓展动态下拉列表

    千次阅读 2020-06-15 18:37:09
    //编辑器扩展脚本放到Editor下 using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; [CustomEditor(typeof(objPropPrefab))] //指定要编辑的脚本对象 public ...
  • 今天看到雨松的微博推荐的,很全的unity编辑器拓展教程,特别详细,好多都不知道的,转给大家看看,顺便也留个备份。日文的,看代码或者看图应该能看懂,或者可以借助chrome或者360浏览器的翻译网页查看。地址:...
  • 4.拓展原有的按键菜单的按键 using UnityEngine; using UnityEditor; public class MyEditor : MonoBehaviour { [MenuItem("Window/FunAAAAAAAAAAAA",false,1)] //在原有的Window菜单下添加一个按键 static ...
  • Unity编辑器扩展---动画事件编辑器(仿UE4动画事件编辑器) Unity编辑器扩展---动画事件编辑器(仿UE4动画事件编辑器) Unity编辑器扩展---动画事件编辑器(仿UE4动画事件编辑器) Unity编辑器扩展---动画事件编辑器...
  • unity编辑器拓展之自动生成脚本模板

    千次阅读 2018-04-21 22:09:15
     第一个是博主的预设,第二个图是拓展窗口,将预设拖到拓展窗口的Trannstorm栏,点击create之后,看到“ok”log之后就可以看到生成的脚本,如下  是不是很方便,路径也没问题。到此,create就算完成了,具体的内容...
  • Unity编辑器拓展之三:拓展Unity的Hierarchy面板
  • 本项目基于layui.layedit富文本编辑器,在其之上拓展而来。 新增功能 html源码模式、插入hr水平线、段落格式、字体颜色、字体背景色、批量上传图片、上传视频、上传附件、锚点、插入表格、插入自定义链接、全屏、...
  • 使用场景 最近在做资源管理模块时,关于资源引用这方面的问题,并不想直接引用某一个asset ,比如Prefab、或者sprite、material等资源,当然可以由...结合ObjectFiled这个编辑器类来介绍,我们拖入某一个asset,然后...
  • //将场景设置为dirty,为了删除后保存场景,这个在做编辑器的时候经常要用到 UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene())...
  • 先占个坑,后面待续
  • Unity编辑器拓展之九:SearchField

    千次阅读 2018-05-13 18:43:35
    Unity官方介绍文档: ... Properties 1、autoSetFocusOnFindCommand:当用户按下“Ctrl/Cmd + F”时,聚焦该控件,默认值为true 相关代码: private bool m_AutoSetFo...
  • Unity编辑器拓展<一>

    千次阅读 2017-03-20 00:22:48
    什么是编辑器拓展 Unity是一个编辑器,编辑器就是可以进行编辑的工具,比如我们的文本Text就是一个文本编辑器,那我们的Unity其实就是用来编辑游戏的,编辑的扩展就是对编辑器本身的功能进行添加。但是由于Unity是不...
  • NGUI ComponentSelector增加搜索框 目的 最近在使用NGUI,给Sprite选择图集的时候,没有搜索框要挨个找挺麻烦,因此修改...关于SearchField介绍请看 Unity编辑器拓展之九 代码 以下代码是修改的NGUI里的Compone...
  • Unity编辑器拓展之十:UI对齐工具

    千次阅读 2018-06-22 22:45:19
    示例图 开发思路 1、获取选中的UI 2、根据对齐规则修改所选中UI的坐标 代码 UGUIAlign.cs using System.Collections; using System.Collections.Generic; using UnityEditor;... Top = 1...
  • 如果没有看过Unity编辑器拓展之一:ReorderableList可重新排序的列表框(简单使用)的,可以先看这一篇:  http://blog.csdn.net/qq_26999509/article/details/77782177 在此基础上,来绘制更加复杂的类 先提供一...
  • Unity3d编辑器拓展

    千次阅读 2015-07-24 18:38:26
    http://blog.sina.com.cn/s/blog_471132920101n8cr.html 原创文章如需转载请注明:转载自风宇冲Unity3D教程学院    Unity3d编辑器拓展 Unity Editor编辑器拓展一般用
  • Unity编辑器拓展之一:ReorderableList可重新排序的列表框
  • 本人第一次写博客,跟随者雨凇大哥的脚步,行走在游戏开发...unity编辑器-Project 右键菜单拓展 代码如下: using System.Collections; using System.Collections.Generic; using UnityEngine; using Un...
  • 预制体层级全预览 目的 Asset面板下预制体并不会...代码中用了Unity编辑器拓展之十一:通过Image Name反查Prefab 中的一个类PrefabTreeView using System.Collections; using System.Collections.Generic; usi...
  • 编辑器拓展 https://blog.csdn.net/zxl321365712/article/details/80080586 蛮牛上一个详细的博客 http://www.manew.com/thread-90330-1-1.html siki关于编辑器拓展的视频教程(免费) ...
  • 博客迁移 个人博客站点,欢迎... 通过这篇文章,希望可以帮助大家学会如何分析反编译出的代码,分析UnityEditor如何实现的编辑器,以此来写出更加实用的插件 以上知识分享,如有错误,欢迎指出,共同学习,共同进步。
  • 关闭unity主界面的时候,怎么让自定义的窗口也被自动关闭,需要绑定事件还是重写某个方法
  • 有时候想自动将预制添加到场景中,但是又不想破坏预制的链接关系,这时候可以使用PrefabUtility类进行操作。 1、使用AssetDatabase.GetAssetPath获取预制路径 foreach (Object obj in Selection.objects) { ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 55,558
精华内容 22,223
关键字:

编辑器拓展