游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

手机开发过程中的一些常见界面

发布时间:2014-01-06 14:17:11 Tags:,,,,

作者:Arturs Sosins

作为Gideros(跨平台手机游戏开发工具)的一大开发者,我每天的工作便是整合不同平台的不同功能到我们的开发生态系统中。

因为Gideros提供了插件系统去延伸有效的功能,所以我们很容易添加每个所支持平台的任何原生功能,但是:

1.开发者并不会使用跨平台工具去编写平台特定插件

2.存在许多不同且可行的插件,也存在许多可行的广告平台,更不用说其它插件选择了

所以问题就在于如何满足大多数客户的需求,即无需编写并绑定到我们每个系统中而让他们更轻松地整合广告,应用内部购买以及其它平台特定程序库。

对此的想法是为每种常用的程序库类型创造一个插件,这将作为一个界面并且能够伴随着任何内部程序库而运行。

它实现了什么:

它加速了插件开发—-你可以重新使用同样的绑定代码,并在同样的界面下捆绑程序库函数。

它为开发者加速了代码整合—-例如他们逐渐熟悉了一个广告界面,并将其整合到自己的每款应用中。

它能够实现程序库之间更轻松的转变—-开发者可以面向每个广告程序库(基于所提供的程序库)反复使用同样的代码。

常见的广告界面

所以我们接近的第一个界面便是广告界面。尽管我们很容易想出界面本身,但因为原生执行所具有的巨大差异性,执行具有很大的难度。例如,有些广告的呈现就像标准的Android视角,而其它则呈现出一些内部对象,并尝试着附加到ViewGroup中。当其中一个广告立刻实体化,其它广告将等待来自服务器的反应,之后才能进行添加,定位与呈现。

我们所看到的结果界面包含了大多数开发者所需要的内容:

Ads.new(“框架名”)—-构造函数

Ads:setKey()—-提供程序库特定密钥

Ads:showAd(广告类型)—-呈现特定的广告

Ads:hideAd()—-隐藏广告

Ads:enableTesting()—-如可以的话去测试广告

Event.AD_RECEIVED—-收到的广告及其呈现

Event.AD_FAILED—-不能呈现广告

Event.AD_ACTION_BEGIN—-用户开始在广告上行动

Event.AD_ACTION_END—-用户行动结束

Event.AD_DISMISSED—-广告被从屏幕上删除

Event.AD_ERROR—-这是个错误(如不正确的设置等等)

常见的应用内部购买界面

执行常见的应用内部购买界面更加困难。主要是因为不同程序库之间的工作流程是完全不同的。但除此之外还需要考虑其它的问题。

不同商店的产品标识可能不同,所以你必须将其与一整天内部产品广告相匹配。

检测怎样的应用内部购买在安装应用的手机上是可行的。这在不同市场中的结果也是不同的,但是在Android里,它主要是以检测带有特定程序包的应用是否被安装与此而结束,以此去检测它是否会支持一个特定的市场。

Google Billing V3

GoogleBillingWorkflow(from gamasutra)

GoogleBillingWorkflow(from gamasutra)

Google Billing具有的主要区别在于,你必须在利用所有产品做任何事前知道它们所有的标识符。此外,你还需要消费可消费的产品,并留下利益。

IOS StoreKit

IOS StoreKit具有的主要区别在于,每次购买都必须进行确认。

StoreKit(from gamasutra)

StoreKit(from gamasutra)

Ouya IAP

在ouya的工作流程中,应用并不会收到有关购买事件的任何确认,也就是购买将由服务器进行处理,但是应用不会接收到相关通知。所以你必须记得等待购买并时不时试着复原它们而进行核对。

Ouya(from gamasutra)

Ouya(from gamasutra)

亚马逊IAP

亚马逊应用内部购买最烦人的一点便是分页结果,所以当你需要恢复购买时,它们将提供给你一串结果,但这可能不是所有的结果,你需要检查是否存在更多结果,并基于当前的偏移请求新页面。

此外亚马逊还不会提供收据ID,所以你必须独自去执行它(或使用一些第三方服务器)以核实被恢复的购买等等。不要忘记这一ID应该是独立于设备的,只与用户相连接,所以用户能够在任何设备通过自己的帐号恢复购买。

似乎基于恢复,亚马逊将返回所有的购买,不只是利益,还有消费品。

Amazon(from gamasutra)

Amazon(from gamasutra)

结果

为了包含所有的框架,我们要求用户提供产品Id数组,即通过使用密钥作为应用内部产品Id,并提供其中的消费品。当然,我们可以留给最终开发者消费品和确认信息,但这似乎不能涵盖界面内部的所有内容并为应用内部购买提供较小但却清楚的API。

所以关于所有提到的应用内部程序库最终的工作流程应该如下:

require “iab”
local iaps = IAB.detectStores()
iab = nil
if iaps[1] == “google” then
iab = IAB.new(iaps[1])
iab:setUp(“google-key”)
–using google product identifiers
iab:setProducts({p1 = “googleprod1″, p2 = “googleprod2″, p3 = “googleprod3″})
elseif iaps[1] == “amazon” then
iab = IAB.new(iaps[1])
–using amazon product identifiers
iab:setProducts({p1 = “amazonprod1″, p2 = “amazonprod2″, p3 = “amazonprod3″})
elseif iaps[1] == “ios” then
iab = IAB.new(iaps[1])
–using ios product identifiers
iab:setProducts({p1 = “iosprod1″, p2 = “iosprod2″, p3 = “iosprod3″})
end

–load previous purchases
purchases = dataSaver.loadValue(“purchases”)
–if there were no purchases
if not purchases then
–create and store empty table
purchases = {}
dataSaver.saveValue(“purchases”, purchases)
end

–if we have a supported store
if iab then
–set which products are consumables
iab:setConsumables({“p1″, “p2″})

–if purchase complete
iab:addEventListener(Event.PURCHASE_COMPLETE, function(e)
–if it was not previousle purchases
if not purchases[e.receiptId] then
–save purchase
purchases[e.receiptId] = true
dataSaver.saveValue(“purchases”, purchases)

if (e.productId == “p1″) then
–p1 was purchased
elseif (e.productId == “p2″) then
–p2 was purchased
elseif (e.productId == “p3″) then
–p3 was purchased
end
AlertDialog.new(“Purchase Completed”, “Purchase successfully completed”, “Ok”):show()
end
end)

–if there was an error
iab:addEventListener(Event.PURCHASE_ERROR, function(e)
AlertDialog.new(“Purchase Canceled”, e.error, “Ok”):show()
end)

–call restore on each app starts
–or make a button to allow users to restore purchases
iab:restore()
end

–some where in your code to make a purchase
stage:addEventListener(Event.MOUSE_UP, function()
if iab then
iab:purchase(“p1″)
end
end)

在制品(WIP)和未来的工作

我们在未来将致力的其它程序库类型将包含控制器界面(在制品),其最困难的部分便是跨越所有平台而映射出不同的控制器设备按键到一个常见的地图上。

下一个理念则是创造排行榜与成就界面,其最困难的任务则是同步不同框架之间的成就与数据。

常见界面的精通类将基于同样的界面在不同的平台上为不同的框架创造多人游戏支持。

本文为游戏邦/gamerboom.com编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦

Common Interfaces for mobile development (same code for all platforms)

by Arturs Sosins

As one of the developers of Gideros – cross platform mobile game development tool, my everyday task consist of incorporate different features of different platforms into our development eco system.

As Gideros offers plugin system for extending available features, it is quite easy to add almost any native functionality of each supported platform, but.

1.Developers do not come to cross platform tool to write platform specific plugins

2.There such a huge varriaty of possible plugins, there are tons of possible Advertising platforms, not even talking about other plugin options

So the problem was, how to satisfy the needs of the most clients, by making it easier for them to integrate Ads, In-app purchases and other platform specific libraries and without having to write and bind to our system every one of them.

And the idea was to create one single plugin for each type of commonly needed library, which would function as an interface and could work with any library wrapped underneath.

What it accomplishes:

It speeds up plugin development – you can reuse the same binding code, only wrapping library’s functions under same interface

It speeds up code integration for developers – they are getting familiar only with one single Ads Interface, for example, and integrate it in their every app

It allows to easily switch between libraries – developers can basically reuse same code for every Ads library which is under provided interface

Common Ads Interface

So the first one we approached was the Ads Interface. While it was quite easy to come up with the interface itself, the implementation proved quite difficult, due to huge difference of the native implementations. For example, some Ads behaved like standard Android Views, other held some internal objects and tried to get attached to the ViewGroup somehow internally. Ones instantiated immediately, while others waited for a response from server, and only then could be added, positioned and displayed.

The resulting interface we have looks something like that, and covers most of the developer needs:

Ads.new(“frameworkname”) –constructor

Ads:setKey() –provide library specific keys

Ads:showAd(adType) –display specific ad

Ads:hideAd() –hide ad

Ads:enableTesting() –enable test ads if available

Event.AD_RECEIVED –ad received and is displayed

Event.AD_FAILED — failed to display ad

Event.AD_ACTION_BEGIN –user began action on ad

Event.AD_ACTION_END –user action ended

Event.AD_DISMISSED –ad was removed from the screen

Event.AD_ERROR –there was an error (incorrect set up, etc)

Common In-App purchase interface

Implementing common In-App interface was much more difficult. Mostly because the workflows between different libraries, were completely different. But there were also other problems that should be considered.

Product identifiers for different stores could be different, so you would have to match them up into one single set of internal product Ids.

Detecting which In-app purchases are available on the phone, where the app is installed. This really differs from market to market, but on Android it mostly end with checking if app with specific package is installed, to check if it would support a specific market.

Google Billing V3

Main difference for Google Billing was the fact, that you had to knew all your product identifiers before you could do anything with them. Additionally you would need to consume the consumable products, while leaving entitlements.

IOS StoreKit

Main difference for IOS StoreKit, was that every purchase had to be confirmed.

Ouya IAP

In ouya’s worflow there are no confirmations of the purchase event received by the app, thus it brings to a situation, where the purchase was processed by the server, but the app was not notified of it. So you must remember pending purchases and recheck them by trying to restore them form time to time.

Amazon IAP

The most annoying thing with Amazon In-app purchases was the pagination of results, so when you call for restoring the purchases, they give you bunch of results, but that might not be all of them, you need to check if there is more and request new page with the current offset.

Additionally Amazon does not provide receipt ID, so you must implement it on your own (or use some 3rd party server) to verify restored purchases, etc. And don’t forget this ID should be device independent and only link to user, so user could restore the purchase from his account on any device.

And it seems that upon restoring Amazon returns all purchases, not only entitlements, but also consumables.

Result

To cover all frameworks, we required the user to provide the array (well actually table, since it is Lua) of product Ids, by using keys as app internal product Ids, and also provide which of them are consumables. Of crouse we could leave consumption and confirmation to end developer, but it seemed too appealing to cover all that internally in the interface and provide a small clean API for in-app purchases.

So the end united workflow for all mentioned In-app libraries looks like this:

require “iab”
local iaps = IAB.detectStores()
iab = nil
if iaps[1] == “google” then
iab = IAB.new(iaps[1])
iab:setUp(“google-key”)
–using google product identifiers
iab:setProducts({p1 = “googleprod1″, p2 = “googleprod2″, p3 = “googleprod3″})
elseif iaps[1] == “amazon” then
iab = IAB.new(iaps[1])
–using amazon product identifiers
iab:setProducts({p1 = “amazonprod1″, p2 = “amazonprod2″, p3 = “amazonprod3″})
elseif iaps[1] == “ios” then
iab = IAB.new(iaps[1])
–using ios product identifiers
iab:setProducts({p1 = “iosprod1″, p2 = “iosprod2″, p3 = “iosprod3″})
end

–load previous purchases
purchases = dataSaver.loadValue(“purchases”)
–if there were no purchases
if not purchases then
–create and store empty table
purchases = {}
dataSaver.saveValue(“purchases”, purchases)
end

–if we have a supported store
if iab then
–set which products are consumables
iab:setConsumables({“p1″, “p2″})

–if purchase complete
iab:addEventListener(Event.PURCHASE_COMPLETE, function(e)
–if it was not previousle purchases
if not purchases[e.receiptId] then
–save purchase
purchases[e.receiptId] = true
dataSaver.saveValue(“purchases”, purchases)

if (e.productId == “p1″) then
–p1 was purchased
elseif (e.productId == “p2″) then
–p2 was purchased
elseif (e.productId == “p3″) then
–p3 was purchased
end
AlertDialog.new(“Purchase Completed”, “Purchase successfully completed”, “Ok”):show()
end
end)

–if there was an error
iab:addEventListener(Event.PURCHASE_ERROR, function(e)
AlertDialog.new(“Purchase Canceled”, e.error, “Ok”):show()
end)

–call restore on each app starts
–or make a button to allow users to restore purchases
iab:restore()
end

–some where in your code to make a purchase
stage:addEventListener(Event.MOUSE_UP, function()
if iab then
iab:purchase(“p1″)
end
end)

WIP and future works

Other library types that we will work on in future would include Controller Interface (Work in progress), where the most difficult part will be to map different controller device buttons to one single common mapping across all platforms

Next idea is to create leader board and achievement interface, where the most difficult task could be synchronizing achievements and stats between different frameworks.

And master class of common interfaces would be to create multiplayer support for different frameworks on different platforms under the same interface.(source:gamasutra)


上一篇:

下一篇: