AR涂涂乐⭐八、(add)优化原代码“7”、正方形识别图改为矩形识别图、增加BGM和App 图标

发布于:2022-12-21 ⋅ 阅读:(515) ⋅ 点赞:(0)

在这里插入图片描述

 

目录

🟥 项目问题及解决思路

🟧 ScreenShot:依附于地球

🟨 Area

🟩 本章注意事项:


🟥 项目问题及解决思路

问题1:观察前面代码可发现,屏幕的宽和高在start()时便被复制,不可改变,导致旋转屏幕高宽调转方向,代码中数值却不会改变,发生问题。

解决思路:
1、
将高宽的赋值定义在Update()
2、同时注意ScreenShot代码中,储存屏幕像素的Textureshot = new Texture2D(ScreenWidth, ScreenHeight, TextureFormat.RGB24, false)原在Start()函数中,他不能跟着高宽代码写到Update()函数中(不停地new很容易卡死程序),应将它放于截图函数ScreenShot_Button()中(截图时使用最新的高宽数值,且此函数只运行一次)。

问题2:正方形识别图改为矩形(长方形)。shader为正方形矩阵,原正方形截图可传给shader的矩阵,现识别图改为长方形,长方形传给正方形shader矩阵会先压缩成正方形,截图变形严重,需要的截图部分错位,无法传递正确信息,shader不能改变,识别操作需要改变。

解决思路:
1、
新建两个面片,PlaneA用来识别图片和显示不同的提示颜色,PlaneB用来截图

2、PlaneA跟长方形识别图一样大,PlaneB跟上下用背景色补全成一个正方形的识别图一样大(透明色,也可是白色等,它要跟实体识别图矩形范围一致,不影响识别)

🟧 ScreenShot:依附于地球

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

public class ScreenShot : MonoBehaviour
{
    private int ScreenWidth, ScreenHeight;
    private Texture2D Textureshot;  //申请变量储存屏幕截图  Texture纹理意思,Texture2D为内存中的平面纹理/图片。

    private Vector2 PlaneWH;   //获取面片二维方向上高和宽的长度!               
    private Vector3 TopLeft_pl_w, BottomLeft_pl_w, TopRight_pl_w, BottomRight_pl_w;   //记录面片的世界坐标  三维!

    public GameObject Earth;
    public GameObject EarthFrame;

    public GameObject PlaneA;
    public GameObject PlaneB;   //储存面片,因为本代码附在空的GameObject“UIManger”上,不再是面片plane,所以计算面片坐标时不该写gameObject.transform.parent.position(此时的gameObject指空的UIManger,而不是我们想要的面片),所以需要public后传递plane。
    public GameObject EarthA;  //储存太阳系统的地球,用于将截图信息传递给它的shader

    void Update()
    {
        ScreenWidth = Screen.width;
        ScreenHeight = Screen.height;   //直接在Start中声明赋值无影响。
    }

    public void ScreenShot_Button()
    {
        Textureshot = new Texture2D(ScreenWidth, ScreenHeight, TextureFormat.RGB24, false);  //属性定义纹理的模式用TextureFormat.RGB24颜色。
                                                                                             //标准格式:Texture2D(int width,int height,TextureFormat format,bool mipmap)    bool mipmap,是一种分级纹理,在屏幕中显示大小不同时给予不同的纹理,此处不用。
        
        //我们需要的是面片在空间中的大小,但我们获取到的是面片的实际大小,它本身缩放了0.1倍,它父集缩放50倍,所以他在空间实际是扩大了5倍,所以要*5。
        PlaneWH = new Vector2(PlaneB.GetComponent<MeshFilter>().mesh.bounds.size.x, PlaneB.GetComponent<MeshFilter>().mesh.bounds.size.z) * 5 * 0.5f;   //空间中x、z是长宽,y是空间中的高度!

        //获得图片的四个点世界坐标(gameobject指的是面片,它的父集是图片)(position是图片中心的位置,所以需要加上面片x,y向长度)
        TopLeft_pl_w = PlaneB.transform.parent.position + new Vector3(-PlaneWH.x, 0, PlaneWH.y);
        BottomLeft_pl_w = PlaneB.transform.parent.position + new Vector3(-PlaneWH.x, 0, -PlaneWH.y);
        TopRight_pl_w = PlaneB.transform.parent.position + new Vector3(PlaneWH.x, 0, PlaneWH.y);
        BottomRight_pl_w = PlaneB.transform.parent.position + new Vector3(PlaneWH.x, 0, -PlaneWH.y);

        //将截图的四个点坐标传递给Earth shader   1f是为了凑齐shader中的4X4数组,所以new Vector4,另外shader中的数值为浮点型,所以此处加f,不加就成了整形了。
        Earth.GetComponent<Renderer>().material.SetVector("_Uvpoint1", new Vector4(TopLeft_pl_w.x, TopLeft_pl_w.y,TopLeft_pl_w.z, 1f));
        Earth.GetComponent<Renderer>().material.SetVector("_Uvpoint2", new Vector4(BottomLeft_pl_w.x, BottomLeft_pl_w.y, BottomLeft_pl_w.z, 1f));
        Earth.GetComponent<Renderer>().material.SetVector("_Uvpoint3", new Vector4(TopRight_pl_w.x, TopRight_pl_w.y, TopRight_pl_w.z, 1f));
        Earth.GetComponent<Renderer>().material.SetVector("_Uvpoint4", new Vector4(BottomRight_pl_w.x, BottomRight_pl_w.y, BottomRight_pl_w.z, 1f));

        //将截图的四个点坐标传递给EarthFrame shader   1f是为了凑齐shader中的4X4数组,所以new Vector4,另外shader中的数值为浮点型,所以此处加f,不加就成了整形了。
        EarthFrame.GetComponent<Renderer>().material.SetVector("_Uvpoint1", new Vector4(TopLeft_pl_w.x, TopLeft_pl_w.y, TopLeft_pl_w.z, 1f));
        EarthFrame.GetComponent<Renderer>().material.SetVector("_Uvpoint2", new Vector4(BottomLeft_pl_w.x, BottomLeft_pl_w.y, BottomLeft_pl_w.z, 1f));
        EarthFrame.GetComponent<Renderer>().material.SetVector("_Uvpoint3", new Vector4(TopRight_pl_w.x, TopRight_pl_w.y, TopRight_pl_w.z, 1f));
        EarthFrame.GetComponent<Renderer>().material.SetVector("_Uvpoint4", new Vector4(BottomRight_pl_w.x, BottomRight_pl_w.y, BottomRight_pl_w.z, 1f));

        //将截图的四个点坐标传递给EarthA shader   1f是为了凑齐shader中的4X4数组,所以new Vector4,另外shader中的数值为浮点型,所以此处加f,不加就成了整形了。
        EarthA.GetComponent<Renderer>().material.SetVector("_Uvpoint1", new Vector4(TopLeft_pl_w.x, TopLeft_pl_w.y, TopLeft_pl_w.z, 1f));
        EarthA.GetComponent<Renderer>().material.SetVector("_Uvpoint2", new Vector4(BottomLeft_pl_w.x, BottomLeft_pl_w.y, BottomLeft_pl_w.z, 1f));
        EarthA.GetComponent<Renderer>().material.SetVector("_Uvpoint3", new Vector4(TopRight_pl_w.x, TopRight_pl_w.y, TopRight_pl_w.z, 1f));
        EarthA.GetComponent<Renderer>().material.SetVector("_Uvpoint4", new Vector4(BottomRight_pl_w.x, BottomRight_pl_w.y, BottomRight_pl_w.z, 1f));

        Matrix4x4 P = GL.GetGPUProjectionMatrix(Camera.main.projectionMatrix, false);   //获取截图时的投影矩阵
        Matrix4x4 V = Camera.main.worldToCameraMatrix;    //获取截图时世界坐标到相机的矩阵
        Matrix4x4 VP = P * V;

        Earth.GetComponent<Renderer>().material.SetMatrix("_VP",VP);   //将截图的转化信息传递给Earth shader。
        EarthFrame.GetComponent<Renderer>().material.SetMatrix("_VP", VP);   //将截图的转化信息传递给EarthFrame shader。
        EarthA.GetComponent<Renderer>().material.SetMatrix("_VP", VP);   //将截图的转化信息传递给EarthA shader。

        Textureshot.ReadPixels(new Rect(0, 0, ScreenWidth, ScreenHeight), 0, 0);
        //获取屏幕的像素信息
        //第一个0,0是获取屏幕像素的起始点
        //ScreenWidth, ScreenHeight是获取屏幕像素的范围
        //第二个0,0是填充Texture2D时起始的坐标

        Textureshot.Apply();
        //确认之前的Texture2D的修改,此时才真正应用。此时截图保存在内存中,未使用。

        Earth.GetComponent<Renderer>().material.mainTexture = Textureshot;
        //获取地球主纹理,并将 截图赋值给它。
        EarthFrame.GetComponent<Renderer>().material.mainTexture = Textureshot;
        EarthA.GetComponent<Renderer>().material.mainTexture = Textureshot;

        PlaneA.SetActive(false);   //截图之后,将面片失效
        PlaneB.SetActive(false);
    }
}
//增添:声明EarthFrame——将面片四点坐标赋值给EarthFrame shader——将投影矩阵Matrix4x4赋值给EarthFrame shader——最后截图赋值给地球仪主主纹理
//实际上是先写的shader渲染方法,最后将截到的图片以此渲染方法赋值给模型

🟨 Area

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;   //因为屏幕自适度原因,使用了Canvas的Canvas Scaler组件(Canvas来自Unity的UI)

public class Area : MonoBehaviour {

    public Material Red_Mate;
    public Material Green_Mate;
    public Material Tran_Mate;

    public GameObject SuccessPlan_Image;   //储存识别识别图成功的“识别成功”图片
    public GameObject Earth;     //用于给延迟函数调用地球的截图脚本

    private CanvasScaler Cans;  //申请变量储存UI屏幕自适度(长&宽)的缩放组件
    private float X_Sc;   //申请浮点型变量储存实际的缩放比例(实际的/Unity使用的)

    private Vector2 TopLeft_UI, BottomLeft_UI, TopRight_UI, BottomRight_UI;   //记录扫描框坐标  二维!我们可以看到Vector2是淡蓝色,代表一个类,所以需要实例化。
    private Vector3 TopLeft_pl_w, BottomLeft_pl_w, TopRight_pl_w, BottomRight_pl_w;  //    //记录面片的世界坐标  三维!
    private Vector2 PlaneWH;       //获取面片二维方向上高和宽的长度!
    Vector2 TopLeft_pl_sc, BottomLeft_pl_sc, TopRight_pl_sc, BottomRight_pl_sc;      //记录面片的屏幕坐标

    private bool HasRe=false;  //储存是否识别成功bool函数

    void Start () {
        Cans = GameObject.Find("Canvas").gameObject.GetComponent<CanvasScaler>();  //获取屏幕自适度,GameObject指GameObject.Find("Canvas")这个物体,gameObject指GameObject这个物体。待测试去掉gameOgject是否可运行。
       
    }
	
	void Update () {
        X_Sc = Screen.width / Cans.referenceResolution.x;
        //为什么是除以x呢?因为在unity中,UI Scale Mode选的是Scale with Screen Scale,根据屏幕比例自动缩放,并且下方的
        //Screen Match Mode——Match Width or Height滑动条中,我们将滑动条拖到了最左侧的Width,所以是用屏幕宽度/自定义的宽度。


        //计算扫描框四个点位置
        TopLeft_UI = new Vector2(Screen.width - 400*X_Sc, Screen.height + 300 * X_Sc) * 0.5f;  //因为不确定屏幕分辨率大小,所以需先获取;记得f!
        BottomLeft_UI = new Vector2(Screen.width - 400 * X_Sc, Screen.height - 300 * X_Sc) * 0.5f;  //屏幕左下角(0,0),向右x轴,向上y轴
        TopRight_UI = new Vector2(Screen.width + 400 * X_Sc, Screen.height + 300 * X_Sc) * 0.5f;
        BottomRight_UI = new Vector2(Screen.width + 400 * X_Sc, Screen.height - 300 * X_Sc) * 0.5f;

        //我们需要的是面片在空间中的大小,但我们获取到的是面片的实际大小,它本身缩放了0.1倍,它父集缩放50倍,所以他在空间实际是扩大了5倍,所以要*5。
        PlaneWH = new Vector2(gameObject.GetComponent<MeshFilter>().mesh.bounds.size.x * 50*0.1f, gameObject.GetComponent<MeshFilter>().mesh.bounds.size.z * 50*0.0646f) *0.5f;   //空间中x、z是长宽,y是空间中的高度!

        //获得图片的四个点世界坐标(gameobject指的是面片,它的父集是图片)(position是图片中心的位置,所以需要加上面片x,y向长度)
        TopLeft_pl_w = gameObject.transform.parent.position + new Vector3(-PlaneWH.x, 0, PlaneWH.y);
        BottomLeft_pl_w = gameObject.transform.parent.position + new Vector3(-PlaneWH.x, 0, -PlaneWH.y);
        TopRight_pl_w = gameObject.transform.parent.position + new Vector3(PlaneWH.x, 0, PlaneWH.y);
        BottomRight_pl_w = gameObject.transform.parent.position + new Vector3(PlaneWH.x, 0, -PlaneWH.y);

        //获取面片的屏幕坐标
        TopLeft_pl_sc = Camera.main.WorldToScreenPoint(TopLeft_pl_w);   //Camera.main.WorldToScreenPoint()是封装好的函数,将三维向量转化为屏幕的二维向量。
        BottomLeft_pl_sc = Camera.main.WorldToScreenPoint(BottomLeft_pl_w);
        TopRight_pl_sc = Camera.main.WorldToScreenPoint(TopRight_pl_w);
        BottomRight_pl_sc = Camera.main.WorldToScreenPoint(BottomRight_pl_w);

        if (TopLeft_pl_sc.x>TopLeft_UI.x && TopLeft_pl_sc.y < TopLeft_UI.y && BottomLeft_pl_sc.x > BottomLeft_UI.x && BottomLeft_pl_sc.y > BottomLeft_UI.y&&TopRight_pl_sc.x<TopRight_UI.x&&TopRight_pl_sc.y<TopRight_UI.y&&BottomRight_pl_sc.x<BottomRight_UI.x&&BottomRight_pl_sc.y>BottomRight_UI.y)
        {
            if(HasRe==false)
            {
                gameObject.GetComponent<Renderer>().material = Green_Mate;   //当面片完全处于扫描框中时执行代码赋予材质
                StartCoroutine("SuccessUI");  //调用截图成功的延迟函数
                StartCoroutine("ScreenShot");  //调用截图的延迟函数
                HasRe = true;  //原先识别状态HasRe为false,现在识别成功,赋值true,且函数是每一帧都调用的,所以能和else中false一起供延迟函数判断此时识别图还是否位于扫描框中。
                //且这个if没有else,意味着HasRe为teur时每一帧调用后不会重复调用if中的调用命令。
            }           
        }
        else
        {
            gameObject.GetComponent<Renderer>().material = Red_Mate;
            HasRe = false;
        }
    }

    IEnumerator SuccessUI()
    {
        yield return new WaitForSeconds(0.5f);   //延迟0.5s
        SuccessPlan_Image.SetActive(true);    //原先的“识别成功”图片未关闭状态,现在激活。
        gameObject.GetComponent<Renderer>().material = Tran_Mate;  //将面片由绿色变为透明,除去截图时的影响。
    }

    IEnumerator ScreenShot()
    {
        yield return new WaitForSeconds(2.0f);
        if (HasRe==true)    //只有当识别状态为true时才执行,避免这2s发生移除扫描框操作。
        {
            gameObject.GetComponent<Renderer>().material = Tran_Mate;  //将面片由绿色变为透明,除去截图时的影响。
            Earth.GetComponent<ScreenShot>().ScreenShot_Button();    //调用地球ScreenShot脚本中的ScreenShot_Button()函数!
        }       
    }
}

🟩 本章注意事项:

我们在代码中确定了识别框UI的大小,所以一定不能在Unity中随意拖动识别框改变它的大小,否则计算不成功。

 

 

 

大家还有什么问题,欢迎在下方留言!


 

在这里插入图片描述


如果你有 技术的问题  项目开发

都可以加下方联系方式

和我聊一聊你的故事🧡

 

 

本文含有隐藏内容,请 开通VIP 后查看