Unity Editor Extensions – Handle 和Gizmos私人定制
今天要和大家介绍下Handle和Gizmos私人定制,先贴一个 Grid网格,可以标记一个对象的正方形范围等
拖拽到指定的对象就OK了。
- using UnityEngine;
- using System.Collections;
- // DrawGizmoGrid.cs
- // draws a useful reference grid in the editor in Unity.
- // 09/01/15 - Hayden Scott-Baron
- // twitter.com/docky
- // no attribution needed, but please tell me if you like it ^_^
- public class DrawGizmoGrid : MonoBehaviour
- {
- // universal grid scale
- public float gridScale = 1f;
- // extents of the grid
- public int minX = -15;
- public int minY = -15;
- public int maxX = 15;
- public int maxY = 15;
- // nudges the whole grid rel
- public Vector3 gridOffset = Vector3.zero;
- // is this an XY or an XZ grid?
- public bool topDownGrid = true;
- // choose a colour for the gizmos
- public int gizmoMajorLines = 5;
- public Color gizmoLineColor = new Color (0.4f, 0.4f, 0.3f, 1f);
- // rename centre the gameobject upon first time dragging the script into the editor.
- void Reset ()
- {
- if (name == "GameObject")
- name = "~~ GIZMO GRID ~~";
- transform.position = Vector3.zero;
- }
- // draw the grid :)
- void OnDrawGizmos ()
- {
- // orient to the gameobject, so you can rotate the grid independently if desired
- Gizmos.matrix = transform.localToWorldMatrix;
- // set colours
- Color dimColor = new Color(gizmoLineColor.r, gizmoLineColor.g, gizmoLineColor.b, 0.25f* gizmoLineColor.a);
- Color brightColor = Color.Lerp (Color.white, gizmoLineColor, 0.75f);
- // draw the horizontal lines
- for (int x = minX; x < maxX 1; x )
- {
- // find major lines
- Gizmos.color = (x % gizmoMajorLines == 0 ? gizmoLineColor : dimColor);
- if (x == 0)
- Gizmos.color = brightColor;
- Vector3 pos1 = new Vector3(x, minY, 0) * gridScale;
- Vector3 pos2 = new Vector3(x, maxY, 0) * gridScale;
- // convert to topdown/overhead units if necessary
- if (topDownGrid)
- {
- pos1 = new Vector3(pos1.x, 0, pos1.y);
- pos2 = new Vector3(pos2.x, 0, pos2.y);
- }
- Gizmos.DrawLine ((gridOffset pos1), (gridOffset pos2));
- }
- // draw the vertical lines
- for (int y = minY; y < maxY 1; y )
- {
- // find major lines
- Gizmos.color = (y % gizmoMajorLines == 0 ? gizmoLineColor : dimColor);
- if (y == 0)
- Gizmos.color = brightColor;
- Vector3 pos1 = new Vector3(minX, y, 0) * gridScale;
- Vector3 pos2 = new Vector3(maxX, y, 0) * gridScale;
- // convert to topdown/overhead units if necessary
- if (topDownGrid)
- {
- pos1 = new Vector3(pos1.x, 0, pos1.y);
- pos2 = new Vector3(pos2.x, 0, pos2.y);
- }
- Gizmos.DrawLine ((gridOffset pos1), (gridOffset pos2));
- }
- }
- }
3、Layingthe groundwork for our handles为我们处理奠定基础
4、Creatinga handle
5、Drawinglines in the scene view
6、Coloringhandles
7、Dynamicallysizing handles
- public class TerrainPiece : MonoBehaviour
- {
- public SpriteRenderer spriteRenderer;
- public Vector3 mountPoint;
- void Awake ()
- {
- if (spriteRenderer == null)
- spriteRenderer = GetComponentInChildren<SpriteRenderer>();
- }
- }
下面就来定制这个类:
- [CustomEditor(typeof(TerrainPiece))]
- public class TerrainPieceEditor : Editor
- {
- SerializedProperty mountPointProp; // TerrainPiece. mountPoint
- TerrainPiece terrainPiece; // TerrainPiece
- void OnEnable()
- {
- terrainPiece = (TerrainPiece) target;
- mountPointProp = serializedObject.FindProperty("mountPoint");
- }
- void OnSceneGUI()
- {
- serializedObject.Update();
- Vector3 worldMountPt = terrainPiece.transform.TransformPoint(mountPointProp.vector3Value); // 转成世界坐标系
- float sizeFactor = HandleUtility.GetHandleSize(worldMountPt) * 0.25f; // 这样就不会随着scene面板的远近而动态改变大小,一直不变。
- Handles.color = Color.magenta; // 设置颜色
- worldMountPt = Handles.FreeMoveHandle(worldMountPt, Quaternion.identity, sizeFactor * 0.2f, Vector3.zero, Handles.RectangleCap); // 拖动handle来改变值
- Handles.DrawLine(worldMountPt - Vector3.up * sizeFactor, worldMountPt Vector3.up * sizeFactor);
- Handles.DrawLine(worldMountPt - Vector3.right * sizeFactor, worldMountPt Vector3.right * sizeFactor);
- Vector3 mountPointLocal = terrainPiece.transform.InverseTransformPoint(worldMountPt); // 转成相对父级的本地坐标
- mountPointLocal.z = 0;
- mountPointProp.vector3Value = mountPointLocal;
- serializedObject.ApplyModifiedProperties();
- }
- }
8、Previewingthe status bar
9、Sizinga status bar with handles
10、Snappinghandles
- public class Statusbar : MonoBehaviour
- {
- publicstring barTextureName = "statusbar"; //The name of our bar texture (must be located in Resources folder)
- publicGradient colorGrad;
- publicVector2 offset;
- publicVector2 drawSize = new Vector2(5f, 1f);
- protectedTexture2D barTexture;
- protectedfloat targetPercent = 1f; //The normalized percentage the bar is to represent
- protectedfloat displayPercent = 1f; //The value actually used to render the bar, allows us to animate the bar towardthe targetPercent value
- protectedRect srcRect; //The rect of the area to use from the source texture
- protectedRect scrExtents; //The rect that defines the screen area extents
- protectedVector2 size;
- 。。。。。。。。。。。。。。。。。。。。。。
- }
---- Editor 的定制显示?:
- [CustomEditor(typeof(Statusbar))]
- public class StatusbarEditor : Editor
- {
- SerializedPropertyoffsetProp;
- SerializedPropertydrawSizeProp;
- Statusbarbar; //要定制的类
- staticTexture2D barTexture; //用于显示加载的图片
- voidOnEnable()
- {
- bar= (Statusbar) target;
- offsetProp= serializedObject.FindProperty("offset");
- drawSizeProp= serializedObject.FindProperty("drawSize");
- barTexture= Resources.Load(bar.barTextureName, typeof(Texture2D)) as Texture2D;
- }
- voidOnSceneGUI()
- {
- serializedObject.Update();
- DrawBar();
- DrawHandles();
- serializedObject.ApplyModifiedProperties();
- }
- voidDrawBar()
- {
- Vector2pos = HandleUtility.WorldToGUIPoint(bar.transform.position (Vector3)bar.offset); // 坐标转换
- Vector2size = bar.drawSize / bar.GetWorldUnitsPerPixel(Camera.current); //大小
- RectscreenRect = new Rect(pos.x - size.x * 0.5f, pos.y - size.y * 0.5f,
- size.x, size.y); // 显示位置
- Handles.BeginGUI();
- GUI.DrawTexture(screenRect,barTexture); //显示图片
- Handles.EndGUI();
- }
- voidDrawHandles()
- {
- Handles.matrix= bar.transform.localToWorldMatrix; // 矩阵变换初值
- Vector3barPos = offsetProp.vector2Value; //位置初值
- floathandleSize = HandleUtility.GetHandleSize(barPos) * 0.1f; // 大小初值
- Handles.color= Color.green; //设置颜色
- //Bar position/offset: //计算位置,这是中间的圆handle
- barPos= Handles.FreeMoveHandle(barPos, Quaternion.identity, handleSize, Vector3.zero,Handles.CircleCap);
- //Save new offset:
- offsetProp.vector2Value= barPos;
- // 通过拖拽改变大小
- //Top handle:
- Vector3handlePt = barPos Vector3.up * bar.drawSize.y * 0.5f;
- Vector3newPos = Handles.FreeMoveHandle(handlePt, Quaternion.identity, handleSize,Vector3.zero, Handles.RectangleCap);
- Vector2delta = new Vector2(0, newPos.y - handlePt.y);
- drawSizeProp.vector2Value = delta;
- //Bottom handle:
- handlePt= barPos Vector3.down * bar.drawSize.y * 0.5f;
- newPos= Handles.FreeMoveHandle(handlePt, Quaternion.identity, handleSize,Vector3.zero, Handles.RectangleCap);
- delta= new Vector2(0, newPos.y - handlePt.y);
- drawSizeProp.vector2Value-= delta;
- //Left handle:
- handlePt= barPos Vector3.left * bar.drawSize.x * 0.5f;
- newPos= Handles.FreeMoveHandle(handlePt, Quaternion.identity, handleSize, Vector3.zero,Handles.RectangleCap);
- delta= new Vector2(newPos.x - handlePt.x, 0);
- drawSizeProp.vector2Value-= delta;
- //Right handle:
- handlePt= barPos Vector3.right * bar.drawSize.x * 0.5f;
- newPos= Handles.FreeMoveHandle(handlePt, Quaternion.identity, handleSize,Vector3.zero, Handles.RectangleCap);
- delta= new Vector2(newPos.x - handlePt.x, 0);
- drawSizeProp.vector2Value = delta;
- }
- }
运行效果:
11、Executingscripts in edit mode
- [ExecuteInEditMode]
- public class Statusbar : MonoBehaviour
- { }
这样Statusbar中的代码就会执行了,在Game面板中就可以看到ship的上方有两个血条。
如果我拖动Scene面板中的血条或者大小,下方就会跟着改变。
还想这样改变大小怎么办:
添加如下:
- [ExecuteInEditMode]
- public class Statusbar : MonoBehaviour
- {
- voidOnGUI()
- {
- ………………………………………………..
- #if UNITY_EDITOR
- if(!Application.isPlaying)
- size= drawSize / GetWorldUnitsPerPixel(Camera.main);
- #endif
- ………………………………………………..
- }
12、Drawinga detection range indicator
// 可以在Scene中动态改变坦克的攻击范围:
- [CustomEditor(typeof(GroundTurretAI))]
- public class GroundTurretAIEditor : Editor
- {
- GroundTurretAIai;
- SerializedPropertyrangeProp;
- voidOnEnable()
- {
- ai= (GroundTurretAI) target; // 要定制的脚本
- rangeProp= serializedObject.FindProperty("detectionRange"); // 得到其中的范围属性
- }
- voidOnSceneGUI()
- {
- serializedObject.Update();
- Handles.color= Color.green;
- // RadiusHandle来拖来改变值
- rangeProp.floatValue= Handles.RadiusHandle(Quaternion.identity, ai.transform.position,rangeProp.floatValue);
- serializedObject.ApplyModifiedProperties();
- }
- }
不过通过 拖动scene的视图可以发现,这个东西是3D的球形。
在13、中单独做一个2D的
13、Visualizingour range indicator for 2D games
- [CustomEditor(typeof(GroundTurretAI))]
- public class GroundTurretAIEditor : Editor
- {
- GroundTurretAIai;
- SerializedPropertyrangeProp;
- void OnEnable()
- {
- ai= (GroundTurretAI) target;
- rangeProp= serializedObject.FindProperty("detectionRange");
- }
- voidOnSceneGUI()
- {
- serializedObject.Update();
- Handles.color= Color.green;
- Vector3aiPos = ai.transform.position;
- floathandleSize = HandleUtility.GetHandleSize(aiPos) * 0.15f;
- //Left handle:
- Vector3handlePos = aiPos Vector3.left * rangeProp.floatValue;
- handlePos= Handles.FreeMoveHandle(handlePos, Quaternion.identity, handleSize,Vector3.zero, Handles.SphereCap);
- rangeProp.floatValue= Vector3.Distance(aiPos, handlePos);
- //Right handle:
- handlePos= aiPos Vector3.right * rangeProp.floatValue;
- handlePos= Handles.FreeMoveHandle(handlePos, Quaternion.identity, handleSize,Vector3.zero, Handles.SphereCap);
- rangeProp.floatValue= Vector3.Distance(aiPos, handlePos);
- //Top handle:
- handlePos= aiPos Vector3.up * rangeProp.floatValue;
- handlePos= Handles.FreeMoveHandle(handlePos, Quaternion.identity, handleSize,Vector3.zero, Handles.SphereCap);
- rangeProp.floatValue= Vector3.Distance(aiPos, handlePos);
- //Bottom handle:
- handlePos= aiPos Vector3.down * rangeProp.floatValue;
- handlePos= Handles.FreeMoveHandle(handlePos, Quaternion.identity, handleSize,Vector3.zero, Handles.SphereCap);
- rangeProp.floatValue= Vector3.Distance(aiPos, handlePos);
- //Detection area:
- Handles.color= new Color(0, 1f, 0, 0.15f);
- Handles.DrawSolidDisc(aiPos,Vector3.back, rangeProp.floatValue);
- serializedObject.ApplyModifiedProperties();
- }
- }
看看效果:
通过拖动四个中的任何一个绿色的小圆,就可以改变范围了。
14、Drawingthe turret field of fire handles
- [CustomEditor(typeof(GroundTurret))]
- public class GroundTurretEditor : Editor
- {
- constfloat arcRadius = 12f;
- SerializedPropertyminAngleProp;
- SerializedPropertymaxAngleProp;
- GroundTurretturret;
- voidOnEnable()
- {
- turret= (GroundTurret) target; // 要定制的脚本
- //目标属性
- minAngleProp= serializedObject.FindProperty("minAngle");
- maxAngleProp= serializedObject.FindProperty("maxAngle");
- }
- voidOnSceneGUI()
- {
- serializedObject.Update();
- //Set the handle color:
- Handles.color= Color.red;
- //Draw handle controllings the minimum angle:
- Vector3handlePos = turret.transform.position turret.transform.TransformDirection(HandleHelper.GetVectorFromAngle(turret.minAngle))* arcRadius;
- floathandleSize = HandleUtility.GetHandleSize(handlePos) * 0.1f;
- handlePos= Handles.FreeMoveHandle(handlePos, Quaternion.identity, handleSize,Vector3.zero, Handles.CircleCap);
- //Calculate the angle from the new handle position:
- minAngleProp.floatValue= HandleHelper.GetAngleFromHandlePos(handlePos, turret.transform);
- //Draw handle controllings the maximum angle:
- handlePos= turret.transform.position turret.transform.TransformDirection(HandleHelper.GetVectorFromAngle(turret.maxAngle))* arcRadius;
- handleSize= HandleUtility.GetHandleSize(handlePos) * 0.1f;
- handlePos= Handles.FreeMoveHandle(handlePos, Quaternion.identity, handleSize,Vector3.zero, Handles.CircleCap);
- //Calculate the angle from the new handle position:
- maxAngleProp.floatValue= HandleHelper.GetAngleFromHandlePos(handlePos, turret.transform);
- serializedObject.ApplyModifiedProperties();
- }
- }
通过拖动两个红色的小圆圈,就确定了,炮筒的攻击视野了。
15、Visualizingthe turret field of fire
就在14、中的最后添加如下的内容:
- //Calculate the angle from the new handle position:
- maxAngleProp.floatValue= HandleHelper.GetAngleFromHandlePos(handlePos, turret.transform);
- //Draw field-of-fire arc:
- Handles.color= new Color(1f, 0, 0, 0.1f);
- Vector3startVec = turret.transform.TransformDirection(HandleHelper.GetVectorFromAngle(minAngleProp.floatValue));
- Handles.DrawSolidArc(turret.transform.position,Vector3.forward, startVec, maxAngleProp.floatValue - minAngleProp.floatValue,arcRadius);
- serializedObject.ApplyModifiedProperties();
看看效果呢?
16、IntroducingGizmos
来到 Gun.cs的脚本中添加如下的函数:
例如:
- voidOnDrawGizmos () // 函数会一直执行
- {
- Gizmos.DrawSphere(transform.position, 3f);
- void OnDrawGizmosSelected() // 函数只有脚本所在对象被选择时执行
- {
- Vector3shotVector = Vector3.zero;
- Vector3arrowTip;
- Vector3arrowLeft = Vector3.zero;
- Vector3arrowRight = Vector3.zero;
- floatarrowLength = 5f;
- switch(direction)
- {
- caseShotDirection.Up:
- shotVector= Vector3.up;
- arrowLeft= Vector3.left * arrowLength * 0.2f;
- arrowRight= -arrowLeft;
- break;
- caseShotDirection.Down:
- shotVector= Vector3.down;
- arrowLeft= Vector3.right * arrowLength * 0.2f;
- arrowRight= -arrowLeft;
- break;
- caseShotDirection.Left:
- shotVector= Vector3.left;
- arrowLeft= Vector3.down * arrowLength * 0.2f;
- arrowRight= -arrowLeft;
- break;
- caseShotDirection.Right:
- shotVector= Vector3.right;
- arrowLeft= Vector3.up * arrowLength * 0.2f;
- arrowRight= -arrowLeft;
- break;
- }
- arrowTip= shotVector * arrowLength;
- arrowLeft = shotVector * arrowLength * 0.7f;
- arrowRight = shotVector * arrowLength * 0.7f;
- Gizmos.color= Color.yellow;
- Gizmos.matrix= transform.localToWorldMatrix;
- Gizmos.DrawLine(arrowTip,Vector3.zero);
- Gizmos.DrawLine(arrowTip,arrowLeft);
- Gizmos.DrawLine(arrowTip,arrowRight);
- }
17、DrawingGizmos in a custom editor to complete our course
在16中,把相当于Editor的内容写在逻辑脚本中很不合适。
所以:
- [CustomEditor(typeof(Gun))]
- public class GunEditor : Editor
- {
- [DrawGizmo(GizmoType.SelectedOrChild)]
- staticvoid DrawDirection(Gun gun, GizmoType gizmoType)
- {
- if(GizmoType.Selected == (gizmoType & GizmoType.Selected)) // 脚本所在对象被选择就不执行,父对象没有问题:
- return;
- Vector3shotVector = Vector3.zero;
- Vector3arrowTip;
- Vector3arrowLeft = Vector3.zero;
- Vector3arrowRight = Vector3.zero;
- floatarrowLength = 5f;
- switch(gun.direction)
- {
- caseGun.ShotDirection.Up:
- shotVector= Vector3.up;
- arrowLeft= Vector3.left * arrowLength * 0.2f;
- arrowRight= -arrowLeft;
- break;
- caseGun.ShotDirection.Down:
- shotVector= Vector3.down;
- arrowLeft= Vector3.right * arrowLength * 0.2f;
- arrowRight= -arrowLeft;
- break;
- caseGun.ShotDirection.Left:
- shotVector= Vector3.left;
- arrowLeft= Vector3.down * arrowLength * 0.2f;
- arrowRight= -arrowLeft;
- break;
- caseGun.ShotDirection.Right:
- shotVector= Vector3.right;
- arrowLeft= Vector3.up * arrowLength * 0.2f;
- arrowRight= -arrowLeft;
- break;
- }
- arrowTip= shotVector * arrowLength;
- arrowLeft = shotVector * arrowLength * 0.7f;
- arrowRight = shotVector * arrowLength * 0.7f;
- Gizmos.color= Color.yellow;
- Gizmos.matrix= gun.transform.localToWorldMatrix;
- Gizmos.DrawLine(arrowTip,Vector3.zero);
- Gizmos.DrawLine(arrowTip,arrowLeft);
- Gizmos.DrawLine(arrowTip,arrowRight);
- }
- }