导语:本文宽泛的梳理了游戏产品数据相关的数据埋点内容,包含游戏数据埋点的一些原则和技巧。主要面向刚刚接触游戏数据业务的新人,希望这篇文章能有所帮助。
数据埋点概述
1. 什么是数据埋点
数据埋点是一切数据分析的基石。它指在特定的程序功能被触发时,将这个行为记录下来。例如,当玩家登录时,记录登陆行为;在购买时记录订单等。当这些行为不被记录时,数据分析是没有任何基础数据可以分析的。
数据埋点就是解决在处理当程序功能被触发时,应该如何记录这个行为并通过合适的渠道上报的问题。
2. 游戏数据的分类
按照服务的对象分类可以分为:运营数据和产品个性化数据。
运营数据(质量数据)用来监测产品的健康程度,收益效益。例如活跃情况,收入情况等等。
产品数据往往着力于产品自身的设计验证,玩法验证以及数值平衡调整的内容。
按照数据的时间属性分类可以分为:流水数据和状态数据。
简而言之,流水数据是每一次行为的历史存档,例如A玩家击杀了B玩家1次;状态数据是一种状态数值,例如A玩家当前的击杀数是1。
在接下来的关于数据埋点的内容中,主要涉及的是针对游戏产品内容设计的数据埋点相关内容。质量指标(运营数据)应当采用公司规定通用的数据指标的埋点方式和上报定义。
3. 游戏数据分析的基本流程
在进行游戏产品的数据分析时一般会遵循下面的流程。
数据设计中就包含了数据埋点与数据上报路径的处理。游戏的功能开发结束后,数据埋点的开发就应该提上日程。最后在数据分析时通过数据清洗、筛选、提取、分析的步骤得到相关的结论来验证设计,或为下一个设计方案提供支撑。
在首次分析数据的过程中不断优化下来的数据处理方式就会逐步的制作成标准的数据处理流程。使用自动化脚本(洛子系统),idata服务(创建经营分析页面)来方便需要重复分析或长期监控的数据分析需求。
4. 数据埋点的基础原则
数据埋点时我们需要遵循一些原则,这些原则与APP数据埋点类似。
数据埋点的准备工作
在真正进到数据埋点之前,负责数据埋点的同学可以从两个方面着手了解。一个是数据库系统的基础知识,另一个是对业务功能机制穿透性的理解。
1. 了解一些数据库内容
1.1 信息与数据
众所周知信息与数据是有一定的关系的,那么数据埋点的同学就需要能够从信息中拆解出数据,也需要能够从数据中还原出信息。
举个例子,张三今晚在李四家吃饭,这个行为信息变成数据就是:
人物 | 时间 | 地点 | 吃饭行为 |
---|---|---|---|
张三 | 今晚 | 李四家 | 1 |
也就是张三今晚在李四家吃了1次饭。
在策划描述自己想要了解到的数据指标时,策划往往会不够深入的去拆解本身的数据,而是描述一个经过计算后的数据指标,或者是趋势信息。这时候数据侧就需要拆解出组成指标或信息的基础数据是什么,进而进行埋点。
为了更好的理解信息与数据,举个例子:策划需要了解到武器的命中率以此来判断武器的性能是否满足设计的要求。
那么他提出的数据需求就是:
“武器的命中率是多少?”
那么对于数据来说,要得到命中率的数据时,它首先要得到:
“命中次数、开火次数”
而对于在游戏中发生的程序功能来说,它描述的行为信息的记录是:
“玩家使用某个武器在某个时间某个地点开火,这次开火是否被命中,命中了什么物体。”
假设玩家在单局游戏里使用AK开火10次,命中6次,那么相对应的程序的行为流水记录应该是:
人物 | 时间 | 地点 | 武器 | 是否开火 | 是否命中 |
---|---|---|---|---|---|
玩家A | | | AK-47 | 1 | 1 |
玩家A | | | AK-47 | 1 | 0 |
玩家A | | | AK-47 | 1 | 0 |
玩家A | | | AK-47 | 1 | 1 |
玩家A | | | AK-47 | 1 | 0 |
玩家A | | | AK-47 | 1 | 1 |
玩家A | | | AK-47 | 1 | 1 |
玩家A | | | AK-47 | 1 | 1 |
玩家A | | | AK-47 | 1 | 1 |
玩家A | | | AK-47 | 1 | 0 |
经过数据埋点设计统计后上报单局游戏数据记录则是:
人物 | 武器 | 开火次数 | 命中次数 |
---|---|---|---|
玩家A | AK-47 | 10 | 6 |
很显然可以得到命中率是60%。但在这个例子中,通常为了节省上报数据的性能,行为记录按上报的时间节点被统计成一个阶段结果来上报,得到这个数据的同时,一些信息(时间、地点)被丢失了。
1.2 关系型数据库和范式
我们埋点的数据和数据分析使用的数据都是基于关系模型(ER模型)建立的关系型数据库,关系型数据库有着自己的范式原则。(具体的知识请自行查阅。)
了解关系型数据库的范式原则对设计数据埋点上报时的数据表结构会起到非常重要的作用。范式也会避免数据表设计中出现的一些奇怪的问题。
这里浅显的将前3个关系范式解释一下:
● 关系数据库是一个只有第一行定义每一列数据意义的EXCEL表;
● 它每一列都有唯一标识可以查询,且不可以重复;
● 在有多个从属关系的情况下,最好依照关系拆分成多个表。
以武器统计表为例,记录如下信息
一个AK在装配了A、B两个配件情况下开火6次。
一个M4在装配了B配件开火3次。
我们来看下面两个表结构设计:
表1:
武器 | 配件 | 开火次数 |
---|---|---|
AK | A | 6 |
AK | B | 6 |
M4 | B | 3 |
表2:
武器 | 是否装配件A | 是否装配配件B | 开火次数 |
---|---|---|---|
AK | 1 | 1 | 6 |
M4 | 0 | 1 | 3 |
表1的结构会明显的在统计数据时造成数据错误,因为配件A、B是两个并列的关系。
而表2的结构会随着装配情况的变化而不断的增加字段或增加每次上报的数据记录,“是否装配A”+“是否装配B”的组合成为了开火次数的组合主键。
2. 充分、深入、贯穿且宏观的了解需要埋点的业务
进行数据埋点的同学一定要向着全面了解埋点的业务功能而努力。因为数据埋点的本质就潜藏在业务功能逻辑之中。同时数据的定义也依赖业务的功能逻辑。
举个例子,我们假设武器命中率是指在攻击确定目标时的命中率。在无法获得开火朝向的数据采集中,命中率这个武器性能指标仅在一定程度上有意义,它一定小于定义中的武器命中率,因为玩家无意义的开火也被统计在其中。那么在进行数据解释和数据分析的过程中,数据侧需要明确的清楚这些类似的问题。
这类问题会产生的数据理解歧义,数据上报失败的情况会非常多!!!!!!
因此,数据埋点设计一定需要策划、程序、数据多方共同参与讨论。
数据埋点时的注意项
终于进入到数据埋点的策略,主要来自工作经历后对数据的理解,若有错漏欢迎指出。
1. 游戏数据的本质
首先让我们理解一下游戏数据的本质,也就是描述行为信息基本四要素:时间、人物、地点、事件。
时间:客观时间与时机。客观时间是系统的记录时间,而时机分为触发记录的时机和上报数据的时机
人物:发生行为的主体。例如,玩家角色,工会,道具,团队等
地点:发生行为的环境。可以是游戏模式,也可以是其他字段条件
行为:被记录下来的事件数据本身
2. 明确上报时机
数据埋点时选择的上报时机十分的重要。合理的选择记录数据的触发时机,并在考虑性能的情况下适当的选择上报的时机。这一点需要策划、数据、程序三方重点沟通讨论记录某个行为信息的上报时机。因为它不仅仅涉及到数据的有效性,数据定义本身,还涉及到数据上报产生的性能消耗等诸多内容。
以吃鸡类的单局组队情况下的战绩上报为例,当上报时机是某个玩家死亡时,上报的战绩中得到的排名为该玩家相对于局内所有玩家的排名(死亡顺序),而真正在战绩部分展示的小队排名,需要等待其队友结束游戏时才能得到。这时就需要根据表的服务对象设计不同的表格来处理对数据的实时性要求。
在面对单局中大量的玩家行为时,如果在每一个行为发生时都上报数据,势必会造成上报带宽的堵塞以及客户端响应速度的延时。这对于有着较高的延时要求来说的游戏是致命的。因此就需要将触发记录记录在本地,并统计计算后在某一个空闲时机记录上报,例如物品拾取操作的统计结果,或前文提到的武器使用情况这类的数据。
3. 灵活选择记录数据主体
当需要记录玩家的击杀信息,如果所有的击杀数据都在该玩家游戏结束时上报,对于单局玩家数量较多的游戏来说。单次上报的数据量条数= 该玩家击杀数据的人数,那么这种“一对多”的上报的数据量就有可能超过数据上行带宽而导致数据丢包。而如果选择记录被击杀信息记录这个击杀信息,只需要通过查询击杀者信息,就可以统计得到某个玩家的击杀信息。而在被击杀玩家死亡时上报的数据占用就只是一次发送一条记录而已。
大部分情况下,我们的数据信息都会与玩家绑定相关,因为最终的数据分析是围绕着玩家的游戏行为的。适当的转换记录行为的主体就可以起到节省带宽同时规整数据表结构框架的作用。
4. 设计数据表的字段战争
4.1 留意字段间的从属关系
在设计数据的表结构时,需要留心字段定义的值属性与组别属性的转换问题,也就是字段定义的从属关系,涉及灵活的遵循范式的问题。
假设一个武器需要上报不同距离下的命中率表现,那么想当然的就是,某个武器在某个距离下的开火次数和命中次数分别是多少,自然出现下面的表结构。
武器 | 命中距离 | 开火次数 | 命中次数 |
---|---|---|---|
| | | |
先不考虑开火次数是否能够被统计在某个命中距离的下的问题。每一个命中次数都即从属于这个武器,也从属于这个命中距离,在这个表结构里“武器+命中距离”成为了记录的主键,因此一把武器在每一局游戏中都有可能上报无视个记录,而每一个命中距离记录的数据信息都有可能是命中次数只为1。
再看下面这种类型的表结构
武器 | 命中距离>A | 命中距离<=A | 开火次数 | 命中次数 |
---|---|---|---|---|
| | | | |
很显然,”命中距离<A次数”和”命中距离>=A次数”是没有从属关系的,它们都从属于武器。那么这样的表就不会产生多条冗余的记录。这一点与之前所说的了解范式相通。
4.2 巧用二进制标识
在需要使用一个字段来记录状态时,例如玩家的死亡原因,我们通常会采用数值替换的方式,例如当值为1表示玩家被枪击杀,2表示跌落死亡,3表示载具碰撞死亡等。在标记状态时,我有时候会遇到在该字段需要扩展出一个维度的情况。
考虑玩家之间的关系问题。假设玩家之间有好友关系、微信好友关系、师徒关系、QQ好友关系、情侣关系、基友关系等,我们自然的就会想到要用一个字段来标识玩家的关系,因此这个关系数据就会被记录为
玩家1 | 玩家2 | 关系标识字段 |
---|---|---|
| | |
关系标识字段:
当=1时,表示微信关系
当=2时,表示QQ关系
当=3时,表示师徒关系
……
先不考虑玩家A、B之间顺序对调后重复记录的问题。玩家与玩家之间的关系种类中,既有互斥的关系也有不互斥的关系。对于互斥的关系,我们使用一个关系标识字段来表示记录,而不互斥的关系就很难被表达出来。
面对这个问题,我们可以采用扩展字段数量的方式来特殊标记,让每一个标识字段对应一个关系的有无。比如
玩家1 | 玩家2 | 关系标识字段1 | 关系标识字段2 | 关系标识字段3 | …… |
---|---|---|---|---|---|
| | | | | |
这样的方式会增加开发新字段、数据表更新入库的工作成本。
面对这个问题我们还可以使用二进制的方式来在字段内打开一个新的标识维度来处理同时存在的状态。
设计一个N位的二进制标识;
第一位代表好友关系
第二位代表微信好友关系
……
那么以同时为好友、师徒、情侣、微信关系为例,一个8位的二进制关系标识值就是:
好友 | 师徒 | 情侣 | 微信 | 基友 | 战队 | 保留位 | |
---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
转换为十进制int值就是=240
在数据表中的体现就为:
玩家1 | 玩家2 | 关系标识值 |
---|---|---|
A | B | 240 |
4.3 利用最大字段大小
一般来说tglog数据上报中一般会将字段作为string值和int值存储。单个字段大小不能超过32KB,一条记录大小不能超过64KB。那么对于字段设计来说,我们为了避免产生多条记录的情况在有些情况下可以尝试将大量的连续有规律的信息记录在一个字段内。在数据提取出来后再进行分割处理。
例如玩家好友的关系信息。对于每两个玩家来说,他们之间的关系都是互相作用的。因此在记录关系时,我们需要指定二者中的一个记录主键,另一个作为记录的属性。比如在个人战绩中展现队友与个人战绩主体的关系。
确定了从属关系的数据表结构其含义就会变为“该条记录是玩家A的好友信息,他的好友是B”而不是“A和B是好友关系”。
玩家1 | 玩家1的好友1 |
---|---|
A | B |
在通过A查询A的好友都有那些玩家时,我们没有办法动态延展表的结构(即在表的末尾新增位置,当然可以通过固定限制好友数量的方式来解决这个问题)
那么实际上我们可以充分利用单个字段的容量大小。
将“玩家1的好友1”设计成一个string字段,并记录一段包含好友信息的长字符串,用特定的符号分割每一个好友的信息。
玩家1 | 玩家1的好友们 |
---|---|
A | B,C,D,E,F…… |
若以玩家的角色ID进行记录,每个角色长度为11位,加上英文逗号1位。
那么32KB可以存储的好友个数=32*1024/(11+1) ≈ 2730个好友。
4.4 合并表结构
如果严格依照关系范式来设计数据表,对于一个产品我们可能会有很多个数据表结构。在执行数据处理提取时,需要不断的在各个表之间关联取交集查询。这样在执行数据提取时就会进行大量的循环重复查询。相应的时间成本就会偏高。这个时候就需要结合表的服务对象来合并表结构
例如玩家的组队信息与单局战绩是两个表。
组队表:
队伍ID | 队员1 | 队员2 | 队员3 | 队员4 |
---|---|---|---|---|
12345 | A | B | C | D |
单局战绩:
玩家ID,模式,战绩情况
玩家ID | 模式 | 战绩情况 |
---|---|---|
20001 | 1 | X |
在面向对外平台的战绩相关的数据应用或组队活动的查询时,为了能够实现战绩的实时输出,单局战绩与组队信息就可以合并成为一张表,以减少生成实时查询结果时的查表时间。
玩家ID | 模式 | 战绩情况 | 队伍ID | 队员2 | 队员3 | 队员4 |
---|---|---|---|---|---|---|
20001 | 1 | X | 12345 | B | C | D |
在有些情况下将记录拆分为两个不同的表格以应对上报时机或者是性能方面的考虑也是要视情况设计的。
总之,灵活且努力的思考高效的埋点策略,深入了解业务并和程序大佬进行深入的交流。
APP数据与游戏数据
在查阅了一些APP相关的数据埋点文章后,我发现游戏数据与APP数据的埋点其实是同源的。对于运营数据(质量数据)来说,二者几乎没有差异;对于产品数据来说,APP和游戏数据的埋点在侧重点和实现逻辑上略显不同。下面将讨论一下产品数据埋点的二者差异。
1. 数据侧重的因果
首先APP数据与游戏数据的行为数据都是对用户行为的观测记录:APP的数据埋点对于用户行为下钻的,在我的理解中更像是对行为诱因的追求;而游戏的数据埋点则更着重于用户行为的结果产生的数据。
APP产品数据的基础行为主要可以拆解为:页面停留(曝光)和按钮点击。功能性的数据埋点,大部分可以通过这两者的组合来进行追踪式的数据埋点,或标记相关的功能触发点。更像是“用户行为的来源于什么。”
游戏数据就会选择“用户行为产生了什么后果”方式来记录信息。举个例子,新闻浏览类App更关心用户触达某个页面的时间,以及来源路径;而游戏更关心用户开火之后,造成的伤害是多少,也就是 “用户行为之后的结果”。
游戏数据如果从基础触发虽然也可以被描述为操作的组合与停滞操作的时间,但面对复杂的游戏行为和功能,这样的组合在追溯起来比APP就更加复杂。试想一下“BABA上上下下左左右右”的这种系列的数据记录,以及找寻对应信息含义的而产生的工作量。
2. 实现逻辑上的差异
对于产品数据来说,APP数据埋点可以拆解为两个基础的行为的优势让它可以较快且完整的建立起一套数据埋点的规范,或者是进行有较强的通用性埋点设计。而游戏数据因为游戏设计的玩法和创新层出不穷,功能设计也并非千篇一律,通常会选择针对功能去设计埋点,很难 “一招鲜吃遍天”。
APP的数据在前端的行为埋点有以下三类方式:
功能埋点 | 将数据埋点写在特定的功能中 |
---|---|
可视化埋点 | 将数据埋点利用前端的脚本追踪 |
无埋点 | 在一个全埋点的SDK上制作APP |
游戏数据埋点能否仿照APP数据埋点的方式呢?
对于游戏埋点来说:
功能埋点 | 游戏的功能很少带有普适性,功能埋点相对合适。当然如果是基于一个固定模式固定玩法不断迭代微创新的系列产品来说或许会有一个基础的数据埋点框架 |
---|---|
可视化埋点 | 游戏之间实现方式有着使用引擎不同的重大差异 |
无埋点 | 游戏功能繁复,全埋点几乎不可实现 |
*很多质量数据已经有了基础的SDK记录,但针对游戏内容本身的数据埋点很少有统一的模块。
基于此这也是为什么游戏数据的埋点设计要与游戏功能设计一同并行的,其实这个流程还能够简化或像APP一样更加的系统化和自动化。APP上使用的可视化埋点和无埋点也许会在将来随着游戏程序框架的统一或功能框架的完善被逐步开发出来。
写在最后
字段设计中还有很多例如数值大小在计算后越界溢出的问题也需要在数据表结构设计时就考量好。甚至有些要到了最后才会发现,因此在整个数据环节中,红线可能是经历最多的流程。其实往往会祈祷问题在这里集中出现,因为问题如果不在这里出现,那就会浪费一次获得数据信息的版本机会。数据信息丢失了就不会再有弥补的机会。
数据埋点流程图