公司经营的软件与绘图相关,这天,客户提出来一个需求,希望能在绘图的任意位置添加备注,方便厂家之间沟通。
客户要求:可以在任意位置添加、修改、删除。
实现过程:
1、如何创建?
这个问题很简单,UI界面添加一个Button,绑定一个事件,点击的时候创建一个游戏物体专门用来显示文字。显示文字使用TextMesh组件,如下图:
2、如何修改文字?
这个问题也很容易解决,添加一个inputfiled组件,获取用户输入的文本,然后再将用户输入的内容传递给TextMesh即可。
3、如何实现移动这些文字?
给每个物体添加一个脚本,跟随鼠标或者手势滑动的位置。代码如下:
public class Move : MonoBehaviour {
Ray ray;
Vector3 position;
RaycastHit hit;
//判断当前是否被选中
public bool isSelect=true;
void LateUpdate()
{
if (Input.GetMouseButton(0)&& isSelect)
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
if (hit.transform.gameObject.tag == "Note")
{
transform.position = hit.point;
}
}
}
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved&& isSelect)
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
if (hit.transform.gameObject.tag == "Note")
{
transform.position = hit.point;
}
}
}
}
}
4、如何知道客户想修改的是哪一个文本?
创建一个控制脚本,里面定义一个空物体,作为当前物体的引用。使用射线,获取当前用户点击到的文本,拿到当前用户点击的物体,当用户点击另一个的时候,将当前物体的引用改为新点击的即可。代码细节如下:
public class Test : MonoBehaviour {
GameObject currentObj;
Ray ray;
RaycastHit hit;
public GameObject parentObj;
public InputField InputField;
public Button OKButton;
public Button createButton;
public Button delButton;
private Color black;
//当前被选中的为黄色
private Color yellow;
void Start () {
black = new Color(0, 0, 0);
yellow = new Color(215, 215, 0);
OKButton.onClick.AddListener(() => ReviseText());
createButton.onClick.AddListener(() => CreateNote());
delButton.onClick.AddListener(()=>Del());
}
void CreateNote()
{
Resert();
GameObject obj = new GameObject("Test");
currentObj = obj;
var textMesh = obj.AddComponent<TextMesh>();
textMesh.text = "测试字体";
textMesh.fontSize = 100;
obj.transform.SetParent(transform);
obj.transform.localPosition = new Vector3(0, 0, 100);
obj.transform.SetParent(parentObj.transform);
obj.tag = "Note";
obj.GetComponent<TextMesh>().color = yellow;
obj.GetComponent<TextMesh>().anchor = TextAnchor.MiddleCenter;
obj.AddComponent<Move>();
obj.GetComponent<Move>().isSelect = true;
//TextMeshPro插件添加碰撞体不会随着字体改变而改变,所以最后添加
obj.AddComponent<BoxCollider>();
}
//重置上一个选中的状态
private void Resert()
{
//将先前选中的备注设置为黑色
if (currentObj != null)
{
currentObj.GetComponent<TextMesh>().color = black;
currentObj.GetComponent<Move>().isSelect = false;
}
}
void ReviseText()
{
if (currentObj!=null)
{
currentObj.GetComponent<TextMesh>().text = InputField.text;
//TextMeshPro插件添加碰撞体不会随着字体改变而改变,所以需要删除后重新添加
Destroy(currentObj.GetComponent<BoxCollider>());
currentObj.AddComponent<BoxCollider>();
}
}
void Del()
{
if (currentObj != null)
Destroy(currentObj);
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
if (hit.transform.gameObject.tag == "Note")
{
if (currentObj != null)
{
currentObj.GetComponent<TextMesh>().color = black;
currentObj.GetComponent<Move>().isSelect = false;
}
currentObj = hit.transform.gameObject;
currentObj.GetComponent<TextMesh>().color = yellow;
currentObj.GetComponent<Move>().isSelect = true;
}
}
}
if(Input.touchCount==1&&Input.GetTouch(0).phase == TouchPhase.Began)
{
ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
if (Physics.Raycast(ray, out hit))
{
if (hit.transform.gameObject.tag == "Note")
{
Resert();
currentObj = hit.transform.gameObject;
currentObj.GetComponent<TextMesh>().color = yellow;
currentObj.GetComponent<Move>().isSelect = true;
}
}
}
}
}
效果如下:
总结:总的来说还是一个比较简单易实现的小功能,主要用到Input类、射线类
后续:实际应用的时候可以创建一个空物体作为预制件,提前添加好需要用到的脚本,实际开发调用的时候可以减少代码量,提高阅读效率。
2020.8.26后续更新,客户画的图有大有小,希望可以自定义字体的大小,看起来更搭配。
新增一个Slider组件,通过这个滑动轴来定义大小。
在测试脚本中给Slider组件添加事件。
slider.onValueChanged.AddListener((value)=>
{
if (currentObj != null)
currentObj.transform.localScale = new Vector3(value,value,value);
});
为了客户的更好使用,客户点击到每一个字体的时候,这个时候Slider的value应该改为当前值。
分别在创建新字体和点击字体时候调用。
//重置滑块的Value值
private void ResetSliderValue()
{
slider.value = currentObj.transform.localScale.x;
}
实际效果:
客户在提完改变大小的要求后,希望能增加多种颜色自由选择。简单想了一下,解决方法有俩。
1:调用调色板(比较复杂,而且不知道手机上是否支持,暂且放弃,百度可以搜索到相关方法,是可行的)
2:使用DropDown组件。将常用颜色放入到下拉框中,供客户选择。
UI界面设置如下:
代码添加事件即可。
dropdown.onValueChanged.AddListener((value) =>
{
if (currentObj != null)
{
switch (value)
{
case 0:
currentObj.GetComponent<TextMesh>().color = Color.green;
break;
case 1:
currentObj.GetComponent<TextMesh>().color = Color.blue;
break;
case 2:
currentObj.GetComponent<TextMesh>().color = Color.red;
break;
}
}
});
实际效果:
但是UGUI默认的DropDown有若干个问题。
1、下拉菜单弹出时,首选项(即第一个)是被选中的状态。此时调用首选项无效。
2、如果上次选择了某一项,下一次还是选择这个,不会执行OnVlaueChanged事件。(这个很致命,用户有时候会挨个设置一样的颜色,结果发现无效)。
改进方法:看UGUI源码,自己做一个DropDown组件。(挖个坑,有空的时候回来填坑)。
9.3后续更新。
选中字体的时候,字体的position会直接改为点击的位置,这就造成了点击字体的位置不是中心位置的时候会造成便宜,需要优化,优化代码如下。
移动脚本中增加一个偏移量。
public Vector3 pianyi;
点击的时候计算下偏移量,然后用点击的位置减去原来的位置再减去偏移量,就可以得到鼠标移动的距离,用原来的坐标位置加上移动的距离即可。
移动脚本代码,写在lateupdate中
transform.position += new Vector3(hit.point.x - transform.position.x - pianyi.x, hit.point.y - transform.position.y - pianyi.y, 0);
测试脚本代码
currentObj.GetComponent<Move>().pianyi = hit.point - currentObj.transform.position;
优化后的效果:
Demo