安卓上使用ble,并和unity通信

 首先是java代码

package com.miyan.lib4unity;

import android.Manifest;
import android.app.Activity;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ProviderInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.widget.BaseAdapter;
import android.widget.Toast;
import android.content.pm.PackageManager;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.PortUnreachableException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.UUID;
import java.util.logging.LogRecord;
import java.io.UnsupportedEncodingException;

public class Link2Unity {

    private Activity unityActivity;
    public Activity getUnityActivity(){

        if (unityActivity == null){
            try {
                Class<?> classType = Class.forName("com.unity3d.player.UnityPlayer");
                unityActivity = (Activity)classType.getDeclaredField("currentActivity").get(classType);

            }
            catch (ClassNotFoundException e){}
            catch (IllegalAccessException e){}
            catch (NoSuchFieldException e){}
        }
        return unityActivity;
    }

    boolean callUnity(String gameobjectName, String functionName, String args){
        try {
            Class<?> classType = Class.forName("com.unity3d.player.UnityPlayer");
            Method method = classType.getMethod("UnitySendMessage", String.class, String.class, String.class);
            method.invoke(classType, gameobjectName, functionName, args);
            return true;
        }
        catch (ClassNotFoundException e){}
        catch (NoSuchMethodException e){}
        catch (IllegalAccessException e){}
        catch (InvocationTargetException e){}

        return false;
    }

    public boolean testConnection(String msg){

        Toast.makeText(getUnityActivity(), msg, Toast.LENGTH_LONG);

        callUnity("Link2Android", "GetMsgFromAndroid", "hello");
        return true;
    }

/*
    private String[] permissions = new String[]{
            Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.CAMERA,
            Manifest.permission.BLUETOOTH,
            Manifest.permission.BLUETOOTH_ADMIN
    };
    public void requestPower(){

        callUnity("Link2Android", "GetMsgFromAndroid", String.valueOf(Build.VERSION.SDK_INT));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            callUnity("Link2Android", "GetMsgFromAndroid", String.valueOf(Build.VERSION_CODES.M));
            for (int i = 0; i < permissions.length; i++) {
                callUnity("Link2Android", "GetMsgFromAndroid", permissions[i]);

                if (ActivityCompat.checkSelfPermission(getUnityActivity(), permissions[i]) != PackageManager.PERMISSION_GRANTED){
                    ActivityCompat.requestPermissions(getUnityActivity(), permissions, 1);
                    callUnity("Link2Android", "GetMsgFromAndroid", permissions[i]);
                    //Toast.makeText(getUnityActivity(), "request", Toast.LENGTH_SHORT).show();

                    break;
                }
            }
        }
    }
*/

    //蓝牙部分
    private boolean isAppStopped = false;
    public void setAppStopped(boolean msg){
        isAppStopped = msg;
        callUnity("Link2Android", "GetWarningsFromAndroid", "is stop receiving: " + isAppStopped);
    }
    private static long scanPeriod = 5000;//10s后停止扫描
    public void setScanPeriod(int value){
        scanPeriod = value * 1000;
        callUnity("Link2Android", "GetWarningsFromAndroid", "scan period: " + scanPeriod);
    }
    private boolean scanning;
    private ArrayList<BluetoothDevice> bluetoothDevices = new ArrayList<BluetoothDevice>();

    private BluetoothAdapter bluetoothAdapter;
    public BluetoothAdapter getBluetoothAdapter(){

        if (bluetoothAdapter == null){
            final BluetoothManager bluetoothManager = (BluetoothManager)getUnityActivity().getSystemService(Context.BLUETOOTH_SERVICE);
            bluetoothAdapter = bluetoothManager.getAdapter();
        }

        return bluetoothAdapter;
    }

    private PackageManager packageManager;
    public PackageManager getPackageManager(){

        if (packageManager == null)
            packageManager = getUnityActivity().getPackageManager();

        return packageManager;
    }

    public boolean checkBleSupport(){
        //callUnity("Link2Android", "GetWarningsFromAndroid", "check ble support");
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){
            return false;
        }

        if (getBluetoothAdapter() == null){
            return false;
        }
        return true;
    }

    public boolean askToOpenBluetooth(){
        //callUnity("Link2Android", "GetWarningsFromAndroid", "ask to open ble");
        if (!getBluetoothAdapter().isEnabled()){

            getBluetoothAdapter().enable();
        }
        callUnity("Link2Android", "GetWarningsFromAndroid", "ble is enabled: " + String.valueOf(getBluetoothAdapter().isEnabled()));
        return getBluetoothAdapter().isEnabled();
    }

    public void scanLeDevice(final boolean enable){
        //callUnity("Link2Android", "GetWarningsFromAndroid", "begin scan");
        //bluetoothDevices.clear();
        Handler handler = new Handler();

        if (enable){
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    scanning = false;
                    callUnity("Link2Android", "GetWarningsFromAndroid", "time out stop scan");
                    try{
                        getBluetoothAdapter().getBluetoothLeScanner().stopScan(scanCallback);
                    }
                    catch (Exception e){
                        callUnity("Link2Android", "GetWarningsFromAndroid",
                                "outer scan error: " + e.getMessage() + "\n" + e.getLocalizedMessage());
                    }
                }
            }, scanPeriod);

            bluetoothDevices.clear();
            scanning = true;
            callUnity("Link2Android", "GetWarningsFromAndroid", "start scan");
            getBluetoothAdapter().getBluetoothLeScanner().startScan(scanCallback);
        }
        else {
            scanning = false;
            getBluetoothAdapter().getBluetoothLeScanner().stopScan(scanCallback);
            callUnity("Link2Android", "GetWarningsFromAndroid", "order stop scan");
        }
    }

    public void stopScan(){
        scanLeDevice(false);
    }

    private String bleNameHeader = "Test";
    public void setBleNameHeader(String header){
        bleNameHeader = header;
        callUnity("Link2Android", "GetWarningsFromAndroid", "scan filter: " + bleNameHeader);
    }

    private ScanCallback scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);

            if (!bluetoothDevices.contains(result.getDevice()) && result.getDevice().getName() != null){
                callUnity("Link2Android", "GetWarningsFromAndroid", result.getDevice().getName() + " / " + bleNameHeader);

                if (result.getDevice().getName().contains(bleNameHeader)){

                    StringBuilder sb = new StringBuilder();
                    sb.append(result.getDevice().getName());
                    sb.append("&");
                    sb.append(result.getDevice().getAddress());

                    callUnity("Link2Android", "GetDevice",sb.toString());

                    bluetoothDevices.add(result.getDevice());
                }
            }
        }
    };

    private BluetoothDevice curDevice;
    private BluetoothGatt bluetoothGatt;

    public void connectBleDevice(String deviceName){
        callUnity("Link2Android", "GetWarningsFromAndroid", "order name:  " + deviceName);
        for (int i = 0; i < bluetoothDevices.size(); i++) {

            //callUnity("Link2Android", "GetWarningsFromAndroid",
            //        "is equal: " + String.valueOf(bluetoothDevices.get(i).getName().compareTo(deviceName) == 0) );

            if (bluetoothDevices.get(i).getName().compareTo(deviceName) == 0){

                curDevice = bluetoothDevices.get(i );
                //callUnity("Link2Android", "GetWarningsFromAndroid", "connect name:  " + curDevice.getName());

                bluetoothGatt = curDevice.connectGatt(getUnityActivity(), true, bluetoothGattCallback);
                  if (bluetoothGatt.connect()){
                       //bluetoothGatt.discoverServices();
                  }
            }


        }
    }

    private BluetoothGattService bluetoothGattService;
    private BluetoothGattCharacteristic writeCharacter;
    private BluetoothGattCharacteristic readCharacter;
    //public static String Service_uuid = "0000ffe0-0000-1000-8000-00805f9b34fb";
    public static String Service_uuid = "0003CDD0-0000-1000-8000-00805F9B0131";
    //public static String Characteristic_uuid_TX = "0000ffe1-0000-1000-8000-00805f9b34fb";
    public static String Characteristic_uuid_TX = "0003CDD1-0000-1000-8000-00805F9B0131";
    //public static String Characteristic_uuid_RX = "0000ffe3-0000-1000-8000-00805f9b34fb";
    public static String Characteristic_uuid_RX = "0003CDD2-0000-1000-8000-00805F9B0131";

    public void setServiceUUID(String uuid){
        Service_uuid = uuid;
    }

    public void setChracteristicWriteUUID(String uuid){
        Characteristic_uuid_TX = uuid;
    }

    public void setCharacteristicReadUUID(String uuid){
        Characteristic_uuid_RX = uuid;
    }

    private BluetoothGattCallback bluetoothGattCallback= new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            if (status == BluetoothGatt.GATT)
                callUnity("Link2Android", "GetWarningsFromAndroid", "gatt success");

            if (newState == BluetoothProfile.STATE_CONNECTED){
                callUnity("Link2Android", "GetWarningsFromAndroid", "connected");
                callUnity("Link2Android", "IsConnectSuccess", "true");

                bluetoothGatt.discoverServices();
            }
            else if (newState == BluetoothProfile.STATE_DISCONNECTED){
                callUnity("Link2Android", "GetWarningsFromAndroid", "disconnected");
                callUnity("Link2Android", "IsConnectSuccess", "false");
                disconnectBle("android");
            }

        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            if (status == BluetoothGatt.GATT_SUCCESS){

                //callUnity("Link2Android", "GetWarningsFromAndroid", "service uuid: " + Service_uuid);
                //callUnity("Link2Android", "GetWarningsFromAndroid", "character read uuid: " + Characteristic_uuid_TX);
                //callUnity("Link2Android", "GetWarningsFromAndroid", "character write uuid: " + Characteristic_uuid_RX);

                bluetoothGattService = bluetoothGatt.getService(UUID.fromString(Service_uuid));
                List<BluetoothGattCharacteristic> characteristics = bluetoothGattService.getCharacteristics();
//                if (characteristics != null){
//
//                    for (int i = 0; i < characteristics.size(); i++) {
//                        callUnity("Link2Android", "GetWarningsFromAndroid", "get uuid: " + characteristics.get(i).getUuid().toString());
//                    }
//                }

                writeCharacter = bluetoothGattService.getCharacteristic(UUID.fromString(Characteristic_uuid_RX));
                //boolean isSuccess2 = bluetoothGatt.setCharacteristicNotification(readCharcter, true);
                /*
                List<BluetoothGattDescriptor> descriptors2 = readCharcter.getDescriptors();
                for (BluetoothGattDescriptor descriptor : descriptors2){
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    bluetoothGatt.writeDescriptor(descriptor);
                }
                */

                readCharacter = bluetoothGattService.getCharacteristic(UUID.fromString(Characteristic_uuid_TX));
                boolean isSucces = bluetoothGatt.setCharacteristicNotification(readCharacter, true);
                List<BluetoothGattDescriptor> descriptors = readCharacter.getDescriptors();
                for (BluetoothGattDescriptor descriptor : descriptors){
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    bluetoothGatt.writeDescriptor(descriptor);
                }

            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
            //callUnity("Link2Android", "GetWarningsFromAndroid", "read:  "+characteristic.getStringValue(0));
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            //callUnity("Link2Android", "GetWarningsFromAndroid", "write:  "+ Characteristic_uuid_RX + " "+characteristic.getStringValue(0));
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            //callUnity("Link2Android", "GetWarningsFromAndroid", "change:  "+ characteristic.getUuid());

            if (isAppStopped == false){
                byte[] buffer = characteristic.getValue();
                checkBuffer(buffer);
            }
        }
    };

    private String bytesToString(byte[] buffer){
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < buffer.length; i++) {
            sb.append(buffer[i] + " ");
        }
        return sb.toString();
    }

    private int mark;
    private LinkedList<Byte> list = new LinkedList<Byte>();
    private byte space = 0x20;
    private void checkBuffer(byte[] buffer){
        for (int i = 0; i < buffer.length; i++) {
            if (mark == 0){
                if (buffer[i] == 0x24){
                    mark = 1;
                    list.clear();
                    list.add(buffer[i]);
                }
            }
            else if (mark == 1){
                list.add(buffer[i]);

                if (buffer[i] == 0x0D){
                    mark = 0;
                    //这里将所有字节处理后发给unity
                    if (isAppStopped == false)
                        callUnity("Link2Android", "GetMsgFromAndroid", list.toString());
                }
                else{
                    //这里都是有效数据
                    //if (buffer[i] != 0x0d)
                    //    list.add(buffer[i]);
                    //else
                    //    list.add(space);
                }
            }
        }
    }

    public void disconnectBle(String caller){
        callUnity("Link2Android", "GetWarningsFromAndroid", "get " + caller + " disconnect order");

        try{
            if (curDevice == null || bluetoothGatt == null)
                return;

            callUnity("Link2Android", "GetWarningsFromAndroid", "disconnect: " + bluetoothGatt.getDevice().getName());
            bluetoothGatt.disconnect();
            bluetoothGatt.close();
            bluetoothGatt = null;
        }
        catch (Exception e){
            callUnity("Link2Android", "GetWarningsFromAndroid", "disconnect error: " + e.getMessage());
        }

    }

    public void writeToBle(String msg){
        //readCharacter.setValue(msg);
        //bluetoothGatt.writeCharacteristic(readCharacter);

        writeCharacter.setValue(msg);
        bluetoothGatt.writeCharacteristic(writeCharacter);
    }

    //private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
    //    @Override
    //    public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
    //        callUnity("Link2Android", "GetWarningsFromAndroid", device.getName());
    //    }
    //};
}

然后是unity代码

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design;
using UnityEngine;

public class Link2Android : MonoBehaviour
{
    public static Link2Android instance;
    private void Awake()
    {
        instance = this;
    }

    private AndroidJavaObject jo;
    private AnalysisRecData analysisRecData = new AnalysisRecData();

    private Action<bool> actionConnectCallback;
    private Action<BleDevice> actionScanCallback;

    public BleDevice curDevice;
    public Queue<string> queueRecData = new Queue<string>();

    private readonly string bleFilter = "Test";
    public float scanPeriod { get; } = 8;

    void Start()
    {
        jo = new AndroidJavaObject("com.miyan.lib4unity.Link2Unity");
        bool isSupport = jo.Call<bool>("checkBleSupport");
        if (isSupport == false)
        {
            UIPanelMgr.instance.ToPanelQuit(LanguageMgr.instance.localizeLanguage.deviceDontSupportBle);
        }
        bool isOpen = jo.Call<bool>("askToOpenBluetooth");
        if (isOpen == false)
        {
            UIPanelMgr.instance.ToPanelQuit(LanguageMgr.instance.localizeLanguage.failedToOpenBle);
        }

        jo.Call("setBleNameHeader", bleFilter);//过滤头
        jo.Call("setScanPeriod", (int)scanPeriod);//设置扫描超时
        //jo.Call("setServiceUUID", "0000ff00-0000-1000-8000-00805f9b34fb");
        //jo.Call("setChracteristicWriteUUID", "0000ff02-0000-1000-8000-00805f9b34fb");
        //jo.Call("setCharacteristicReadUUID", "0000ff02-0000-1000-8000-00805f9b34fb");
    }



    public void ScanBleDevice(Action<BleDevice> scanCallback)
    {
        actionScanCallback = scanCallback;
        jo.Call("scanLeDevice", true);
    }

    public void GetDevice(string device)
    {
        UIPanelMgr.instance.DisplayWarnings("get device: " + device);
        try
        {
            string[] deviceMsg = device.Split('&');
            if (deviceMsg[0].StartsWith(bleFilter))
            {
                BleDevice cbs = new BleDevice
                {
                    nameBle = deviceMsg[0],
                    mac = deviceMsg[1]
                };
                actionScanCallback?.Invoke(cbs);
            }
            else
            {
                return;
            }
        }
        catch (Exception e)
        {
            print(e.Message);
        }
    }

    public void ConnectBleDevice(BleDevice device, Action<bool> connectCallback)
    {
        actionConnectCallback = connectCallback;
        StopAllCoroutines();
        StartCoroutine(ConnectBleDevice(device));
    }

    IEnumerator ConnectBleDevice(BleDevice device)
    {
        jo.Call("stopScan");
        yield return new WaitForSeconds(0.15f);
        jo.Call("disconnectBle", "before connect new");
        yield return new WaitForSeconds(0.3f);
        jo.Call("connectBleDevice", device.nameBle);

        curDevice = device;
        yield return new WaitForSeconds(10);//10s连接超时
        actionConnectCallback?.Invoke(false);
        jo.Call("disconnectBle", "timer out");
    }

    public void DisconnectBle()
    {
        if (curDevice != null)
        {
            jo.Call("disconnectBle", "quit");
        }
    }

    public void IsConnectSuccess(string torf)
    {
        bool isSuccess = bool.Parse(torf);
        UIPanelMgr.instance.SetOtherDeviceState(curDevice.nameBle);

        actionConnectCallback?.Invoke(isSuccess);
        StopAllCoroutines();
        //ui的变化
        UIPanelMgr.instance.RefreshBleState(isSuccess);
    }

    public void GetMsgFromAndroid(string msg)
    {
        //UIPanelMgr.instance.DisplayWarnings("get msg: " + msg);
        //将数据放入队列
        queueRecData.Enqueue(msg);
    }

    public void GetWarningsFromAndroid(string warning)
    {
        UIPanelMgr.instance.DisplayWarnings("android warning: " + warning);
    }

    private void OnApplicationPause(bool pause)
    {
        if (jo != null)
            jo.Call("setAppStopped", pause);
    }

    private void Update()
    {
        if (queueRecData.Count > 0)
        {
            string msg = "";
            for (int i = 0; i < queueRecData.Count; i++)
            {
                msg = queueRecData.Dequeue();
            }
            curDevice = analysisRecData.DealBleData(msg, curDevice);
            //刷新ui
            UIPanelMgr.instance.RefreshPanelDevice(curDevice);
        }
    }
}

 

原文地址:https://blog.csdn.net/qq_39247426/article/details/115866435

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


这篇文章主要介绍了Unity游戏开发中外观模式是什么意思,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家...
这篇文章主要介绍Unity中地面检测方案的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1.普通射线在角色坐标(一般是脚底)...
这篇文章主要介绍了Unity游戏开发中如何消除不想要的黄色警告,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带...
这篇文章主要介绍了Unity中有多少种渲染队列,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解
这篇文章主要介绍Unity中如何实现Texture,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!了解Texture2D 如上图,Texture2D是一张
小编给大家分享一下Unity中DOTS要实现的特点有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让...
这篇文章给大家分享的是有关unity中如何实现UGUI遮罩流光特效的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。下面是核心shader:Sh...
这篇文章主要为大家展示了“Unity中如何实现3D坐标转换UGUI坐标”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下...
这篇文章主要介绍了Unity游戏开发中设计模式的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家...
这篇文章主要介绍了Unity中如何实现仿真丝袜渲染,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了...
这篇文章给大家分享的是有关Unity插件OVRLipSync有什么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。项目需要接入对话口型动...
这篇文章主要介绍了Unity性能优化之DrawCall的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家...
这篇文章给大家分享的是有关Unity给力插件之Final IK怎么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。这插件有什么用:一般游...
这篇文章给大家分享的是有关Unity中如何内嵌网页插件UniWebView的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、常见Unity中内...
小编给大家分享一下Unity如何做流体物理的几个轮子,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让...
小编给大家分享一下Unity中Lod和Occlusion Culling的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收...
这篇文章将为大家详细讲解有关Unity中LineRenderer与TrailRenderer有什么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获...
这篇文章主要介绍了Unity中coroutine问题的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起...
这篇文章将为大家详细讲解有关unity中spine怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。骨骼动画首先我们来看到...
这篇文章主要为大家展示了“Unity Shader后处理中如何实现简单均值模糊”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学...