使用Epic在线服务管理玩家统计数据

Epic Games技术客户经理Rajen Kishna |
2021年12月14日
在该入门系列接下来的三篇文章中,我们将深入介绍三个密切相关的接口:统计排行榜成就。我们首先从统计开始,它用于管理玩家的统计数据,如收集的道具数量、玩家完成某个关卡的最短时间、胜利或失败的总次数等。在本文中,我们将介绍:
  • 统计、排行榜和成就
  • 更改我们的客户端策略
  • 在开发人员门户中创建统计
  • 使用鼠标点击模拟统计
  • 采集统计
  • 查询统计
  • 使用限制
  • 获取代码

统计、排行榜和成就

在深入探讨统计之前,我想先花点时间解释一下统计、排行榜和成就这三个接口之间的关系。统计接口提供了追踪玩家统计数据的底层系统,当通过排行榜接口为玩家排名,或通过成就接口自动解锁成就时,需要使用统计接口。统计接口也可以单独使用,不一定需要使用Epic在线服务的这些其他功能。

成就接口也提供了手动触发成就解锁的API,能够代替基于统计接口的自动解锁,但排行榜接口必须与底层的统计接口连接。在接下来的两篇文章中,我们将详细介绍这些接口。

更改我们的客户端策略

要使用统计接口,我们必须在客户端策略中添加操作:
 
  1. 访问https://dev.epicgames.com/portal/登录开发人员门户。
  2. 在左侧菜单中导航到你的产品,进入“产品设置”,在“产品设置”界面中点击“客户端”选项卡。
  3. 在你使用的客户端策略旁边点击三点图标,然后点击“详情”。
  4. 向下滚动至“功能”并点击“统计”旁的开关按钮。
  5. 勾选“findStatsForLocalUser”和“ingestForLocalUser”操作旁的复选框。
    • 值得注意的是,在这里,我们只使用所需的最少操作,这有助于防止滥用服务调用。
  6. 点击“保存并退出”。
 
Developer Portal Client Policy Stats
客户端策略允许的统计功能和操作

在开发人员门户中创建统计

统计应在开发人员门户中创建,可被定义为以下四种聚合类型中的一种:
 
  • SUM——所有被采集统计的合计值
  • LATEST——所有被采集统计中的最后一个
    • 请注意,这是唯一一种无法用于自动解锁成就的类型
  • MIN——所有被采集统计中最小的整数
  • MAX——所有被采集统计中最大的整数

对于所有统计聚合类型,被采集的统计都必须存储为单个32位整数。

我们来为每种聚合类型定义一个统计,这样就可以在示例应用程序中查看它们的行为。
  1. 在左侧菜单中导航到你的产品,然后依次点击“游戏服务”和“统计”。
  2. 在这里,可以看到我们为产品设置的每个部署的全部现有统计。在示例应用程序中选择正在使用的部署,并点击屏幕右上角蓝色的“新统计”按钮。
  3. 输入名称“SumStat”,并选择SUM聚合类型。点击蓝色的“创建”按钮完成创建。
  4. 为LATEST、MIN和MAX聚合类型重复步骤3,并将统计分别命名为“LatestStat”、“MinStat”和“MaxStat”。

Developer Portal Stats
开发人员门户中的统计

注意,在“搜索”按钮旁的右上角有一个连接图标。我们可以通过点击这个图标查看统计与哪些排行榜和成就相关联。目前,这里还看不到任何东西,但在即将发布的排行榜和成就文章中我们还会回到这里。

最后要注意的是“重置玩家统计”按钮旁蓝色的“新统计”按钮。点击它将出现一个弹出窗口,让我们可以通过玩家的PUID搜索他们,并在不影响统计定义本身的情况下重置他们的个人统计信息。

使用鼠标点击模拟统计

为了在我们的示例应用程序中模拟统计,我们要设置一个带有点击计数器的按钮。
 
  1. 在Views文件夹中创建一个名为“StatsView”的新用户控件:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <StackPanel Grid.Column="1" Grid.Row="0" Grid.RowSpan="2">
        <Button Width="100" Height="23" Margin="2" Content="Ingest stats" Command="{Binding StatsIngest}" />
        <Button Width="100" Height="23" Margin="2" Content="Query stats" Command="{Binding StatsQuery}" />
    </StackPanel>

    <StackPanel Grid.Column="0" Grid.Row="0">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Clicks:" Margin="2" />
            <TextBlock Text="{Binding Clicks}" Margin="2" />
        </StackPanel>

        <Button HorizontalAlignment="Left" Width="100" Height="23" Margin="2" Content="Add click" Command="{Binding StatsClick}" />
    </StackPanel>
</Grid>

 
  1. 打开StatsView.xaml.cs,附加ViewModel:

public partial class StatsView : UserControl
{
    public StatsViewModel ViewModel { get { return ViewModelLocator.Stats; } }

    public StatsView()
    {
        InitializeComponent();
        DataContext = ViewModel;
    }
}

 
  1. 在ViewModels文件夹中添加StatsViewModel.cs类:

public class StatsViewModel : BindableBase
{
    private int _clicks;
    public int Clicks
    {
        get { return _clicks; }
        set { SetProperty(ref _clicks, value); }
    }
}

 
  1. 在ViewModelLocator.cs中添加对StatsViewModel的引用:

private static StatsViewModel _statsViewModel;
public static StatsViewModel Stats
{
    get { return _statsViewModel ??= new StatsViewModel(); }
}

 
  1. 在Commands文件夹中添加StatsClickCommand.cs类:

public class StatsClickCommand : CommandBase
{
    public override bool CanExecute(object parameter)
    {
        return !string.IsNullOrWhiteSpace(ViewModelLocator.Main.ProductUserId);
    }

    public override void Execute(object parameter)
    {
        ViewModelLocator.Stats.Clicks++;
    }
}

 
  1. 打开StatsViewModel.cs,声明和实例化新命令:

public StatsClickCommand StatsClick { get; set; }

public StatsViewModel()
{
    StatsClick = new StatsClickCommand();
}

 
  1. 在ViewModelLocator.cs中向RaiseConnectCanExecuteChanged()方法添加以下代码行,确保我们只有在通过连接接口成功登录后才能调用统计UI:

Stats.StatsClick.RaiseCanExecuteChanged();
 
  1. 最后,将StatsView添加到MainWindow.xaml中的TabControl:

<TabItem x:Name="Stats" Header="Stats">
    <views:StatsView />
</TabItem>


现在,当我们运行应用程序,并通过身份验证接口或连接接口验证身份时,我们可以转到统计选项卡并使用按钮来增加点击计数。接下来我们将使用它来采集统计信息。

[IMAGE]
在我们的应用程序UI中模拟点击

采集统计

我们将使用刚刚创建的点击计数器,在同一时刻为我们创建的所有四个统计采集数据。这将突显出不同聚合类型之间的行为差异,并简化统计采集的调用。
 
  1. 在Services文件夹中添加StatsService.cs类来保存采集逻辑:

public static class StatsService
{
    public static void Ingest(int count)
    {
        var ingestStatOptions = new IngestStatOptions()
        {
            LocalUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId),
            TargetUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId),
            Stats = new IngestData[]
            {
                new IngestData() { StatName = "SumStat", IngestAmount = count },
                new IngestData() { StatName = "LatestStat", IngestAmount = count },
                new IngestData() { StatName = "MinStat", IngestAmount = count },
                new IngestData() { StatName = "MaxStat", IngestAmount = count }
            }
        };

        ViewModelLocator.Main.StatusBarText = $"Ingesting stats (count: <{count}>)...";

        App.Settings.PlatformInterface.GetStatsInterface().IngestStat(ingestStatOptions, null, (IngestStatCompleteCallbackInfo ingestStatCompleteCallbackInfo) =>
        {
            Debug.WriteLine($"IngestStat {ingestStatCompleteCallbackInfo.ResultCode}");

            if (ingestStatCompleteCallbackInfo.ResultCode == Result.Success)
            {
                ViewModelLocator.Stats.Clicks = 0;
            }
            ViewModelLocator.Main.StatusBarText = string.Empty;
        });
    }
}

 
  • 这里的调用非常简单:我们使用PUID和想要采集的统计数组来实例化Stats.IngestStatOptions。
  • 然后我们调用Stats.IngestStat,传入Options结构以完成采集。
 
  1. 在Commands文件夹中添加StatsIngestCommand.cs类:

public class StatsIngestCommand : CommandBase
{
    public override bool CanExecute(object parameter)
    {
        return !string.IsNullOrWhiteSpace(ViewModelLocator.Main.ProductUserId);
    }

    public override void Execute(object parameter)
    {
        StatsService.Ingest(ViewModelLocator.Stats.Clicks);
    }
}

 
  1. 打开StatsViewModel.cs,声明并实例化命令:

public StatsClickCommand StatsClick { get; set; }
public StatsIngestCommand StatsIngest { get; set; }

public StatsViewModel()
{
    StatsClick = new StatsClickCommand();
    StatsIngest = new StatsIngestCommand();
}

 
  1. 在ViewModelLocator.cs中向RaiseConnectCanExecuteChanged()方法添加以下代码行:

Stats.StatsIngest.RaiseCanExecuteChanged();

现在我们再次运行应用程序,完成身份验证,并稍微增加点击计数。然后,我们可以点击“Ingest stats”按钮将数值发送到后端。再重复一次上述操作(点击几次并采集数据),然后前往开发人员门户查看结果:
 
  1. 将应用程序UI中的ProductUserId复制到剪贴板。
  2. 在左侧菜单中导航到你的产品,然后依次点击“游戏服务”和“统计”。
  3. 点击“重置玩家统计”按钮,并在弹出窗口中将我们刚刚复制的PUID粘贴到搜索框中,然后点击“搜索”按钮。

你现在应该能看到该用户的全部四个统计,以及相应的聚合行为。
 
[IMAGE]
开发人员门户中的重置玩家统计UI

查询统计

我们最后要实现的是在我们的应用程序中查询这些统计,这样我们就可以直接查看它们,而不用通过开发人员门户:
 
  1. 在StatsView.xaml中添加下面的ListView,作为主网格元素的最后一个子节点:

<ListView x:Name="StatsListView" Grid.Row="1" Margin="2" ItemsSource="{Binding Stats}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Name" Width="200" DisplayMemberBinding="{Binding Name}">
                <GridViewColumn.HeaderContainerStyle>
                    <Style TargetType="{x:Type GridViewColumnHeader}">
                        <Setter Property="HorizontalContentAlignment" Value="Left" />
                    </Style>
                </GridViewColumn.HeaderContainerStyle>
            </GridViewColumn>
            <GridViewColumn Header="StartTime" Width="150" DisplayMemberBinding="{Binding StartTime}">
                <GridViewColumn.HeaderContainerStyle>
                    <Style TargetType="{x:Type GridViewColumnHeader}">
                        <Setter Property="HorizontalContentAlignment" Value="Left" />
                    </Style>
                </GridViewColumn.HeaderContainerStyle>
            </GridViewColumn>
            <GridViewColumn Header="EndTime" Width="150" DisplayMemberBinding="{Binding EndTime}">
                <GridViewColumn.HeaderContainerStyle>
                    <Style TargetType="{x:Type GridViewColumnHeader}">
                        <Setter Property="HorizontalContentAlignment" Value="Left" />
                    </Style>
                </GridViewColumn.HeaderContainerStyle>
            </GridViewColumn>
            <GridViewColumn Header="Value" Width="100" DisplayMemberBinding="{Binding Value}">
                <GridViewColumn.HeaderContainerStyle>
                    <Style TargetType="{x:Type GridViewColumnHeader}">
                        <Setter Property="HorizontalContentAlignment" Value="Left" />
                    </Style>
                </GridViewColumn.HeaderContainerStyle>
            </GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>

 
  1. 打开StatsViewModel.cs并添加以下成员:

private ObservableCollection<Stat> _stats;
public ObservableCollection<Stat> Stats
{
    get { return _stats; }
    set { SetProperty(ref _stats, value); }
}
 
  1. 打开StatsService.cs并添加以下方法:

public static void Query()
{
    var queryStatsOptions = new QueryStatsOptions()
    {
        LocalUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId),
        TargetUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId)
    };

    ViewModelLocator.Main.StatusBarText = $"Querying stats...";

    App.Settings.PlatformInterface.GetStatsInterface().QueryStats(queryStatsOptions, null, (OnQueryStatsCompleteCallbackInfo onQueryStatsCompleteCallbackInfo) =>
    {
        Debug.WriteLine($"QueryStats {onQueryStatsCompleteCallbackInfo.ResultCode}");

        if (onQueryStatsCompleteCallbackInfo.ResultCode == Result.Success)
        {
            var getStatCountOptions = new GetStatCountOptions()
            {
                TargetUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId)
            };
            var statCount = App.Settings.PlatformInterface.GetStatsInterface().GetStatsCount(getStatCountOptions);

            for (uint i = 0; i < statCount; i++)
            {
                var copyStatByIndexOptions = new CopyStatByIndexOptions()
                {
                    StatIndex = i,
                    TargetUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId)
                };
                var result = App.Settings.PlatformInterface.GetStatsInterface().CopyStatByIndex(copyStatByIndexOptions, out var stat);

                if (result == Result.Success)
                {
                    ViewModelLocator.Stats.Stats.Add(stat);
                }
            }
        }

        ViewModelLocator.Main.StatusBarText = string.Empty;
    });
}
  • 我们使用Stats.QueryStats将Epic在线服务的统计数据填充到我们的缓存中。
    • 你可以选择在Stats.QueryStatOptions中指定一列统计名称,从而仅查询特定的统计信息。
  • 然后我们使用Stats.GetStatsCount来了解需要迭代多少统计,并使用Stats.CopyStatByIndex从缓存中检索统计数据。
 
  1. 在Commands文件夹中添加一个StatsQueryCommand.cs类:

public class StatsQueryCommand : CommandBase
{
    public override bool CanExecute(object parameter)
    {
        return !string.IsNullOrWhiteSpace(ViewModelLocator.Main.ProductUserId);
    }

    public override void Execute(object parameter)
    {
        ViewModelLocator.Stats.Stats = new ObservableCollection<Stat>();
        StatsService.Query();
    }
}

 
  1. 打开StatsViewModel.cs,声明和实例化命令:

public StatsClickCommand StatsClick { get; set; }
public StatsIngestCommand StatsIngest { get; set; }
public StatsQueryCommand StatsQuery { get; set; }

public StatsViewModel()
{
    StatsClick = new StatsClickCommand();
    StatsIngest = new StatsIngestCommand();
    StatsQuery = new StatsQueryCommand();
}

 
  1. 在ViewModelLocator.cs的RaiseConnectCanExecuteChanged()方法中添加以下代码行:

Stats.StatsQuery.RaiseCanExecuteChanged();

现在,我们可以再次启动应用程序,并使用“Query stats”按钮直接在UI上查看当前玩家的统计:
 
[IMAGE]
在我们的应用程序UI中查询统计

使用限制

统计存在一些适当的使用限制,为所有玩家确保了可靠性和可用性。撰写本文时,存在下列限制(请务必参阅这篇文档了解最新信息):
  • 每个部署中的统计定义不超过500个
  • 每次采集调用中不超过3000个玩家的统计
  • 统计名称长度不能超过256个字符

另外,对于客户端API调用或服务器调用,也分别存在依用户或部署而定的速率限制,可以在这篇文档中了解。

获取代码

在下方可以获取本文的代码。请按照GitHub仓库中的使用说明设置下载的代码。
现在我们已经了解了如何管理玩家统计数据,接下来我们可以深入研究如何使用EOS排行榜为这些分数排名。

包含本系列所有文章的列表可以在系列参考中找到。如有反馈或问题,请访问社区论坛。

    你的成功就是我们的成功

    Epic秉承开放、整合的游戏社区理念。
    通过免费向所有人提供在线服务,我们致力于帮助更多开发者服务好他们自己的玩家社区。