概述



U3D提供了一套拓展编辑器的接口,可以用于直接在编辑器非播放模式运行程序。常用于运行一些工具程序,例如资源管理。在做技能编辑器等工具程序时,也可以使用运行模式接口会比较简单(这样也方便开放游戏创意工坊给玩家)。使用编辑器去做一些渲染相关的预览(如粒子系统,动画预览)会麻烦一点,有时候需要查询和反射使用U3D引擎未暴露的接口。

U3D编辑器相关官方文档查询链接:https://docs.unity3d.com/cn/current/Manual/GUIScriptingGuide.html

业务需求分析



这是常用的一些需求和接口,多数的用法比较简单的在这里简单介绍一下。

OnGUI

代码化在视口绘制UI,常用于绘制一些DebugUI,如官方示例:

OnGUI官方介绍

using UnityEngine;
using System.Collections; public class GUITest : MonoBehaviour { void OnGUI ()
{
// 创建背景框
GUI.Box(new Rect(10,10,100,90), "Loader Menu"); // 创建第一个按钮。如果按下此按钮,则会执行 Application.Loadlevel (1)
if(GUI.Button(new Rect(20,40,80,20), "Level 1"))
{
Application.LoadLevel(1);
} // 创建第二个按钮。
if(GUI.Button(new Rect(20,70,80,20),"Level 2"))
{
Application.LoadLevel(2);
}
}
}

Editor和ExecuteInEditMode特性

  • ExecuteInEditMode特性:让脚本在编辑器模式运行。
  • Editor:封装一些UI绘制的接口,如(重新OnInspectorGUI以在Inspector窗口绘制UI界面)。

Editor和ExecuteInEditMode特性相关官方介绍

EditorWindow

继承EditorWindow重写以在编辑器创建绘制一个独立的编辑器窗口。

using UnityEditor;
using UnityEngine; public class MyWindow : EditorWindow
{
string myString = "Hello World";
bool groupEnabled;
bool myBool = true;
float myFloat = 1.23f; // 将名为"My Window"的菜单项添加到 Window 菜单
[MenuItem("Window/My Window")]
public static void ShowWindow()
{
//显示现有窗口实例。如果没有,请创建一个。
EditorWindow.GetWindow(typeof(MyWindow));
} void OnGUI()
{
GUILayout.Label ("Base Settings", EditorStyles.boldLabel);
myString = EditorGUILayout.TextField ("Text Field", myString); groupEnabled = EditorGUILayout.BeginToggleGroup ("Optional Settings", groupEnabled);
myBool = EditorGUILayout.Toggle ("Toggle", myBool);
myFloat = EditorGUILayout.Slider ("Slider", myFloat, -3, 3);
EditorGUILayout.EndToggleGroup ();
}
}

EditorWindow官方示例

Serializable属性绘制器

Serializable U3D官方翻译为属性绘制器,字面意思是可被序列化的。大概的效果是被Serializable特性标记可以被U3D序列化并在U3D进行绘制和提供更方便的操作,如下:

using System;
using UnityEngine; enum IngredientUnit { Spoon, Cup, Bowl, Piece } // 自定义 Serializable 类
[Serializable]
public class Ingredient
{
public string name;
public int amount = 1;
public IngredientUnit unit;
} public class Recipe : MonoBehaviour
{
public Ingredient potionResult;
public Ingredient[] potionIngredients;
}

属性绘制器官方介绍

外观风格/皮肤

GUIStyle和GUISkin提供了接口来定制化UI绘制的外观风格,区别在于GUIStyle常用于某个控件的风格绘制,而GUISkin则会应用于其下文中所有控件的绘制,具体使用细节见官方介绍

GUISkin官方介绍

GUIStyle官方介绍

PreviewRenderUtility 预览窗口渲染工具

PreviewRenderUtility 个人认为它是一个预览窗口的渲染工具类,官方对其介绍几乎没有。但是在EditorWindow、Inspector窗口预览模型、动画、粒子特效会用到。例如这个特效的预览下文会具体介绍:

常用控件速查表

名称 代码 示例
Label GUI.Label (new Rect (25, 25, 100, 30), "Label");
Button if (GUI.Button (new Rect (25, 25, 100, 30), "Button"))
TextField 单行输入框 str = GUI.TextField (new Rect (25, 25, 100, 30), str);
TextArea 多行输入框 str = GUI.TextArea (new Rect (25, 25, 100, 30), str);
Toggle 勾选框 b = GUI.Toggle (new Rect (25, 25, 100, 30), toggleBool, "Toggle");
HorizontalSlider 水平滑块 h = GUI.HorizontalSlider (new Rect (25, 25, 100, 30), hSliderValue, 0.0f, 10.0f);
Toolbar 页签 Idx = GUI.Toolbar (new Rect (25, 25, 250, 30), Idx, BtnNames);
SelectionGrid 平铺列表 Idx = GUI.SelectionGrid (new Rect (25, 25, 300, 60), Idx, Names, Cow);
ScrollView 滚动列表 pos = GUI.BeginScrollView (rect, scrollViewVector, rectContent);
//XXX
GUI.EndScrollView();
Window 小窗口 w = GUI.Window (0, wRect, drawDele, "win");

注意手动排版使用GUI.XXX,自动排版使用GUILayout.XXX

布局速查表

布局的一般用法是,例如组Group

GUI.BeginGroup

//控件A

//控件B ...

GUI.EndGroup



using (new GroupScope)

{

//控件A

//控件B ...

}

布局 用法 预览
组 Group 固定相对位置
区域 Area 用于自动布局一组控件,与组类似 -
水平布局 Horizontal 水平布局一组控件
垂直布局 Vertical 垂直布局一组控件

GUILayoutOption可以某些重写自动布局参数,例如:

GUILayout.Button ("My width has been overridden", GUILayout.Width (95));

GUILayout.Width重写了自动布局这个按钮的宽

特效预览窗口

U3D自带的特效预览方式是拖到Scene窗口上,有的时候做编辑器(例如技能编辑器)时需要预览的特效,切来切去太麻烦了。而且U3D拖到Scene才能预览这种方式也很麻烦,也可以自己拓展选中特效在Hir上预览。

关键点

  • 使用反射调用库函数绑定特效
  • 使用prevRU渲染GameObj

代码示例

LibUtil 调用库函数

public class LibUtil
{
public static Type ParticleSystemEditorUtils
{
get
{
var assembly = Assembly.GetAssembly(typeof(Editor));
return assembly.GetType("UnityEditor.ParticleSystemEditorUtils");
}
} public static ParticleSystem lockedParticleSystem
{
get
{
var info = ParticleSystemEditorUtils.GetProperty("lockedParticleSystem", BindingFlags.Static | BindingFlags.NonPublic);
return (ParticleSystem)info.GetValue(null, null);
}
set
{
var info = ParticleSystemEditorUtils.GetProperty("lockedParticleSystem", BindingFlags.Static | BindingFlags.NonPublic);
info.SetValue(null, value, null);
}
} public static bool editorIsScrubbing
{
set
{
var info = ParticleSystemEditorUtils.GetProperty("playbackIsScrubbing", BindingFlags.Static | BindingFlags.NonPublic);
info.SetValue(null, value, null);
}
} public static float editorPlaybackTime
{
get
{
var info = ParticleSystemEditorUtils.GetProperty("playbackTime", BindingFlags.Static | BindingFlags.NonPublic);
return (float)info.GetValue(null, null);
}
set
{
var info = ParticleSystemEditorUtils.GetProperty("playbackTime", BindingFlags.Static | BindingFlags.NonPublic);
info.SetValue(null, value, null);
}
} public static void StopEffect()
{
var assembly = Assembly.GetAssembly(typeof(Editor));
var util = assembly.GetType("UnityEditor.ParticleSystemEffectUtils");
var info = util.GetMethod("StopEffect", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { }, new ParameterModifier[] { });
info.Invoke(null, null);
}
}

拓展ObjectPreview

public class MyView : ObjectPreview
{
PreviewRenderUtility prevRU;
GameObject ins; Mesh fm;
Texture2D ft;
Material fma; static int prevCulLay = 31; bool isRunning = false; Editor e; ParticleSystem ps
{
get
{
var p = ins.GetComponent<ParticleSystem>();
return p;
}
} public void BindEditor(Editor editor)
{
e = editor;
} public void Repaint()
{
if (e != null)
e.Repaint();
} void DrawPlayBtn()
{
if (GUILayout.Button("Play"))
Play();
if (GUILayout.Button("Stop"))
Stop();
} public override void OnPreviewSettings()
{
using (new EditorGUILayout.HorizontalScope())
{
DrawPlayBtn();
}
} public void Play()
{
var p = ps;
Stop();
if (p != null && isRunning == false)
{
isRunning = true;
LibUtil.lockedParticleSystem = p;
p.Play();
LibUtil.editorIsScrubbing = false;
EditorApplication.update += Update;
}
} public void Stop()
{
var p = ps;
if (p != null && isRunning == true)
{
LibUtil.editorIsScrubbing = false;
LibUtil.editorPlaybackTime = 0f;
LibUtil.StopEffect();
isRunning = false;
p.Stop();
EditorApplication.update -= Update;
}
} //PreviewRenderUtility渲染
public void Render()
{
var flag = StartLight(); var viewDir = new Vector2(120f, -20f);
var cam = prevRU.camera; var zoomFactor = 1.0f;
var avatarScale = 1.0f; cam.nearClipPlane = 0.5f * zoomFactor;
cam.farClipPlane = 100f * avatarScale;
Quaternion rot = Quaternion.Euler(-viewDir.y, -viewDir.x, 0f);
var camPos = rot * (Vector3.forward * -5.5f * zoomFactor); // + bodyPos + pivotPosOff;
cam.transform.position = camPos;
cam.transform.rotation = rot; var refIns = ins; Matrix4x4 mat = Matrix4x4.TRS(refIns.transform.position, Quaternion.identity, Vector3.one * 5f * avatarScale);
var refPos = refIns.transform.position;
fma.mainTextureOffset = -new Vector2(refPos.x, refPos.z) * 5f * 0.08f * (1f / avatarScale);
fma.SetVector("_Alphas", new Vector4(0.5f * 1f, 0.3f * 1f, 0f, 0f));
Graphics.DrawMesh(fm, mat, fma, prevCulLay, cam, 0); cam.Render();
EndLighting(flag);
} public override void OnPreviewGUI(Rect r, GUIStyle background)
{
if (prevRU == null)
{
CreatePrevRU();
} prevRU.BeginPreview(r, background);
Render();
prevRU.EndAndDrawPreview(r);
} public void Update()
{
if (!isRunning)
return; if (ps != null)
{
Repaint();
}
} public override void Initialize(UnityEngine.Object[] targets)
{
base.Initialize(targets);
} public void CreatePrevRU()
{
if (prevRU != null)
return; prevRU = new PreviewRenderUtility(true);
prevRU.cameraFieldOfView = 30.0f; var cam = prevRU.camera;
cam.cullingMask = 1 << prevCulLay;
cam.allowHDR = false;
cam.allowMSAA = false; CreateIns();
CreateFloor();
} void CreateIns()
{
DestoryIns();
ins = GameObject.Instantiate(target) as GameObject;
SetUpInsArr(ins);
prevRU.AddSingleGO(ins);
} void CreateFloor()
{
fm = Resources.GetBuiltinResource<Mesh>("New-Plane.fbx");
ft = (Texture2D)EditorGUIUtility.Load("Avatar/Textures/AvatarFloor.png");
var s = EditorGUIUtility.LoadRequired("Previews/PreviewPlaneWithShadow.shader") as Shader;
fma = new Material(s);
fma.mainTexture = ft;
fma.mainTextureScale = Vector2.one * 20f;
fma.SetVector("_Alphas", new Vector4(0.5f, 0.3f, 0f, 0f));
fma.hideFlags = HideFlags.HideAndDontSave;
} bool StartLight()
{
Light[] lights = prevRU.lights; lights[0].intensity = 1.4f;
lights[0].transform.rotation = Quaternion.Euler(40f, 40f, 0f);
lights[1].intensity = 1.4f;
Color ambient = new Color(0.1f, 0.1f, 0.1f, 0f);
InternalEditorUtility.SetCustomLighting(lights, ambient);
bool fog = RenderSettings.fog;
Unsupported.SetRenderSettingsUseFogNoDirty(false);
return fog;
} void EndLighting(bool old)
{
Unsupported.SetRenderSettingsUseFogNoDirty(old);
InternalEditorUtility.RemoveCustomLighting();
} public void SetUpInsArr(GameObject go)
{
go.hideFlags = HideFlags.HideAndDontSave;
go.layer = prevCulLay; foreach (Transform t in go.transform)
SetUpInsArr(t.gameObject);
} public void DestoryIns()
{
if (ins == null)
return; GameObject.DestroyImmediate(ins);
ins = null;
} public override void Cleanup()
{
DestoryIns();
if (prevRU != null)
{
prevRU.Cleanup();
prevRU = null;
}
base.Cleanup();
}
} public class MyEditor : Editor
{
MyView p; MyView preview
{
get
{
if (p == null)
{
p = new MyView();
p.Initialize(targets);
p.BindEditor(this);
}
return p;
}
} public override bool HasPreviewGUI()
{
return preview.HasPreviewGUI();
} public override void OnPreviewSettings()
{
preview.OnPreviewSettings();
} public override void OnPreviewGUI(Rect r, GUIStyle background)
{
preview.OnPreviewGUI(r, background);
} public void Clearup()
{
p.Cleanup();
p = null;
}
}

EditorWindow拓展

public class MyWnd : EditorWindow
{
[MenuItem("编辑器/MyWnd")]
static public void PopUp()
{
var w = EditorWindow.GetWindow<MyWnd>("MyWnd");
w.minSize = new Vector2(800, 600);
w.Show();
} MyEditor e;
GameObject go;
GameObject pf; private void OnGUI()
{
EditorGUI.BeginChangeCheck();
pf = (GameObject)EditorGUILayout.ObjectField(pf, typeof(GameObject), false);
if (EditorGUI.EndChangeCheck())
{
if (e != null)
{
e.Cleanup();
e = null;
} e = (MyEditor)Editor.CreateEditor(pf, typeof(MyEditor));
} if (e)
{
e.OnPreviewSettings();
e.OnPreviewGUI(GUILayoutUtility.GetRect(400, 400), EditorStyles.whiteLabel);
Repaint();
}
}
}

动画预览

动画预览相对会简单一点,U3D有一个AnimationClipEditor来预览动画,只是封在了库里没有暴露出来。AnimationClipEditor需要一个AnimClip文件和一个Avatar,比较麻烦。若是Avatar的GameObj上挂载Anim组件可以获取到AnimClip,可以优化下直接拖入Avatar预览动画。

关键点

  • 使用AnimationClipEditor预览动画,使用反射调用库函数优化直接拖入Avatar预览动画
  • AnimationClipEditor类名需要从Debug工具中查看,比如VS断点。然后到U3D的官方C#库函数反射文件中查看
  • 跳转链接:U3D官方C#反射文件

代码示例

反射工具类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Reflection; public class CSRefUtil
{
static public void SetValuePublic(object obj, string name, params object[] param)
{
var filed = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public);
filed.SetValue(obj, param);
} static public void SetValuePrivate(object obj, string name, params object[] param)
{
var filed = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.NonPublic);
filed.SetValue(obj, param);
} static public T GetValuePublic<T>(object obj, string name, params object[] param)
{
return (T)obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public).GetValue(obj);
} static public T GetValuePrivate<T>(object obj, string name, params object[] param)
{
return (T)obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj);
} static public void SetPropertyPublic(object obj, string name, params object[] param)
{
var property = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
property.SetMethod.Invoke(obj, param);
} static public void SetPropertyPrivate(object obj, string name, params object[] param)
{
var property = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.NonPublic);
property.SetMethod.Invoke(obj, param);
} static public T GetPropertyPublic<T>(object obj, string name)
{
var property = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
return (T)property.GetMethod.Invoke(obj, new object[] { });
} static public T GetPropertyPrivate<T>(object obj, string name, params object[] param)
{
var property = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.NonPublic);
return (T)property.GetMethod.Invoke(obj, new object[] { });
} static public T CallMethodPublic<T>(object obj, string name, params object[] param)
{
var method = obj.GetType().GetMethod(name, BindingFlags.Instance | BindingFlags.Public);
return (T)method.Invoke(obj, param);
} static public T CallMethodPrivate<T>(object obj, string name, params object[] param)
{
var method = obj.GetType().GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic);
return (T)method.Invoke(obj, param);
}
}

编辑器窗口拓展

public class MyEditor : EditorWindow
{
Editor previewAnimWnd;
AnimationClip previewAnim;
bool isPreviewAnimDirty = false;
object avatarWnd;
Vector2 AnimViewSize = new Vector2(800, 600);
GameObject animGo; [MenuItem("编辑器/MyEditor")]
public static void PopUp()
{
var win = GetWindow<MyEditor>();
win.minSize = new Vector2(800, 800);
win.Show();
} void OnGUI()
{
DrawPreviewAnim();
}
// 清理资源
void OnDisable()
{
animGo = null;
previewAnim = null; if (previewAnimWnd != null)
previewAnimWnd = null; if (avatarWnd != null)
avatarWnd = null;
} void SetPreviewAnim(AnimationClip anim)
{
previewAnim = anim;
isPreviewAnimDirty = true;
} public void DrawPreviewAnim()
{
EditorGUI.BeginChangeCheck();
animGo = (GameObject)EditorGUILayout.ObjectField(animGo, typeof(GameObject), false);
if (EditorGUI.EndChangeCheck())
{
if (animGo != null)
{
var animator = animGo.GetComponent<Animator>();
var anim = animator.runtimeAnimatorController.animationClips[0];
SetPreviewAnim(anim);
}
} if (isPreviewAnimDirty)
{
isPreviewAnimDirty = false;
if (previewAnim != null)
{
previewAnimWnd = Editor.CreateEditor(previewAnim);
previewAnimWnd.OnInspectorGUI();
avatarWnd = CSRefUtil.GetValuePrivate<object>(previewAnimWnd, "m_AvatarPreview");
CSRefUtil.CallMethodPrivate<object>(avatarWnd, "SetPreview", animGo);
}
} EditorGUILayout.BeginVertical(GUILayout.Width(AnimViewSize.x), GUILayout.Height(AnimViewSize.y)); //绘制
if (previewAnimWnd != null)
{
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
previewAnimWnd.OnPreviewSettings();
}
AnimationMode.StartAnimationMode();
previewAnimWnd.OnInteractivePreviewGUI(GUILayoutUtility.GetRect(AnimViewSize.x, AnimViewSize.y), EditorStyles.whiteLabel);
AnimationMode.StopAnimationMode();
}
EditorGUILayout.EndVertical();
}
}

备注

  • U3D对编辑器的很多功能都没有介绍清楚,碰到问题建议Google搜索或去讨论群去问下。
  • 还有布局缩进等不是很常见的功能,建议用到的时候去Google或者浏览官方文档。
  • U3D有对编辑器UI树形控件支持
  • 编辑器拓展开发量大可以考虑使用Unity编辑器扩展Odin插件,不过是收费的

U3D编辑器开发&粒子特效/动画预览器示例的更多相关文章

  1. 李洪强iOS开发之苹果使用预览截图

    李洪强iOS开发之苹果使用预览截图 01 在预览的图片中选中你要截得区域  02 - command + C   03 - Command + N 04 - Command + S (保存)

  2. iOS开发之使用Storyboard预览UI在不同屏幕上的运行效果

    在公司做项目一直使用Storyboard,虽然有时会遇到团队合作的Storyboard冲突问题,但是对于Storyboard开发效率之高还是比较划算的.在之前的博客中也提到过,团队合作使用Storyb ...

  3. 安卓开发实用技巧:TextView预览

    背景: 使用TextView时,为了方便在开发工具中预览效果,需要在TextView中设置文字(如:android:text="Hello World"),但是等到后面提交时,为了 ...

  4. 微信小程序开发之真机预览

    1:真机预览时上传组件的坑: 当在真机里面使用上传组件,当进入选择相片或者拍照的时候,小程序会进入后台,调用APP onHide()方法,选择完返回小程序是会调用App Onshow()方法,然后调用 ...

  5. [js开源组件开发]-手机端照片预览组件

    手机端照片预览组件 可怜的我用着华为3C手机,用别人现成的组件都好卡,为了适应我这种屌丝,于是自己简化写了一版的照片预览效果,暂时无缩放功能,以后可能有空再加吧,你也可以自己加下,这是个github上 ...

  6. Android开发:实时处理摄像头预览帧视频------浅析PreviewCallback,onPreviewFrame,AsyncTask的综合应用(转)

    原文地址:http://blog.csdn.net/yanzi1225627/article/details/8605061# 很多时候,android摄像头模块不仅预览,拍照这么简单,而是需要在预览 ...

  7. 10款经典的web前端特效的预览及源码

    1.CSS3响应式导航菜单 今天我给大家介绍一下如何使用纯CSS来实现的一个响应式导航菜单,我们使用的是HTML5+CSS3技术,当浏览器窗口变小或者使用手机浏览器访问的时候,原本横条菜单会收缩成一个 ...

  8. 10款jquery图片广告特效的预览及源码下载 改自[帅的相对论]

    原文格式有问题,我来排版了一下,分享给大家. 1.jQuery仿海尔官网全屏焦点图特效代码 Query仿海尔官网全屏焦点图特效代码,带有左右箭头的jQuery焦点图切换特效.当焦点图切换时,下方的三块 ...

  9. ueditor编辑器视频上传不能预览的问题

    ps:来源 https://blog.csdn.net/eadela/article/details/76264168 修改ueditor.all.js文件 ueditor.all.js,17769行 ...

  10. Android Camera开发:给摄像头预览界面加个ZoomBar(附完整代码下载)

    源码:http://files.cnblogs.com/android100/StandardCamera2013-10-18.zip 废话不说了,就是加个seekbar,拖动的话能够调节焦距,让画面 ...

随机推荐

  1. Git新技能-stash操作

    最近开发的工期非常紧迫,一直在忙各种杂七杂八的事情,负责人都还没有创建好测试环境, 所以代码也不能部署.可是项目经理催促开发进度又催得很急,新的开发需求必须在指定的时间内 完成,我们只得想办法去克服困 ...

  2. mybatis-增删改查和配置

    加入log4j日志功能 加入依赖 <!-- log4j日志 --> <dependency> <groupId>log4j</groupId> < ...

  3. CSP-S游记

    第三次考csp-s了,希望这次不要二等 Day ?(初赛) 之前校内模拟赛平均下来都在班级中游,所以不求高分但是觉得过没问题(事实好像确实如此 先开题,选择题很水秒了(devinNB猜到了考Linux ...

  4. python 类相关 下划线相关 __init__

    类 1.静态方法 class C(object): @staticmethod def f(): print('runoob'); C.f(); # 静态方法无需实例化 cobj = C() cobj ...

  5. Linux网络通信(线程池和线程池版本的服务器代码)

    线程池 介绍 线程池: 一种线程使用模式.线程过多会带来调度开销,进而影响缓存局部性和整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务.这避免了在处理短时间任务时创建与销毁线程的 ...

  6. React+echarts (echarts-for-react) 画中国地图及省份切换

    有足够的地图数据,可以点击到街道,示例我只出到市级 以umi为框架,版本是: "react": "^18.2.0", "umi": &quo ...

  7. 修改input标签里面的提示文字(placeholder)的样式

    使用 ::-webkit-input-placeholder 伪类 input::-webkit-input-placeholder{ // 修改字体大小 font-size:12px; // 修改文 ...

  8. vulnhub靶场之DEATHNOTE: 1

    准备: 攻击机:虚拟机kali.本机win10. 靶机:DEATHNOTE: 1,网段地址我这里设置的桥接,所以与本机电脑在同一网段,下载地址:https://download.vulnhub.com ...

  9. 【OpenStack云平台】安装Centos操作系统

    视频教程:https://live.csdn.net/v/236820 1.环境准备 准备实验所需要的环境,需要安装VMware Workstation.使用的系统镜像为CentOS-7.5-x86_ ...

  10. 错误“AxImp.exe”已退出,代码为 -1163019603

    最近调试项目时突然出现错误"AxImp.exe"已退出,代码为 -1163019603 发现引用中的组件出现了一个感叹号 经过核对是锐浪报表的组件出现了问题,尝试打开报表设计器也无 ...