编码成就系统的最佳途径

我正在考虑devise一个在我的网站上使用的成就系统的最佳方式。 数据库结构可以find最好的方式来告诉3个或更多的连续logging丢失 ,这个线程真的是从开发人员的想法的延伸。

我在这个网站上有很多关于徽章/成就系统的问题就是这样 – 这都是谈话,没有代码。 实际的代码实现在哪里呢?

我在这里提出了一个devise,希望人们能够贡献并希望为可扩展成就系统编码创build一个良好的devise。 我不是说这是最好的,远非如此,但这是一个可能的出发点。

请随时提供您的想法。


我的系统devise理念

似乎普遍的共识是创build一个“基于事件的系统” – 每当一个已知的事件发生像一个职位创build,删除等,它就像这样调用事件类。

$event->trigger('POST_CREATED', array('id' => 8)); 

事件类然后找出什么徽章“听”这个事件,然后它requires该文件,并创build该类的实例,如下所示:

 require '/badges/' . $file; $badge = new $class; 

然后它调用传递trigger被调用时收到的数据的默认事件;

 $badge->default_event($data); 

徽章

这就是真正的魔法发生的地方。 每个徽章都有自己的查询/逻辑来确定是否应颁发徽章。 每个徽章都采用如下格式:

 class Badge_Name extends Badge { const _BADGE_500 = 'POST_500'; const _BADGE_300 = 'POST_300'; const _BADGE_100 = 'POST_100'; function get_user_post_count() { $escaped_user_id = mysql_real_escape_string($this->user_id); $r = mysql_query("SELECT COUNT(*) FROM posts WHERE userid='$escaped_user_id'"); if ($row = mysql_fetch_row($r)) { return $row[0]; } return 0; } function default_event($data) { $post_count = $this->get_user_post_count(); $this->try_award($post_count); } function try_award($post_count) { if ($post_count > 500) { $this->award(self::_BADGE_500); } else if ($post_count > 300) { $this->award(self::_BADGE_300); } else if ($post_count > 100) { $this->award(self::_BADGE_100); } } } 

awardfunction来自一个扩展类的Badge ,基本上是检查用户是否已经被授予该徽章,如果没有,将更新徽章数据库表。 徽章课程还负责检索用户的所有徽章并将其以arrays等方式返回(因此徽章可以显示在用户个人资料中)

什么时候该系统在一个已经存在的网站上首次实现?

还有一个“cron”工作查询可以添加到每个徽章。 原因是因为当徽章系统首次实施和初始化时,应该已经获得的徽章还没有被授予,因为这是基于事件的系统。 所以CRON的工作是根据需要运行每个徽章来奖励任何需要的东西。 例如,上面的CRON工作将如下所示:

 class Badge_Name_Cron extends Badge_Name { function cron_job() { $r = mysql_query('SELECT COUNT(*) as post_count, user_id FROM posts'); while ($obj = mysql_fetch_object($r)) { $this->user_id = $obj->user_id; //make sure we're operating on the right user $this->try_award($obj->post_count); } } } 

由于上面的cron类扩展了主徽章类,所以它可以重用逻辑函数try_award

之所以我为此创build专门的查询,是因为我们可以“模拟”以前的事件,即遍历每个用户的post并触发事件类,如$event->trigger()这将是非常缓慢的,特别是对于许多徽章。 所以我们创build一个优化的查询。

什么用户得到奖励? 所有关于根据事件授予其他用户

Badgeawardfunction作用于user_id – 他们将永远得到奖励。 默认情况下,徽章授予引起事件发生的人,即会话用户ID(对于default_event函数,这是真实的,尽pipeCRON工作显然循环遍历所有用户并授予单独用户)

举个例子,在一个编码挑战网站上,用户提交他们的编码条目。 pipe理员然后判断条目,完成后,将结果发布到挑战页面供所有人查看。 发生这种情况时,会调用POSTED_RESULTS事件。

如果您想为所有发布的post的用户授予徽章,可以说,如果他们排在前五名之内,那么您应该使用cron作业(尽pipe裸露在心的话会更新所有用户,而不仅仅是针对这个挑战结果被张贴了)

如果您想要使用cron作业来更新特定区域,请查看是否有方法将过滤参数添加到cron作业对象中,并获取cron_job函数以使用它们。 例如:

 class Badge_Top5 extends Badge { const _BADGE_NAME = 'top5'; function try_award($position) { if ($position <= 5) { $this->award(self::_BADGE_NAME); } } } class Badge_Top5_Cron extends Badge_Top5 { function cron_job($challenge_id = 0) { $where = ''; if ($challenge_id) { $escaped_challenge_id = mysql_real_escape_string($challenge_id); $where = "WHERE challenge_id = '$escaped_challenge_id'"; } $r = mysql_query("SELECT position, user_id FROM challenge_entries $where"); while ($obj = mysql_fetch_object($r)) { $this->user_id = $obj->user_id; //award the correct user! $this->try_award($obj->position); } } 

即使参数未提供,cron函数仍然可以工作。

我曾经在一个叫做面向文档的数据库中实现了一个奖励系统(这对于玩家来说是一个泥巴)。 从我的实现中转化为PHP和MySQL的一些亮点:

  • 有关徽章的每个细节都存储在用户数据中。 如果你使用MySQL,我会确保这个数据在数据库中的每个用户的logging中有一个logging的性能。

  • 每当有问题的人做某事时,代码就会触发带有给定标志的徽章代码,例如flag('POST_MESSAGE')。

  • 一个事件也可能触发一个计数器,例如一个计数的post。 increase_count( 'POST_MESSAGE')。 在这里,你可以有一个检查(通过钩子,或者只是在这个方法中testing),如果POST_MESSAGE计数是> 300,那么你应该有奖励徽章,例如:标志(“300_POST”)。

  • 在标志方法中,我把代码奖励徽章。 例如,如果发送Flag 300_POST,则应该调用徽章reward_badge(“300_POST”)。

  • 在标志方法中,你也应该让用户以前的标志出现。 所以你可以说,当用户有FIRST_COMMENT,FIRST_POST,FIRST_READ你授予徽章(“新用户”),当你得到100_COMMENT,100_POST,300_READ你可以授予徽章(“EXPERIENCED_USER”)

  • 所有这些标志和徽章都需要以某种方式存储。 以某种方式将标志视为位。 如果你希望能够真正有效地存储这些数据,你可以把它们看作是位数,并使用下面的代码:(或者,如果你不想要这种复杂性,你可以使用一个简单的string“000000001111000”。

 $achievments = 0; $bits = sprintf("%032b", $achievements); /* Set bit 10 */ $bits[10] = 1; $achievements = bindec($bits); print "Bits: $bits\n"; print "Achievements: $achievements\n"; /* Reload */ $bits = sprintf("%032b", $achievments); /* Set bit 5 */ $bits[5] = 1; $achievements = bindec($bits); print "Bits: $bits\n"; print "Achievements: $achievements\n"; 
  • 为用户存储文档的好方法是使用json并将用户数据存储在单个文本列中。 使用json_encode和json_decode来存储/检索数据。

  • 为跟踪其他用户操作的某些用户数据的活动,请在该项目上添加数据结构,并在其中使用计数器。 例如读数。 使用与上述相同的技术来授予徽章,但更新当然应该归入拥有者用户的职位。 (例如文章阅读1000次徽章)。

UserInfuser是一个开源的游戏化平台,实现了徽章/积分服务。 你可以在这里查看它的API: http : //code.google.com/p/userinfuser/wiki/API_Documentation

我实现了它,并尽量减lessfunction的数量。 这里是一个PHP客户端的API:

 class UserInfuser($account, $api_key) { public function get_user_data($user_id); public function update_user($user_id); public function award_badge($badge_id, $user_id); public function remove_badge($badge_id, $user_id); public function award_points($user_id, $points_awarded); public function award_badge_points($badge_id, $user_id, $points_awarded, $points_required); public function get_widget($user_id, $widget_type); } 

最终结果是通过使用小部件以有意义的方式显示数据。 这些小部件包括:奖杯案例,排行榜,里程碑,现场通知,排名和积分。

API的实现可以在这里find: http : //code.google.com/p/userinfuser/source/browse/trunk/serverside/api/api.py