文章目录
  1. 1. 前言
  2. 2. 原生开发
    1. 2.1. Android原生开发
      1. 2.1.1. Unity导出Gradle工程
      2. 2.1.2. 创建原生Android工程
      3. 2.1.3. 确保正确的Gradle,Android Gradle Plugin和JDK
      4. 2.1.4. 添加Android Library
      5. 2.1.5. 导出Android Library AAR
      6. 2.1.6. Unity访问原生代码
    2. 2.2. IOS原生开发
  3. 3. SDK
    1. 3.1. Firebase
      1. 3.1.1. Firebase Unity接入
        1. 3.1.1.1. 登录Firebase创建项目
        2. 3.1.1.2. 在Firebase中注册应用
        3. 3.1.1.3. 添加Firebase Unity SDK
        4. 3.1.1.4. 确认Google Play服务版本要求
        5. 3.1.1.5. 打包测试Firebase Analytics
        6. 3.1.1.6. Unity版Firebase Analytics使用入门
    2. 3.2. AdMob
  4. 4. Reference

前言

在游戏开发过程中,无论是做手游还是端游还是小游戏,我们或多或少都会需要接入第三方SDK去支持一些原生功能,不会全部都自己从0开发(比如广告SDK,统计SDK,支付SDK等),本章节就是为了深入SDK接入实战,打通SDK接入流程,同时深入Unity+原生开发,了解原生开发过程中的理论和实践知识。

原生开发

原生开发是指除了Unity提供的官方功能,我们还能在不同平台自己进行原生功能开发。

Android原生开发

Android我们通过自定义MainActivity和编写原生Android功能代码实现原生功能,然后在Unity测通过AndroidJavaClass去访问原生Android Activty和调用原生代码功能。Untiy支持我们自己导出**AAR(包含代码和资源)或者Jar(仅仅包含代码)**然后放到Plugins/Android/libs目录下直接使用,接下来我将深入从0实战如何搭建一个能够自定义开发原生功能流程框架搭建。

Unity导出Gradle工程

为了方便参考Unity能正确使用的Gradle工程相关设置,在搭建我们自己的原生Android工程前,我通过代码打包给Unity Android设置好相关打包配置,然后通过Unity自带的Gradle打包导出一个Android工程。

导出后我使用Android Studio打开发现报错:

UnityExportGradleProjectOpenFailed

这是因为Unity 2022.3.62f3导出的Gradle工程的JDK默认使用的是JAVA_HOME指向的,而之前我为了配置Jenkins打包将JAVA_HOME指向了最新的Java 21。但Unity 2022.3.62f3导出的Gradle工程使用的是Gradle 7.5.1,Gradle 7.5.1不支持运行在Java 21上,所以导致出问题。

UnityExportGradleProjectUseEnvDefaultJSDK

所以我们需要将导出Gradle工程的JDK指向Unity对应版本的JDK(对于我本地来说是以下路径):

1
G:\Software\UnityAllVersions\2022.3.62f3\2022.3.62f3\Editor\Data\PlaybackEngines\AndroidPlayer\OpenJDK

修改后重新打开Gradle工程经历漫长的Gradle依赖下载后会发现工程能正确打开了:

AfterSettingGradleProjectJDK

接下来我们就能创建我们的原生Android工程,并参考导出的Gradle工程进行配置设置了。

Unity 2022.3.62f3用的Gradle版本是7.51,Android Gradle Plugin是7.4.2,JDK是11

Note:

  1. Gradle相关东西大部分都需要翻墙下载,请确保网络环境支持
  2. Gradle 是通用构建系统。它不只用于 Android,也能构建普通 Java、Kotlin、Groovy 等项目
  3. Android Gradle Plugin,简称 AGP,是运行在 Gradle 上的 Android 构建插件

创建原生Android工程

Android工程分为两大类,一类是Application(用于打包APK),另一类是library(用于导出AAR或Jar)

我们安装好Android Studio后,File->New->New Project->新建一个Empty Activity的NativeFramworkAndroidProject Android工程:

CreateNativeFramworkAndroidProject

创建后会发现工程里有很多kotlin相关的内容,比如MainActivity.kt,app/build.gradle里有很多kotlin相关内容,这个好像是因为Android官方最新推的就是kotlin所以默认都带了。我们只是想扩展Unity的原生MainActivity去扩展编写原生功能,并不是想完全当做Android原生开发,所以我希望的还是走Android+java相关的原生开发方式,后续创建Android Library后会完全放弃并删除Android App相关。

创建完Android工程后我发现后台任务一直提示ImportingNativeFrameworkAndroidProject” Gradle Project,我打开View->Tool Window->Build看到好像在处理SDK 36相关的东西(AI说是工程默认用的AGP 9.0所以会去找SDK 36),但我本机还没安装SDK 36:

ImportingGradleProjectWithSDK36

为了让项目Import成功后使用New Module创建Android Library这里需要先打开Tools->SDK Manager->勾选SDK版本并安装(SDK 36):

InstallSDK36

这里只是为了让Android工程导入通过,后续我会根据Unity导出的Gradle工程去严格修改Android相关工具配置!

安装完成后打开NativeFrameworkAndroidProject的Android工程经历漫长的导入,最终Android工程导入完成打开成功了:

OpenAndroidProjectSuccess

Note:

  1. 创建Android工程的Build configuration language选项要选Groovy DSL,使用Groovy语言来编写.gradle文件(考虑到Unity导出Gradle工程也是用的Groovy),虽然Android官方推荐选Kotlin DSL
  2. 主要包名要和我们游戏里的包名保持一致
  3. Minimus SDK最好也保持和项目设置一致

确保正确的Gradle,Android Gradle Plugin和JDK

接下来我们先确保前面提到的Gradle,Android Gradle Plugin和JDK使用和Unity对应版本一致先。

在修改各个目标文件之前,我们先看下AI给出的项目结构和各文件的作用说明:

AndroidFolderStructure

AndroidProjectReponsibility

  • 保持根gradle.properties内容和导出Gradle工程gradle.properties一致:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    org.gradle.jvmargs=-Xmx4096M
    org.gradle.parallel=true
    unityStreamingAssets=UnityServicesProjectConfiguration.json, google-services-desktop.json
    # Android Resolver Properties Start
    android.useAndroidX=true
    android.enableJetifier=true
    # Android Resolver Properties End
    unityTemplateVersion=7
    android.useAndroidX=true
    android.enableJetifier=true
    org.gradle.welcome=never
    android.aapt2FromMavenOverride=G\:\\Software\\UnityAllVersions\\2022.3.62f3\\2022.3.62f3\\Editor\\Data\\PlaybackEngines\\AndroidPlayer\\SDK\\build-tools\\34.0.0\\aapt2.exe

    我们会在build.gradle里指定buildToolsVersion版本,但如果不强制指定aapt2工具,Android Gradle Tool会选用默认依赖的相关工具导致出错,为了确保跟Unity版本使用工具一致这里还是需要明确指定aapt2FromMavenOverride工具

  • 保持JDK版本一致

    将项目的Gradle JDK设置(File->Settings->Build,Execution,Deployment->Build Tools->Gradle->Gradle JDK)成我们Unity对应版本需要的JDK路径:

    SetUnityGradleJDKLocation

  • 保持Gradle版本一致

    打开Android Project根目录下的gradle-wrapper.properties保持和Unity导出的Gradle工程设置一致如下:

    1
    distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
  • 保持Android Gradle Plugin版本一致

    打开我们Android Project根目录下的build.gradle文件修改成如下:

    1
    2
    3
    4
    5
    // Top-level build file where you can add configuration options common to all sub-projects/modules.
    plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.android.library) apply false
    }

    如果发现Sync Gradle报错如下:

    1
    org.gradle.api.GradleException: Error resolving plugin [id: 'com.android.application', version: '9.0.1']

    这是因为我们创建的Android工程里的libs.version.toml里还起订了Android Gradle Plugin的版本信息,将libs.versions.toml里AGP版本修改到7.4.2:

    1
    2
    [versions]
    agp = "7.4.2"

    真正关联上Application和Library使用哪个Android Gradle Plugin版本的配置是在libs.version.toml

    ConnectAndroidGradlePluginVersionConfig

    再次Sync Gradle报错如下:

    1
    2
     problem occurred evaluating project ':app'.
    > Could not find method compileSdk() for arguments [build_6jjk1t0mf4mcnif7jl7frtd5s$_run_closure1$_closure3@519d74c7] on extension 'android' of type com.android.build.gradle.internal.dsl.BaseAppModuleExtension.

    这是因为我们将Android Gradle Plugin版本改到7.4.2后,不识别app/build.gradle(unitynativeframework/build.gradle里同理)里的以下写法:

    1
    2
    3
    compileSdk {
    version = release(36)
    }

    需要修改成如下:

    1
    compileSdkVersion 36

    最终终于通过了Sync Gradle同步。

    我们通过View->Tool Windows->Gradle->Execute Gradle Task输入**:unitynativeframework:assembleRelease**执行Android Library生成AAR,报错如下:

    GenerateAndroidLibraryFailedLog

    根据AI的说法是我们虽然指定了AGP版本7.4.2,但没有显示指定Build Tools的版本,所以他默认去找配套的30.0.3版本Build Tools了,同时发现找不到,所以解决问题的关键是要配置跟Unity版本配套的Build Tools版本,在对应build.gradle里分别指定以下文本:

    1
    buildToolsVersion '34.0.0'

Note:

  1. 注意将部分build.gradle里指定的minSdk和targetSdk也同步成我们目标需要的值

添加Android Library

接下来我们创建一个Android Liabrary:

CreateAndroidLibraryProject

添加Unity对应版本的classes.jar到Android Library的lib目录下:

AddUnityClassJarToAndroidLibrary

添加自定义的MainActivity.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package com.tonytang.unitynativeframework;

import android.os.Bundle;

import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.util.Log;
import android.util.DisplayMetrics;

import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;

public class MainActivity extends UnityPlayerActivity {

public final static String mUnityCallBackHandler = "GameLauncher";

public Context mContext = null;

public Activity mCurrentActivity = null;

private int mWidth;

private int mHeight;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Log.d("JNI", "onCreate()");
mContext = this.getApplicationContext();
mCurrentActivity = this;

DisplayMetrics dm = getResources().getDisplayMetrics();
mWidth = dm.widthPixels;
mHeight = dm.heightPixels;
Log.d("JNI","mWidth :" + mWidth);
Log.d("JNI","mHeight :" + mHeight);
}

protected void onStart()
{
super.onStart();

Log.d("JNI", "onStart()");
}

protected void onResume()
{
super.onResume();

Log.d("JNI", "onResume()");
}

protected void onPause()
{
super.onPause();

Log.d("JNI", "onPause()");
}

protected void onStop()
{
super.onStop();

Log.d("JNI", "onStop()");
}

protected void onDestroy()
{
super.onDestroy();

Log.d("JNI", "onDestroy()");
}

public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);

Log.d("JNI", "onWindowFocusChanged(" + hasFocus + ")");
}

@Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
Log.d("JNI","onConfigurationChanged()");
}

//Unity call part
public void javaMethod(String csparam)
{
Log.d("JNI", "javaMethod() with parameter:" + csparam);
UnityPlayer.UnitySendMessage(mUnityCallBackHandler,"resUnityMsg", "java param");
}
}

在Android Library的build.gradle里添加以下配置,将classes.jar仅编译但不导出避免和Unity的classes.jar出现双份冲突:

1
2
3
4
dependencies {
******
compileOnly files('libs/classes.jar')
}

Unity对应版本的classes.jar路径在对应版本安装目录下,不确实哪个可以直接拷贝导出的Gradle工程里的classes.jar

将导出的Gradle工程下的unityLibrary的AndroidManifest.xml拷贝到Unity项目Plugins/Android目录下作为基准AndroidManifest.xml使用,但要将android:name=”com.unity3d.player.UnityPlayerActivity”修改成我们自己的包名android:name=”com.tonytang.unitynativeframework.MainActivity”,这一步是将默认的Unity MainActivity修改成我们自定义的MainActivity的关键

CoreDataInAndroidManifestXML

*符号包着的那一段是指定将com.tonytang.unitynativeframework.MainActivity作为启动Activity的相关配置

为什么我们的Android Library工程不需要配置像Unity导出的Gradle工程相关的NDK配置?为什么 Unity 导出工程需要 NDK?

Unity 导出的 unityLibrary/build.gradle 不只是普通 Android Library,它包含:

  • Unity Player 运行时;
  • IL2CPP 生成的 C++;
  • Unity 原生库;
  • 游戏程序集和资源;
  • 多个 CPU 架构的 .so
  • IL2CPP 编译任务。

而我们编译Android Library的工程只有Android Java代码,是构建一个纯Java Android Library的AAR所以不需要使用NDK工具,如果未来需要包含C++或者.so或者使用JNI啥的才需要NDK。

导出Android Library AAR

打开Gradle窗口(View-> Tool Windows -> Gradle),准备导出Library AAR:

点击Execute Gradle Task并输入:unitynativeframework:assembleRelease

等待执行完成后发现AAR生成成功:

AndroidLibraryBuildSuccess

导出成功后我们将导出的unitynativeframework-release.aar放到Unity工程的Plugins/Android/libs目录下等待使用。

CopyAARToAndroidPluginsLibFolder

为什么Build Gradle Task输入:unitynativeframework:assembleRelease会有效?

因为assembleRelease 就是 Android Gradle Plugin 自动创建的任务,不需要你手写。

assemble:装配、打包构建产物。

Release:构建 release 变体。

对于Android Library来说,

1
2
3
4
5
assembleRelease
→ 编译 Release 代码和资源
→ 处理 Manifest
→ 打包 classes.jar
→ 生成 Release AAR

对 Android Application 来说:

1
2
assembleRelease
→ 生成 Release APK

Task的名称来源于build.gradle里:

1
2
3
4
5
buildTypes {
release {
minifyEnabled false
}
}

以上配置Android Gradle Plugin就会创建assembleRelease的任务。

Unity访问原生代码

Android原生访问代码功能实例:

AndroidNativeManager.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
******

#if UNITY_ANDROID
/// <summary>
/// AndroidNativeManager.cs
/// Android原生管理类
/// </summary>
public class AndroidNativeManager : NativeManager
{
/// <summary>
/// Android主Activity对象
/// </summary>
private AndroidJavaObject mAndroidActivity;

/// <summary>
/// 初始化
/// </summary>
public override void init()
{
Debug.Log("AndroidNativeManager:init()");
Debug.Log($"Application.platform:{Application.platform}");
if (Application.platform == RuntimePlatform.Android)
{
// 这里使用using的目的是确保AndroidJavaClass对象尽快被删除
// Unity访问原生Android的Java对象抽象(源码查看Unity官方的UntiyPlayer.java文件)
using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
// 获得当前Activity(这里是为了获得MainActivity)
mAndroidActivity = jc.GetStatic<AndroidJavaObject>("currentActivity");
if (mAndroidActivity == null)
{
Debug.Log("获取UnityMainActivity::mAndroidActivity失败!");
}
else
{
Debug.Log("获取UnityMainActivity::mAndroidActivity成功!");
}
}
}
}

/// <summary>
/// 调用原生方法
/// </summary>
public override void callNativeMethod()
{
Debug.Log("AndroidNativeManager:callNativeMethod()");
if (mAndroidActivity != null)
{
mAndroidActivity.Call("javaMethod", "cs param");
}
}
}
#endif

然后在按钮点击的时候调用:

GameLauncher.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// <summary>
/// 游戏入口
/// </summary>
public class GameLauncher : MonoBehaviour
{
******

/// <summary>
/// 调用原生方法
/// </summary>
public void onCallNativeMethodClick()
{
Debug.Log("onCallNativeMethodClick()");
LogCallNativeMethodBtnClickEvent();
NativeManager.Singleton.callNativeMethod();
}
}

打包安装运行:

CustomMainActivityRunningLog

从日志可以看出我们成功实现了导出自定义AAR+指定我们自定义的MainActivity作为游戏启动Activity的功能。

我们点击按钮执行调用原生Android的javaMethod方法:

CallAndroidNativeMethodSuccess

从截图可以看出我们成功调用了我们在Android的MainActivity里自定义的javaMethod方法,同时我们通过UnityPlayer.UnitySendMessage的方式成功回调到了Unity测的NativeMessageHandler.resUnityMsg()方法和传参

至此我们想要实现搭建Android Library导出AAR(含自定义MainActivity入口)的框架流程就算正式打通了。

IOS原生开发

IOS原生开发同理,IOS也有属于自己的程序启动入口(UnityAppController.mm),结合以前的学习(详情参考:Unity原生])我们可以知道要想自定义IOS原生入口编写功能,我们需要编写一个继承至UnityAppController的类作为程序入口,目前IOS好像支持Objective-C和Swift,以下是以Objective-C为例:

AppController.h

1
2
3
4
5
6
7
8
// 自定义Unity AppCOntroller
//导入Unity的UnityAppController.h,自定义AppController需要继承至UnityAppController
#import "UnityAppController.h"

@interface AppController : UnityAppController
@property(nonatomic,strong) UINavigationController *naVC;
-(void) createUI;
@end

AppController.mm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//自身的头文件
#import "AppController.h"
//导入我们自己写接口的类的.h文件,用于实现自定义的UIViewController
#import "UnityViewController.h"
//其他文件
#import <Foundation/Foundation.h>

@implementation AppController
-(void) createUI
{
NSLog(@"AppController:createUI()");
_rootController = [[UIViewController alloc]init];
_rootView = [[UIView alloc]initWithFrame:[UIScreen mainScreen].bounds];
_rootController.view = _rootView;

//自定义viewcontroller添加到现有的viewcontroller上
UnityViewController *vc = [[UnityViewController alloc]init];
self.naVC =[[UINavigationController alloc]initWithRootViewController:vc];

//添加自定义view到rootView上
[_rootView addSubview:self.naVC.view];

_window.rootViewController = _rootController;
[_window bringSubviewToFront:_rootView];
[_window makeKeyAndVisible];
}
@end

//这是一个固定写法,因为这个代码,所以启动界面是从这里启动
IMPL_APP_CONTROLLER_SUBCLASS(AppController);

通过上面的代码,我们完成了下面两件事:

  1. 自定义Unity IOS程序入口
  2. 添加了自定义的View(绑定到自定义UIViewController上)到rootView上

成功绑定到自定义UIViewController上后,我们就能通过自定义的UIViewController去和IOS打交道了。

UnityViewController.h

1
2
3
4
5
6
7
//自定义的UIViewController
#import "UnityAppController.h"

@interface UnityViewController : UIViewController
//权限申请
- (void)audioAuthAction;
@end

UnityViewController.mm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//包含自身头文件
#import "UnityViewController.h"

//其他头文件
#import "UnityAppController+ViewHandling.h"
#import <UI/UnityView.h>
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

@implementation UnityViewController

- (void)viewDidLoad
{
NSLog(@"UnityViewController:viewDidLoad()");
[super viewDidLoad];
[self.view addSubview:GetAppController().unityView];
GetAppController().unityView.frame = self.view.frame;

// Do any additional setup after loading the view.
// 权限申请相关
[self audioAuthAction];
}

//权限申请
- (void)audioAuthAction
{
//麦克风权限申请
NSLog(@"UnityViewController:audioAuthAction()");
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
NSLog(@"%@",granted ? @"麦克风准许":@"麦克风不准许");
}];
}

-(void)viewWillAppear:(BOOL)animated{
//隐藏导航条view
NSLog(@"UnityViewController:viewWillAppear()");
self.navigationController.navigationBar.hidden = YES;
}

-(void)viewDidDisappear:(BOOL)animated
{
NSLog(@"UnityViewController:viewDidDisappear()");
self.navigationController.navigationBar.hidden = NO;
}
@end

可以看到通过自定义的UIViewController,我们就可以直接编写对应原生功能(e.g. 权限申请)代码了。

SDK

SDK 是「Software Development Kit」的縮寫,中文稱為「軟體開發工具套件」

很多跨平台功能需要处理原生的东西,且这些功能可以说是对大部分游戏都需求 统一的,所以很多时候这些功能(比如广告SDK,统计SDK,支付SDK等)会被做成SDK供我们直接集成使用。

Firebase

Firebase 是一组面向移动端的云服务和运营工具。它把账号、数据存储、云函数、崩溃收集、数据分析、远程配置、推送等常见能力组合在一起,并提供 Unity/C++ SDK。

Firebase能提供很多我游戏开发中会用到的一些SDK功能,比如崩溃统计,数据大大点,消息推送,登录授权等。

在游戏开发过程中如果我们的游戏走海外移动端,基本都会选Firebase。但在国内受限于网络的原因,部分Firebase功能我们需要选择其他替代品。

AI给出的国内外推荐服务选择:

能力 海外版 中国大陆版
崩溃监控 Firebase Crashlytics Bugly、国内 APM、Sentry
数据分析 Firebase Analytics 数数、神策、火山、友盟或自建
远程配置 Firebase Remote Config 国内 CDN/配置中心/自建
Android 推送 FCM 厂商推送或聚合推送
iOS 推送 FCM/APNs 优先评估直接 APNs
登录 Firebase Auth 国内账号、渠道账号、自研
云存档 Firestore 国内后端和数据库
云函数 Cloud Functions 国内云函数或自建服务
文件存储 Firebase Storage 国内对象存储和 CDN
实时战斗 专用游戏服务器 专用游戏服务器

Note:

  1. Firebase在PC端支持不太完善

Firebase Unity接入

接下来以实战Unity接入Firebase的崩溃监控数据功能为例。

Firebase接入条件限制:

  • 安装 Unity 2021 LTS 或更高版本。更早的版本也可能兼容,但不享受积极支持。

  • (仅限 Apple 平台)安装以下各项:

    • Xcode 16.2 版或更高版本
    • CocoaPods 1.12.0 或更高版本
  • 确保您的 Unity 项目满足以下要求:

    • 对于 iOS - 目标为 iOS 15 或更高版本
    • 对于 tvOS - 目标为 tvOS 15 或更高版本
    • 对于 Android - 目标 API 级别为 23 (Marshmallow) 或更高版本
  • 设置一台实体设备或使用模拟器运行您的应用。

    • 对于 Apple 平台 - 设置实体设备或使用 iOS 或 tvOS 模拟器。

      您是要用 Cloud Messaging?

    • 对于 Android - 模拟器必须使用包含 Google Play 的模拟器映像。

  • 使用您的 Google 账号登录 Firebase

详细Firebase接入Unity文档

登录Firebase创建项目

Firebase项目创建

CreateFirebaseProject

创建Firebase项目成功后可以看到我们创建的项目:

CreateFirebaseProjectSuccess

在Firebase中注册应用

打开我们创建的Firebase项目:

OpenFirebaseProject

点击添加应用后选择对应平台,这里以Android平台为例:

RegisterFirebaseAndroidApp

根据要求下载google-services.json文件并放到项目Assets目录下:

DownloadGoogleServicesJsonFile

SaveGoogleServicesJsonToAssetFolder

google-services.json内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"project_info": {
"project_number": "******",
"project_id": "unitynativeframework",
"storage_bucket": "unitynativeframework.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "******",
"android_client_info": {
"package_name": "com.TonyTang.UnityNativeFramework"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "******"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

可以看到根据我们注册应用传入的项目包名,Firebase生成了project_number,mobilesdk_app_id,api_key等相关的值。

添加Firebase Unity SDK

下载Firebase Unity SDK

DownloadAllFirebaseUnitySDK

可以看到里面包含了各种Firebase提供的SDK包,这里我以**数据统计SDK(FirebaseAnalytics.unitypackage)**作为实战SDK。

将FirebaseAnalytics.unitypackage导入Unity。

导入过程中会问一些是否激活特定额外功能,这个根据需求选择,我目前都选择了No,但最后提示Enable Android Auto-resolution时要选择Enable,让他自行解决下Android的一些依赖冲突:

EnableAndroidAutoResolution

AI的说法是这一步会根据项目所有第三方插件配置文件进行分析,去下载相关依赖AAR,更新Unity的Gradle模版等操作。

FirebaseAnalyticImportAndroidStaff

FirebaseApp.androidlib不是Firebase Analytics SDK本体,它是Firebase项目配置资料库(比如AndroidManifest.xml(定义Firebase的Android Activity)和google-services.xml(定义一些Android资源icon名称))

mainTemplate.gradle 这是Unity Android 构建中的主 Android Library 模块 Gradle 模板,主要用于生成unityLibrary/build.gradle

settingsTemplate.gradle是根Gradle工程,主要负责配置一些内容去决定哪里寻找插件和依赖、工程包含哪些模块。

gradleTemplate.properties是Unity 生成gradle.properties的模版,它控制整个 Gradle 构建进程的属性。

确认Google Play服务版本要求

需要再项目启动时找个时机添加一下CSharp代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using Firebase.Extensions;

private void Start () {
Debug.Log("GameLauncher:Start()");
******

FireBaseInitilization();
}

/// <summary>
/// Firebase初始化
/// </summary>
private void FireBaseInitilization()
{
// 检查Google Play服务版本要求
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available) {
// Create and hold a reference to your FirebaseApp,
// where app is a Firebase.FirebaseApp property of your application class.
var app = Firebase.FirebaseApp.DefaultInstance;
Debug.Log($"Firebase app.Name:{app.Name} dependencyStatus:{dependencyStatus}");
OnFirebaseCheckComplete();
// Set a flag here to indicate whether Firebase is ready to use by your app.
} else {
UnityEngine.Debug.LogError(System.String.Format(
"Could not resolve all Firebase dependencies: {0}", dependencyStatus));
// Firebase Unity SDK is not safe to use here.
}
});
}

FireBaseInitilization()里的代码就是Firebase Analytics检查Google Play服务版本要求的核心代码,注意看我们在打点登录事件调用是放在检查通过以后,原因是在Google Play服务版本要求检查通过之前,我们是不允许使用打点功能的。

打包测试Firebase Analytics

打包失败提示如下:

FirebaseAnalyticsBuildFailed

字面意思我们设置的Android最低版本是22,但Firebase Analytics要求Android最低版本是23,所以修改我们打包时设置的Android最低版本到23即可:

1
PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel23;

再次触发打包,依然报错:

FirebaseBuildErrorLog2

根据AI的说法是Unity 2022.3自带的AGP 7.4.2/D8版本较久。旧D8在解析时出错,建议我将minSdkVersion设置成24后终于打包成功了

安装测试可以看到Firebase Analytic的Google Play服务检查通过了:

FirebaseCheckGooglePlayServiceSuccess

Note:

  1. 我使用的Unity版本是2022.3.62f3,Unity Firebase SDK版本是13.15.0
Unity版Firebase Analytics使用入门

Unity版Google Analytics使用入门

Unity接入Firebase Analytics成功后,我们就能使用Firebase Analytics进行数据打点。

定义一个FBAParameters去定义我们Firebase Analytics自定义打点的参数名常量定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// <summary>
/// FBAParameters.cs
/// Firebase Analytics参数定义静态类
/// </summary>
public class FBAParameters
{
/// <summary>
/// 设备唯一标识符
/// </summary>
public const string DeviceUid = "device_uid";

/// <summary>
/// 调用原生方法按钮点击事件
/// </summary>
public const string CallNativeMethodBtnClick = "call_native_method_btn_click";
}

比如我打点一个登录事件打点,打点数据参数是用户设备UID:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/// <summary>
/// 设备唯一标识符
/// </summary>
private string DeviceUid;

private void Start () {
Debug.Log("GameLauncher:Start()");
DeviceUid = SystemInfo.deviceUniqueIdentifier;
Debug.Log($"Device UID: {DeviceUid}");

******
FireBaseInitilization();
}

/// <summary>
/// Firebase初始化
/// </summary>
private void FireBaseInitilization()
{
// 检查Google Play服务版本要求
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available) {
// Create and hold a reference to your FirebaseApp,
// where app is a Firebase.FirebaseApp property of your application class.
var app = Firebase.FirebaseApp.DefaultInstance;
Debug.Log($"Firebase app.Name:{app.Name} dependencyStatus:{dependencyStatus}");
OnFirebaseCheckComplete();
// Set a flag here to indicate whether Firebase is ready to use by your app.
} else {
UnityEngine.Debug.LogError(System.String.Format(
"Could not resolve all Firebase dependencies: {0}", dependencyStatus));
// Firebase Unity SDK is not safe to use here.
}
});
}

/// <summary>
/// Firebase检查完成回调
/// </summary>
private void OnFirebaseCheckComplete()
{
Debug.Log("OnFirebaseCheckComplete()");
LogLoginEvent();
TryCustomEvent();
}

/// <summary>
/// 打点登录事件
/// </summary>
private void LogLoginEvent()
{
Debug.Log("LogLoginEvent()");
FirebaseAnalytics.LogEvent(FirebaseAnalytics.EventLogin, FBAParameters.DeviceUid, DeviceUid);
}

/// <summary>
/// 尝试记录自定义事件
/// </summary>
private void TryCustomEvent()
{
Debug.Log("TryCustomEvent()");
FirebaseAnalytics.LogEvent(FBAParameters.CustomEvent, FBAParameters.DeviceUid, DeviceUid);
}

同时在打一个DIY的调用本地方法按钮点击次数统计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// <summary>
/// 点击调用原生方法按钮事件
/// </summary>
private void LogCallNativeMethodBtnClickEvent()
{
Debug.Log("LogCallNativeMethodBtnClickEvent()");
FirebaseAnalytics.LogEvent(FBAParameters.CallNativeMethodBtnClick, FBAParameters.DeviceUid, DeviceUid);
}

/// <summary>
/// 调用原生方法
/// </summary>
public void onCallNativeMethodClick()
{
Debug.Log("onCallNativeMethodClick()");
LogCallNativeMethodBtnClickEvent();
}

打包后验证:

FirebaseAnalyticLogEventRuntime

可以看到登录日志打点已经出发了,紧接着我点了两次调用Java方法按钮去触发原生方法按钮点击统计打点。(因为代码有点问题反复修改代码测试了多次,导致部分事件统计了多次)

我们打开Firebase对应项目,搜索Events(如果刚发送的没能立刻看到可以搜索Realtime Analytics):

FirebaseAnalyticEventsPage

可以看到无论是官方定义的事件明还是我们自定义的事件名都成功发送统计到了Firebase Analytics后台。

但奇怪的一点是我在Firebase Analytics Events页面上好像只有部分事件能看到我传递的自定义参数:

FirebaseEventLogParamsDetail

CustomLogEventParams

总的来说,Firebase Analytics的功能集成和使用算是基本成功,Firebase Analytics的接入告一段落。

AdMob

Reference

将 Firebase 添加到您的 Unity 项目

Modify Gradle project files with Gradle template files

Gradle for Android

Android App Manifest

Create a custom activity

文章目录
  1. 1. 前言
  2. 2. 原生开发
    1. 2.1. Android原生开发
      1. 2.1.1. Unity导出Gradle工程
      2. 2.1.2. 创建原生Android工程
      3. 2.1.3. 确保正确的Gradle,Android Gradle Plugin和JDK
      4. 2.1.4. 添加Android Library
      5. 2.1.5. 导出Android Library AAR
      6. 2.1.6. Unity访问原生代码
    2. 2.2. IOS原生开发
  3. 3. SDK
    1. 3.1. Firebase
      1. 3.1.1. Firebase Unity接入
        1. 3.1.1.1. 登录Firebase创建项目
        2. 3.1.1.2. 在Firebase中注册应用
        3. 3.1.1.3. 添加Firebase Unity SDK
        4. 3.1.1.4. 确认Google Play服务版本要求
        5. 3.1.1.5. 打包测试Firebase Analytics
        6. 3.1.1.6. Unity版Firebase Analytics使用入门
    2. 3.2. AdMob
  4. 4. Reference