///unity 如何修改材质属性和更换shader//
使用setfloat修改值
1 |
|
这是shader里面的一句
1 |
|
1 |
|
代码控制切换shader。
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using UnityEditor;
using UnityEngine;
public class ReplaceShaderByFileDir : EditorWindow
{
Shader shader;
Shader originShader;
bool isShowReplaceGo = false; //是否显示被替换的物体
string tipMsg = null;
MessageType tipMsgType = MessageType.Info;
List<GameObject> replaceGoList = new List<GameObject>();
int matCount = 0; //材质的数量
Vector2 scrollPos = Vector2.zero;
[MenuItem("Editor/替换场景中的shader")]
public static void OpenWindow()
{
//创建窗口
ReplaceShaderByFileDir window = GetWindow<ReplaceShaderByFileDir>(false, "替换场景中的shader");
window.Show();
}
void OnGUI()
{
GUILayout.Label("原shader:");
originShader = (Shader)EditorGUILayout.ObjectField(originShader, typeof(Shader), true);
//ObjectField(string label, Object obj, Type objType, bool allowSceneObjects, GUILayoutOption[] paramsOptions)
//label字段前面的可选标签 obj字段显示的物体 objType物体的类型 allowSceneObjects允许指定场景物体..
//返回:Object,用户设置的物体
GUILayout.Label("替换shader :");
shader = (Shader)EditorGUILayout.ObjectField(shader, typeof(Shader), true);
GUILayout.Space(8);
//开始一个水平组,所有被渲染的控件,在这个组里一个接着一个被水平放置。该组必须调用EndHorizontal关闭。
GUILayout.BeginHorizontal();
if (GUILayout.Button("批量替换", GUILayout.Height(30)))
{
Replace();
}
if (GUILayout.Button("重置", GUILayout.Height(30)))
{
Reset();
}
//关闭水平组
GUILayout.EndHorizontal();
//提示信息
if (!string.IsNullOrEmpty(tipMsg))
{
//创建一个帮助框,第一个参数是显示的文本,第二个参数是帮助框的提示图标类型
EditorGUILayout.HelpBox(tipMsg, tipMsgType);
}
//创建勾选框
isShowReplaceGo = GUILayout.Toggle(isShowReplaceGo, "显示被替换的GameObject");
if (isShowReplaceGo)
{
if (replaceGoList.Count > 0)
{
//开始滚动视图,scrollPos用于显示的滚动位置
scrollPos = GUILayout.BeginScrollView(scrollPos, GUILayout.Width(Screen.width), GUILayout.Height(Screen.height - 200));
foreach (var go in replaceGoList)
{
EditorGUILayout.ObjectField(go, typeof(GameObject), true);
}
//结束滚动视图
GUILayout.EndScrollView();
}
else
{
EditorGUILayout.LabelField("替换个数为0");
}
}
}
/// <summary>
/// 替换Shader
/// </summary>
void Replace()
{
replaceGoList.Clear();
if (shader == null)
{
tipMsg = "shader为空!";
tipMsgType = MessageType.Error;
return;
}
if (originShader == null)
{
tipMsg = "指定的shader为空!";
tipMsgType = MessageType.Error;
return;
}
else if (originShader.Equals(shader))
{
tipMsg = "替换的shader和指定的shader相同!";
tipMsgType = MessageType.Error;
return;
}
Dictionary<GameObject, Material[]> matDict = GetAllScenceMaterial();
List<Material> replaceMatList = new List<Material>();
foreach (var item in matDict)
{
GameObject tempGo = item.Key;
Material[] mats = item.Value;
int length = mats.Length;
for (int i = 0; i < length; i++)
{
var mat = mats[i];
if (mat != null && mat.shader.Equals(originShader))
{
if (!mat.shader.Equals(shader))
{
replaceGoList.Add(tempGo);
if (!replaceMatList.Contains(mat))
replaceMatList.Add(mat);
}
}
}
}
//替换Material的数量
int replaceMatCount = replaceMatList.Count;
for (int i = 0; i < replaceMatCount; i++)
{
UpdateProgress(i, replaceMatCount, "替换中...");
//替换Shader
replaceMatList[i].shader = shader;
//启用GPU Instancing
replaceMatList[i].enableInstancing = true;
//设置脏标志,标记目标物体已改变,当资源已改变并需要保存到磁盘,Unity内部使用dirty标识来查找
EditorUtility.SetDirty(replaceMatList[i]);
}
// 刷新编辑器,使刚创建的资源立刻被导入,才能接下来立刻使用上该资源
AssetDatabase.Refresh();
// 一般所有资源修改完后调用,调用后Unity会重新导入修改过后的资源
AssetDatabase.SaveAssets();
tipMsg = "替换成功!替换了" + replaceMatCount + "个Material," + replaceGoList.Count + "个GameObject";
tipMsgType = MessageType.Info;
//关闭进度条
EditorUtility.ClearProgressBar();
}
/// <summary>
/// 替换shader的可视化进程
/// </summary>
void UpdateProgress(int progress, int progressMax, string info)
{
string title = "Processing...[" + progress + " / " + progressMax + "]";
float value = (float)progress / progressMax;
//显示进度条
EditorUtility.DisplayProgressBar(title, info, value);
}
/// <summary>
/// 重置
/// </summary>
void Reset()
{
tipMsg = null;
shader = null;
originShader = null;
matCount = 0;
replaceGoList.Clear();
isShowReplaceGo = false;
}
/// <summary>
/// 获取所有场景中的Material
/// </summary>
/// <returns></returns>
Dictionary<GameObject, Material[]> GetAllScenceMaterial()
{
Dictionary<GameObject, Material[]> dict = new Dictionary<GameObject, Material[]>();
List<GameObject> gos = GetAllSceneGameObject();
foreach (var go in gos)
{
Renderer render = go.GetComponent<Renderer>();
if (render != null)
{
Material[] mats = render.sharedMaterials;
if (mats != null && mats.Length > 0 && !dict.ContainsKey(go))
{
dict.Add(go, mats);
matCount += mats.Length;
}
}
}
return dict;
}
/// <summary>
/// 获取所有场景中的物体
/// </summary>
/// <returns></returns>
List<GameObject> GetAllSceneGameObject()
{
List<GameObject> list = new List<GameObject>();
//获取当前活动的场景
Scene scene = SceneManager.GetActiveScene();
//获取场景中所有根游戏对象
GameObject[] rootGos = scene.GetRootGameObjects();
foreach (var go in rootGos)
{
Transform[] childs = go.transform.GetComponentsInChildren<Transform>(true);
foreach (var child in childs)
{
list.Add(child.gameObject);
}
}
return list;
}
}
EditorUtility.SetDirty(Object target)
=======================================
Unity Gizmos 绘制扇形线框扇形是用线条拼起来的,实心的扇形的是提前生成好mesh,然后用drawmesh
using UnityEngine;
public static class GizmosTools
{
/// <summary>
/// 绘制半圆
/// </summary>
public static void DrawWireSemicircle(Vector3 origin,Vector3 direction,float radius,int angle)
{
DrawWireSemicircle(origin,direction,radius,angle,Vector3.up);
}
public static void DrawWireSemicircle(Vector3 origin,Vector3 direction,float radius,int angle,Vector3 axis)
{
Vector3 leftdir = Quaternion.AngleAxis(-angle/2, axis)*direction;
Vector3 rightdir = Quaternion.AngleAxis(angle/2, axis)*direction;
Vector3 currentP = origin + leftdir * radius;
Vector3 oldP;
if (angle!=360)
{
Gizmos.DrawLine(origin,currentP);
}
for (int i = 0; i < angle/10; i++)
{
Vector3 dir= Quaternion.AngleAxis(10*i, axis)*leftdir;
oldP = currentP;
currentP=origin + dir * radius;
Gizmos.DrawLine(oldP,currentP);
}
oldP = currentP;
currentP=origin + rightdir * radius;
Gizmos.DrawLine(oldP,currentP);
if (angle!=360)
{
Gizmos.DrawLine(currentP,origin);
}
}
public static Mesh SemicircleMesh(float radius,int angle,Vector3 axis)
{
Vector3 leftdir = Quaternion.AngleAxis(-angle/2, axis)*Vector3.forward;
Vector3 rightdir = Quaternion.AngleAxis(angle/2, axis)*Vector3.forward;
int pcount = angle / 10;
//顶点
Vector3[] vertexs = new Vector3[3+pcount];
vertexs[0] = Vector3.zero;
int index = 1;
vertexs[index] = leftdir * radius;
index++;
for (int i = 0; i < pcount; i++)
{
Vector3 dir= Quaternion.AngleAxis(10*i, axis)*leftdir;
vertexs[index]= dir * radius;
index++;
}
vertexs[index] = rightdir * radius;
//三角面
int[] triangles=new int[3*(1+pcount)];
for (int i = 0; i < 1+pcount; i++)
{
triangles[3 * i] = 0;
triangles[3 * i + 1] = i+1;
triangles[3 * i + 2] = i+2;
}
Mesh mesh=new Mesh();
mesh.vertices = vertexs;
mesh.triangles = triangles;
mesh.RecalculateNormals();
return mesh;
}
}
=======================================
选中想要优化的文件夹或文件,右键Animation->裁剪浮点数去除Scale。
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;
using UnityEditor;
using System.IO;
namespace EditorTool
{
class AnimationOpt
{
static Dictionary<uint,string> _FLOAT_FORMAT;
static MethodInfo getAnimationClipStats;
static FieldInfo sizeInfo;
static object[] _param = new object[1];
static AnimationOpt ()
{
_FLOAT_FORMAT = new Dictionary<uint, string> ();
for (uint i = 1; i < 6; i++) {
_FLOAT_FORMAT.Add (i, "f" + i.ToString ());
}
Assembly asm = Assembly.GetAssembly (typeof(Editor));
getAnimationClipStats = typeof(AnimationUtility).GetMethod ("GetAnimationClipStats", BindingFlags.Static | BindingFlags.NonPublic);
Type aniclipstats = asm.GetType ("UnityEditor.AnimationClipStats");
sizeInfo = aniclipstats.GetField ("size", BindingFlags.Public | BindingFlags.Instance);
}
AnimationClip _clip;
string _path;
public string path { get{ return _path;} }
public long originFileSize { get; private set; }
public int originMemorySize { get; private set; }
public int originInspectorSize { get; private set; }
public long optFileSize { get; private set; }
public int optMemorySize { get; private set; }
public int optInspectorSize { get; private set; }
public AnimationOpt (string path, AnimationClip clip)
{
_path = path;
_clip = clip;
_GetOriginSize ();
}
void _GetOriginSize ()
{
originFileSize = _GetFileZie ();
originMemorySize = _GetMemSize ();
originInspectorSize = _GetInspectorSize ();
}
void _GetOptSize ()
{
optFileSize = _GetFileZie ();
optMemorySize = _GetMemSize ();
optInspectorSize = _GetInspectorSize ();
}
long _GetFileZie ()
{
FileInfo fi = new FileInfo (_path);
return fi.Length;
}
int _GetMemSize ()
{
return Profiler.GetRuntimeMemorySize (_clip);
}
int _GetInspectorSize ()
{
_param [0] = _clip;
var stats = getAnimationClipStats.Invoke (null, _param);
return (int)sizeInfo.GetValue (stats);
}
void _OptmizeAnimationScaleCurve ()
{
if (_clip != null) {
//去除scale曲线
foreach (EditorCurveBinding theCurveBinding in AnimationUtility.GetCurveBindings(_clip)) {
string name = theCurveBinding.propertyName.ToLower ();
if (name.Contains ("scale")) {
AnimationUtility.SetEditorCurve (_clip, theCurveBinding, null);
Debug.LogFormat ("关闭{0}的scale curve", _clip.name);
}
}
}
}
void _OptmizeAnimationFloat_X (uint x)
{
if (_clip != null && x > 0) {
//浮点数精度压缩到f3
AnimationClipCurveData[] curves = null;
curves = AnimationUtility.GetAllCurves (_clip);
Keyframe key;
Keyframe[] keyFrames;
string floatFormat;
if (_FLOAT_FORMAT.TryGetValue (x, out floatFormat)) {
if (curves != null && curves.Length > 0) {
for (int ii = 0; ii < curves.Length; ++ii) {
AnimationClipCurveData curveDate = curves [ii];
if (curveDate.curve == null || curveDate.curve.keys == null) {
//Debug.LogWarning(string.Format("AnimationClipCurveData {0} don't have curve; Animation name {1} ", curveDate, animationPath));
continue;
}
keyFrames = curveDate.curve.keys;
for (int i = 0; i < keyFrames.Length; i++) {
key = keyFrames [i];
key.value = float.Parse (key.value.ToString (floatFormat));
key.inTangent = float.Parse (key.inTangent.ToString (floatFormat));
key.outTangent = float.Parse (key.outTangent.ToString (floatFormat));
keyFrames [i] = key;
}
curveDate.curve.keys = keyFrames;
_clip.SetCurve (curveDate.path, curveDate.type, curveDate.propertyName, curveDate.curve);
}
}
} else {
Debug.LogErrorFormat ("目前不支持{0}位浮点", x);
}
}
}
public void Optimize (bool scaleOpt, uint floatSize)
{
if (scaleOpt) {
_OptmizeAnimationScaleCurve ();
}
_OptmizeAnimationFloat_X (floatSize);
_GetOptSize ();
}
public void Optimize_Scale_Float3 ()
{
Optimize (true, 3);
}
public void LogOrigin ()
{
_logSize (originFileSize, originMemorySize, originInspectorSize);
}
public void LogOpt ()
{
_logSize (optFileSize, optMemorySize, optInspectorSize);
}
public void LogDelta ()
{
}
void _logSize (long fileSize, int memSize, int inspectorSize)
{
Debug.LogFormat ("{0} \nSize=[ {1} ]", _path, string.Format ("FSize={0} ; Mem->{1} ; inspector->{2}",
EditorUtility.FormatBytes (fileSize), EditorUtility.FormatBytes (memSize), EditorUtility.FormatBytes (inspectorSize)));
}
}
public class OptimizeAnimationClipTool
{
static List<AnimationOpt> _AnimOptList = new List<AnimationOpt> ();
static List<string> _Errors = new List<string>();
static int _Index = 0;
[MenuItem("Assets/Animation/裁剪浮点数去除Scale")]
public static void Optimize()
{
_AnimOptList = FindAnims ();
if (_AnimOptList.Count > 0)
{
_Index = 0;
_Errors.Clear ();
EditorApplication.update = ScanAnimationClip;
}
}
private static void ScanAnimationClip()
{
AnimationOpt _AnimOpt = _AnimOptList[_Index];
bool isCancel = EditorUtility.DisplayCancelableProgressBar("优化AnimationClip", _AnimOpt.path, (float)_Index / (float)_AnimOptList.Count);
_AnimOpt.Optimize_Scale_Float3();
_Index++;
if (isCancel || _Index >= _AnimOptList.Count)
{
EditorUtility.ClearProgressBar();
Debug.Log(string.Format("--优化完成-- 错误数量: {0} 总数量: {1}/{2} 错误信息↓:\n{3}\n----------输出完毕----------", _Errors.Count, _Index, _AnimOptList.Count, string.Join(string.Empty, _Errors.ToArray())));
Resources.UnloadUnusedAssets();
GC.Collect();
AssetDatabase.SaveAssets();
EditorApplication.update = null;
_AnimOptList.Clear();
_cachedOpts.Clear ();
_Index = 0;
}
}
static Dictionary<string,AnimationOpt> _cachedOpts = new Dictionary<string, AnimationOpt> ();
static AnimationOpt _GetNewAOpt (string path)
{
AnimationOpt opt = null;
if (!_cachedOpts.ContainsKey(path)) {
AnimationClip clip = AssetDatabase.LoadAssetAtPath<AnimationClip> (path);
if (clip != null) {
opt = new AnimationOpt (path, clip);
_cachedOpts [path] = opt;
}
}
return opt;
}
static List<AnimationOpt> FindAnims()
{
string[] guids = null;
List<string> path = new List<string>();
List<AnimationOpt> assets = new List<AnimationOpt> ();
UnityEngine.Object[] objs = Selection.GetFiltered(typeof(object), SelectionMode.Assets);
if (objs.Length > 0)
{
for(int i = 0; i < objs.Length; i++)
{
if (objs [i].GetType () == typeof(AnimationClip))
{
string p = AssetDatabase.GetAssetPath (objs [i]);
AnimationOpt animopt = _GetNewAOpt (p);
if (animopt != null)
assets.Add (animopt);
}
else
path.Add(AssetDatabase.GetAssetPath (objs [i]));
}
if(path.Count > 0)
guids = AssetDatabase.FindAssets (string.Format ("t:{0}", typeof(AnimationClip).ToString().Replace("UnityEngine.", "")), path.ToArray());
else
guids = new string[]{};
}
for(int i = 0; i < guids.Length; i++)
{
string assetPath = AssetDatabase.GUIDToAssetPath (guids [i]);
AnimationOpt animopt = _GetNewAOpt (assetPath);
if (animopt != null)
assets.Add (animopt);
}
return assets;
}
}
}
===========================================
能做到去掉Scale曲线,降低浮点精度
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
|
=========================================
取BlobSize代码
AnimationClip aniClip = AssetDatabase.LoadAssetAtPath<AnimationClip> (path);
var fileInfo = new System.IO.FileInfo(path);
Debug.Log(fileInfo.Length);//FileSize
Debug.Log(Profiler.GetRuntimeMemorySize (aniClip));//MemorySize
Assembly asm = Assembly.GetAssembly(typeof(Editor));
MethodInfo getAnimationClipStats = typeof(AnimationUtility).GetMethod("GetAnimationClipStats", BindingFlags.Static | BindingFlags.NonPublic);
Type aniclipstats = asm.GetType("UnityEditor.AnimationClipStats");
FieldInfo sizeInfo = aniclipstats.GetField ("size", BindingFlags.Public | BindingFlags.Instance);
var stats = getAnimationClipStats.Invoke(null, new object[]{aniClip});
Debug.Log(EditorUtility.FormatBytes((int)sizeInfo.GetValue(stats)));//BlobSize
=====================================
用反射物理射线的方法,把这个脚本放到Editor下
/**
* Author: YinPeiQuan
**/
using UnityEngine;
using UnityEditor;
using System;
using System.Linq;
using System.Reflection;
[InitializeOnLoad]
public class RayHitRefrelection
{
public static Type type_HandleUtility;
protected static MethodInfo meth_IntersectRayMesh;
static RayHitRefrelection()
{
var editorTypes = typeof(Editor).Assembly.GetTypes();
type_HandleUtility = editorTypes.FirstOrDefault(t => t.Name == "HandleUtility");
meth_IntersectRayMesh = type_HandleUtility.GetMethod("IntersectRayMesh", (BindingFlags.Static | BindingFlags.NonPublic));
}
public static bool IntersectRayMesh(Ray ray, MeshFilter meshFilter, out RaycastHit hit)
{
return IntersectRayMesh(ray, meshFilter.mesh, meshFilter.transform.localToWorldMatrix, out hit);
}
static object[] parameters = new object[4];
public static bool IntersectRayMesh(Ray ray, Mesh mesh, Matrix4x4 matrix, out RaycastHit hit)
{
parameters[0] = ray;
parameters[1] = mesh;
parameters[2] = matrix;
parameters[3] = null;
bool result = (bool)meth_IntersectRayMesh.Invoke(null, parameters);
hit = (RaycastHit)parameters[3];
return result;
}
}
使用方法,从scene视图中心发条射线检测,获取射线射中的点
public static Vector3 GetSceneViewPosition()
{
if (null == SceneView.lastActiveSceneView)
{
return Vector3.zero;
}
Ray ray = SceneView.lastActiveSceneView.camera.ScreenPointToRay(new Vector3(SceneView.lastActiveSceneView.camera.pixelWidth / 2, SceneView.lastActiveSceneView.camera.pixelHeight / 2, 0));
MeshFilter[] componentsInChildren = GameObject.FindObjectsOfType<MeshFilter>();
float num = float.PositiveInfinity;
Vector3 point = Vector3.zero;
foreach (MeshFilter meshFilter in componentsInChildren)
{
Mesh sharedMesh = meshFilter.sharedMesh;
RaycastHit hit;
if (sharedMesh
&& RayHitRefrelection.IntersectRayMesh(ray, sharedMesh, meshFilter.transform.localToWorldMatrix, out hit)
&& hit.distance < num)
{
point = hit.point;
num = hit.distance;
}
}
return point;
}
=====================================
http://api.fanyi.baidu.com/api/trans/product/apidoc 这个是百度翻译api文档
代码里面必须要加appid和password(秘钥)
上面地址打开管理控制台就可以申请,是免费的
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System;
using System.Net;
using System.Security.Cryptography;
using System.Text;
public class TranslateLanguageTool : EditorWindow
{
//可以直接到百度翻译API的官网申请
//一定要去申请,不然程序的翻译功能不能使用
private static string appId = "";
private static string password = "";
[MenuItem("Tools/翻译工具")]
public static void Open()
{
GetWindow<TranslateLanguageTool>("工具");
}
private Dictionary<string, Dictionary<int, string>> dic = new Dictionary<string, Dictionary<int, string>>();
string sourceStr = "";
string id = "";
public void OnGUI()
{
id = EditorGUILayout.TextField("id", id);
sourceStr = EditorGUILayout.TextField("中文", sourceStr);
if (GUILayout.Button("程序专用翻译"))
{
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.ara);
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.en);
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.fra);
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.de);
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.pt);
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.ru);
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.spa);
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.cht);
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.th);
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.vie);
}
if (GUILayout.Button("翻译"))
{
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.ara, "阿拉伯");
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.fra, "法语");
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.de, "德语");
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.pt, "葡萄牙");
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.ru, "俄语");
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.spa, "西班牙");
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.th, "泰语");
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.cht, "繁体中文");
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.vie, "越南语");
GetTranslationFromBaiduFanyi(id, sourceStr, Language.zh, Language.en, "英文");
}
}
private static TranslationResult GetTranslationFromBaiduFanyi(string id, string q, Language from, Language to, string language = "")
{
string jsonResult = String.Empty;
//源语言
string languageFrom = from.ToString().ToLower();
//目标语言
string languageTo = to.ToString().ToLower();
//随机数
string randomNum = System.DateTime.Now.Millisecond.ToString();
//md5加密
string md5Sign = GetMD5WithString(appId + q + randomNum + password);
//url
string url = String.Format("http://api.fanyi.baidu.com/api/trans/vip/translate?q={0}&from={1}&to={2}&appid={3}&salt={4}&sign={5}",
q,
languageFrom,
languageTo,
appId,
randomNum,
md5Sign
);
WebClient wc = new WebClient();
try
{
jsonResult = wc.DownloadString(url);
}
catch
{
jsonResult = string.Empty;
}
//结果转json
TranslationResult temp = LitJson.JsonMapper.ToObject<TranslationResult>(jsonResult);
if (null != temp)
{
if (string.IsNullOrEmpty(language))
{
for (int i = 0; i < temp.trans_result.Length; i++)
{
string str = @"{""Key"":" + id + @",""Content"":" + @"""" + temp.trans_result[i].dst + @"""" + "},";
Debug.LogError(str);
}
}
else
{
for (int i = 0; i < temp.trans_result.Length; i++)
{
string str = language + "|" + temp.trans_result[i].dst;
Debug.LogError(str);
}
}
}
return null;
}
private static string GetMD5WithString(string input)
{
if (input == null)
{
return null;
}
MD5 md5Hash = MD5.Create();
//将输入字符串转换为字节数组并计算哈希数据
byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
//创建一个 Stringbuilder 来收集字节并创建字符串
StringBuilder sBuilder = new StringBuilder();
//循环遍历哈希数据的每一个字节并格式化为十六进制字符串
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
//返回十六进制字符串
return sBuilder.ToString();
}
}
public class Translation
{
public string src { get; set; }
public string dst { get; set; }
}
public enum Language
{
//百度翻译API官网提供了多种语言,这里只列了几种
zh,
en,
spa,
fra,
th,
ara,
ru,
pt,
de,
el,
vie,
cht,
yue,
}
public class TranslationResult
{
//错误码,翻译结果无法正常返回
public string Error_code { get; set; }
public string Error_msg { get; set; }
public string from { get; set; }
public string to { get; set; }
public string Query { get; set; }
//翻译正确,返回的结果
//这里是数组的原因是百度翻译支持多个单词或多段文本的翻译,在发送的字段q中用换行符(\n)分隔
public Translation[] trans_result { get; set; }
}
=========================================
规则是:
% = ctrl
# = Shift
& = Alt
LEFT/RIGHT/UP/DOWN = 上下左右
F1…F2 = F...
HOME, END, PGUP, PGDN = 键盘上的特殊功能键
特别注意的是,如果是键盘上的普通按键,比如a~z,则要写成_a ~ _z这种带_前缀的 _%#&_LEFT
==========================================
特效发射粒子数 查找
思路步骤如下:
1. 在Editor文件夹下新建一个脚本文件,命名为EffectEmitChecker.cs
2. Resource.LoadAll所有的特效文件存入一个数组
3. 循环遍历特效数组并PrefabUtility.InstantiatePrefab()生成一个实例
4. 获取ParticleEmitter组件,并把相关信息缓存到一个List中
5. DestroyImmediate()删除生成的实例
6. List根据发射粒子数大小排序
7. 在OnGUI()中展示List中的数据
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
public class EffectEmitChecker : EditorWindow
{
float ThumbnailWidth = 40;
float ThumbnailHeight = 40;
GUIStyle style = new GUIStyle();
Vector2 vec2 = new Vector2(0, 0);
List<EffectParticle> listEffect = new List<EffectParticle>(); //缓存特效信息
[MenuItem("Effect/Effect Emit Checker")]
static void MainTask()
{
EffectEmitChecker window = GetWindow<EffectEmitChecker>();
window.LoadEffect(); //加载特效
window.Show();
}
void OnGUI()
{
ListEffect();
}
void LoadEffect()
{
Object[] objs = Resources.LoadAll("Prefabs/AAA"); //"Effects/"读取所有特效文件,可以根据情况改变地址
for (int i = 0; i < objs.Length; i++)
{
GameObject go = PrefabUtility.InstantiatePrefab(objs[i]) as GameObject; //创建实例
if (go == null) continue;
ParticleRenderer[] renderers = go.GetComponentsInChildren<ParticleRenderer>(true); //获取特效实例下的所有ParticleRenderer组件
foreach (ParticleRenderer render in renderers)
{
EffectParticle effect = new EffectParticle();
ParticleEmitter emitter = render.GetComponent<ParticleEmitter>(); //获取ParticleEmitter组件
effect.name = objs[i].name;
effect.material = render.sharedMaterial;
if (emitter != null)
{
effect.maxEmission = emitter.maxEmission; //最大发射粒子数赋值
}
effect.prefab = objs[i];
listEffect.Add(effect);
}
DestroyImmediate(go); //销毁实例
}
listEffect.Sort((x, y) => { return y.maxEmission.CompareTo(x.maxEmission); }); //从大到小排序
style.normal.textColor = Color.red;
style.fixedWidth = 120;
}
void ListEffect()
{
vec2 = EditorGUILayout.BeginScrollView(vec2);
foreach (EffectParticle effectParticle in listEffect)
{
if (effectParticle != null)
{
GUILayout.BeginHorizontal();
Material mat = effectParticle.material;
if (mat != null)
{
//根据材质找到相应的纹理显示
Texture texture = mat.mainTexture;
if (texture != null)
GUILayout.Box(texture, GUILayout.Width(ThumbnailWidth), GUILayout.Height(ThumbnailHeight));
else
GUILayout.Box("N/A", GUILayout.Width(ThumbnailWidth), GUILayout.Height(ThumbnailHeight));
GUILayout.Label("Shader:" + mat.shader.name, GUILayout.Width(140)); //Shader名称
//特效主颜色显示
if (mat.HasProperty("_Color"))
EditorGUILayout.ColorField(mat.color, GUILayout.Width(50));
else if (mat.HasProperty("_TintColor"))
EditorGUILayout.ColorField(mat.GetColor("_TintColor"), GUILayout.Width(50));
else
GUILayout.Box("N/A", GUILayout.Width(50));
}
//发射粒子数判断
float emission = effectParticle.maxEmission;
if (emission < 50)
GUILayout.Label("MaxEmission:" + emission.ToString(), GUILayout.Width(120));
else
GUILayout.Label("MaxEmission:" + emission.ToString(), style); //字体标红
//特效名称,并可定位到相应文件
if (GUILayout.Button(effectParticle.name))
Selection.activeObject = effectParticle.prefab;
//文件所在路径节点
//GUILayout.TextField("Node:" + effectParticle.prefab.g);
GUILayout.EndHorizontal();
}
}
EditorGUILayout.EndScrollView();
}
//特效信息实体类
class EffectParticle
{
public string name;
public Material material;
public float maxEmission;
public Object prefab;
public bool bScaleWithTransform;
public EffectParticle()
{
maxEmission = 0;
}
}
}
无效Material 检测
- 获取并显示我们在Project视图中选择的物体(可以是任何物体,不过暂时只对Material进行处理,未来可以拓展处理其他物体)
- 遍历需要检测的物体路径,目前分为三个检测点:场景、特效、角色
- 因为存在一个Material关联多个物体,所以要用Dictionary<string,List<XXX>>的形式存储数据,分类显示
- 如果检测到就显示出Material和关联物体的相关信息,没有检测到就标注红色并伴有“删除”按钮以供删除。
- 获取并显示我们在Project视图中选择的物体(可以是任何物体,不过暂时只对Material进行处理,未来可以拓展处理其他物体)
- 遍历需要检测的物体路径,目前分为三个检测点:场景、特效、角色
- 因为存在一个Material关联多个物体,所以要用Dictionary<string,List<XXX>>的形式存储数据,分类显示
- 如果检测到就显示出Material和关联物体的相关信息,没有检测到就标注红色并伴有“删除”按钮以供删除。
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
public class MaterialReferenceChecker : EditorWindow
{
Dictionary<string, List<MatDetail>> dictMat = new Dictionary<string, List<MatDetail>>();
int texWidth = 40;
int texHeight = 40;
Vector2 vec2 = new Vector2(0, 0);
bool isCheck = false;
bool isCheckScene = true;
bool isCheckEffect = true;
bool isCheckCharacter = true;
GameObject effectObj = null;
GameObject characterObj = null;
Object[] objs;
[MenuItem("Tools/Check Material References")]
public static void Init()
{
MaterialReferenceChecker window = GetWindow<MaterialReferenceChecker>();
window.Show();
}
void OnGUI()
{
if (!isCheck)
{
if (objs != null && objs.Length > 100) //防止选择太多物体,运算量太大
{
GUILayout.Label("你一次性选择太多资源(超过100个)!请重新选择资源,再点击“刷新”");
if (GUILayout.Button("刷新"))
{
objs = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
}
return;
}
objs = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
GUILayout.BeginHorizontal();
if (objs == null || objs.Length <= 0)
{
GUILayout.Label("你没有选择任何物体,请在Project选择,按住Ctrl可以多选");
return;
}
else
{
GUILayout.Label("你选择了以下物体:共(" + objs.Length + ")个");
}
GUILayout.EndHorizontal();
vec2 = GUILayout.BeginScrollView(vec2);
for (int i = 0; i < objs.Length; i++)
{
ListSelections(objs[i]);
}
GUILayout.BeginHorizontal();
if (GUILayout.Button("开始查找关联"))
{
OnCheckReferences();
isCheck = true;
}
isCheckScene = GUILayout.Toggle(isCheckScene, "遍历场景", GUILayout.Width(70));
isCheckEffect = GUILayout.Toggle(isCheckEffect, "遍历特效", GUILayout.Width(70));
isCheckCharacter = GUILayout.Toggle(isCheckCharacter, "遍历角色", GUILayout.Width(70));
GUILayout.EndHorizontal();
ListMaterials();
GUILayout.EndScrollView();
}
else
{
//刷新数据,必须刷新才能重新查找
vec2 = GUILayout.BeginScrollView(vec2);
for (int i = 0; i < objs.Length; i++)
{
ListSelections(objs[i]);
}
GUILayout.BeginHorizontal();
if (GUILayout.Button("刷新"))
{
isCheck = false;
dictMat.Clear();
}
GUILayout.EndHorizontal();
ListMaterials();
GUILayout.EndScrollView();
}
}
//选中物体列表
void ListSelections(Object selection)
{
GUILayout.BeginHorizontal();
if (selection is Material || selection is Texture)
{
Texture tex = null;
if (selection is Material)
{
Material mat = selection as Material;
if (mat.HasProperty("_MainTex")) tex = mat.mainTexture;
}
else if (selection is Texture)
{
tex = selection as Texture;
}
if (tex != null)
{
//在新窗口打开图片大图
if (GUILayout.Button(tex, GUILayout.Width(texWidth), GUILayout.Height(texHeight)))
{
ZoomInTexture window = GetWindow<ZoomInTexture>();
window.texture = tex;
window.minSize = new Vector2(tex.width, tex.height);
}
}
else
{
GUILayout.Box("N/A", GUILayout.Width(texWidth), GUILayout.Height(texHeight));
}
}
else
{
GUILayout.Box("N/A", GUILayout.Width(texWidth), GUILayout.Height(texHeight));
}
GUILayout.Label("名称:" + selection.name + "\n类型:" + selection.GetType().ToString().Replace("UnityEngine.", "") + "\n路径:" + AssetDatabase.GetAssetPath(selection));
GUILayout.EndHorizontal();
GUILayout.Space(10);
}
//关联物体列表
void ListMaterials()
{
if (dictMat == null || dictMat.Count <= 0)
{
GUILayout.Label("没有关联物体,请点击“开始查找关联”按钮");
return;
}
foreach (string item in dictMat.Keys)
{
List<MatDetail> detailList = dictMat[item];
if (detailList != null && detailList.Count > 0)
{
GUILayout.Space(20);
GUILayout.Label("==================《" + item + "》【Material】==================");
foreach (MatDetail detail in detailList)
{
GUILayout.BeginHorizontal();
//输出可以点击寻找路径的图片
if (detail.mat != null && detail.mat.HasProperty("_MainTex") && detail.mat.mainTexture != null)
{
if (GUILayout.Button(detail.mat.mainTexture, GUILayout.Width(texWidth), GUILayout.Height(texHeight)))
{
Selection.activeObject = detail.mat;
}
}
else
{
if (GUILayout.Button("N/A", GUILayout.Width(texWidth), GUILayout.Height(texHeight)))
{
Selection.activeObject = detail.mat;
}
}
//输出信息
string print = "";
if (detail.type == "Effect" || detail.type == "Character")
print = string.Format("所在路径:{0}\n关联类型:{1}\n关联物体:{2}", detail.assetPath, detail.type, detail.hierarcyPath);
else
print = string.Format("所在路径:{0}\n关联场景:{1}\n关联物体:{2}", detail.assetPath, detail.type, detail.hierarcyPath);
if (detail.type == "NULL")
{
GUIStyle style = new GUIStyle();
style.normal.textColor = Color.red;
GUILayout.Label(print, style);
if (GUILayout.Button("删除", GUILayout.Width(40)))
{
if (AssetDatabase.DeleteAsset(detail.assetPath))
{
ShowNotification(new GUIContent("删除成功"));
}
}
}
else
{
GUILayout.Label(print);
}
GUILayout.EndHorizontal();
}
}
}
}
//开始查找引用
void OnCheckReferences()
{
Object[] objs = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
if (objs == null || objs.Length <= 0) return;
dictMat.Clear();
for (int i = 0; i < objs.Length; i++)
{
List<MatDetail> listDetail = new List<MatDetail>();
if (objs[i] is Material)
{
//遍历所有场景关联物体
if (isCheckScene)
{
if (EditorBuildSettings.scenes.Length <= 0)
{
Debug.LogError("你的可查找场景为空,请在Builder Setting中添加场景");
return;
}
foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
{
if (scene.enabled)
{
EditorApplication.OpenScene(scene.path);
if (objs[i] is Material)
{
MatDetail detail = SetMaterial(objs[i], scene.path);
if (detail != null) listDetail.Add(detail);
}
}
}
}
//遍历Resources特效目录
if (isCheckEffect)
{
Object[] effects = Resources.LoadAll("Effects"); //Resources下的地址,根据需求修改
for (int j = 0; j < effects.Length; j++)
{
if (objs[i] is Material)
{
MatDetail detail = SetEffectMaterial(objs[i], effects[j]);
DestroyImmediate(effectObj);
if (detail != null) listDetail.Add(detail);
}
}
}
//遍历角色目录
if (isCheckCharacter)
{
Object[] characters = GetAssetsOfType(Application.dataPath + "/Characters/", typeof(GameObject), ".prefab"); //自定义地址,根据需求修改
for (int j = 0; j < characters.Length; j++)
{
if (objs[i] is Material)
{
MatDetail detail = SetCharactersMaterial(objs[i], characters[j]);
DestroyImmediate(characterObj);
if (detail != null) listDetail.Add(detail);
}
}
}
}
//找不到关联的处理
if (listDetail.Count <= 0)
{
MatDetail detail = new MatDetail();
detail.mat = objs[i] as Material;
detail.assetPath = AssetDatabase.GetAssetPath(objs[i]);
listDetail.Add(detail);
}
dictMat.Add(objs[i].name, listDetail);
}
}
//获取prefab
public static Object[] GetAssetsOfType(string directPath, System.Type type, string fileExtension)
{
List<Object> tempObjects = new List<Object>();
DirectoryInfo directory = new DirectoryInfo(directPath);
FileInfo[] goFileInfo = directory.GetFiles("*" + fileExtension, SearchOption.AllDirectories);
int goFileInfoLength = goFileInfo.Length;
FileInfo tempGoFileInfo;
string tempFilePath;
Object tempGO;
for (int i = 0; i < goFileInfoLength; i++)
{
tempGoFileInfo = goFileInfo[i];
if (tempGoFileInfo == null)
continue;
tempFilePath = tempGoFileInfo.FullName;
tempFilePath = tempFilePath.Replace(@"\", "/").Replace(Application.dataPath, "Assets");
tempGO = AssetDatabase.LoadAssetAtPath(tempFilePath, typeof(Object)) as Object;
if (tempGO == null)
{
Debug.LogWarning("Skipping Null");
continue;
}
else if (tempGO.GetType() != type)
{
Debug.LogWarning("Skipping " + tempGO.GetType().ToString());
continue;
}
tempObjects.Add(tempGO);
}
return tempObjects.ToArray();
}
//获取场景材质
MatDetail SetMaterial(Object obj, string scenePath)
{
Renderer[] renderers = (Renderer[])FindObjectsOfType(typeof(Renderer)); //这种取法方便,但是disactive的物体取不到,待完善
return GetMatDetail(renderers, obj, scenePath);
}
//获取特效材质
MatDetail SetEffectMaterial(Object select, Object effect)
{
effectObj = PrefabUtility.InstantiatePrefab(effect) as GameObject;
if (effectObj == null) return null;
Renderer[] renderers = effectObj.GetComponentsInChildren<Renderer>(true);
return GetMatDetail(renderers, select, "Effect");
}
//获取角色材质
MatDetail SetCharactersMaterial(Object select, Object character)
{
characterObj = PrefabUtility.InstantiatePrefab(character) as GameObject;
if (characterObj == null) return null;
SkinnedMeshRenderer[] renderers = characterObj.GetComponentsInChildren<SkinnedMeshRenderer>(true);
foreach (SkinnedMeshRenderer renderer in renderers)
{
Material[] mats = renderer.sharedMaterials;
foreach (Material mat in mats)
{
string assetPath = AssetDatabase.GetAssetPath(select);
if (assetPath == AssetDatabase.GetAssetPath(mat))
{
MatDetail detail = new MatDetail();
detail.assetPath = assetPath;
detail.mat = mat;
detail.type = "Character";
detail.hierarcyPath = GetHierarcyPath(renderer.gameObject);
return detail;
}
}
}
return null;
}
//设置并获取MatDetail
MatDetail GetMatDetail(Renderer[] renderers, Object select, string scenePath)
{
foreach (Renderer renderer in renderers)
{
//获取材质
Material[] mats = renderer.sharedMaterials;
foreach (Material mat in mats)
{
string assetPath = AssetDatabase.GetAssetPath(select);
if (assetPath == AssetDatabase.GetAssetPath(mat))
{
MatDetail detail = new MatDetail();
detail.assetPath = assetPath;
detail.mat = mat;
detail.type = scenePath;
detail.hierarcyPath = GetHierarcyPath(renderer.gameObject);
return detail;
}
}
}
return null;
}
//设置路径
string GetHierarcyPath(GameObject go)
{
string path = "/" + go.name;
while (go.transform.parent != null)
{
go = go.transform.parent.gameObject;
path = "/" + go.name + path;
}
return path;
}
//材质详情
class MatDetail
{
public Material mat;
public string assetPath;
public string type;
public string hierarcyPath;
public MatDetail()
{
mat = null;
assetPath = "";
type = "NULL";
hierarcyPath = "NULL";
}
}
}
//查看大图片
public class ZoomInTexture : EditorWindow
{
public Texture texture;
void OnGUI()
{
GUILayout.Box(texture, GUILayout.Width(texture.width), GUILayout.Height(texture.height));
}
} - 获取并显示我们在Project视图中选择的物体(可以是任何物体,不过暂时只对Material进行处理,未来可以拓展处理其他物体)
- 遍历需要检测的物体路径,目前分为三个检测点:场景、特效、角色
- 因为存在一个Material关联多个物体,所以要用Dictionary<string,List<XXX>>的形式存储数据,分类显示
- 如果检测到就显示出Material和关联物体的相关信息,没有检测到就标注红色并伴有“删除”按钮以供删除。
- PrefabUtility.CreateEmptyPrefab
- PrefabUtility.ReplacePrefab
- PrefabUtility.DisconnectPrefabInstance
- PrefabUtility.GetPrefabParent
- PrefabUtility.ConnectGameObjectToPrefab
-
断开引用
断开Prefab引用的代码如下
[MenuItem("Tools/Prefab/去除引用")]
public static void BreakPrefabRef()
{
var select = Selection.activeGameObject;
if (select.activeInHierarchy)
{
PrefabUtility.DisconnectPrefabInstance(select);
Selection.activeGameObject = null;
var prefab = PrefabUtility.CreateEmptyPrefab("Assets/empty.prefab");
PrefabUtility.ReplacePrefab(select, prefab, ReplacePrefabOptions.ConnectToPrefab);
PrefabUtility.DisconnectPrefabInstance(select);
AssetDatabase.DeleteAsset("Assets/empty.prefab");
}
}
虽然PrefabUtility.DisconnectPrefabInstance有断开Prefab的含义,但是如果仅仅使用这个函数会出现下面这个情况,名字的颜色从蓝变白,看起来已经不是一个prefab,但是从Inspector面板中还是能够看到Prefab标记以及Prefab实例才会出现的那三个Select、Revert、Apply按钮。
-
[MenuItem("Tools/Prefab/替换引用")]
public static void RelocalPrefabRef()
{
var select = Selection.activeGameObject;
if (select.activeInHierarchy)
{
var ab = PrefabUtility.GetPrefabParent(select);
if (ab == null)
return;
var oripath = AssetDatabase.GetAssetPath(ab);
var filters = new[] { "prefab file", "prefab" };
var tar = EditorUtility.OpenFilePanelWithFilters("select target", Application.dataPath, filters);
if (string.IsNullOrEmpty(tar))
return;
tar = FileUtil.GetProjectRelativePath(tar);
var tarprefab = AssetDatabase.LoadAssetAtPath<GameObject>(tar);
if (tarprefab == null)
return;
var gname = select.name;
var enable = select.activeInHierarchy;
var pos = select.transform.localPosition;
var rot = select.transform.localRotation;
var scale = select.transform.localScale;
var go = PrefabUtility.ConnectGameObjectToPrefab(select, tarprefab);
go.transform.localPosition = pos;
go.transform.localRotation = rot;
go.transform.localScale = scale;
go.name = gname;
go.SetActive(enable);
Debug.LogFormat("Replace Prefab From:{0} to {1}", oripath, tar);
}
}
代码中主要的流程为
- 使用Selection.activeGameObject获取选中的物体
- 使用PrefabUtility.GetPrefabParent获取这个物体在project中的源prefab
- 使用AssetDatabase.GetAssetPath获取源prefab在project中的路径(后面用于log)
- 使用EditorUtility.OpenFilePanelWithFilters打开一个文件选择窗口让玩家选择一个源prefab
- 使用FileUtil.GetProjectRelativePath获取这个源prefab相对于工程的路径
- 使用将这个源prefab加载到内存中
- 为了保持替换后的位置关系,这里记录了原来的位置信息
- 使用PrefabUtility.ConnectGameObjectToPrefab重新将选中的物体链接到玩家选择的源prefab上,完成prefab引用替换
- 还原原来的位置关系