UE4在Android调用Project Tango

  Project Tango应该说是Google一试水AR的设备,其中Project Tango主要二个功能,一个是获取深度信息,如MS的Kinect,有相当多的设备都有这个功能,二是第一人称相对定位,这个就没那么常见了,如果对这个设备有更深的兴趣,可以看知乎上的这二个链接。

  Google Project Tango获取深度信息的原理是什么?

  Project tango是如何仅凭自身摄像头实现位置追踪的?

  在这就不仔细来说这个东东了,上面二个链接比我自己再来说篇好多了,Project Tango本身有Unity3D的包(googlesamples/tango-examples-unity)如果在Unity3D下开发,相应的东东都已经提供,还有一些实例,能够很容易就开发基于Project Tango的功能出来。

  UE4下就比较麻烦了,google没有针对UE4做相应的包,不过,google提供针对安卓开发的项目,一种是Android Studio项目,一个是供JNI调用的C语言项目。

  googlesamples/tango-examples-java

  googlesamples/tango-examples-c

  UE4针对移动平台感觉还是不那么友好,如Unity调用Android项目,大家顺便一搜,都能搞定,而在UE4下引用安卓项目,如下是一个添加针对Android支持Google Play功能的committed。

  UE4 Google Play support on Android

  可以看到,有些复杂,特别针对我这种UE4与Android都不熟的人,只有想别办法,首先我的需求并不复杂,只是在一个模型与现实重叠的空间利用Project Tango的Motion tracking功能行走,旋转等,简单来说,我现在的办公室环境,利用3D建模做一个和办公室一样的模型,长宽都要对上,这样利用Project Tango 的Tracking,我能只看屏幕也知道我在办公室的那个位置,前面是否有障碍物,就如HTV vive的那二个像个小音箱的东东来检测可活动区域一样。

  UE4本身的脚本就是C++语言,自然我就想到利用上面的tango c来做开发,如下主要记录本文实践这种方法遇到的一些问题。

  首先安装UE4的安卓开发相关所有软件,UE4已经帮你差不多都搞好。安装安卓软件开发工具包(SDK) ,然后对照这个链接下的UE4安卓快速入门自己测试,一个简单的UE4 安卓程序发出来就没问题了。

  tango-examples-c里很简单,一个so文件,相当于win平台中的动态链接库文件,二是一个头文件tango_client_api.h.现在就很简单了,相当于平常我们写C++程序一样,引入动态链接库,然后添加头文件就可,但是UE4中,编译都是靠对应目录下的cs文件编译,我们需要让UE4的规则来引入库与头文件。

  按照UE4大家默认的一些目录位置与命名,先把相应头文件与库放入一个ThirdParth文件夹,然后放入UE4工程文件件,与Source,Binaries等目录平行,如下:

  

  然后打开工程名.Build.cs文件,告诉编译器我们需要引入的库与文件。

public class Office_05 : ModuleRules
{
    public Office_05(TargetInfo Target)
    {
        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Launch", "UMG" });
        //PrivateDependencyModuleNames.AddRange(new string[] { "" });

        //PublicIncludePaths.Add("Runtime/Launch/Public");
        //PrivateIncludePaths.Add("Runtime/Launch/Private/Android");

        if (Target.Platform == UnrealTargetPlatform.Android)
        {
            PublicIncludePaths.AddRange(new string[] { "Core" });
            PublicIncludePaths.Add("C:/NVPACK/android-ndk-r10e/platforms/android-19/arch-arm/usr/include");
            LoadBobsMagic(Target);
        }
    }

    private string ModulePath
    {
        get { return Path.GetDirectoryName(RulesCompiler.GetModuleFilename(this.GetType().Name)); }
    }

    private string ThirdPartyPath
    {
        get { return Path.GetFullPath(Path.Combine(ModulePath, "../../ThirdParty/")); }
    }

    public bool LoadBobsMagic(TargetInfo Target)
    {
        bool isLibrarySupported = false;

        if (Target.Platform == UnrealTargetPlatform.Android)
        {
            isLibrarySupported = true;
            string LibrariesPath = Path.Combine(ThirdPartyPath, "tango_client_api", "lib");
            PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "libtango_client_api.so"));
        }

        if (isLibrarySupported)
        {
            // Include path
            PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "tango_client_api", "include"));
        }
        //Definitions.Add(string.Format("WITH_LIBZPLAY_BINDING={0}", isLibrarySupported ? 1 : 0));
        return isLibrarySupported;
    }
}

UE4 引入库

  PublicDependencyModuleNames我们新添加Launch与UMG,因为我们需要引用这二个库,不然后面引用#include "Android/AndroidJNI.h"里的功能会告诉你没有实现,还有一点特别注意,安卓的功能一定要包含预处理定义PLATFORM_ANDROID当中,当初特别二的以为编译选项选择Android后,就可以直接写NDK代码了,当然现在的编译是由UE4来控制的,所以不用看VS中的错误列表,只需要看VS中的输出,如果提示生成成功,没有error,就可以在设备上发布了。

  然后我们包装一下tango_client_api.h里的功能,演示如何在安卓环境下调用JNIEnv,当前active等。

#pragma once

#include "Components/TextRenderComponent.h"
#include "Office_05.h"
#include "MyCharacter.h"
#include "TangoApp.h"
#include "Engine/TextRenderActor.h"
#include "Components/TextRenderComponent.h"

#if PLATFORM_ANDROID
#include "tango_client_api.h"
#include "Android/AndroidApplication.h"
#include "Android/AndroidJava.h"
#include "Android/AndroidJNI.h"
#endif
/**
 *
 */
class OFFICE_05_API TangoApp
{
private:
    static class UTextRenderComponent* textRender;
    static FVector translation;
    static FQuat quat;

public:
    TangoApp();
    ~TangoApp();

    static FVector& GetTranslation()
    {
        return translation;
    }

    static FQuat& GetQuat()
    {
        return quat;
    }

    static void SetTextRender(class UTextRenderComponent* tRender)
    {
        textRender = tRender;
        appendText(textRender, TEXT("VV"));
    }

    static void appendText(UTextRenderComponent* textCompent, const FString& value, bool overrid = false)
    {
        if (textCompent == nullptr)
            return;

        if (!overrid)
        {
            FText text = textCompent->Text;
            FString newText = text.BuildSourceString();
            newText.Append(" ");
            newText.Append(value);
            textCompent->SetText(FText::FromString(newText));
        }
        else
        {
            textCompent->SetText(FText::FromString(value));
        }

    }

#if PLATFORM_ANDROID

    static void InitTango()
    {
        JNIEnv* Env = FAndroidApplication::GetJavaEnv();
        jint VersionJint = Env->GetVersion();
        int8 Version = (int8)VersionJint;

        TangoApp::appendText(textRender, FString::FromInt(Version));
        jobject currentActive = FAndroidApplication::GetGameActivityThis();
        TangoErrorType type = TangoService_initialize(Env, currentActive);
        TangoApp::appendText(textRender, FString::FromInt((int)type));

        //type = TangoService_setBinder(Env, currentActive);
        TangoApp::appendText(textRender, TEXT("A"));
        TangoApp::appendText(textRender, FString::FromInt((int)type));

        auto tangoConfig = TangoService_getConfig(TANGO_CONFIG_DEFAULT);
        type = TangoConfig_setBool(tangoConfig, "config_enable_motion_tracking", true);
        TangoApp::appendText(textRender, FString::FromInt((int)type));
        if (type == TANGO_SUCCESS)
        {
            TangoApp::appendText(textRender, TEXT("B"));
        }

        type = TangoConfig_setBool(tangoConfig, "config_enable_auto_recovery", true);
        TangoApp::appendText(textRender, FString::FromInt((int)type));
        if (type == TANGO_SUCCESS)
        {
            TangoApp::appendText(textRender, TEXT("C"));
        }

        type = TangoConfig_setBool(tangoConfig, "config_enable_learning_mode", true);
        TangoApp::appendText(textRender, FString::FromInt((int)type));
        if (type == TANGO_SUCCESS)
        {
            TangoApp::appendText(textRender, TEXT("D"));
        }
        //TangoApp::Launch();
        //TangoApp::appendText(text->GetTextRender(), TangoApplication::getPackageName());
        //uuid得不到,相应权限申请不成功,请看TangoApplication::Launch
        char* uuidList = NULL;
        type = TangoService_getAreaDescriptionUUIDList(&uuidList);
        TangoApp::appendText(textRender, FString::FromInt((int)type));
        if (type == TANGO_SUCCESS)
        {
            int lenght = 0;
            for (int i = 0; i < 1000; i++)
            {
                if (uuidList[i] != 0)
                    ++lenght;
                else
                {
                    break;
                }
            }
            FString suuidList(uuidList);
            TangoApp::appendText(textRender, FString::FromInt(lenght));
        }

        TangoCoordinateFramePair pair;
        pair.base = TANGO_COORDINATE_FRAME_START_OF_SERVICE;
        pair.target = TANGO_COORDINATE_FRAME_DEVICE;

        //用来验证相应数据
        //TangoPoseData* poseData = new TangoPoseData();
        //pair.base = TANGO_COORDINATE_FRAME_IMU;
        //pair.target = TANGO_COORDINATE_FRAME_CAMERA_COLOR;
        //TangoService_getPoseAtTime(0, pair, poseData);

        //type = TangoService_connectOnTangoEvent(&TangoApp::onTangoConnectEvent);
        //TangoApp::appendText(textRender), TEXT("E"));
        //TangoApp::appendText(textRender, FString::FromInt((int)type));
        type = TangoService_connectOnPoseAvailable(1, &pair, &TangoApp::TangoService_onPoseAvailable);
        TangoApp::appendText(textRender, TEXT("F"));
        TangoApp::appendText(textRender, FString::FromInt((int)type));

        //FAppEventManager::GetInstance()->PauseRendering();
        type = TangoService_connect(nullptr, tangoConfig);
        TangoApp::appendText(textRender, FString::FromInt((int)type));
        //FAppEventManager::GetInstance()->ResumeRendering();
    }

    static FString getPackageName()
    {
        if (JNIEnv* Env = FAndroidApplication::GetJavaEnv(true))
        {
            auto getPackageMethod = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "getPackageName", "()Ljava/lang/String;", false);
            jstring jsString = (jstring)FJavaWrapper::CallObjectMethod(Env, FJavaWrapper::GameActivityThis, getPackageMethod);
            check(jsString);

            const char * nativeName = Env->GetStringUTFChars(jsString, 0);
            FString ResultName = FString(nativeName);
            Env->ReleaseStringUTFChars(jsString, nativeName);
            Env->DeleteLocalRef(jsString);
            return ResultName;
        }
        return FString();
    }

    static void Launch()
    {
        if (JNIEnv* Env = FAndroidApplication::GetJavaEnv(true))
        {
            //申请ADF权限
            auto intentClass = Env->FindClass("android/content/Intent");
            auto Constructor = Env->GetMethodID(intentClass, "<init>", "()V");
            auto intentObject = Env->NewObject(intentClass, Constructor);
            auto intentMethod = FJavaWrapper::FindMethod(Env, intentClass, "setClassName", "(Ljava/lang/String;Ljava/lang/String;)V", false);
            //auto putExtraMethod = FJavaWrapper::FindMethod(Env, intentClass, "putExtra", "(Ljava/lang/String;Ljava/lang/String;)V", false);
            /*        FJavaWrapper::CallVoidMethod(Env, intentObject, intentMethod, "com.projecttango.tango", "com.google.atap.tango.RequestPermissionActivity");
            FJavaWrapper::CallVoidMethod(Env, intentObject, putExtraMethod, "PERMISSIONTYPE", "ADF_LOAD_SAVE_PERMISSION");
            auto startAMehtod = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "startActivity", "(android/content/Intent;)V", false);
            */
            //FJavaWrapper::CallVoidMethod(Env, currentActive, startAMehtod, intentObject);
            //check(object);
            //auto intentObject = Env->NewGlobalRef(object);
            Env->DeleteLocalRef(intentObject);
        }

        //auto intentMethod = FJavaWrapper::FindMethod(Env, intentClass, "setClassName", "(Ljava/lang/String;Ljava/lang/String;)V", false);
        //auto putExtraMethod = FJavaWrapper::FindMethod(Env, intentClass, "putExtra", "(Ljava/lang/String;Ljava/lang/String;)V", false);
        /*        FJavaWrapper::CallVoidMethod(Env, intentObject, intentMethod, "com.projecttango.tango", "com.google.atap.tango.RequestPermissionActivity");
        FJavaWrapper::CallVoidMethod(Env, intentObject, putExtraMethod, "PERMISSIONTYPE", "ADF_LOAD_SAVE_PERMISSION");
        auto startAMehtod = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "startActivity", "(android/content/Intent;)V", false);
        */
        //FJavaWrapper::CallVoidMethod(Env, currentActive, startAMehtod, intentObject);

        //FJavaClassObject intentObject(FName("android/content/Intent"), "()V");
        //auto intentMethod = intentObject.GetClassMethod("setClassName", "(Ljava/lang/String;Ljava/lang/String;)V");
        //auto putExtraMethod = intentObject.GetClassMethod("putExtra", "(Ljava/lang/String;Ljava/lang/String;)V");
        //intentObject.CallMethod(intentMethod, "com.projecttango.tango", "com.google.atap.tango.RequestPermissionActivity");
        //intentObject.CallMethod(intentMethod, "PERMISSIONTYPE", "ADF_LOAD_SAVE_PERMISSION");
        //auto startAMehtod = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "startActivity", "(android/content/Intent;)V", false);
        //FJavaWrapper::CallVoidMethod(Env, currentActive, startAMehtod, intentObject.GetJObject());

        //auto tangoClass = Env->GetObjectClass(currentActive);
        //auto tangoMethod = FJavaWrapper::FindMethod(FAndroidApplication::GetJavaEnv(true), FJavaWrapper::GameActivityClassID, "launchIntent", "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;I)V", false);
        //auto tangoMethod = FJavaWrapper::FindMethod(Env, tangoClass, "launchIntent", "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;I)V", false);
        //if (tangoMethod != nullptr)
        //{
        //    TangoApplication::appendText(text->GetTextRender(), TEXT("C"));
        //    //使用方法请看AndroidJNI.cpp ->AndroidThunkCpp_Iap_QueryInAppPurchases
        //    jobjectArray tArgs = (jobjectArray)Env->NewObjectArray(1, FJavaWrapper::JavaStringClass, NULL);
        //    jstring StringValue = Env->NewStringUTF(TCHAR_TO_UTF8("PERMISSIONTYPE:ADF_LOAD_SAVE_PERMISSION"));
        //    Env->SetObjectArrayElement(tArgs, 0, StringValue);
        //    Env->DeleteLocalRef(StringValue);
        //    FJavaWrapper::CallVoidMethod(Env, currentActive, tangoMethod, "com.projecttango.tango",
        //        "com.google.atap.tango.RequestPermissionActivity", tArgs, 43);
        //}
    }

    static void onTangoConnectEvent(void* context, const TangoEvent* event)
    {
        appendText(textRender, TEXT("Connect"), true);
        appendText(textRender, FString::SanitizeFloat(event->timestamp));
    }
    //Unity x左右,y上下,z前后 左手
    //https://developers.google.com/project-tango/overview/coordinate-systems#project_tango_coordinate_frames
    //Tango START_OF_SERVICE x左右,y前后,z上下 右手
    //Tango Device_Frame x左右,y上下,z前后 右手
    //UE4 x前后,y左右,z上下 左手
    static void TangoService_onPoseAvailable(void* context, const TangoPoseData* pose)
    {
        if (pose->status_code == TANGO_POSE_VALID &&
            pose->frame.base == TANGO_COORDINATE_FRAME_START_OF_SERVICE &&
            pose->frame.target == TANGO_COORDINATE_FRAME_DEVICE
            )
        {
            translation[0] = pose->translation[0] * 100;
            translation[1] = pose->translation[1] * 100;
            translation[2] = pose->translation[2] * 100;
            quat.X = pose->orientation[0];
            quat.Y = pose->orientation[1];
            quat.Z = pose->orientation[2];
            quat.W = pose->orientation[3];
        }
        else
        {

        }
    }
#endif
};

"C++文件"
#include "Office_05.h"
#include "TangoApp.h"

class UTextRenderComponent* TangoApp::textRender = nullptr;
FVector TangoApp::translation = FVector::ZeroVector;
FQuat TangoApp::quat = FQuat::Identity;

TangoApp::TangoApp()
{
}

TangoApp::~TangoApp()
{
}

TangoApp

  在这里有个失败的尝试,我想通过NDK来申请ADF(区域文件相关权限),见上面的Launch方法,总是在调用CallVoidMethod时失败,而上面的getPackageName又没有问题,想不出来是啥问题。

  通过工具Android Device Monitor,一般来说如果按照UE4的安卓工具包的流程来安装,这个工具在目录C:\NVPACK\android-sdk-windows\tools\monitor.bat下,打开Android Device Monitor,一般来说,我们新建一个Filters,如下图设置。

  如上图设置后,我们得到错误信息是FindMethod得到的方法为空,到这一步后,相关参数应该没有问题,可能是还要引入新的库,总之,在这我们得不到区域文件,那么我们不能通过区域文件来定义,只能通过设备开始位置来定位了,这样我们需要在特定的位置,特定的方向打开这个程序才能正确tracking现实与模型,这样限制太大,所以我们需要提供一UI可以自己修改位置与方向,这样,在开始时,我们先调到我们本身的位置与方向与项目的位置与方向重合。还好,这个东东并不需要我们多花费时间,UE4里本身的功能与内容包里,就有一个C++功能First Person,我们添加到项目中,这个在安卓下就提供二个圈给我们,一个圈调整水平位置,一个调整视角方向,刚好满足我们的需求,现在我们结合First Person与Tango,让Tango本身的路径追踪来替代First Person里的行走,如下是主要的修改位置。

void AFP_FirstPersonCharacter::BeginPlay()
{
    Super::BeginPlay();
    text = FindActor<ATextRenderActor>(TEXT("TextRenderActor2"));
    text1 = FindActor<ATextRenderActor>(TEXT("TextRenderActor3"));
    TangoApp::SetTextRender(text->GetTextRender());
#if PLATFORM_ANDROID
    TangoApp::InitTango();
    //TangoApp::Launch();
#endif
    TangoApp::appendText(text->GetTextRender(), TEXT("T"), true);
    TangoApp::appendText(text1->GetTextRender(), TEXT("R"), true);
}

void AFP_FirstPersonCharacter::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

#if PLATFORM_ANDROID
    //Tango Device_Frame/OpenGL     x左右,y上下,z前后 右手
    //UE4                            x前后,y左右,z上下 左手
    //Tango START_OF_SERVICE        x左右,y前后,z上下 右手
    //Unity                            x左右,y上下,z前后 左手
    ////Device_Frame (Unreal camera to Drive)
    //FMatrix ucTd(-FVector::UpVector, FVector::ForwardVector, FVector::RightVector, FVector::ZeroVector);
    ////(Drive to START_SERVICE)
    //FTransform dTss(TangoApp::GetQuat(), TangoApp::GetTranslation(), FVector(1, 1, 1));
    ////X,Y互换 (START_SERVICE to Unreal world)
    //FMatrix ssTuw(FVector::RightVector, -FVector::ForwardVector, FVector::UpVector, FVector::ZeroVector);

    ////ucTd * dTss * ssTuw
    //FTransform dTuw = FTransform(ucTd) * dTss * FTransform(ssTuw);
    //SetActorRelativeLocation(dTuw.GetLocation());
    //auto rotator = dTuw.Rotator();// (dTuw.GetRotation() * FQuat::FQuat(FVector::ForwardVector, -PI / 2.0f)).Rotator(); //dTuw.Rotator();//

    auto ToConvert = TangoApp::GetQuat();
    auto translation = TangoApp::GetTranslation();
    FQuat TangoToUnrealQuat(ToConvert.Z, -ToConvert.X, -ToConvert.Y, ToConvert.W);
    //0.7071(rad/2) angle = 90 axis = (0,1,0)
    FQuat ConvertedQuat = (FQuat(0.0, 0.7071, 0.0, 0.7071) * TangoToUnrealQuat);
    if (WeaponRange < 0 || WeaponRange >100)
    {
        WeaponRange = 0;
    }
    FVector ConvertedFVector = 1 * cRotaror.RotateVector(FVector(translation[1], translation[0], translation[2] + WeaponRange)) + cPostion;
    SetActorRelativeLocation(ConvertedFVector);
    FRotator rotator = ConvertedQuat.Rotator() + cRotaror;

    if (Controller != nullptr)
    {
        Controller->SetControlRotation(rotator);
    }
    //if (GEngine)
    //{
    //    auto quat = ConvertedQuat; //dTuw.GetRotation();//
    //    GEngine->AddOnScreenDebugMessage(0, 30.f, FColor::Red, "X:" + FString::SanitizeFloat(quat.X)
    //        + " Y:" + FString::SanitizeFloat(quat.Y)
    //        + " Z:" + FString::SanitizeFloat(quat.Z));
    //}
#endif
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(0, 30.f, FColor::Red, "H:" + FString::SanitizeFloat(WeaponRange));
    }
}

void AFP_FirstPersonCharacter::MoveForward(float Value)
{
    if (Value != 0.0f)
    {
        // Add movement in that direction
        AddMovementInput(GetActorForwardVector(), Value);
        cPostion += GetActorForwardVector()*Value;
    }
}

void AFP_FirstPersonCharacter::MoveRight(float Value)
{
    if (Value != 0.0f)
    {
        // Add movement in that direction
        AddMovementInput(GetActorRightVector(), Value);
        cPostion += GetActorRightVector()*Value;
    }
}

void AFP_FirstPersonCharacter::TurnAtRate(float Rate)
{
    // Calculate delta for this frame from the rate information
    AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
    cRotaror.Yaw += Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds();
}

void AFP_FirstPersonCharacter::LookUpAtRate(float Rate)
{
    // Calculate delta for this frame from the rate information
    AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
    cRotaror.Pitch += Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds();
}

Tango Tracking

  Tango里需要注意,每个摄像机,还有设备本身都采用不同的坐标系,如下图。

  想看更完全的介绍,请看 coordinate-systems  这个链接,如果不能打开,提示二个字,红杏。

  嗯,在这差不多了,一个简单利用Tango tracking,漫游办公室的小程序就有了,是不是有点AR的感觉。Tango这部分就差不多完了,但是有个小问题,UI能调整水平方向,高度不能调整,我们增加一个调整高度的UI,顺便演练下蓝图调用C++的API的方法。

  首先我们创建一个基于GameMode的我们自己的MyGameMode.

  然后和上面一样,创建一个基于HUD的自己的MyHUD,在MyGameMode中的HUD选择MyHUD,在蓝图中,我们选择添加用户控件里的用户蓝图,名字设为MyGUI.

  在MyHUD中,添加MyGUI到视图中,相应蓝图设置如下。

  在MyGUI中添加一个Slider,其中OnValueChanged中设置如下:

  其中FP_FirstPersonCharacter添加如下方法。

    UFUNCTION(BlueprintCallable, Category = "Game")
        void SetHeight(float height);

SetHeight

  特性声明BlueprintCallable,其中Category是在蓝图中添加方法的分组名。

  差不多完了,最后想起一个问题,在从Unity 到 UE4 的快速上手与迁移 里的一个API,FindObject<T>,使用不成功,在AActor里使用反正得不到结果,同样的参数,传入如下函数就可以。

    template< class T >
    T* FindActor(FString name)
    {
        for (TActorIterator<T> It(GetWorld()); It; ++It)
        {
            T* actor = *It;
            if (It->GetName() == name)
            {
                return actor;
            }
        }
        return nullptr;
    }

FindActor

  有知道的同学可以说下。

时间: 2024-10-06 01:27:28

UE4在Android调用Project Tango的相关文章

; AutoHotkey全自动安装环境设置和测试JAVA+Eclipas+Android+JRE+JDK+SDK+ADT+Android模拟器+Android Virtual Device Manager+NDK+Studio+Doc+Help+Android Application Project编程调试windows环境[草稿版] DetectHiddenWindows,On SetTitl

; AutoHotkey全自动安装环境设置和测试JAVA+Eclipas+Android+JRE+JDK+SDK+ADT+Android模拟器+Android Virtual Device Manager+NDK+Studio+Doc+Help+Android Application Project编程调试windows环境[草稿版] DetectHiddenWindows,OnSetTitleMatchMode,2 ; 激活窗口并单击按钮IfWinExistActiveControlClick

Project Tango Explorer

https://sensortower.com/android/ie/projecttango-google/app/project-tango-explorer/com.projecttango.tangoexplorer/ Project Tango Explorer demonstrates the core functions of Project Tango. It also allows you to create area description files. Note: This

android调用第三方库——第一篇 (转载)

转自:http://blog.csdn.net/jiuyueguang/article/details/9447245 版权声明:本文为博主原创文章,未经博主允许不得转载. 0:前言: 这两天一直在研究用android的jni调用第三方库,上网搜方法,但是都是泛泛而谈,没有demo,经过我几番折磨,写了n多的 helloword工程,总是不成功,工程名字也就由helloowrd转到shithelloword再转到fuckhelloword再转到 bitchhelloword再转到ganhello

android 调用jni 并打印log

1.创建native方法 public final static native int startservice(); 2.选择项目右击Android tools->Add native  support  就会生成jni文件 如图 3.cd user/alarmTest/bin/命令进入到项目的classes 执行 javah com.li.JNI 包名.类名 可以生成com_yfz_JNI.h文件 然后把com_yfz_JNI.h文件拷贝到jni文件下如图 4修改android.mk文件 L

最美应用-从Android研发project师的角度之[最美时光]

最美应用-从Android研发project师的角度之最美时光 @author ASCE1885的 Github 简书 微博 CSDN 近期发现最美应用这样一个站点.它会定期推介一些非常有意思的app,作为开发人员,每次看到非常棒的app都会从实现角度进行剖析.想着假设是自己将怎样实现呢?因此,就有了这个系列的文章,旨在从技术选型和架构的角度解读一些有意思的app. 最美时光是最美应用团队出品的一个纪念日app,用于记录和分享那些重要的日子,我们就先从这个開始吧! 使用的开源函数库 1. 开源日

Android调用JNI本地方法跟踪目标代码

正如Android调用JNI本地方法经过有点改变章所说跟踪代码是可行的,但是跟踪某些代码会出现anr,点击取消,还是不好运,有提高办法吗?回答是有(gdb还没试过,本文只讨论ida). 下面是我使用 0 * Message("%s = %d\n", GetString(Dword(R2+0x10),-1, ASCSTR_C), R2+0x20)打出的记录 enforceInterface = 1108147904writeInterfaceToken = 1108151492write

arm-eabi-addr2line工具跟踪Android调用堆栈

使用arm-eabi-addr2line工具跟踪Android调用堆栈作者:liangshengyang转自:http://www.linuxidc.com/Linux/2011-01/31803.htm 在通常的C/C++代码中,可以通过响应对内存操作不当引起的Segmentation Fault错误即信号SIGSEGV(11)做出响应处理.只要在程序中设置SIGSEGV的handler中,调用libc的backtrace,打出对应的堆栈信息,很快就能找到问题所在.但在Android中,bio

Android studio project文件结构翻译

Android studio project文件结构翻译 个人翻译,用作备忘. 链接地址:https://developer.android.com/tools/projects/index.html#ApplicationModules Android Project Files Studio的项目文件和设置,设置的作用范围包含所有的module. 以以下的demo为例. .git:git版本控制的文件存放目录. .gradle:gradle执行一些编译所生成的目录 Idea: 由Intell

Android调用系统相机、自定义相机、处理大图片

Android调用系统相机和自定义相机实例 本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理(避免OOM),还有简要提一下有些人SurfaceView出现黑屏的原因. Android应用拍照的两种方式,下面为两种形式的Demo展示出来的效果.    知识点: 一.调用系统自带的相机应用 二.自定义我们自己的拍照界面 三.关于计算机解析图片原理(如何正确加载图片到Android应用中) 所需