Unity游戏选/创建角色界面中 职业能力图六角形
Posted u010019717
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity游戏选/创建角色界面中 职业能力图六角形相关的知识,希望对你有一定的参考价值。
2016.8.22 孙广东
游戏选/创建角色界面中 职业能力图六角形
主要说的是 UGUI 的实现方式!!!!!!
下面这个六边形在很多游戏中都存在了(随便在网上找的, 一会就使用这个图片)。:
在 Asset Store 上搜索, Chart 会得到相关的结果
http://blog.csdn.net/u010019717/
先使用最简单的方式绘制Mesh多边形。
using UnityEngine;
using System.Collections;
public class MeshTest : MonoBehaviour
privateMeshFilter m_MeshFilter;
void Start()
Meshmesh = new Mesh();
mesh.vertices = new Vector3[]
new Vector3(0, 0, 0), // 顶点:0
new Vector3(1, 0, 0), // 顶点:1
new Vector3(1.5f, 1, 0), // 顶点:2
new Vector3(0.6f, 1.5f, 0), // 顶点:3
new Vector3(-0.8f, 1, 0), //顶点:4
;
//遵循顺时针三顶点确定一面
mesh.triangles = new int[]
0, 2, 1, // 面:0, 2, 1
0, 4, 2, // 面:0, 4, 2
4, 3, 2, // 面:4, 3, 2
;
m_MeshFilter = GetComponent<MeshFilter>();
m_MeshFilter.mesh = mesh;
顶点和三角面 就组成这个多边形。
这个功能肯定是 放在UI上, 对 UGUI
首先大概介绍下uGUI的体系结构。在uGUI中,所有的UI组件都要放置在Canvas组件下,由Canvas来管理它的渲染和自适应屏幕等。uGUI提供了Graphic基类,运行时,Canvas会使用CanvasRenderer来渲染它的子级中全部的Graphic组件。所以,如果要自定义外观的控件,从Graphic继承是一个不错的选择。
我们都知道,在unity中3d物体最终是转化成若干网格数据来渲染的,其实ui的渲染也是一样的方法(注意 是Unity5.3之后UGUI才引入 Mesh这个东东)。比如一个Image组件,内部其实是使用4个顶点构成的2个三角形网格外加一个Texture贴图来渲染的。那么要改变控件的外形,只要改变网格数据就可以了,对,就是这么个思路。
Graphic提供了一个虚方法
protected virtual void OnPopulateMesh(VertexHelper vh);
通过重写这个方法,即可修改或重新生成控件的网格数据,从而达到自定义控件显示外观的需求(Unity4为另一个接口OnFillVBO,不过原理是一致的)。而VertexHelper是unity提供的简化网格操作的辅助类,它提供的接口也很简单,诸如添加顶点、添加三角形、添加Quad等。
需要注意的一点是,顶点的坐标是由控件的位置、大小和锚点等决定的,计算时需要综合考虑这些因素。
http://blog.csdn.net/u010019717/
看 UGUI的源代码 :
说的是当该控件(例如Text,Image,RawImage)需要改变顶点的时候,就会自动调用 OnPopulateMesh。
不过该函数是只有在该Craphic组件需要修改的时候才会调用,比如你修改Image的大小,或者它加载的时候才会调用。
这样就导致我们没法及时在编辑器状态下看到我们对mesh的修改, 比如用是将一张Image的四个角分别用四个对象表示,这四个对象的移动,会让这种Image发生形变。但是没法及时更新就没办法了。
还好有 SetNativeSize() 这个方法,其实跟刷新差不多。
直接上代码:
/// CreditCiaccoDavide
/// Sourced from - http://ciaccodavi.de/unity/UIPolygon
namespace UnityEngine.UI.Extensions
[AddComponentMenu("UI/Extensions/Primitives/UI Polygon")]
public class UIPolygon : UIPrimitiveBase
public bool fill = true;
public float thickness = 5;
[Range(3, 360)]
public int sides = 3;
[Range(0, 360)]
public float rotation = 0;
[Range(0, 1)]
public float[] VerticesDistances = newfloat[3];
private float size = 0;
public void DrawPolygon(int _sides)
sides = _sides;
VerticesDistances = newfloat[_sides + 1];
for (int i = 0; i < _sides; i++)VerticesDistances[i] = 1; ;
rotation = 0;
public void DrawPolygon(int _sides,float[] _VerticesDistances)
sides = _sides;
VerticesDistances =_VerticesDistances;
rotation = 0;
public void DrawPolygon(int _sides,float[] _VerticesDistances, float _rotation)
sides = _sides;
VerticesDistances =_VerticesDistances;
rotation = _rotation;
void Update()
size = rectTransform.rect.width;
if (rectTransform.rect.width >rectTransform.rect.height)
size =rectTransform.rect.height;
else
size =rectTransform.rect.width;
thickness =(float)Mathf.Clamp(thickness, 0, size / 2);
protected override voidOnPopulateMesh(VertexHelper vh)
vh.Clear();
Vector2 prevX = Vector2.zero;
Vector2 prevY = Vector2.zero;
Vector2 uv0 = new Vector2(0, 0);
Vector2 uv1 = new Vector2(0, 1);
Vector2 uv2 = new Vector2(1, 1);
Vector2 uv3 = new Vector2(1, 0);
Vector2 pos0;
Vector2 pos1;
Vector2 pos2;
Vector2 pos3;
float degrees = 360f / sides;
int vertices = sides + 1;
if (VerticesDistances.Length !=vertices)
VerticesDistances = newfloat[vertices];
for (int i = 0; i < vertices- 1; i++) VerticesDistances[i] = 1;
// last vertex is also the first!
VerticesDistances[vertices - 1] =VerticesDistances[0];
for (int i = 0; i < vertices;i++)
float outer =-rectTransform.pivot.x * size * VerticesDistances[i];
float inner =-rectTransform.pivot.x * size * VerticesDistances[i] + thickness;
float rad = Mathf.Deg2Rad * (i* degrees + rotation);
float c = Mathf.Cos(rad);
float s = Mathf.Sin(rad);
uv0 = new Vector2(0, 1);
uv1 = new Vector2(1, 1);
uv2 = new Vector2(1, 0);
uv3 = new Vector2(0, 0);
pos0 = prevX;
pos1 = new Vector2(outer * c,outer * s);
if (fill)
pos2 = Vector2.zero;
pos3 = Vector2.zero;
else
pos2 = new Vector2(inner *c, inner * s);
pos3 = prevY;
prevX = pos1;
prevY = pos2;
vh.AddUIVertexQuad(SetVbo(new[] pos0, pos1, pos2, pos3 , new[] uv0, uv1, uv2, uv3 ));
它继承自封装的基类:
using System;
namespaceUnityEngine.UI.Extensions
public class UIPrimitiveBase :MaskableGraphic, ILayoutElement, ICanvasRaycastFilter
[SerializeField]
private Sprite m_Sprite;
public Sprite sprite get returnm_Sprite; set if (SetPropertyUtility.SetClass(ref m_Sprite, value))SetAllDirty();
[NonSerialized]
private Sprite m_OverrideSprite;
public Sprite overrideSprite get return m_OverrideSprite == null ? sprite : m_OverrideSprite; set if(SetPropertyUtility.SetClass(ref m_OverrideSprite, value)) SetAllDirty();
// Not serialized until we supportread-enabled sprites better.
internal float m_EventAlphaThreshold =1;
public float eventAlphaThreshold get return m_EventAlphaThreshold; set m_EventAlphaThreshold = value;
/// <summary>
/// Image's texture comes from theUnityEngine.Image.
/// </summary>
public override Texture mainTexture
get
if (overrideSprite == null)
if (material != null&& material.mainTexture != null)
returnmaterial.mainTexture;
return s_WhiteTexture;
return overrideSprite.texture;
public float pixelsPerUnit
get
float spritePixelsPerUnit =100;
if (sprite)
spritePixelsPerUnit =sprite.pixelsPerUnit;
float referencePixelsPerUnit =100;
if (canvas)
referencePixelsPerUnit =canvas.referencePixelsPerUnit;
return spritePixelsPerUnit /referencePixelsPerUnit;
protected UIVertex[] SetVbo(Vector2[]vertices, Vector2[] uvs)
UIVertex[] vbo = new UIVertex[4];
for (int i = 0; i <vertices.Length; i++)
var vert = UIVertex.simpleVert;
vert.color = color;
vert.position = vertices[i];
vert.uv0 = uvs[i];
vbo[i] = vert;
return vbo;
#region ILayoutElement Interface
public virtual voidCalculateLayoutInputHorizontal()
public virtual voidCalculateLayoutInputVertical()
public virtual float minWidth get return 0;
public virtual float preferredWidth
get
if (overrideSprite == null)
return 0;
returnoverrideSprite.rect.size.x / pixelsPerUnit;
public virtual float flexibleWidth get return -1;
public virtual float minHeight get return 0;
public virtual float preferredHeight
get
if (overrideSprite == null)
return 0;
returnoverrideSprite.rect.size.y / pixelsPerUnit;
public virtual float flexibleHeight get return -1;
public virtual int layoutPriority get return 0;
#endregion
#region ICanvasRaycastFilter Interface
public virtual boolIsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
if (m_EventAlphaThreshold >= 1)
return true;
Sprite sprite = overrideSprite;
if (sprite == null)
return true;
Vector2 local;
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform,screenPoint, eventCamera, out local);
Rect rect = GetPixelAdjustedRect();
// Convert to have lower leftcorner as reference point.
local.x += rectTransform.pivot.x *rect.width;
local.y += rectTransform.pivot.y *rect.height;
local = MapCoordinate(local, rect);
// Normalize local coordinates.
Rect spriteRect =sprite.textureRect;
Vector2 normalized = newVector2(local.x / spriteRect.width, local.y / spriteRect.height);
// Convert to texture space.
float x = Mathf.Lerp(spriteRect.x,spriteRect.xMax, normalized.x) / sprite.texture.width;
float y = Mathf.Lerp(spriteRect.y,spriteRect.yMax, normalized.y) / sprite.texture.height;
try
returnsprite.texture.GetPixelBilinear(x, y).a >= m_EventAlphaThreshold;
catch (UnityException e)
Debug.LogError("UsingclickAlphaThreshold lower than 1 on Image whose sprite texture cannot be read." + e.Message + " Also make sure to disable sprite packing for thissprite.", this);
return true;
/// <summary>
/// Return image adjusted position
/// **Copied from Unity's Imagecomponent for now and simplified for UI Extensions primatives
/// </summary>
/// <paramname="local"></param>
/// <paramname="rect"></param>
/// <returns></returns>
private Vector2 MapCoordinate(Vector2local, Rect rect)
Rect spriteRect = sprite.rect;
return new Vector2(local.x *spriteRect.width / rect.width, local.y * spriteRect.height / rect.height);
Vector4 GetAdjustedBorders(Vector4border, Rect rect)
for (int axis = 0; axis <= 1;axis++)
float combinedBorders =border[axis] + border[axis + 2];
if (rect.size[axis] <combinedBorders && combinedBorders != 0)
float borderScaleRatio =rect.size[axis] / combinedBorders;
border[axis] *=borderScaleRatio;
border[axis + 2] *=borderScaleRatio;
return border;
#endregion
将脚本拖拽到 Canvas 下的一个空对象上。
然后设置参数(六边形,随便着色、为了和背景图一直旋转了一下,然后就是六个值的设定)
效果就和一开始的截图一样了!
提到的 OnFillVBO 的实现方式(这个方法是 Unity5.3 之前的版本和 SetNativeSize一个原理)
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
[ExecuteInEditMode]
public class SimpleImage : Graphic
protected override voidOnFillVBO (List<UIVertex> vbo)
Vector2 corner1 =Vector2.zero;
Vector2 corner2 =Vector2.zero;
corner1.x = 0f;
corner1.y = 0f;
corner2.x = 1f;
corner2.y = 1f;
corner1.x -=rectTransform.pivot.x;
corner1.y -=rectTransform.pivot.y;
corner2.x -=rectTransform.pivot.x;
corner2.y -=rectTransform.pivot.y;
corner1.x *=rectTransform.rect.width;
corner1.y *=rectTransform.rect.height;
corner2.x *=rectTransform.rect.width;
corner2.y *=rectTransform.rect.height;
vbo.Clear();
UIVertex vert =UIVertex.simpleVert;
vert.position = newVector2(corner1.x, corner1.y);
vert.color = color;
vbo.Add(vert);
vert.position = newVector2(corner1.x, corner2.y);
vert.color = color;
vbo.Add(vert);
vert.position = newVector2(corner2.x, corner2.y);
vert.color = color;
vbo.Add(vert);
vert.position = newVector2(corner2.x, corner1.y);
vert.color = color;
vbo.Add(vert);
http://blog.csdn.net/u010019717/
http://blog.csdn.net/u010019717/
以上是关于Unity游戏选/创建角色界面中 职业能力图六角形的主要内容,如果未能解决你的问题,请参考以下文章
Unity2D游戏角色动画教程-使用Photoshop创建2D游戏角色素材
游戏开发教程BehaviorDesigner插件制作AI行为树(Unity | 保姆级教程 | 动态图演示 | Unity2021最新版)
游戏开发教程BehaviorDesigner插件制作AI行为树(Unity | 保姆级教程 | 动态图演示 | Unity2021最新版)