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<Camera>();

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


    /// <summary>
    /// 获取指定距离下相机视口四个角的坐标
    /// </summary>
    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


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

这里写图片描述

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

耳朵里有只风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值