游戏开发工具箱(3) 开箱即用的云游戏后端——PlayFab快速 ...
前言你是一位有些游戏客户端开发经验的开发者或爱好者,有空的时候也想自己开发几款游戏发布到线上跑跑。但是对实现游戏服务端并不熟悉。
网上是有一些开源的游戏客户端+服务端双端的框架,比如——ET,但是所有的功能仍然需要新造一遍轮子。我们想要的游戏后端功能其实很简单,无外乎——登录、物品管理、成就、排行榜、以及内购活动,如果自带埋点统计功能,就完美了。
有没有一个开箱即用的游戏后端服务,已经内置好了这些基础的功能,开发者只要轻松修改一下后台的配置,简单写几行胶水性的代码,就能在客户端中快速接入这些功能?而且完全不用担心网络延迟、动态扩容、DDoS攻击等专业性的问题?
这样的游戏后端服务现在已经趋于成熟,PlayFab就是让你省事省心的一款云托管游戏后端服务。
这篇文章分为上、中、下三篇。
上篇介绍PlayFab的基本概念,手把手带大家熟悉PlayFab的基本操作和Unity下的接口调用;中篇带大家一览官方的案例教程;下篇准备带大家学习,开箱即用的Unity端的插件Cross-platform Backend Solution,一览PlayFab在开发中的全貌(不过下篇可能会延期)。
简介
PlayFab是一个全方位的游戏后端云服务,为开发者提供了一系列游戏开发所需的功能,例如玩家管理、云端存储、虚拟商品交易、统计分析等。
它于2018年被微软收购并被整合到了Azure云服务当中。
目前它已经支持了超过5000多款游戏,托管了超过25亿玩家的账户。像微软的《我的世界》、育碧的《彩虹六号:围攻》、Hello Game的《无人深空》等大作也都采用了它作为可靠的服务后端。
服务于众多游戏的PlayFab
接下来我们将一起探索——如何使用PlayFab的托管服务来轻松构建和管理游戏后端。
各位乘客请坐稳扶好,我们即将发车。
快速开始
注册PlayFab
在使用PlayFab之前,我们需要先注册一个PlayFab账号:https://developer.playfab.com/en-us/sign-up
注册PlayFab账号
需要注意一点,尽量使用邮箱地址注册而不是“Sign in with Microsoft”,使用后者在未来使用CloudScript时会遇到一些账号和权限的问题。
然后在邮箱中查收激活邮件,点击VERIFY YOUR EMAIL ADDRESS按钮。
激活邮件
接着,在跳转页面填写注册信息登录即可。
登录PlayFab
成功登录后,我们就第一次进入到了PlayFab的后台:
第一次进入到PlayFab后台
新建游戏后端项目
在第一次进入PlayFab后台时,PlayFab已经为我们默认生成了一个名为“My Game”的后台项目模板。
不过,还是让我们从手动创建一个新的游戏后台项目开始吧。
点击My Game Studio页签的右侧的“...”按钮,在弹出的列表中点选“New title”。
新建一个游戏后台项目
填写新项目的名称等信息(比如这里我命名新项目为HelloPlayfab),点击Create title按钮创建项目。
填写new title的名称
项目创建成功后,会自动跳转回最初的后台页面,现在就可以看到我们的新项目了。
新项目创建成功
红框圈出的新项目页签的右下角的ID:91135,未来在Unity客户端中会用到,需要留意一下。
点击HelloPlayfab页签,我们将进入到HelloPlayfab的项目后台。左侧是一些功能菜单,右侧是信息面板。
HelloPlayfab的项目后台
现在我们已经成功创建了HelloPlayfab项目的游戏后端,接下来我们将在Unity客户端项目中接入PlayFab的SDK,完成第一次客户端与游戏后端的网络通信。
新建Unity项目
我们先新建一个Unity工程,命名为HelloPlayfab。(你也可以直接打开自己想要接入PlayFab的Unity工程)
创建Unity工程
为Unity项目接入PlayFab SDK
我们从 https://learn.microsoft.com/zh-cn/gaming/playfab/sdks/unity3d/ 页面的下载链接中,点选“快速下载链接:PlayFab SDK的Unity编辑器扩展”。注意,只用下载“PlayFab SDK的Unity编辑器扩展”这一项就可以,它会在Unity中自动下载和管理Unity PlayFab SDK。
下载PlayFab SDK的Unity编辑器扩展
将下载完成的PlayFabEditorExtensions.unitypackage导入到项目工程(双击该文件)。
导入成功后,Unity中会自动弹出PlayFab的注册和登录页。前面我们已经注册过了,所以点击左下角的LOG IN按钮(看上去是个标签,实际是个按钮)。
点击LOG IN按钮
在登录页面填写自己的邮箱和前面注册的密码,点击LOG IN按钮。
填写账号信息
登录完成后,该页面会提示我们需要安装SDK,我们点击Install PlayFab SDK按钮。
点击Install PlayFab SDK
插件会自动下载最新的PlayFab SDK并完成安装,我们在安装完成的页面点击SET MY TITLE按钮,来完成对PlayFab后端项目的连接。
点击SET MY TITLE
我们选择STUDIO下的我们在上文【新建后端项目】中创建的HelloPlayfab项目,将REQUEST TYPE设置为Unity Web Request。
设置与游戏后端项目的连接
现在,我们已经完成了Unity项目对PlayFab SDK的接入,接下来就让我们开始写第一行Hello PlayFab吧!
Hello PlayFab
我们在工程中新建一个名为PlayFabHello.cs的脚本文件,将其挂载到场景中新建的名为PlayFabHello的GameObject上。
创建并挂载PlayFabHello脚本到GameObject上
将PlayFabHello.cs的内容替换为如下的代码:
using System;
using PlayFab;
using PlayFab.ClientModels;
using UnityEngine;
public class PlayFabHello : MonoBehaviour
{
public void Start()
{
if (string.IsNullOrEmpty(PlayFabSettings.staticSettings.TitleId))
{
//如果没有设置TitleId,就设置一个默认值
PlayFabSettings.staticSettings.TitleId = "91135";
}
var request = new LoginWithCustomIDRequest { CustomId = "GettingStartedGuide", CreateAccount = true};
PlayFabClientAPI.LoginWithCustomID(request, OnLoginSuccess, OnLoginFailure);
}
private void OnLoginSuccess(LoginResult result)
{
Debug.Log($"Hello PlayFab! {result.PlayFabId}");
}
private void OnLoginFailure(PlayFabError error)
{
Debug.LogError(error.GenerateErrorReport());
}
}
这段代码完成了为玩家在游戏后端进行注册和登录的功能。
点击运行游戏,如果从Console窗口看到如下日志,则第一次客户端同游戏云后台通信就成功了。
Hello PlayFab! 的日志
让我们回到游戏的后台,会看到已经有了第一个用户的登录统计信息了。
第一个用户登录的信息
选择页面左侧的Players页签,在右侧的信息页点击Search按钮,在查询到的玩家列表中点选第一个条目。
在Player页面中搜索自己
在弹出的页面中即可看到这个玩家(也就是我们自己)所有相关的数据信息了。玩家信息页的条目很多,第一次看到会觉得有点复杂,不过里面的概念和内容我们即将在后文中进行讲解。
玩家信息页的内容
截至目前,我们完成了PlayFab后端项目的创建,以及在Unity工程中SDK的接入,并且完成了第一次客户端向服务后端的注册和登录请求。在开始真正使用PlayFab提供的各种功能前,让我们先来了解一些PlayFab的基本概念,这将有助于我们更好地使用它。
基本概念
PlayFab数据(PlayFab Data)
PlayFab数据主要包含了:游戏数据(Title Data)、玩家数据(Player Data)、角色数据(Character Data)和组数据(Group Data)等数据类型。同时提供了实体(Entity)、内容分发网络(CDN)以及Webhooks等数据管理和配置的功能。
游戏数据(Title Data)
游戏数据是一组纯文本的键值对(Key/Value Pairs,简称KVP),用于在服务器后端存储和管理游戏的配置信息。游戏数据又细分为基本游戏数据(Title Data)和内部游戏数据(Title Internal Data),前者客户端可以直接访问,而后者只能被PlayFab后端或者开发者自己部署的经过授权的游戏服务所访问。
玩家数据(Player Data)
玩家数据,也称为User Data,在PlayFab系统里,User Data与Player Data是完全等价的。
玩家数据是应用于单个玩家或玩家组(共享)的数据,PlayFab提供两种存储玩家数据的方式:
[*]实体(Entity):允许跨玩家、角色和组将数据存储在对象和文件中。
[*]玩家数据(UserData):由PlayFab存储为键值对(KVP)形式的信息。
官方推荐新的项目使用实体的方式。
玩家数据的访问权限有三种类型:
[*]客户端:游戏客户端可以直接增删查改该类型的数据。
[*]只读:服务器可以增删查改该类型的数据,而客户端只能读取,不能进行操作。
[*]内部:只有服务器能增删查改的数据,客户端是无法访问的。
PlayFab内置了一些基本的玩家数据集合和系统,比如玩家档案(Player Profiles)、玩家背包(Player Inventory)、玩家详情(Player Details)、玩家封号系统(Player Ban System)、玩家细分(Player Segments)等。这些内容可以从文末的扩展阅读中进一步了解和学习。
实体(Entity)
实体是PlayFab API在操作时最基本的可寻址“内容”。 每个实体都有一个“类型”和 ID,它们共同组成了该实体的唯一标识。 有些类型的实体是PlayFab内置的,例如命名空间(namespace)、游戏(title)、组(group)、所有游戏共享玩家实体(master_player_account)和 本款游戏玩家实体(title_player_account)等。
每个实体都有一个配置文件,其中包含该实体拥有的各种资源。 例如,对象、文件、语言设置、策略等。
实体之间存在父子级关系,这些关系决定了实体间资源访问的权限。
PlayFab经济(PlayFab Economy)
PlayFab经济系统可以帮助游戏开发者轻松创建、管理和优化游戏中的虚拟经济系统。它可以管理虚拟货币、道具、消费品等虚拟物品,并提供了像收入跟踪、道具管理、虚拟商店等的相关功能。目前PlayFab已经提供了最新的v2预览版,不过在本文中,我们先从原始版本进行讲解。
PlayFab经济系统涉及到了如下的关键概念:
[*]目录(Catalog):目录是所有可用虚拟物品的列表。
[*]目录项(Catalog Item):目录项是代表了所有可用的虚拟商品,从捆绑包到宝箱,都属于它的范畴。
[*]背包/库存(Inventory):每个玩家账号都有一个背包,它包含了玩家拥有的所有物品实例以及获得这些物品的历史记录。
[*]商店(Store):商店是目录的子集,在商店中的物品售价会替代原始目录中的售价。
[*]商店细分(Store Segments):为特定分组的玩家提供的不同的虚拟商品目录。
[*]虚拟货币(Virtual Currency):v1提供10余种虚拟货币(v2提供1000种)。虚拟货币可以从目录或商店中购买虚拟物品。
[*]用户生成内容(User Generate Content):只有v2提供,玩家能够创建、上传和搜索的游戏内容。
PlayFab分析(PlayFab Analytics)
PlayFab还提供了一系列的工具来帮助开发者进行数据统计和分析,甚至进行A/B测试(A/B Test)。
就此打住
我猜大家看到这里,已经手痒难耐,不想再看这些枯燥的概念了。那就让我们直接上手快速地使用一下PlayFab提供的基础API吧。
速览API
现在让我们快速过一遍PlayFab的常用API。这主要涉及到两个脚本。
准备Unity工程
打开我们在[快速开始->Hello PlayFab]一节中创建的Unity项目,在工程中新建一个脚本命名为PlayFabUsage.cs,将默认生成的代码替换为如下内容:
using System;
using System.Collections.Generic;
using PlayFab;
using PlayFab.ClientModels;
using PlayFab.DataModels;
using UnityEngine;
public class PlayFabUsage
{
#region 使用游戏数据(TitleData)
public static void GetTitleData()
{
var request = new PlayFab.ClientModels.GetTitleDataRequest();
PlayFab.PlayFabClientAPI.GetTitleData(request, result =>
{
foreach (var dataPair in result.Data)
{
Debug.Log($"GetTitleData == {dataPair.Key} == {dataPair.Value}");
}
},
error =>
{
Debug.LogError($"GetTitleData == {error.GenerateErrorReport()}");
}
);
}
#endregion
#region 使用用户数据(UserData)
public static void UpdateUserDataRequest(string userId, string loginTime)
{
var updateUserDataRequest = new PlayFab.ClientModels.UpdateUserDataRequest
{
Data = new Dictionary<string, string>
{
{&#34;UserId&#34;, userId},
{&#34;LoginTime&#34;, loginTime}
}
};
PlayFab.PlayFabClientAPI.UpdateUserData(updateUserDataRequest, result =>
{
Debug.Log($&#34;UpdateUserDataRequest == Success {result.DataVersion}&#34;);
},
error =>
{
Debug.LogError($&#34;UpdateUserDataRequest == {error.GenerateErrorReport()}&#34;);
}
);
}
public static void GetUserDataRequest()
{
var request = new PlayFab.ClientModels.GetUserDataRequest();
PlayFab.PlayFabClientAPI.GetUserData(request, result =>
{
foreach (var dataPair in result.Data)
{
Debug.Log($&#34;GetUserDataRequest == {dataPair.Key} == {dataPair.Value.Value}&#34;);
}
},
error =>
{
Debug.LogError($&#34;GetUserDataRequest == {error.GenerateErrorReport()}&#34;);
}
);
}
#endregion
#region 使用实体数据(EntityData)
public static void SetEntityObject(string entityKey, string entityType)
{
var debugData = new Dictionary<string, object>()
{
{&#34;Atk&#34;, 100},
{&#34;Hp&#34;, 1000},
};
var dataList = new List<SetObject>()
{
new SetObject()
{
ObjectName = &#34;MyPlayerData&#34;,
DataObject = debugData,
},
};
var newSetObjectRequest = new SetObjectsRequest()
{
Entity = new PlayFab.DataModels.EntityKey(){Id = entityKey, Type = entityType},
Objects = dataList,
};
PlayFabDataAPI.SetObjects(newSetObjectRequest, setResult =>
{
Debug.Log($&#34;SetEntityObject == Success with version {setResult.ProfileVersion}&#34;);
},
error =>
{
Debug.LogError(error.ErrorMessage);
});
}
public static void GetEntityObject(string entityKey, string entityType)
{
var newGetObjectRequest = new GetObjectsRequest()
{
Entity = new PlayFab.DataModels.EntityKey(){Id = entityKey, Type = entityType},
};
PlayFabDataAPI.GetObjects(newGetObjectRequest, getResult =>
{
Debug.Log($&#34;GetEntityObject == Success with version {getResult.ProfileVersion}&#34;);
foreach (var dataPair in getResult.Objects)
{
Debug.Log($&#34;GetEntityObject == {dataPair.Key} == {dataPair.Value.DataObject}&#34;);
}
},
error =>
{
Debug.LogError(error.ErrorMessage);
});
}
#endregion
#region 购买物品
public static void PurchaseHealthPotion(Action<string> onPurchaseFinish)
{
var purchaseItemRequest = new PlayFab.ClientModels.PurchaseItemRequest
{
CatalogVersion = &#34;Items&#34;,
ItemId = &#34;HealthPotion&#34;,
Price = 10,
VirtualCurrency = &#34;CN&#34;
};
PlayFab.PlayFabClientAPI.PurchaseItem(purchaseItemRequest, result =>
{
Debug.Log($&#34;PurchaseHealthPotion == {result.Request.GetType().Name} Success {result.Items.ItemId}&#34;);
onPurchaseFinish?.Invoke(result.Items.ItemInstanceId);
},
error =>
{
Debug.LogError($&#34;PurchaseHealthPotion == {error.GenerateErrorReport()}&#34;);
onPurchaseFinish?.Invoke(string.Empty);
}
);
}
public static void GetUserInventory()
{
var request = new PlayFab.ClientModels.GetUserInventoryRequest();
PlayFab.PlayFabClientAPI.GetUserInventory(request, result =>
{
foreach (var item in result.Inventory)
{
Debug.Log($&#34;GetUserInventory == {item.ItemId} == {item.DisplayName} : {item.ItemInstanceId} count: {(item.RemainingUses.HasValue ? item.RemainingUses.Value : 0)}&#34;);
}
},
error =>
{
Debug.LogError($&#34;GetUserInventory == {error.GenerateErrorReport()}&#34;);
}
);
}
public static void ConsumePotion(string itemInstanceId)
{
var consumeItemRequest = new PlayFab.ClientModels.ConsumeItemRequest
{
ItemInstanceId = itemInstanceId,
ConsumeCount = 1
};
PlayFab.PlayFabClientAPI.ConsumeItem(consumeItemRequest, result =>
{
Debug.Log($&#34;ConsumePotion == {result.Request.GetType().Name}-{itemInstanceId} Success&#34;);
},
error =>
{
Debug.LogError($&#34;ConsumePotion == {error.GenerateErrorReport()}&#34;);
}
);
}
#endregion
#region 使用排行榜
public static void SubmitHighScore(int highScore)
{
var request = new PlayFab.ClientModels.UpdatePlayerStatisticsRequest
{
Statistics = new List<PlayFab.ClientModels.StatisticUpdate>
{
new PlayFab.ClientModels.StatisticUpdate
{
StatisticName = &#34;Daily High Score&#34;,
Value = highScore
}
}
};
PlayFab.PlayFabClientAPI.UpdatePlayerStatistics(request, result =>
{
Debug.Log($&#34;SubmitHighScore == {result.Request.GetType().Name} Success&#34;);
},
error =>
{
Debug.LogError($&#34;SubmitHighScore == {error.GenerateErrorReport()}&#34;);
}
);
}
public static void GetLeaderboard(int highScoreCount)
{
var request = new PlayFab.ClientModels.GetLeaderboardRequest
{
StatisticName = &#34;Daily High Score&#34;,
StartPosition = 0,
MaxResultsCount = 10
};
PlayFab.PlayFabClientAPI.GetLeaderboard(request, result =>
{
Debug.Log($&#34;GetLeaderboard == {result.Request.GetType().Name} Success&#34;);
foreach (var player in result.Leaderboard)
{
Debug.Log($&#34;GetLeaderboard == {player.Position} == {player.PlayFabId} == {player.StatValue}&#34;);
}
},
error =>
{
Debug.LogError($&#34;GetLeaderboard == {error.GenerateErrorReport()}&#34;);
}
);
}
public static void GetLeaderboardAroundPlayer(int highScoreCount)
{
var request = new PlayFab.ClientModels.GetLeaderboardAroundPlayerRequest
{
StatisticName = &#34;Daily High Score&#34;,
MaxResultsCount = highScoreCount
};
PlayFab.PlayFabClientAPI.GetLeaderboardAroundPlayer(request, result =>
{
Debug.Log($&#34;GetLeaderboardAroundPlayer == {result.Request.GetType().Name} Success&#34;);
foreach (var player in result.Leaderboard)
{
Debug.Log($&#34;GetLeaderboardAroundPlayer == {player.Position} == {player.PlayFabId} == {player.StatValue}&#34;);
}
},
error =>
{
Debug.LogError($&#34;GetLeaderboardAroundPlayer == {error.GenerateErrorReport()}&#34;);
}
);
}
#endregion
}
然后将我们之前创建的PlayFabHello.cs的代码替换为如下:
using System;
using PlayFab;
using PlayFab.ClientModels;
using UnityEngine;
public class PlayFabHello : MonoBehaviour
{
private string _loginUserId= string.Empty;
private string _loginTime = string.Empty;
private string _itemInstanceId = string.Empty;
private string _entityId = string.Empty;
private string _entityType = string.Empty;
public void Start()
{
if (string.IsNullOrEmpty(PlayFabSettings.staticSettings.TitleId))
{
//如果没有设置TitleId,就设置一个默认值
PlayFabSettings.staticSettings.TitleId = &#34;91135&#34;;
}
var request = new LoginWithCustomIDRequest { CustomId = &#34;GettingStartedGuide&#34;, CreateAccount = true};
PlayFabClientAPI.LoginWithCustomID(request, OnLoginSuccess, OnLoginFailure);
}
public void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
PlayFabUsage.GetTitleData();
}
if (Input.GetKeyDown(KeyCode.B))
{
if(string.IsNullOrEmpty(_loginUserId) || string.IsNullOrEmpty(_loginTime))
{
Debug.LogError(&#34;请先登录&#34;);
return;
}
PlayFabUsage.UpdateUserDataRequest(_loginUserId, _loginTime);
}
if (Input.GetKeyDown(KeyCode.C))
{
PlayFabUsage.GetUserDataRequest();
}
if(Input.GetKeyDown(KeyCode.D))
{
if(string.IsNullOrEmpty(_entityId) || string.IsNullOrEmpty(_entityType))
{
Debug.LogError(&#34;请先登录&#34;);
return;
}
PlayFabUsage.SetEntityObject(_entityId, _entityType);
}
if (Input.GetKeyDown(KeyCode.E))
{
if(string.IsNullOrEmpty(_entityId) || string.IsNullOrEmpty(_entityType))
{
Debug.LogError(&#34;请先登录&#34;);
return;
}
PlayFabUsage.GetEntityObject(_entityId, _entityType);
}
if (Input.GetKeyDown(KeyCode.F))
{
PlayFabUsage.PurchaseHealthPotion(itemInstanceId =>
{
_itemInstanceId = itemInstanceId;
});
}
if (Input.GetKeyDown(KeyCode.G))
{
PlayFabUsage.GetUserInventory();
}
if(Input.GetKeyDown(KeyCode.H))
{
if(string.IsNullOrEmpty(_itemInstanceId))
{
Debug.LogError(&#34;请先购买物品&#34;);
return;
}
PlayFabUsage.ConsumePotion(_itemInstanceId);
}
if (Input.GetKeyDown(KeyCode.I))
{
PlayFabUsage.SubmitHighScore(UnityEngine.Random.Range(0,1000));
}
if (Input.GetKeyDown(KeyCode.J))
{
PlayFabUsage.GetLeaderboard(10);
}
if (Input.GetKeyDown(KeyCode.K))
{
PlayFabUsage.GetLeaderboardAroundPlayer(10);
}
}
private void OnLoginSuccess(LoginResult result)
{
Debug.Log($&#34;Hello PlayFab! {result.PlayFabId}&#34;);
_loginUserId = result.PlayFabId;
_loginTime = DateTime.Now.ToString(&#34;yyyy-MM-dd HH:mm:ss&#34;);
_entityId = result.EntityToken.Entity.Id;
_entityType = result.EntityToken.Entity.Type;
}
private void OnLoginFailure(PlayFabError error)
{
Debug.LogError(error.GenerateErrorReport());
}
}替换完成后,让我们来逐一调试PlayFab的提供的各种功能。
获取游戏数据(Title Data)
我们先在PlayFab后台左侧选中Content,在右侧顶部选中Title Data,然后在TITLE DATA条目中点击蓝色的New Title Data按钮,创建属于游戏的配置数据:
创建title data
然后在新页面中添加两个键值对: 以及 ,记得点击保存(Save)按钮。
新建两个kvp
运行Unity工程,在游戏运行后,用键盘按A键,即可在Console窗口看到获取到游戏数据的日志:
获取游戏数据的日志
获取游戏数据的API是PlayFab.PlayFabClientAPI.GetTitleData,示例代码在PlayFabUsage.cs中:
public static void GetTitleData()
{
var request = new PlayFab.ClientModels.GetTitleDataRequest();
PlayFab.PlayFabClientAPI.GetTitleData(request, result =>
{
foreach (var dataPair in result.Data)
{
Debug.Log($&#34;GetTitleData == {dataPair.Key} == {dataPair.Value}&#34;);
}
},
error =>
{
Debug.LogError($&#34;GetTitleData == {error.GenerateErrorReport()}&#34;);
}
);
}如果你认真阅读上文中的基本概念了,就不会问“那么写游戏数据是什么API呢?”之类的问题了。
使用用户数据(User Data)
因为客户端有对玩家数据的增删查改的权限,所以我们直接运行客户端,先用键盘按B键发出更新玩家数据的请求,然后按C键,拉取玩家数据。Console窗口会显示如下日志:
修改和获取玩家数据的日志
在PlayFab后台,筛选出玩家后也能看到他的玩家数据:
PlayFab后台查看玩家数据
新建或者修改玩家数据的API是:PlayFab.PlayFabClientAPI.UpdateUserData,读取玩家数据的API为:PlayFab.PlayFabClientAPI.GetUserData。示例代码在PlayFabUsage.cs中:
public static void UpdateUserDataRequest(string userId, string loginTime)
{
var updateUserDataRequest = new PlayFab.ClientModels.UpdateUserDataRequest
{
Data = new Dictionary<string, string>
{
{&#34;UserId&#34;, userId},
{&#34;LoginTime&#34;, loginTime}
}
};
PlayFab.PlayFabClientAPI.UpdateUserData(updateUserDataRequest, result =>
{
Debug.Log($&#34;UpdateUserDataRequest == Success {result.DataVersion}&#34;);
},
error =>
{
Debug.LogError($&#34;UpdateUserDataRequest == {error.GenerateErrorReport()}&#34;);
}
);
}
public static void GetUserDataRequest()
{
var request = new PlayFab.ClientModels.GetUserDataRequest();
PlayFab.PlayFabClientAPI.GetUserData(request, result =>
{
foreach (var dataPair in result.Data)
{
Debug.Log($&#34;GetUserDataRequest == {dataPair.Key} == {dataPair.Value.Value}&#34;);
}
},
error =>
{
Debug.LogError($&#34;GetUserDataRequest == {error.GenerateErrorReport()}&#34;);
}
);
}使用实体数据(Entity Data)
同样的,客户端也对Entity Data有增删查改的权限,让我们再次运行Unity客户端。在键盘上按下E,读取后端实体数据;然后按下D设置玩家的实体数据;最后再按下E,重新读取后端的实体数据。Console中的日志如下:
修改并查询Entity Data
在PlayFab后台,筛选出玩家后能看到他的实体数据:
PlayFab后台查看玩家实体数据
特别的,需要留意蓝色框选的文字,PlayFab的免费账号,只能为每个玩家提供5个1000字节以下的实体数据对象。
新建或者修改实体数据对象的API是:PlayFabDataAPI.SetObjects,读取实体数据对象的API为:PlayFabDataAPI.GetObjects。示例代码在PlayFabUsage.cs中:
public static void SetEntityObject(string entityKey, string entityType)
{
var debugData = new Dictionary<string, object>()
{
{&#34;Atk&#34;, 100},
{&#34;Hp&#34;, 1000},
};
var dataList = new List<SetObject>()
{
new SetObject()
{
ObjectName = &#34;MyPlayerData&#34;,
DataObject = debugData,
},
};
var newSetObjectRequest = new SetObjectsRequest()
{
Entity = new PlayFab.DataModels.EntityKey(){Id = entityKey, Type = entityType},
Objects = dataList,
};
PlayFabDataAPI.SetObjects(newSetObjectRequest, setResult =>
{
Debug.Log($&#34;SetEntityObject == Success with version {setResult.ProfileVersion}&#34;);
},
error =>
{
Debug.LogError(error.ErrorMessage);
});
}
public static void GetEntityObject(string entityKey, string entityType)
{
var newGetObjectRequest = new GetObjectsRequest()
{
Entity = new PlayFab.DataModels.EntityKey(){Id = entityKey, Type = entityType},
};
PlayFabDataAPI.GetObjects(newGetObjectRequest, getResult =>
{
Debug.Log($&#34;GetEntityObject == Success with version {getResult.ProfileVersion}&#34;);
foreach (var dataPair in getResult.Objects)
{
Debug.Log($&#34;GetEntityObject == {dataPair.Key} == {dataPair.Value.DataObject}&#34;);
}
},
error =>
{
Debug.LogError(error.ErrorMessage);
});
}
用虚拟货币购买物品
接下来让我们看一下更复杂的功能——用虚拟货币购买物品(并自动装进背包)。
我们先在PlayFab后台定义一个虚拟货币。点击左侧的Economy,选中右侧顶部的Currency,在信息页中点击New Currency按钮。
创建新虚拟货币
在新虚拟货币页填写虚拟货币的基本信息,此处我们将货币code设为“CN”,显示名称为Coin,并设置玩家的初始Coin数量为1000。
填写Coin货币数据
接着,我们定义一个新目录(Catalogs)。点击左侧Economy,在右侧选中Catalogs,在信息页中点击New catalog按钮。
创建新目录
在新目录页面将其命名为“Items”,并点击保存。
创建Items目录
在新的Items页面中,点击右侧的New Item按钮,创建一个新的Item
创建新Item
我们新添加的Item为HealthPotion(生命药水),填写好相应的信息后,注意下部的PRICES栏,将Currency设置为CN(我们上一步新建的虚拟货币),Amount设为10。即玩家可以消耗10CN币获得一个HealthPotion。
添加新的生命药水
现在,我们已经准备好了虚拟货币和物品,就差完成购买,然后装进背包了(这一步是自动的)。
运行Unity,我们在键盘上按F键请求购买生命药水;然后按G键获得背包所有内容;最后按H键消耗一瓶购买的生命药水。Console窗口输出的日志如下:
购买和消耗生命药水日志
细心的你可能已经发现了,为什么一次购买就获得了10瓶生命药水呢?因为在前面配置Catalog Item信息的时候,右侧的Consumable->By Count填写的是10。
PlayFab购买物品的API是PlayFab.PlayFabClientAPI.PurchaseItem;获取背包的API为:PlayFab.PlayFabClientAPI.GetUserInventory;消耗物品的API为:PlayFab.PlayFabClientAPI.ConsumeItem。示例代码在PlayFabUsage.cs中:
public static void PurchaseHealthPotion(Action<string> onPurchaseFinish)
{
var purchaseItemRequest = new PlayFab.ClientModels.PurchaseItemRequest
{
CatalogVersion = &#34;Items&#34;,
ItemId = &#34;HealthPotion&#34;,
Price = 10,
VirtualCurrency = &#34;CN&#34;
};
PlayFab.PlayFabClientAPI.PurchaseItem(purchaseItemRequest, result =>
{
Debug.Log($&#34;PurchaseHealthPotion == {result.Request.GetType().Name} Success {result.Items.ItemId}&#34;);
onPurchaseFinish?.Invoke(result.Items.ItemInstanceId);
},
error =>
{
Debug.LogError($&#34;PurchaseHealthPotion == {error.GenerateErrorReport()}&#34;);
onPurchaseFinish?.Invoke(string.Empty);
}
);
}
public static void GetUserInventory()
{
var request = new PlayFab.ClientModels.GetUserInventoryRequest();
PlayFab.PlayFabClientAPI.GetUserInventory(request, result =>
{
foreach (var item in result.Inventory)
{
Debug.Log($&#34;GetUserInventory == {item.ItemId} == {item.DisplayName} : {item.ItemInstanceId} count: {(item.RemainingUses.HasValue ? item.RemainingUses.Value : 0)}&#34;);
}
},
error =>
{
Debug.LogError($&#34;GetUserInventory == {error.GenerateErrorReport()}&#34;);
}
);
}
public static void ConsumePotion(string itemInstanceId)
{
var consumeItemRequest = new PlayFab.ClientModels.ConsumeItemRequest
{
ItemInstanceId = itemInstanceId,
ConsumeCount = 1
};
PlayFab.PlayFabClientAPI.ConsumeItem(consumeItemRequest, result =>
{
Debug.Log($&#34;ConsumePotion == {result.Request.GetType().Name}-{itemInstanceId} Success&#34;);
},
error =>
{
Debug.LogError($&#34;ConsumePotion == {error.GenerateErrorReport()}&#34;);
}
);
}使用排行榜
要使用排行榜,我们需要先在PlayFab后台新建一个排行榜。选中左侧的Leaderboard,在右侧信息页面点击New Leaderboard按钮。
创建新排行榜
PlayFab支持不同刷新频率和排行方法的排行榜,我们就新建一个简单的每日高分排行榜如下:
填写排行榜信息
特别注意,因为排行榜依赖于玩家上传的分数信息,所以需要在后台项目中开启相应的权限,具体操作如下:
点击页面左上角的齿轮设置按钮,选择Title settings
选择Title settings
在设置页面中选中右侧顶部的API Features,勾选红色框选的Allow client to post player statistics选项,最后点击Save保存。
我们已经在PlayFab后台完成了排行榜的基本设置,只需运行Unity即可。先在键盘上按下I(大写字母I,不是数字1)来上传一个随机的成绩;再按下J来获取最高的10条排行榜信息;最后按下K来获取玩家排行附近的10条信息。
Console窗口输出的日志如下:(不过因为目前只有我们自己一个用户,所以J和K返回的数据一样)
输出排行榜信息
玩家上传成绩到排行榜的API是PlayFab.PlayFabClientAPI.UpdatePlayerStatistics;获取排行榜最高的N个玩家数据的API为PlayFab.PlayFabClientAPI.GetLeaderboard;获取玩家排行附近N个玩家数据的API为PlayFab.PlayFabClientAPI.GetLeaderboardAroundPlayer。示例代码在PlayFabUsage.cs中:
public static void SubmitHighScore(int highScore)
{
var request = new PlayFab.ClientModels.UpdatePlayerStatisticsRequest
{
Statistics = new List<PlayFab.ClientModels.StatisticUpdate>
{
new PlayFab.ClientModels.StatisticUpdate
{
StatisticName = &#34;Daily High Score&#34;,
Value = highScore
}
}
};
PlayFab.PlayFabClientAPI.UpdatePlayerStatistics(request, result =>
{
Debug.Log($&#34;SubmitHighScore == {result.Request.GetType().Name} Success&#34;);
},
error =>
{
Debug.LogError($&#34;SubmitHighScore == {error.GenerateErrorReport()}&#34;);
}
);
}
public static void GetLeaderboard(int highScoreCount)
{
var request = new PlayFab.ClientModels.GetLeaderboardRequest
{
StatisticName = &#34;Daily High Score&#34;,
StartPosition = 0,
MaxResultsCount = 10
};
PlayFab.PlayFabClientAPI.GetLeaderboard(request, result =>
{
Debug.Log($&#34;GetLeaderboard == {result.Request.GetType().Name} Success&#34;);
foreach (var player in result.Leaderboard)
{
Debug.Log($&#34;GetLeaderboard == {player.Position} == {player.PlayFabId} == {player.StatValue}&#34;);
}
},
error =>
{
Debug.LogError($&#34;GetLeaderboard == {error.GenerateErrorReport()}&#34;);
}
);
}
public static void GetLeaderboardAroundPlayer(int highScoreCount)
{
var request = new PlayFab.ClientModels.GetLeaderboardAroundPlayerRequest
{
StatisticName = &#34;Daily High Score&#34;,
MaxResultsCount = highScoreCount
};
PlayFab.PlayFabClientAPI.GetLeaderboardAroundPlayer(request, result =>
{
Debug.Log($&#34;GetLeaderboardAroundPlayer == {result.Request.GetType().Name} Success&#34;);
foreach (var player in result.Leaderboard)
{
Debug.Log($&#34;GetLeaderboardAroundPlayer == {player.Position} == {player.PlayFabId} == {player.StatValue}&#34;);
}
},
error =>
{
Debug.LogError($&#34;GetLeaderboardAroundPlayer == {error.GenerateErrorReport()}&#34;);
}
);
}
以上就是 PlayFab快速上手指南(上) 的全部内容。
我们使用PlayFab+Unity快速调通了:玩家匿名注册和登录、客户端获取游戏配置、增删查改玩家数据及实体数据、用虚拟货币购买虚拟物品并管理背包,以及每日排行榜等基本功能。
还有一些重要的功能比如CloudScript我们尚未触及,不过会在下一篇(中篇)进行讲解,并剖析几个PlayFab官方提供的示例Demo来深入学习这套系统。
如果你对此感兴趣的话记得收藏哟,我们下(中)期见~
往期回顾
【游戏开发工具箱(1) 打造游戏感的利器——Unity Feel 插件浅析】
【游戏开发工具箱(2) 为所有人设计游戏——休闲向左硬核向右(上)】
【游戏开发工具箱(101) 未来已来——AI画图工具 Midjourney 漫游指南】
扩展阅读
PlayFab简介:
PlayFab账号登录基础知识和最佳实践:
PlayFab数据概览:
PlayFab经济快速入门:
PlayFab排行榜快速入门:
页:
[1]