ar导航unity3d

2018-12-07 08:32:21 baidu_33643757 阅读数 1101
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

大概操作步骤按笔记006执行。

1.背景

环境:

OS:Windows10
Android Studio3.2
Unity 2018.1.2f1

思路

Unity端:主调方,UI
Android端:底层接口,打包aar方式

2.步骤

2.1 Android Studio基础配置

2.1.1 新建工程、导入Unity接口包。

对比百度demo,导入百度lib,如图:
在这里插入图片描述
模仿demo,新建Activity,如下:
在这里插入图片描述

2.1.2 配置app的build.gradle

//apply plugin: 'com.android.application'
apply plugin: 'com.android.library'

android {
    compileSdkVersion 28

    // Add
    sourceSets {
        main {
            //Path to your source code
            main {
                jniLibs.srcDir('libs')
                jni.srcDirs = []
            }
            java {
                srcDir 'src/main/java'
            }
        }
    }

    defaultConfig {
//        applicationId "com.polin.baiduarmapsdk"
        minSdkVersion 15
        targetSdkVersion 28
//        versionCode 1
//        versionName "1.0"
//        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    // Add
    lintOptions {
        abortOnError false
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation files('libs/android-support-v4.jar')
    implementation files('libs/BaiduLBS_Android.jar')
    implementation files('libs/classes.jar')
    implementation files('libs/gson-2.2.4.jar')
    implementation files('libs/org.apache.http.legacy.jar')
}

// Add Backup
//task to delete the old jar
task deleteOldJar(type: Delete) {
    delete 'release/BaiduMapKit.jar'
}

//task to export contents as jar
task exportJar(type: Copy) {
    from('build/intermediates/packaged-classes/release/')
    into('release/')
    include('classes.jar')
    ///Rename the jar
    rename('classes.jar', 'BaiduMapKit.jar')
}
exportJar.dependsOn(deleteOldJar, build)

2.1.3 配置AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.polin.baiduarmapsdk">

    <!--访问相机权限-->
    <uses-permission android:name="android.permission.CAMERA"/>
    <!--访问闪光灯-->
    <uses-permission android:name="android.permission.FLASHLIGHT"/>
    <!-- 这个权限用于进行网络定位-->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
    <!-- 这个权限用于访问GPS定位-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
    <!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位-->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
    <!-- 获取运营商信息,用于支持提供运营商信息相关的接口-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
    <!-- 这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
    <!-- 用于读取手机当前的状态-->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
    <!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    <!-- 访问网络,网络定位需要上网-->
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:label="@string/app_name">

        <!-- 开发密钥 -->
        <meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="****">
        </meta-data>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data
                android:name="unityplayer.UnityActivity"
                android:value="true" />
        </activity>
        <activity android:name=".ArActivity" >
            <meta-data
                android:name="unityplayer.UnityActivity"
                android:value="true" />
        </activity>
        <activity android:name=".BuildingArActivity" >
            <meta-data
                android:name="unityplayer.UnityActivity"
                android:value="true" />
        </activity>
        <activity android:name=".SceneryArActivity">
            <meta-data
                android:name="unityplayer.UnityActivity"
                android:value="true" />
        </activity>
        <!-- 接入百度地图定位SDK -->
        <service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote"></service>
    </application>

</manifest>

2.1.4 尝试打包成aar

在这里插入图片描述
在该目录下找到aar包,连同AndroidManifest.xml一起,即是我们将放入Unity中的Android资源。
在这里插入图片描述
至此,Android Studio端的配置完成。

2.2 Unity端基础配置

2.2.1 新建工程

2.2.2 填入Android工程包名

2.3 编写与测试

2.3.1 尝试在AndroidManifest.xml中添加百度Key,并获取在其中注册获取手机权限

将MainActivity作为入口,编写工具类LocSdkClient、PermissionsChecker、UntiyClient。
LocSdkClient:

public class LocSdkClient {
    /**
     *
     * 百度定位sdk 坐标获取工具类
     */
    private static LocSdkClient mInstance = null;
    public LocationClient mLocationClient = null;

    private LocSdkClient(Context context) {

        mLocationClient = new LocationClient(context.getApplicationContext());
        // 声明LocationClient类
        // mLocationClient.registerLocationListener(myListener);
        // 注册监听函数

        LocationClientOption option = new LocationClientOption();

        option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
        // 可选,设置定位模式,默认高精度
        // LocationMode.Hight_Accuracy:高精度;
        // LocationMode. Battery_Saving:低功耗;
        // LocationMode. Device_Sensors:仅使用设备;

        option.setCoorType("bd09");
        // 可选,设置返回经纬度坐标类型,默认gcj02
        // gcj02:国测局坐标;
        // bd09ll:百度经纬度坐标;
        // bd09:百度墨卡托坐标;
        // 海外地区定位,无需设置坐标类型,统一返回wgs84类型坐标

        option.setScanSpan(2000);
        // 可选,设置发起定位请求的间隔,int类型,单位ms
        // 如果设置为0,则代表单次定位,即仅定位一次,默认为0
        // 如果设置非0,需设置1000ms以上才有效

        option.setOpenGps(true);
        // 可选,设置是否使用gps,默认false
        // 使用高精度和仅用设备两种定位模式的,参数必须设置为true

        option.setLocationNotify(true);
        // 可选,设置是否当GPS有效时按照1S/1次频率输出GPS结果,默认false

        option.setIgnoreKillProcess(false);
        // 可选,定位SDK内部是一个service,并放到了独立进程。
        // 设置是否在stop的时候杀死这个进程,默认(建议)不杀死,即setIgnoreKillProcess(true)

        option.SetIgnoreCacheException(false);
        // 可选,设置是否收集Crash信息,默认收集,即参数为false

        option.setWifiCacheTimeOut(5 * 60 * 1000);
        // 可选,7.2版本新增能力
        // 如果设置了该接口,首次启动定位时,会先判断当前WiFi是否超出有效期,若超出有效期,会先重新扫描WiFi,然后定位

        option.setEnableSimulateGps(false);
        // 可选,设置是否需要过滤GPS仿真结果,默认需要,即参数为false

        mLocationClient.setLocOption(option);
        // mLocationClient为第二步初始化过的LocationClient对象
        // 需将配置好的LocationClientOption对象,通过setLocOption方法传递给LocationClient对象使用
        // 更多LocationClientOption的配置,请参照类参考中LocationClientOption类的详细说明

        mLocationClient.start();
        // mLocationClient为第二步初始化过的LocationClient对象
        // 调用LocationClient的start()方法,便可发起定位请求e
    }

    public LocationClient getLocationStart() {
        return mLocationClient;
    }

    public static LocSdkClient getInstance(Context context) {
        if (mInstance == null) {
            Class var0 = LocSdkClient.class;
            synchronized (LocSdkClient.class) {
                if (mInstance == null) {
                    mInstance = new LocSdkClient(context);
                }
            }
        }
        return mInstance;
    }
}

PermissionsChecker:

/**
 * 权限申请
 */

public class PermissionsChecker {

    private final Context mContext;

    public PermissionsChecker(Context context) {
        mContext = context.getApplicationContext();
    }
    // 判断权限集合
    public boolean lacksPermissions() {
        String[] permissions = new String[] {
//                Manifest.permission.FLASHLIGHT,
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.CAMERA,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.INTERNET,
                Manifest.permission.READ_PHONE_STATE,
                Manifest.permission.CHANGE_WIFI_STATE,
                Manifest.permission.ACCESS_NETWORK_STATE,
                Manifest.permission.ACCESS_WIFI_STATE
        };
        for (String permission : permissions) {
            if (lacksPermission(permission)) {
                return true;
            }
        }
        return false;
    }
    // 判断是否缺少权限
    private boolean lacksPermission(String permission) {
        return ContextCompat.checkSelfPermission(mContext, permission) == PackageManager.PERMISSION_DENIED;
    }
}

UntiyClient:

public class UntiyClient extends UnityPlayerActivity {
    /**
     * unity项目启动时的的上下文
     */
    private static Activity _unityActivity;
    /**
     * 获取unity项目的上下文
     * @return
     */
    public static Activity GetActivity(){
        if(null == _unityActivity) {
            try {
                Class<?> classtype = Class.forName("com.unity3d.player.UnityPlayer");
                Activity activity = (Activity) classtype.getDeclaredField("currentActivity").get(classtype);
                _unityActivity = activity;
            } catch (ClassNotFoundException e) {

            } catch (IllegalAccessException e) {

            } catch (NoSuchFieldException e) {

            }
        }
        return _unityActivity;
    }

    /**
     * 调用Unity的方法
     * @param gameObjectName    调用的GameObject的名称
     * @param functionName      方法名
     * @param args              参数
     * @return                  调用是否成功
     */
    public static 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;
    }
}

MainActivity:

public class MainActivity extends UnityPlayerActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        setContentView(R.layout.activity_main);
    }

        // MainActivity初始化函数
    public void init(){
        // 判断权限
        PermissionsChecker permissionsChecker = new PermissionsChecker(GetActivity());
        if (permissionsChecker.lacksPermissions()) {
            Toast.makeText(GetActivity(), "缺少权限,请开启权限!", Toast.LENGTH_LONG).show();
            openSetting();
        }
        else{
            Toast.makeText(GetActivity(), "权限已开启!", Toast.LENGTH_LONG).show();
        }
    }

    /**
     * 打开设置权限界面
     *
     * @param
     */
    public void openSetting() {
        Intent intent = new Intent();
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        Uri uri = Uri.fromParts("package", getPackageName(), null);
        intent.setData(uri);
        startActivity(intent);
    }
}

在Unity中创建MainActivity初始化测试按钮:
在这里插入图片描述

导出aar包与AndroidManifest.xml到Unity的Plugins/Android中,编写Unity主调脚本:

public class UIControl : MonoBehaviour {

	// Use this for initialization
	void Start () {
        transform.Find("MainActivityInit").GetComponent<Button>().onClick.AddListener(InitMainActivity);       
    }

    public void InitMainActivity()
    {
        AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject currentActivity = unity.GetStatic<AndroidJavaObject>("currentActivity");
        currentActivity.Call("init");
    }
}

打包apk
报错1.
在这里插入图片描述
解决方案1.
把Unity打包方式从Gradle改为Internal。
报错2.
在这里插入图片描述
解决方案2.
报错信息显示:aar包中的Unity接口包classes.jar重复了,那就删掉。
用压缩软件打开aar包,找到libs目录下的classes.jar,删除此包。
在这里插入图片描述
报错3.
在这里插入图片描述
解决方案3.
这些资源找不到,再次打开aar包,找到./res/values/values.xml并进行修改:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorAccent">#D81B60</color>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <string name="app_name">BaiduArMapSDK</string>
    <style name="AppTheme" parent="android:Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="android:colorPrimary">@color/colorPrimary</item>
        <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="android:colorAccent">@color/colorAccent</item>
    </style>
</resources>

报错4.
在这里插入图片描述
解决方案4.
依然找不到这个主题,我们就不用它了

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorAccent">#D81B60</color>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <string name="app_name">BaiduArMapSDK</string>
    <style name="AppTheme" parent="android:Theme.Light">
        <!-- Customize your theme here. -->
        <item name="android:colorPrimary">@color/colorPrimary</item>
        <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="android:colorAccent">@color/colorAccent</item>
    </style>
</resources>

报错5.
在这里插入图片描述
解决方案5.
layout.xml报错,我们去aar包中找到它:./res/layout/activity_main.xml,删掉这些:
在这里插入图片描述
保存,在Unity中reimport,继续打包。
报错6.
在这里插入图片描述
解决方案6.
LocSdkClient类名不一致,删掉此类,重新建类。
依然报错,clean project,rebuild project,重新导包。
此步骤成功。_
在这里插入图片描述
经测试,aar包与AndroidManifest.xml缺一不可!

2.3.2 AR模块初始化

包括手机权限申请和百度Key认证
修改MainActivity.xml:

public class MainActivity extends UnityPlayerActivity implements OnGetDataResultListener, OnGetPoiSearchResultListener{

    public static ArInfoScenery arInfoScenery; // 景区
    public static ArBuildingResponse arBuildingResponse; // 识楼
    public static List<PoiInfoImpl> poiInfos; // 探索
    private PoiSearch mPoiSearch = null;
    private ArSdkManager mArSdkManager = null;
    private LatLng center = new LatLng(22.866084, 108.285501);
    int radius = 500; // 500米半径
    private int loadIndex = 0;
    // 自定义多点数据
    private ArLatLng[] latLngs = {
            new ArLatLng(22.865926, 108.284935),
            new ArLatLng(22.866817, 108.286763),
            new ArLatLng(22.866484, 108.287801),
            new ArLatLng(22.866531, 108.285363),
            new ArLatLng(22.863861, 108.285308)
    };

    // MainActivity初始化函数
    public void init(){
        // ArSDK模块初始化
        ArSdkManager.getInstance().initApplication(GetActivity().getApplication(), new MyGeneralListener());

        // 若用百度定位sdk,需要在此初始化定位SDK
        LocSdkClient.getInstance(GetActivity()).getLocationStart();

        // 若用探索功能需要再这集成检索模块 在使用 SDK 各组间之前初始化 context 信息,传入 ApplicationContext
        SDKInitializer.initialize(GetActivity().getApplication());
        // 检索模块 自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
        // 包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
        SDKInitializer.setCoordType(CoordType.BD09LL);

        // 如果需要检索,初始化搜索模块,注册搜索事件监听
        mPoiSearch = PoiSearch.newInstance();
        mPoiSearch.setOnGetPoiSearchResultListener(this);
        // 如果需要Ar景区功能、Ar识楼功能要注册监听
        mArSdkManager = ArSdkManager.getInstance();
        mArSdkManager.setOnGetDataResultListener(this);

        // 判断权限
        PermissionsChecker permissionsChecker = new PermissionsChecker(GetActivity());
        if (permissionsChecker.lacksPermissions()) {
            Toast.makeText(GetActivity(), "缺少权限,请开启权限!", Toast.LENGTH_LONG).show();
            openSetting();
        }
        else{
            Toast.makeText(GetActivity(), "权限已开启!", Toast.LENGTH_LONG).show();
        }
    }

    /**
     * 打开设置权限界面
     *
     * @param
     */
    public void openSetting() {
        Intent intent = new Intent();
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        Uri uri = Uri.fromParts("package", getPackageName(), null);
        intent.setData(uri);
        startActivity(intent);
    }

    static class MyGeneralListener implements MKGeneralListener {
        // 1、事件监听,用来处理通常的网络错误,授权验证错误等
        @Override
        public void onGetPermissionState(int iError) {
            // 2、非零值表示key验证未通过
            if (iError != 0) {
                // 授权Key错误:
                Toast.makeText(GetActivity(),"arsdk 验证异常,请在AndoridManifest.xml中输入正确的授权Key,并检查您的网络连接是否正常!error: " + iError, Toast
                                .LENGTH_LONG).show();
            } else {
                Toast.makeText(GetActivity(), "key认证成功", Toast.LENGTH_LONG).show();
            }
        }

        // 回调给ArSDK获取坐标(demo调用百度定位sdk)
        @Override
        public ArBDLocation onGetBDLocation() {
            // 3、用于传递给ArSdk经纬度信息
            // a、首先通过百度地图定位SDK获取经纬度信息
            // b、包装经纬度信息到ArSdk的ArBDLocation类中 return即可
            BDLocation location =
                    LocSdkClient.getInstance(ArSdkManager.getInstance().getAppContext()).getLocationStart()
                            .getLastKnownLocation();
            if (location == null) {
                return null;
            }
            ArBDLocation arBDLocation = new ArBDLocation();
            // 设置经纬度信息
            arBDLocation.setLongitude(location.getLongitude());
            arBDLocation.setLatitude(location.getLatitude());
            return arBDLocation;
        }
    }

    @Override
    public void onGetPoiResult(PoiResult result) {
        if (result == null || result.error == SearchResult.ERRORNO.RESULT_NOT_FOUND) {
            Toast.makeText(this, "未找到结果", Toast.LENGTH_LONG)
                    .show();
            return;
        }
        if (result.error == SearchResult.ERRORNO.NO_ERROR) {
            poiInfos = new ArrayList<PoiInfoImpl>();
            for (PoiInfo poi : result.getAllPoi()) {
                ArPoiInfo poiInfo = new ArPoiInfo();
                ArLatLng arLatLng = new ArLatLng(poi.location.latitude, poi.location.longitude);
                poiInfo.name = poi.name;
                poiInfo.location = arLatLng;
                PoiInfoImpl poiImpl = new PoiInfoImpl();
                poiImpl.setPoiInfo(poiInfo);
                poiInfos.add(poiImpl);
            }
            Toast.makeText(this, "查询到: " + poiInfos.size() + " ,个POI点", Toast.LENGTH_SHORT).show();
            Intent intent = new Intent(MainActivity.this, ArActivity.class);
            MainActivity.this.startActivity(intent);
            return;
        }
        if (result.error == SearchResult.ERRORNO.AMBIGUOUS_KEYWORD) {

            // 当输入关键字在本市没有找到,但在其他城市找到时,返回包含该关键字信息的城市列表
            String strInfo = "在";
            for (CityInfo cityInfo : result.getSuggestCityList()) {
                strInfo += cityInfo.city;
                strInfo += ",";
            }
            strInfo += "找到结果";
            Toast.makeText(this, strInfo, Toast.LENGTH_LONG)
                    .show();
        }
    }

    @Override
    public void onGetPoiDetailResult(PoiDetailResult result) {
        if (result.error != SearchResult.ERRORNO.NO_ERROR) {
            Toast.makeText(this, "抱歉,未找到结果", Toast.LENGTH_SHORT)
                    .show();
        } else {
            Toast.makeText(this, result.getName() + ": " + result.getAddress(), Toast.LENGTH_SHORT)
                    .show();
        }
    }

    @Override
    public void onGetPoiDetailResult(PoiDetailSearchResult poiDetailSearchResult) {}

    @Override
    public void onGetPoiIndoorResult(PoiIndoorResult poiIndoorResult) {}

    @Override
    public void onGetSceneryResult(ArSceneryResponse var1){}

    @Override
    public void onGetBuildingResult(ArBuildingResponse var1){}
}

重新打包,成功。
在这里插入图片描述

2.3.2 测试Activity跳转

在MainActivity.xml中添加onClick()方法监听Unity中场景转换按键事件:

public void onClick(String index){
        switch (index){
            // 单点数据展示
            case ConstantValue.singlePointShow:
                Intent intent2SinglePoint = new Intent();
                intent2SinglePoint.setClass(GetActivity(),ArActivity.class);
                GetActivity().startActivity(intent2SinglePoint);
                break;
            // 多点数据展示
            case ConstantValue.multiPointsShow:
                Intent intent2MultiPoint = new Intent();
                intent2MultiPoint.setClass(GetActivity(),ArActivity.class);
                GetActivity().startActivity(intent2MultiPoint);
                break;
            // 景区功能 传入uid信息
            case ConstantValue.sceneryArShow:
                break;
            // 识楼功能
            case ConstantValue.buildingArShow:
                break;
            // 探索功能
            case ConstantValue.exploreArShow:
                break;
            default:
                break;
        }
    }

修改activity_ar.xml,默认布局会报错。证明Unity中调用的Android原生布局不能为空。以下xml为测试用。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/editText2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="This is ArActivity" />
</LinearLayout>

修改Unity控制脚本UIControl:

public class UIControl : MonoBehaviour {

    AndroidJavaClass unity;
    AndroidJavaObject currentActivity;

    private Button singlePointShowBtn;
    private Button multiPointsShowBtn;
    private Button sceneryArShowBtn;
    private Button buildingArShowBtn;
    private Button exploreArShowBtn;

    private void Awake()
    {
        transform.Find("MainActivityInit").GetComponent<Button>().onClick.AddListener(InitMainActivity);
        singlePointShowBtn = transform.Find("SinglePointShow").GetComponent<Button>();
        multiPointsShowBtn = transform.Find("MultiPointsShow").GetComponent<Button>();
        sceneryArShowBtn = transform.Find("SceneryArShow").GetComponent<Button>();
        buildingArShowBtn = transform.Find("BuildingArShow").GetComponent<Button>();
        exploreArShowBtn = transform.Find("ExploreArShow").GetComponent<Button>();
    }

    // Use this for initialization
    void Start () {
        unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        currentActivity = unity.GetStatic<AndroidJavaObject>("currentActivity");

        singlePointShowBtn.onClick.AddListener(delegate () { OnClick(singlePointShowBtn.name); });
        multiPointsShowBtn.onClick.AddListener(delegate () { OnClick(multiPointsShowBtn.name); });
        sceneryArShowBtn.onClick.AddListener(delegate () { OnClick(sceneryArShowBtn.name); });
        buildingArShowBtn.onClick.AddListener(delegate () { OnClick(buildingArShowBtn.name); });
        exploreArShowBtn.onClick.AddListener(delegate () { OnClick(exploreArShowBtn.name); });
    }

    public void InitMainActivity()
    {       
        currentActivity.Call("init");
    }

    public void OnClick(string id)
    {
        switch (id)
        {
            case ConstantValue.singlePointShow:
                currentActivity.Call("onClick", ConstantValue.singlePointShow);
                break;
            case ConstantValue.multiPointsShow:
                currentActivity.Call("onClick", ConstantValue.multiPointsShow);
                break;
            case ConstantValue.sceneryArShow:
                currentActivity.Call("onClick", ConstantValue.sceneryArShow);
                break;
            case ConstantValue.buildingArShow:
                currentActivity.Call("onClick", ConstantValue.buildingArShow);
                break;
            case ConstantValue.exploreArShow:
                currentActivity.Call("onClick", ConstantValue.exploreArShow);
                break;
            default:
                break;
        }
    }
}

重新编译,打包。
在这里插入图片描述
成功跳转。此外,到目前为止已成功实现Unity中调用Android原生Activity、layout。

2.2.3 参考百度demo完成ArActivity

报错1. unity调用Android,在真机上闪退
原因:aar包中的so库与unity库冲突。
解决方法:将aar包中的so库移出aar包,并删除多余空目录
在这里插入图片描述
显示成功
在这里插入图片描述
报错2.AR食堂图标依旧没有显示
原因:
在这里插入图片描述

待续…

[注] 若在Unity中出现不能打包的情况,可试着配置gradle.properties
注释掉该句:

org.gradle.jvmargs=-Xmx1536m

参考:
link百度API
link最简单四步查看Android studio SHA1.不用输入命令。

2018-03-22 19:46:38 zz13995900221 阅读数 403
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

1、数据通讯服务(Global Service)

1UDP数据接收(UDP Receive Service)

2UDP数据发送(UDP Board)

3CAN数据(CAN Service)

数据格式参考Net Command下的CAN所对应的FramePacket.cs(帧数据包)类。

4)导航状态数据的接收服务(NAVI Data Service)

其中,(4NAVI Data Service用于接收两种类型的导航数据:GpsNaviRoutePacketGpsRealtimeNaviPacket

2AR逻辑算法(Logic)

1)命名空间 Packet.CAN     CANFramePacket.cs   

2)命名空间 ArHud.Logic    

Main.cs   Main类中当收到Packet.CAN.FramePactet时触发OnCANFrameReceived事件。

CarMotionState.cs    更新车辆状态信息,CarMotionState类中Update方法完成,其状态信息主要包括车辆前行速度、车辆航向角速度以及车辆坐标系等数据。

3)命名空间 ArHud.Logic.Adas  

Main.cs    Main类中的Update方法完成,更新ADAS信息,ADAS信息主要包括前方障碍物信息(FPWFCW)和车道线信息(LDW),该方法中除了更新以上信息外,还需要判断车辆当前车道前方预警距离内是否有障碍物(行人和车辆)以及车辆是否偏离两边车道线,并将这些信息通过UDP方式发送出去以便在非AR屏上提示。 

PreciseLane.cs    PreciseLaneCalc.cs

4)命名空间 ArHud.Logic.Navi

Main.cs    Main类中的Update方法完成,导航状态信息主要包括GPS车辆行驶路径进程(GpsProgressTracker)、实时车辆行驶路径进程(RealProgressTracker)、车头行驶方向姿态信息(RunAttitudeTracker)以及下一个导航节点信息(NaviGuideNotifier)等。

RunAttitudeTracker.cs  Route.cs     RealProgressTracker.cs    Progress.cs

GpsRealtimeState.cs   NaviDataService.cs

其中ArHud.Logic.NaviMain类中的Update方法中:

之前面AR逻辑算法Logic所提到的更新导航状态信息:

① GPS车辆行驶路径进程(GpsProgressTracker)

② 实时车辆行驶路径进程(RealProgressTracker)

③ 车头行驶方向姿态信息(RunAttitudeTracker)

④ 下一个导航节点信息(NaviGuideNotifier)

除了之前上面所提到的更新导航状态信息之外,在3:

还需要根据这些信息向ARHUD数据包ArHudFramePacket类中更新不同的数据类型成员。主要为:

① 导航事件状态(naviStatusEvent)

② 导航路径点集(naviPathPoints)

③ 导航关键节点(naviKeyNode)

④ 前方障碍物信息(fcw/fpw)

⑤ 车道线信息(ldw)

⑥ 车速(carspeed)

⑦ 转向角度(turnAngle)

注意ArHud.Logic.NaviMain类中的Update方法中,只有

① 导航事件状态(naviStatusEvent)

② 导航路径点集(naviPathPoints)

③ 导航关键节点(naviKeyNode)

⑦ 转向角度(turnAngle)

3、在AR场景动画显示(AR Scene and AR UI)中AR SceneAR场景)主要包含导航箭头动画(Navigation)和安全警示动画(Safety Warning)两部分。

1)其中导航箭头动画主要包括:

① 直行箭头动画(Navi Arrow Line)

② 转弯箭头动画(Navi Turn Path)

其中直行箭头动画(Navi Arrow Line)ArrowLine.cs脚本   

脚本事件OnArHudFrameReceived

转弯箭头动画(Navi Turn Path)TurnPath.cs脚本   

脚本事件OnArHudFrameReceived

2)安全警示动画主要包括:

① 障碍物警示动画(Forward Car/Forward Pest(行人))

② 车道线警示动画(Lane(道路))

其中障碍物警示动画(Forward Car/Forward Pest)

Forward Warning.cs脚本和Smooth Move Animation.cs脚本

Acc.cs 格子显示   FcwInformation.cs 障碍物信息

3)车道线警示动画(Lane)

Lane Departure Warning.cs脚本        Departure偏离

New Lane Departure Warning.cs脚本

脚本中事件OnArHudFramePacketReceived     LineRenderer3D.cs线渲染器组件脚本

ParamCurve3.cs脚本    CurveRenderer.cs脚本   Poly3.cs脚本)

车道线偏离警示运用了第二节中Logic逻辑算法Mobile Eye车道线方程X=C3*X3 + C2*X2 + C1*X + C0)

4、在AR UIAR UIAR UI用户界面)主要包含面板调整(Canvas Adjust)UI信息切换(Navi UI Controller)两部分。

① 面板调整(Canvas Adjust);查看CanvasAdjust类的Update方法

② UI信息切换(Navi UI Controller);根据ARHUD数据包(ArHudFramePacket)中的导航事件状态(NaviStatusEvent)的类型切换。查看NaviUiController类的OnArHudFramePacketReceived事件。

5、在AR画面成像校正(AR System)

1)观测画面的畸变校正

2)车辆视角坐标系校正

3)画面投影成像的显示比例校正(透视投影)

标定工作生成标定文件(ar_calib_data),在读取标定文件内容之后,畸变校正算法需要计算成像画面上每个像素在观测画面上的对应位置,并显示该位置期望的颜色值(R,G,0)。这一过程主要运用了双线性插值算法完成。

FullMappingGeneration 脚本

Generate方法。

②在前面的畸变校正算法中,还包含了车辆坐标标定。

脚本Ar System其作用主要是用来读写标定文件。除了前面标定文件ar_calib_data还有文件ar_root_transform

Ar System脚本。

产生透视投影效果需要给Camera组件输入透视投影矩阵,ar_calib_data中两个坐标点(Xmin,Ymin)(Xmax,Ymax)和远近裁剪平面距离(Far/Near Clipping Plane)

ArUndistortionEffect脚本。

6、 在实车路况数据记录(Log System)

1)数据记录(Sensor Data Record):保存

① 路径点集数据(GpsNaviRoutePacket)

② GPS导航实时状态数据(GpsRealtimeNaviPacket)

③ Mobile Eye检测到的ADAS数据(CAN.FramePacket)

2)数据重放Sensor Data Replay

实车驾驶状态和环境数据读取出来,进行模拟再现。

两个脚本

① Sensor Data Record.cs   数据记录脚本

② Sensor Data Replay.cs   数据重放脚本

7 在本地在线模拟调试(Debug)

Debug中:行驶轨迹模拟和障碍物模拟。

1)车道线行驶轨迹模拟

① Car From Motion.cs 脚本  ② Precise Lane Calc Visualizer.cs 脚本

顺便看代码脚本PreciseLaneCalc.cs

2)障碍物模拟绘制

① Debug下与Adas绑定的Main.cs 脚本

app_windshieldar\ForDebug\scripts\Adas\Main.cs

8、在APP环境配置(APP)

1AR坐标系以及画面模式显示上的调整:

APP.cs 脚本  app_windshieldar\basic\scripts\app.cs

2AR动画的显示策略调整

AppConfig.cs 脚本  app_windshieldar\basic\scripts\appconfig.cs

2016-11-04 15:09:15 sinat_35761779 阅读数 2272
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

【公开课】蓝鸥Unity3D ——游戏/VR/AR开发

领先于所谓主程实战项目的碎片化、零星知识点!蓝鸥拥有强大企业实训经验、以进程式体系化教学,真实企业项目开发层层推进。一线技术+开发素养,成高薪就业的又一大保障。
本套课程是一套完整的 Unity 学习课程。从零基础开始,包括《C# 语言》相关课程、《Unity 引擎》相关课程和《项目实践》的相关课程,一步步循序渐进、由易到难,最终带领学生达到 Unity 中级开发工程师的水平。会详细讲解 C# 语言、Unity 引擎基础、动画系统、2D开发、uGUI、导航系统、粒子系统、数据处理、网络系统等,另外还包括 HoloLens 开发技术、C++、算法和数据结构、设计模式等内容,以及最后的一个完整的商业级项目的开发。
【公开课】蓝鸥Unity3D <wbr>——游戏/VR/AR开发
宋博首发VR技术的大牛讲师
蛮牛上声音最性感、Unity课程视频100%好评的小波老师,具有5年IT行业从业经验,精通各类常用开发语言。多次参加Unity中国大会,并第一时间分享最新开发技术,如Unite2016回来就首发VR建模等多项新功能,成为国内少有的VR开发技术最早掌控者。

分类:实战演武
标签: 开发技巧

第一期:Unity3D、AR/VR、游戏 公开课【完结】
日期:10月24日
1、游戏和AR/VR行业
【公开课】蓝鸥Unity3D <wbr>——游戏/VR/AR开发 
2、Unity学习路径和方法

【公开课】蓝鸥Unity3D <wbr>——游戏/VR/AR开发 
3、介绍蓝鸥在线Unity课程

【公开课】蓝鸥Unity3D <wbr>——游戏/VR/AR开发 
第二期:连续三天领略Unity 同游戏、AR/VR 开发的魅力!!!【更新中】
【公开课】蓝鸥Unity3D <wbr>——游戏/VR/AR开发 
1Unity 和 AR/VR 行业
学习目标:详细分析当前 AR/VR 行业的发展情况,深度讲解 Unity 与游戏和 AR/VR 行业的关系,介绍蓝鸥在线课程
课程内容:行业发展现状、未来发展方向、在线课程介绍
2:Unity 项目实践
学习目标:使用 Unity 快速制作一款 3D 游戏,讲解简单的 C# 语法,和 Unity 编辑器的使用方法
课程内容:项目需求分析场景搭建脚本实现
【公开课】蓝鸥Unity3D <wbr>——游戏/VR/AR开发 
3HTC Vive 项目实践
学习目标:使用 Unity 制作 HTC Vive 的 VR 项目,简单讲解 HTC Vive 的操作方法、开发环境配置及开发流程
课程内容:HTC Vive 环境配置HTC Vive 使用说明HTC Vive 项目开发
【公开课】蓝鸥Unity3D <wbr>——游戏/VR/AR开发 
2019-04-28 14:34:28 weixin_41814169 阅读数 3557
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

最近在研究一个问题,就是通过Unity平台做出AR导航功能,类似于地图的样子,但不是通过GPS定位,室内应用的那种。

大概的效果图是:

 

 

现在功能还没有实现,有同在做这方面的小伙伴加群学习:663515959

2016-03-11 11:46:42 wuyt2008 阅读数 17273
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

首先说,这个尝试失败,属于死在去医院的路上那种。


基于地理定位的增强现实,AR全息实景,是一种高大上的说法,说直白点就是山寨类似随便走这样的应用。

打开应用,搜索周边信息,然后再把信息叠加在摄像头拍摄到的内容上面。



思路:用手机移动来控制unity中的camrea,将摄像头拍摄到的内容作为背景。获取地理信息,将信息转化成文字添加到unity的世界中。


1、用手机移动控制unity中的camrea。

这段代码中unity的论坛中找到,但是时间很久远,改了下发现能用。

http://forum.unity3d.com/threads/sharing-gyroscope-controlled-camera-on-iphone-4.98828/

using UnityEngine;
using System.Collections;

public class CameraManager : MonoBehaviour {



	private bool gyroBool;
	private Gyroscope gyro;
	private Quaternion rotFix;

	public void Start ()
	{
		Transform currentParent = transform.parent;
		GameObject camParent = new GameObject ("GyroCamParent");
		camParent.transform.position = transform.position;
		transform.parent = camParent.transform;
		GameObject camGrandparent = new GameObject ("GyroCamGrandParent");
		camGrandparent.transform.position = transform.position;
		camParent.transform.parent = camGrandparent.transform;
		camGrandparent.transform.parent = currentParent;

		gyroBool = SystemInfo.supportsGyroscope;

		if (gyroBool) {

			gyro = Input.gyro;
			gyro.enabled = true;

			if (Screen.orientation == ScreenOrientation.LandscapeLeft) {
				camParent.transform.eulerAngles = new Vector3 (90, 90, 0);
			} else if (Screen.orientation == ScreenOrientation.Portrait) {
				camParent.transform.eulerAngles = new Vector3 (90, 180, 0);
			} else if (Screen.orientation == ScreenOrientation.PortraitUpsideDown) {
				camParent.transform.eulerAngles = new Vector3 (90, 180, 0);
			} else if (Screen.orientation == ScreenOrientation.LandscapeRight) {
				camParent.transform.eulerAngles = new Vector3 (90, 180, 0);
			} else {
				camParent.transform.eulerAngles = new Vector3 (90, 180, 0);
			}

			if (Screen.orientation == ScreenOrientation.LandscapeLeft) {
				rotFix = new Quaternion (0, 0,0.7071f,0.7071f);
			} else if (Screen.orientation == ScreenOrientation.Portrait) {
				rotFix = new Quaternion (0, 0, 1, 0);
			} else if (Screen.orientation == ScreenOrientation.PortraitUpsideDown) {
				rotFix = new Quaternion (0, 0, 1, 0);
			} else if (Screen.orientation == ScreenOrientation.LandscapeRight) {
				rotFix = new Quaternion (0, 0, 1, 0);
			} else {
				rotFix = new Quaternion (0, 0, 1, 0);
			}

			//Screen.sleepTimeout = 0;
		} else {
			#if UNITY_EDITOR
			print("NO GYRO");
			#endif
		}
	}

	public void Update ()
	{
		if (gyroBool) {
			Quaternion quatMap;
			#if UNITY_IOS
			quatMap = gyro.attitude;
			#elif UNITY_ANDROID
			quatMap = new Quaternion(gyro.attitude.x,gyro.attitude.y,gyro.attitude.z,gyro.attitude.w);
			#endif
			transform.localRotation = quatMap * rotFix;
		}
	}
}


2、背景摄像头显示摄像机内容

摄像头的内容可以显示在guitexure上也可以显示在plan上,但是在guitexrue上显示的时候,方向转了90度,最后只好显示在plan上。

using UnityEngine;
using System.Collections;

public class WebCamManager : MonoBehaviour {

	// Use this for initialization
	void Start () {

		WebCamTexture webcamTexture = new WebCamTexture ();

		//如果有后置摄像头,调用后置摄像头
		for (int i = 0; i < WebCamTexture.devices.Length; i++) {
			if (!WebCamTexture.devices [i].isFrontFacing) {
				webcamTexture.deviceName = WebCamTexture.devices [i].name;
				break;
			}
		}

		Renderer renderer = GetComponent<Renderer>();  
		renderer.material.mainTexture = webcamTexture;  
		webcamTexture.Play();  
	}
}


3、调用高德地图的地理定位和搜索附近

详细内容请看我之前的博客

http://blog.csdn.net/wuyt2008/article/details/50774017

http://blog.csdn.net/wuyt2008/article/details/50789423


4、当搜索到内容以后,将名称信息添加到unity的世界里。

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

public class ARMange : MonoBehaviour {

	public List<PlaceInfo> places = new List<PlaceInfo>();
	public GameObject perfab;
	public PlaceInfo location = new PlaceInfo ();

	public void ShowPlaces(){
		ClearPlace ();

		for (int i = 0; i < places.Count; i++) {

			GameObject newPlace = Instantiate<GameObject> (perfab);
			newPlace.transform.parent = this.transform;

			double posZ = places [i].Latitude - location.Latitude;
			double posX = places [i].Longitude - location.Longitude;

			float z = 0;
			float x = 0;
			float y = 0;

			if (posZ > 0) {
				z = 500f;
			} else {
				z = -500f;
			}

			if (posX > 0) {
				x = 500f;
			} else {
				x = -500f;
			}

			z = z + (float)(posZ * 1000);
			x = x + (float)(posX * 1000);
			y = y + i * 20;

			newPlace.transform.position = new Vector3 (x, y, z);
			newPlace.transform.LookAt (this.transform);
			newPlace.transform.Rotate (new Vector3 (0f, 180f, 0f));

			newPlace.gameObject.GetComponentInChildren<Text> ().text = places [i].Name;
		}
	}

	private void ClearPlace(){
		GameObject[] oldPlaces = GameObject.FindGameObjectsWithTag ("Place");
		for (int i = 0; i < oldPlaces.Length; i++) {
			Destroy (oldPlaces [i].gameObject);
		}
	}

}


5、这个时候显示内容没问题,但是方向会偏移。于是加了个指南针来矫正方向

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class CompassManage : MonoBehaviour {

	public Transform cam;

	void Start () {
		Input.location.Start ();
		Input.compass.enabled = true;
	}
	
	// Update is called once per frame
	void Update () {
		transform.rotation = Quaternion.Euler(0, cam.eulerAngles.y-Input.compass.trueHeading, 0);
	}
}


6、最后遇到的,我无法解决的问题

简单一句话,就是滤波。这个应用需要准确稳定的判断出当前手机方向位置状态,但是,输入的内容,重力,罗盘,加速度都是在不断变化,并且会有偏移的量,需要滤波。

虽然大致知道了是应该用互补滤波和卡尔曼滤波,但是,我的水平只能看懂名字,看不懂内容。

数学无力的我只好放弃。等遇到别人写好的代码再抄下吧。


这是死在半路上的结果的样子




这样的结果呢,当然是不甘心的,但是没时间去仔细研究这个问题了,所以只好放弃。如果哪位大侠知道怎么根据重力,罗盘,加速判断手机状态的,在这里跪求先。

源码和编译的apk:http://download.csdn.net/detail/wuyt2008/9458508

====================



在SearchManage.cs文件中,我把搜索范围限定在了昆明,

			//txtInfo.text = txtInfo.text + "\r\n";
			AndroidJavaObject query = amapHelper.Call<AndroidJavaObject>("getPoiSearch",inputQuery.text,"","0871");
			txtInfo.text = txtInfo.text + "query get...";
将0871改成其他地方的区号就可以了,(为空是全国范围,但是没验证过)