孙广东 2015.6.20
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); } }
??