0
点赞
收藏
分享

微信扫一扫

20220326-代码日记-Unity画符

寒羽鹿 2022-03-30 阅读 30
c#unity

今日学习: Unity画符

在实体中,我们可以在纸上进行涂鸦;而在一些软件中,我们可以使用手指进行涂鸦,或者用手绘板、鼠标进行画图;游戏《阴阳师》中也有画符。我今天学习的内容,就是在unity中进行画符(实际上就是模仿阴阳师的画符)。

想要在unity中进行画符,首先要知道,怎么才能画出来。鼠标(或者手指)在屏幕上移动,然后走过的地方,就留下了痕迹,这就成了画符。提取关键部分:鼠标移动,走过的地方,痕迹。再简化些,就是鼠标经过的位置,以及屏幕上画图。

鼠标移动

我们先来看第一个部分:鼠标经过的位置。我们需要知道鼠标经过的位置,才能画出来图像。

有没有一种方法,将鼠标经过的位置记录下来呢?当然,我学到了一种方法:将鼠标经过的位置都记录下来,存在一个List中,当鼠标移动时,对链表的内容实时更新,这样,鼠标经过的位置就全部被记录下来了。

//创建一个List链表
private List<Vector3> allPoints;

private void Start()
{
    allPoints = allPoints = new List<Vector3>();
}

private void Update()
{
    if(Input.GetMouseButton(0))
    {
        //创建一个Vector3类型的临时变量,用于存放视图坐标。
        Vector3 tmpView = Camera.main.ScreenToViewportPoint(Input.mousePosition);
            allPoints.Add(tmpView);
    }
}

复习一下unity中的几种坐标系:

  • 世界坐标系:(0,0,0)为坐标原点;
  • 局部坐标系:以物体本身的位置为原点;
  • 相机坐标系:以相机位置为原点;
  • 屏幕坐标系:以屏幕左下角为原点(0,0),右上角是屏幕的宽和高。例如,如果屏幕为1280*720,那么,屏幕右上角就是(1280,720);
  • 视口坐标系:左下角为(0,0)点,右上角为(1,1)点;
  • 绘制UI的坐标系:与屏幕坐标系不同,是以左上角为原点,右下角是屏幕的宽高。

当然还有其他坐标系,例如我刚刚查到的惯性坐标系,嵌套坐标系等,这些坐标系在今天的复习回顾中用不到,暂且搁置。

着重介绍一下视口坐标系。跟前几天绘制小地图一样,绘制画符也会用到比例,视口坐标系有一个好处,可以当比例使用。当一个物体位于屏幕正中心时,它的视口坐标系下的坐标就是就(0.5, 0.5),当把这个物体映射到一张300*270的图片(图片左下角为原点)上时,它在图片上的坐标就是(150,135)。
Camera.main.ScreenToViewportPoint()这个方法就是将屏幕坐标系转换为视口坐标系。

记录鼠标位置部分已完成,接下来就是在屏幕上画图。

屏幕上画图

这部分内容在之前的学习中没有接触过,用到了一个对我来说是一个全新知识点的东西:GL。

GL在之前我是完全不了解的,而且我对OpenGL也不了解。因此关于GL的代码,我没有理解透彻,只能看着代码然后将自己的理解写成注释。跟着课程学的时候,这段的代码也是跟着课程一样,直接从API中复制粘贴过来的。

具体的东西,直接在代码里面写吧,这样也方便以后查看。尽管这不符合我心中对于代码注释的规范。
这部分代码改动过,具体原本的代码,请查看API:GL

    //定义一个材质,这个材质是画出的线的材质。
    static Material lineMaterial;
    /// <summary>
    /// 创建线的材质。
    /// </summary>
    static void CreateLineMaterial()
    {
        if (!lineMaterial)
        {
            // Unity has a built-in shader that is useful for drawing
            // simple colored things.
            Shader shader = Shader.Find("Hidden/Internal-Colored");
            lineMaterial = new Material(shader);
            #region 以下代码我看不懂
            lineMaterial.hideFlags = HideFlags.HideAndDontSave;
            // Turn on alpha blending
            lineMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
            lineMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
            // Turn backface culling off
            lineMaterial.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off);
            // Turn off depth writes
            lineMaterial.SetInt("_ZWrite", 0);
            #endregion
        }
    }

    // Will be called after all regular rendering is done
    public void OnRenderObject()
    {
        CreateLineMaterial();
        // Apply the line material
        lineMaterial.SetPass(0);
        //变成正交投影
        GL.LoadOrtho();
        GL.Begin(GL.LINES);
        //换颜色
        GL.Color(Color.red);
        for (int i = 1; i < allPoints.Count; i++)
        {
            //创建两个临时变量,用于存放前一个点的位置和当下这一个点的位置。
            Vector3 tmpFront = allPoints[i - 1];
            Vector3 tmpBack = allPoints[i];
            //将这两个点提交上去。这关于GL.Vertex()这个方法,我还没有理解。
            GL.Vertex3(tmpFront.x, tmpFront.y, tmpFront.z);
            GL.Vertex3(tmpBack.x, tmpBack.y, tmpBack.z);
        }
        GL.End();
    }

这段代码,我基本属于不理解的状态。Unity的GL这一块儿同样暂且搁置。

将画出来的画符映射到一个画板上:

    public void GenerateTexture()
    {
        paintTexture = new Texture2D(300, 400);
        for (int i = 1; i < allPoints.Count; i++)
        {
            Vector3 tmpFront = allPoints[i - 1];
            Vector3 tmpBack = allPoints[i];

            //将图片的宽和高乘上一个比例,得到相对于图片(0,0)坐标的位置。
            //上一个鼠标位置点的X,Y的值
            float paintFrontX = paintTexture.width * tmpFront.x;
            float paintFrontY = paintTexture.height * tmpFront.y;
            //当前鼠标位置点的X,Y值
            float paintBackX = paintTexture.width * tmpBack.x;
            float paintBackY = paintTexture.height * tmpBack.y;

            //用于插值运算
            int tmpCount = 50;
            for (int j = 0; j < tmpCount; j++)
            {
                //插值:a*t+1-tb
                //参数:需要渐进变换的参数,峰顶值,变化速率 
                int tmpX = (int)Mathf.Lerp(paintFrontX, paintBackX, j / (float)tmpCount);
                //像素点需要一个整型参数
                int tmpY = (int)Mathf.Lerp(paintFrontY, paintBackY, j / (float)tmpCount);
                //设置图片像素点的颜色。
                paintTexture.SetPixel(tmpX, tmpY, Color.red);
            }
        }
        //paintTexture.SetPixel(paintX, paintY, Color.red);
        paintTexture.Apply();
        //将物体材质更换为当前代码创建的这个纹理。
        transform.GetComponent<Renderer>().material.mainTexture = paintTexture;
    }

接收画符的纹理创建好之后,在Update里面引用它。当我们松开鼠标的时候,屏幕上的画符就消失,然后画符呈现在画板上。

private void Update()
    {
        if (Input.GetMouseButton(0))
        {
            //得到点
            Vector3 tmpView = Camera.main.ScreenToViewportPoint(Input.mousePosition);
            allPoints.Add(tmpView);
        }
        if (Input.GetMouseButtonUp(0))
        {
            GenerateTexture();
            allPoints.Clear();
        }
    }

完整代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PointerUI : MonoBehaviour
{
    List<Vector3> allPoints;
    Texture2D paintTexture;
    
    private void Start()
    {
        allPoints = new List<Vector3>();
    }

    private void Update()
    {
        if (Input.GetMouseButton(0))
        {
            //得到点
            Vector3 tmpView = Camera.main.ScreenToViewportPoint(Input.mousePosition);
            allPoints.Add(tmpView);
        }
        if (Input.GetMouseButtonUp(0))
        {
            GenerateTexture();
            allPoints.Clear();
        }
    }
    
    public void GenerateTexture()
    {
        paintTexture = new Texture2D(300, 400);
        for (int i = 1; i < allPoints.Count; i++)
        {
            Vector3 tmpFront = allPoints[i - 1];
            Vector3 tmpBack = allPoints[i];

            //将图片的宽和高乘上一个比例,得到相对于图片(0,0)坐标的位置。
            //上一个鼠标位置点的X,Y的值
            float paintFrontX = paintTexture.width * tmpFront.x;
            float paintFrontY = paintTexture.height * tmpFront.y;
            //当前鼠标位置点的X,Y值
            float paintBackX = paintTexture.width * tmpBack.x;
            float paintBackY = paintTexture.height * tmpBack.y;

            //用于插值运算
            int tmpCount = 50;
            for (int j = 0; j < tmpCount; j++)
            {
                //插值:a*t+1-tb
                //参数:需要渐进变换的参数,峰顶值,变化速率 
                int tmpX = (int)Mathf.Lerp(paintFrontX, paintBackX, j / (float)tmpCount);
                //像素点需要一个整型参数
                int tmpY = (int)Mathf.Lerp(paintFrontY, paintBackY, j / (float)tmpCount);
                //设置图片像素点的颜色。
                paintTexture.SetPixel(tmpX, tmpY, Color.red);
            }
        }
        //paintTexture.SetPixel(paintX, paintY, Color.red);
        paintTexture.Apply();
        //将物体材质更换为当前代码创建的这个纹理。
        transform.GetComponent<Renderer>().material.mainTexture = paintTexture;
    }
 //定义一个材质,这个材质是画出的线的材质。
    static Material lineMaterial;
    /// <summary>
    /// 创建线的材质。
    /// </summary>
    static void CreateLineMaterial()
    {
        if (!lineMaterial)
        {
            // Unity has a built-in shader that is useful for drawing
            // simple colored things.
            Shader shader = Shader.Find("Hidden/Internal-Colored");
            lineMaterial = new Material(shader);
            #region 以下代码我看不懂
            lineMaterial.hideFlags = HideFlags.HideAndDontSave;
            // Turn on alpha blending
            lineMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
            lineMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
            // Turn backface culling off
            lineMaterial.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off);
            // Turn off depth writes
            lineMaterial.SetInt("_ZWrite", 0);
            #endregion
        }
    }

    // Will be called after all regular rendering is done
    public void OnRenderObject()
    {
        CreateLineMaterial();
        // Apply the line material
        lineMaterial.SetPass(0);
        //变成正交投影
        GL.LoadOrtho();
        GL.Begin(GL.LINES);
        //换颜色
        GL.Color(Color.red);
        for (int i = 1; i < allPoints.Count; i++)
        {
            //创建两个临时变量,用于存放前一个点的位置和当下这一个点的位置。
            Vector3 tmpFront = allPoints[i - 1];
            Vector3 tmpBack = allPoints[i];
            //将这两个点提交上去。这关于GL.Vertex()这个方法,我还没有理解。
            GL.Vertex3(tmpFront.x, tmpFront.y, tmpFront.z);
            GL.Vertex3(tmpBack.x, tmpBack.y, tmpBack.z);
        }
        GL.End();
    }
}

在Unity场景中创建一个Quad物体,将脚本挂载到它身上。然后运行。下面是效果:
请添加图片描述


这部分内容,学得不够透彻。理解得也不够深入。
关于unity中GL这部分,暂时不打算学。过段时间,可能是一两年,两三年,再去学习,学习GL,OpenGL,Shader,等到那时候再去整理思路。
目前我能保持每天花最多一小时学习Unity,这已经是最好的状态了,因为我有比这更重要的事情去做,而Unity被我当做了业余时间一件能放松的事情。毕竟经常看手机刷短视频也不好,浪费时间不说,更不能提升自己。这就作为一个爱好发展下去吧。希望这个爱好能坚持一个月。先一个月吧,我也不是个能有长久爱好的人。
至于昨天和前天为什么没有写日记,请你——未来的我——听我解(jie)释(kou)。
是这样的,前天学的东西,GL这块没跟上,又不想返回去再看一遍,网又差,API加载不出来,我就放弃了,所以前天没写。昨天为啥没写呢,因为昨天我又把前天学的课返了一遍,又学了昨天应该学的新课。学完之后,已经十一点多快十二点了,我就去睡觉了。
其实吧,就是懒,拖延症犯了。
请你放心,我……我不一定会改啊。太难了。比跑步还难。

举报

相关推荐

0 条评论