公司经营的软件与绘图相关,这天,客户提出来一个需求,希望能在绘图的任意位置添加备注,方便厂家之间沟通。

客户要求:可以在任意位置添加、修改、删除。

实现过程:

1、如何创建?

这个问题很简单,UI界面添加一个Button,绑定一个事件,点击的时候创建一个游戏物体专门用来显示文字。显示文字使用TextMesh组件,如下图:

unity 添加字幕 unity怎么加文字_unity 添加字幕


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;
                }
            }
        }
    }
}

效果如下:

unity 添加字幕 unity怎么加文字_unity3d_02

总结:总的来说还是一个比较简单易实现的小功能,主要用到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;
    }

实际效果:

unity 添加字幕 unity怎么加文字_偏移量_03


客户在提完改变大小的要求后,希望能增加多种颜色自由选择。简单想了一下,解决方法有俩。

1:调用调色板(比较复杂,而且不知道手机上是否支持,暂且放弃,百度可以搜索到相关方法,是可行的)

2:使用DropDown组件。将常用颜色放入到下拉框中,供客户选择。

UI界面设置如下:

unity 添加字幕 unity怎么加文字_unity 添加字幕_04


代码添加事件即可。

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;
                }
            }
        });

实际效果:

unity 添加字幕 unity怎么加文字_unity3d_05


但是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;

优化后的效果:

unity 添加字幕 unity怎么加文字_unity3d_06


Demo