如何制作使用于 Unity 的 Windows Store 插件

Unity3D 是一个功能强大的跨平台游戏开发引擎,支持市面上常见的各种平台,不过,跟各个平台相关的功能,只能使用插件来完成,本文将最近开发插件的心得做一点分享,希望能有更多人,因而更轻松地写出自己的插件,让 Unity 写出的程序,结合 Windows 平台的功能,更轻易的上架到 Windows 平台!


最近在写一个 Unity 的游戏,预计在游戏中显示广告,并加上 IAP 功能让用户购买,以去除广告项目,
因为打算支持 Windows 平台,所以要写个用于 Windows Store 的插件让 Unity 程序调用使用,
初步实践后,分享了一下目前我的作法,也希望对这方面有想法的朋友能够交流一下。
 
内容分为以下几个步骤:
一、建立 Unity editor 及 Windows app 使用的 plugin DLL project,一致化两个组件名称及命名空间,共用主程序
二、编写插件的主要内容,包括共用和各自的部分(条件编译)
三、将产生的插件 DLL 加到 Unity project,供 Unity 程序调用
 
一、
 
Windows Store plugin 要分别写两个部分:  供 Unity editor 调用及 Windows App 调用的部分,
第一个是在 Unity Editor 中执行程序时调用的 dll, (/Assets/Plugins/xxxx.dll)
第二个是在包成 windows app 时会调用的 dll (/Assets/Plugins/Metro/xxxx.dll)
 
这里使用 Visual Studio Community 2015 来编写,
首先,我们先做 Unity editor dll,在文件 - 新增项目 - Visual C# - WIndows - 传统桌面
新增一个类库,命名为 StorePlugin.Editor (如图)
 

 
 
因为两个 dll是分别写给 editor 和 app 使用,
除非 Unity 程序中要一直使用如 #if !UNITY_EDITOR 或 #if UNITY_METRO 的条件编译,让某些函数只在app内执行
否则最好两支 dll 中的 function,都一模一样,(该有一样的 function,但 内容则视平台而定)
所以 class name/function name都要设为一样, 因此我们将部分命名做些调整,
设定 项目 - StorePlugin.Editor 属性里的组件名称、默认命名空间改为 StorePlugin,
目标 Framework 也修改为 Unity 有支持的 .Net Framework 3.5 (太新的 .Net Framework 在 Unity 内执行会有问题)


并在属性 - 建置内,定义条件式编译的符号 UNITY_EDITOR

并将 namespace StorePlugin.Editor 改为 StorePlugin, Class1 改为 MyStore

 
改好了后会发现,程序库会不能 build,必须将 using System.Threading.Tasks; 移除后才正常,这里
改好后就可以保存,再来新增第二个程序库
 
 
接着选取 文件 - 新增项目 - Visual C# - WIndows - Windows 8 - 通用,输入项目名称 StorePlugin.Windows
 

 
 
接下来,如同 StorePlugin.Editor,也修改组件名称命名空间为 StorePlugin
 

 
 
并在属性 - 建置内,定义条件式编译的符号 UNITY_METRO,以便在写仅用于 app 的部分时,作条件式编译使用
 

 
 
不同的是,程序的部分,这里要将它删除! (之后与 StorePlugin.Editor 共用同一程序)
 

 
 
在项目名称上,选择 加入 - 现有项目
 

 
 
选择文件到 StorePlugin.Editor 下的 Class1.cs,
在下方的 加入钮旁边点选小三角形下拉,选择 加入做为连结,共用原来 StorePlugin.Editor 的 Class1.cs,
两个项目共用同一个程序档,以后只要改这只程序 Class1.cs即可,
 

 
 
 
二、
 
接下来,我们在我们的插件DLL中,加上一个简单的功能来做测试,
修改 Class1.cs,让程序抓到目前有的产品列表名称字符串并返回给 Unity,
 
这里要注意的是,因为是让 editor & windows 两支 dll 共用 Class1.cs 程序,所以不同的部分我们可以使用条件式编译来做区隔,
(#if !UNITY_EDITOR #else #endif), UNITY_EDITOR 的部分因为是用在 unity editor 调试,并不是执行于 WP8.1 or Win8.1环境下,
无法抓到产品列表,所以只能简单返回 "EDITOR_SKIP" 字符串代替,另外,store api 中 async/await function call的部分也无法在unity中使用,
目前我还没找到更好的方法,因此使用 polling 方法来取代原来 Widows App 的作法,如下
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#if !UNITY_EDITOR
using System.Threading.Tasks;
using Windows.ApplicationModel.Store;
#endif

namespace StorePlugin
{
    public class MyStore
    {
		static string retlist = "--";
		static bool isready = false;

		public static void IssueListProductInfo() { DoGetProductList(); }
		public static bool IsProductListReady() { return isready; }
		public static string GetProductListStr() { return retlist; }

#if !UNITY_EDITOR
		static async void DoGetProductList() { string retlist = await GetProductListStrAsync(); }
		static async Task GetProductListStrAsync()
		{
			try
			{
				isready = false;
				ListingInformation info = await CurrentAppSimulator.LoadListingInformationAsync();
				foreach (var product in info.ProductListings)
					retlist += product.Value.Name + " ";
			}
			catch (Exception e)
			{
				retlist = "Fail:" + e.ToString();
			}
			isready = true;
			return retlist;
		}
#else
		public static void DoGetProductList()
		{
			retlist = "EDITOR_SKIP";
			isready = true;
		}
#endif
	}
}
程序中,简单的调用测试用的 CurrentAppSimulator.LoadListingInformationAsync() 以得到产品资讯,
(正式版需将 CurrentAppSimulator 改为 CurrentApp, 另外 Windows Phone project 中, 要将 Package.appxmanifest 里的  并将产品名称存到字符串,以便之后用 GetProductListStr() 回传;
接下来分别建置 StorePlugin.Editor 及 StorePlugin.Windows 两个项目,得到两个 StorePlugin.dll



三、
 
Plugin 的程序部分告一段落,接下来换到 Unity 这边的实践,我们简单建一个名为 CallStore 的新 Unity project,
在 Assets 下,建立两个数据夹 Plugins, PluginsMetro,并开启文件总管,
将 StorePlugin.Editor/bin/Debug/StorePlugin.dll 拉到 AssetsPlugins下,
StorePlugin.Windows/bin/Debug/StorePlugin.dll拉到 AssetsPluginsMetro下,
 

 
 
并且,选取 AssetsPlugins/StorePlugin.dll ,在 Inspector 窗口 设定为 Editor 使用
 

 
 
接下来,我们写一支测试程序看执行结果,建一个 Scripts 目录并新增一个 TestStore.cs
 

using UnityEngine;
using System.Collections;

public class TestStore : MonoBehaviour {

	// Use this for initialization
	void Start () {
		StorePlugin.MyStore.IssueListProductInfo();
	}
	
	// Update is called once per frame
	void Update () {
	}

	void OnGUI()
	{
		if (StorePlugin.MyStore.IsProductListReady())
			GUI.Label(new Rect(10, 10, 200, 100), StorePlugin.MyStore.GetProductListStr());
		else GUI.Label(new Rect(10, 10, 200, 100), "Not ready");
	}
}


 
(于一开始送出 IssueListProductInfo(),并在 OnGUI() polling (if StorePlugin.MyStore.IsProductListReady()),直到 Ready 显示结果)
 
 
点选 Main Camera,并在 Inspector 窗口下,将 TestStore.cs 加入后,

在 Unity Editor直接执行,结果如:

接下来,产生实际的 APP 来测试,在 Build Setting 下产生 Universal8.1的两种程序

使用 Visual Studio Express 2003 for Windows 分别产生WP8.1及Win81的APP, 这样就完成了我们 Unity Windows Store Plugin 的第一个版本了!

(这里为什么不用 Visual Studio Community 2015 来产生? 因为我用它开启UNITY产生的project都会有错误消息!,我不知道是我人品不好还是怎么样,2013 每个版本用起来都很顺,2015 就是容易有问题! 开 Win10 项目有问题,开这个也有问题,什么时候会出 2016 啦???)

结果如下: WP8.1

 
Windows 8.1
 

 
参考文献: http://www.microsoftvirtualacademy.com/training-courses/porting-unity-games-to-windows-store-and-windows-phone
 
范例下载: UnityWinPlugin.zip