Unity + ZXing + 屏幕旋转自动自适应 + 自定义扫码界面

由于使用 ZXing 在unity进行二维码识别功能的人较多,这边我也试着使用了一下ZXing。发现如下几个问题:

  • ZXing仅仅只提供了二维码和条形码等的解码,并没有提供在unity中的预制件(也就是prefab)。这样就会导致很多第一次使用zxing的盆友,需要自己写一个自适应屏幕大小和根据屏幕旋转自动缩放的摄像机。
  • 需要自行编写一个能够自适应屏幕大小和根据屏幕旋转自动缩放的摄像机,以保证渲染出来的WebCamTexture(就是摄像头获得过来的图像)不会拉伸。
  • Zxing的解码操作并不能放在update里进行操作,不然会计算量过大导致严重卡顿甚至卡死。
  • 也是由于zxing的解码操作计算容易导致卡顿,从摄像头获得过来的texture(也就是WebCamTexture),不能使用高分辨率,不然也会卡死。

因此,我这边放出我的解决方案,供广大unity码农使用。下载包里面提供了预制件和使用示例。至于自定义扫码界面,因为我这边是unity的实现方式,并不是android或者ios等其他平台的原生扫码实现方式,所以直接在我的prefab的基础上加界面就可以了。

下载地址:ScanQRCoder.unitypackage

主要源码如下:

using UnityEngine;
using System.Collections;
using ZXing;
using System;
using System.Runtime.InteropServices;

public class ScanQRCoder : MonoBehaviour
{
    public static event Action<string> OnDecoderMessage;

    private Camera renderCam;
    private MeshRenderer meshRender;
    private WebCamTexture webCamTexture;
    private DeviceOrientation lastDeviceOrientation;

    public void ReInitRect()
    {
        StopCoroutine(DecodeQR());
        webCamTexture.Stop();
        Start();
    }

    private void Start()
    {
        lastDeviceOrientation = Input.deviceOrientation;
        Init();
        meshRender.material.mainTexture = webCamTexture;
        webCamTexture.Play();
        StartCoroutine(DecodeQR());
    }

    private void Init()
    {
        renderCam = GetComponentInChildren();

        int webCamDeviceIndex = -1;
        for (int i = 0; i < WebCamTexture.devices.Length; i++)
        {
            if (!WebCamTexture.devices[i].isFrontFacing)
            {
                webCamDeviceIndex = i;
                break;
            }
        }
        if (webCamDeviceIndex == -1 && WebCamTexture.devices.Length > 0) webCamDeviceIndex = 0;

        Vector3[] camfov = GetCameraFovPositionByDistance(renderCam, renderCam.transform.localPosition.y);
        float w = Vector3.Distance(camfov[1], camfov[0]);
        float h = Vector3.Distance(camfov[2], camfov[0]);

        if(Screen.width >= Screen.height)
        {
            h = w;
        }
        else
        {
            w = h;
        }

        meshRender = GetComponentInChildren();
        meshRender.transform.localScale = new Vector3(w / 10, 1, h / 10);

        webCamTexture = new WebCamTexture(WebCamTexture.devices[webCamDeviceIndex].name, 200, 200);//这里的第二第三个参数可以根据机子的性能而调高,也就是分辨率越高

    }

    void onDecoderMessage(string code)
    {
        if (OnDecoderMessage != null)
        {
            OnDecoderMessage(code);
            Debug.Log("code:" + code);
        }
    }
    private IEnumerator DecodeQR()
    {
        yield return new WaitForSeconds(1f);
        try
        {
            var barcodeReader = new BarcodeReader { AutoRotate = true, TryHarder = true };
            Result result = barcodeReader.Decode(webCamTexture.GetPixels32(), webCamTexture.width, webCamTexture.height);
            //Debug.Log("QR Code w h: " + webCamTexture.width + " " + webCamTexture.height);
            if (result != null)
            {
                onDecoderMessage(result.Text);
                //Debug.Log("QR Code: " + result.Text);
            }
        }
        catch { }
        if (Input.deviceOrientation != lastDeviceOrientation)
        {
            ReInitRect();
        }
        else
        {
            if (webCamTexture.isPlaying)
                StartCoroutine(DecodeQR());
        }
    }

    private static byte[] ColorArrayToByteArray(Color[] colors)
    {
        if (colors == null || colors.Length == 0)
            return null;

        int lengthOfColor32 = Marshal.SizeOf(typeof(Color));
        int length = lengthOfColor32 * colors.Length;
        byte[] bytes = new byte[length];

        GCHandle handle = default(GCHandle);
        try
        {
            handle = GCHandle.Alloc(colors, GCHandleType.Pinned);
            IntPtr ptr = handle.AddrOfPinnedObject();
            Marshal.Copy(ptr, bytes, 0, length);
        }
        finally
        {
            if (handle != default(GCHandle))
                handle.Free();
        }

        return bytes;
    }


    /// 
    /// 获取指定距离下相机视口四个角的坐标
    /// 
    private Vector3[] GetCameraFovPositionByDistance(Camera cam, float distance)
    {
        Vector3[] corners = new Vector3[4];
        Array.Resize(ref corners, 4);
        // Top left
        corners[0] = cam.ViewportToWorldPoint(new Vector3(0, 1, distance));
        // Top right
        corners[1] = cam.ViewportToWorldPoint(new Vector3(1, 1, distance));
        // Bottom left
        corners[2] = cam.ViewportToWorldPoint(new Vector3(0, 0, distance));
        // Bottom right
        corners[3] = cam.ViewportToWorldPoint(new Vector3(1, 0, distance));
        return corners;
    }

}

使用示例:

using UnityEngine;
using System.Collections;

public class ScanQRCoderDemo : MonoBehaviour
{
    public ScanQRCoder scanner;

    private void OnEnable()
    {
        ScanQRCoder.OnDecoderMessage += OnDecoderMessage;
    }

    private void OnDisable()
    {
        ScanQRCoder.OnDecoderMessage -= OnDecoderMessage;
    }

    public void OnDecoderMessage(string msg)
    {
        Debug.Log("二维码信息:" + msg);
    }
}

2017.04.05更新

现发现了新的实现方式,帧率和分辨率都非常满意!强烈推荐使用。
里面有示例场景。

下载地址:ScanQRCoder2


【个人广告】
希望大家可以支持我的个人微信号“小游戏情报局

Unity + ZXing + 屏幕旋转自动自适应 + 自定义扫码界面_第1张图片

你可能感兴趣的:(unity)