通用活动系统的设计与实现

Jan 13, 2015


活动系统对于一个游戏而言是异常重要的,活动不仅能够吸引玩家营造游戏内热闹的气氛,在一定程度上也能够提升收入(比如充值活动)。游戏活动是游戏在运营的过程中,临时、短期或者间隙地将一些内容加入到游戏中,丰富游戏的玩点和内容,刺激玩家参与其中,从而提高游戏的在线率以及人气等。这种内容一般具有刺激消费的性质,这些内容的不固定性和不同游戏基础系统的表现方式也给游戏带入一些新鲜的元素。

但是活动是有很多类型的,比如充值类活动,闯关类活动,收集类活动等,而充值类活动可能又分为角色首次充值活动,角色累计充值活动等。萌仙以前的情况是,每当要开某种类型的活动时候,就需要程序配合去现场做一个,并不是成系统的做,而是在某些地方直接加上活动的逻辑。比如要开一个充值x2的活动,那么就是直接在充值逻辑之前加上充值x2的逻辑。

这种方式带来的问题是,每开一次活动都需要程序去修改代码从而实现活动逻辑,非常容易出错,而且代码也非常糟糕,所以需要一个由策划和运营去配置的活动系统,程序在完成了这个类型的活动之后交付给策划和运营即可。

萌仙内的活动是支持GM工具配置的,根据现有的情况,大致的流程如下:

GM工具–>中转服务器transit_sever–>game server–>zone_center

然后将已开启的活动推送至每一个玩家,对于通用性,我们应该有足够的考量。

某一类型的活动,可以将之抽象出来以各种字段来表述放置在lua的table表中,比如活动一般都会有一个标题(title),描述(description),详细规则信息(detail),还需要有一些额外的条件限制,还需要配置相应的奖励等,甚至还需要支持针对开服时间不同来配置不同的活动,明确上述需求之后,可以将活动大致抽象出来:

Data = 
{
	{
		title,
		description,
		details,
		startTime,
		endTime,
		isEnabled,
		timeType,
		payStart,
		payEnd,
		propertyCount
	}
};

以上面的充值类活动为例:startTime和andTime为活动的开始时间与结束时间,timeType决定startTime和endTime的时间类型:

  1. DateTime:说明为具体的日期时间。
  2. ServerOpenDays:说明以区服开服天数为准,startTime和endTime取值为相对区服的开服天数。
  3. AlwaysOpen:说明此活动不受时间限制,数据固定常开活动。
  4. payStart和payEnd决定充值区间,数据活动的规则部分。
  5. propertyCount决定该充值活动的返还倍率。

通过上面的数据,我们可以按照顺序列出相应的步骤:

  1. 循环遍历Data,找到一个可用的活动数据,返回给客户端。
  2. 客户端收到活动数据后将字段进行组合,形成一个规则和内容都比较清晰的活动。
  3. 玩家参加活动,然后开始充值。
  4. 服务端在收到充值请求后,调用活动系统暴露出的接口,检查当前玩家是否能参加可用的活动。
  5. 玩家能参加可用的充值活动后,根据上面的规则,检查充值金额是否满足要求。
  6. 满足要求之后根据propertyCount计算出需要返还的金额返给玩家。
  7. 日志记录,活动参加结束。

这样,一个活动的参加流程就结束了。由于我们的活动不仅支持在脚本中配置,还支持在web端进行配置,在web端的数据需要存储在数据库中,这样即可实现运营可以一次性配置数天数月甚至数年所需要的活动,保证条件就是运营将活动时间算好即可。流程如下:

GM工具–>transit_server–>game server–>DB

由于脚本中也会配置一些活动,那么在启动服务器的时候,活动数据应该是怎么处理?具体处理方式如下:

  1. 脚本优先于数据库启动,脚本中的活动会先生效。
  2. 数据库启动后通知活动系统活动数据已取。
  3. 每一种类型的活动对应一个key标识,活动系统在收到通知后会通过key设置所有的活动。
  4. 从数据库取活动数据结束。

因此活动系统的设计应该是:

ActivityManager = {
	["RoleFirstPayActivity"] = Import("zone_scripts/role_first_pay_activity.lua"),
}; 

function LoadActivity()
	-- 从数据库加载数据
end

function GetActivityData(key)
	-- 将活动数据返回给transi\_server,根据key区分类型
end

function SetActivityData(key, value)
	-- 设置活动数据,根据key区分类型
end 

function SaveActivityData(key)
	-- 保存活动数据,根据key区分类型
end

function GetActivityClientData(player)
	-- 推送活动数据至玩家
end

ActivityManager会以key加载所有的活动,而每种类型的活动会有一个相对应的脚本,其逻辑实现也在相应的脚本中。上面其实忽略一些细节方面的实现,比如中转服务器如何调用到游戏里来,中转服务器与游戏服务器的沟通以什么协议沟通(json)等,这些方面都会在后续的内容中说明。