성과 시스템을 코딩하는 가장 좋은 방법


85

내 사이트에서 사용할 성과 시스템을 디자인하는 가장 좋은 방법을 생각하고 있습니다. 데이터베이스 구조는 3 개 이상의 연속 레코드 누락을 알리는 최상의 방법 에서 찾을 수 있으며이 스레드는 개발자로부터 아이디어를 얻을 수있는 확장입니다.

이 웹 사이트에서 배지 / 업적 시스템에 대해 많은 이야기를 할 때 제가 가진 문제는 단지 모든 이야기이고 코드가 없다는 것입니다. 실제 코드 구현 예제는 어디에 있습니까?

저는 여기에서 사람들이 기여할 수있는 디자인을 제안하고 확장 가능한 성취 시스템을 코딩하기위한 좋은 디자인을 만들 수 있기를 바랍니다. 나는 이것이 최고라고 말하는 것은 아니지만 가능한 시작 블록입니다.

여러분의 아이디어를 자유롭게 기여해주십시오.


내 시스템 디자인 아이디어

일반적인 합의는 "이벤트 기반 시스템"을 만드는 것입니다. 포스트 생성, 삭제 등과 같은 알려진 이벤트가 발생할 때마다 이벤트 클래스를 이렇게 호출합니다.

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

그런 다음 이벤트 클래스는이 이벤트에 대해 "수신"하는 배지를 찾은 다음 requires해당 파일에 추가하고 다음 과 같이 해당 클래스의 인스턴스를 만듭니다.

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);
  }

 }
}

award함수는 Badge기본적으로 사용자가 이미 해당 배지를 받았는지 확인 하는 확장 클래스에서 나옵니다 . 그렇지 않은 경우 배지 db 테이블을 업데이트합니다. 배지 클래스는 또한 사용자의 모든 배지를 검색하고 배열로 반환하는 등의 작업을 처리합니다 (예 : 사용자 프로필에 배지를 표시 할 수 있음).

이미 라이브 사이트에서 시스템이 처음 구현되는 경우는 어떻습니까?

각 배지에 추가 할 수있는 "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 클래스는 main badge 클래스를 확장하므로 논리 함수를 재사용 할 수 있습니다. try_award

이에 대한 특수 쿼리를 생성하는 이유는 이전 이벤트를 "시뮬레이션"할 수 있기 때문입니다. 즉, 모든 사용자 게시물을 $event->trigger()살펴보고 특히 많은 배지에 대해 매우 느린 것처럼 이벤트 클래스를 트리거 할 수 있기 때문입니다. 따라서 대신 최적화 된 쿼리를 만듭니다.

어떤 사용자가 상을 받습니까? 이벤트를 기반으로 다른 사용자 에게 수여하는 모든 것

Badge클래스 award기능에 역할 user_id- 그들은 항상 상을 받게됩니다. (이것은 마찬가지입니다 기본적으로 배지 즉에게 세션 사용자 ID 발생하는 경우 발생하는 사람에게 수여된다 default_event크론 작업은 분명히 모든 사용자와 상 별도의 사용자 통해 루프하지만, 기능)

따라서 사용자가 코딩 항목을 제출하는 코딩 챌린지 웹 사이트에서 예를 들어 보겠습니다. 그런 다음 관리자는 항목을 판단하고 완료되면 모든 사람이 볼 수 있도록 챌린지 페이지에 결과를 게시합니다. 이 경우 POSTED_RESULTS 이벤트가 호출됩니다.

게시 된 모든 항목에 대해 사용자에게 배지를 부여하고 싶다면, 예를 들어 상위 5 위 안에 들었다면 크론 작업을 사용해야합니다. 결과가 게시되었습니다)

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 기능은 매개 변수가 제공되지 않은 경우에도 계속 작동합니다.



2
관련이 있지만 중복되지는 않습니다. 두 번째 단락을 읽으십시오. "이 웹 사이트에서 배지 / 업적 시스템에 대해 많은 이야기를 할 때 제가 가진 문제는 단지 말입니다. 모든 것이 말하고 코드가 없습니다. 실제 코드 구현 예제는 어디에 있습니까?"
Gary Green

1
글쎄, 작업 코드를 작성하는 것은 어느 정도까지만 가능합니다. 구현이 너무 복잡해지면 사람들이 이론 만 제공하는 것이 정상이라고 말하고 싶습니다.
Gordon

답변:


9

문서 지향 데이터베이스라고 부르는 보상 시스템을 한 번 구현했습니다 (플레이어에게는 진흙이었습니다). 내 구현의 일부 하이라이트, PHP 및 MySQL로 번역 :

  • 배지에 대한 모든 세부 정보는 사용자 데이터에 저장됩니다. MySQL을 사용하는 경우 성능을 위해 데이터베이스의 사용자 당 하나의 레코드에이 데이터가 있는지 확인했을 것입니다.

  • 문제의 사람이 무언가를 할 때마다 코드는 주어진 플래그 (예 : flag ( 'POST_MESSAGE'))로 배지 코드를 트리거합니다.

  • 하나의 이벤트가 카운터 (예 : 게시물 수)를 트리거 할 수도 있습니다. 증가 _ 횟수 ( 'POST_MESSAGE'). 여기에서 POST_MESSAGE 카운트가 300을 초과하면 배지를 받아야한다는 것을 확인할 수 있습니다 (후크 또는이 메서드에서 테스트 만 수행). 예 : flag ( "300_POST").

  • 플래그 방법에서는 배지를 보상하는 코드를 넣었습니다. 예를 들어, 플래그 300_POST가 전송되면 배지 reward_badge ( "300_POST")를 호출해야합니다.

  • flag 메서드에서는 사용자에게 이전 플래그가 있어야합니다. 따라서 사용자에게 FIRST_COMMENT, FIRST_POST, FIRST_READ가 있으면 배지 ( "NEW USER")를 부여하고 100_COMMENT, 100_POST, 300_READ를 받으면 배지 ( "EXPERIENCED_USER")를 부여 할 수 있습니다.

  • 이 모든 깃발과 배지는 어떻게 든 저장해야합니다. 플래그를 비트로 생각하는 방식을 사용하십시오. 이것이 정말로 효율적으로 저장되기를 원한다면, 그것들을 비트로 생각하고 아래 코드를 사용하십시오 : (또는 이러한 복잡성을 원하지 않는다면 맨 문자열 "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 번 읽었습니다).


1
배지 시스템의 고전적인 추세는 새 통계에 대한 새 필드를 테이블에 추가하는 것입니다. 나에게는 테이블에 이미있는 데이터에서 계산할 수있는 미러링 된 데이터 (MyISAM 테이블에서 매우 빠른 간단한 COUNT () 일 수 있음)를 100 % 저장할 것이기 때문에 그것은 약간의 쉬운 탈출구와 나쁜 생각처럼 보입니다. 정확한). 실적이 목표라면 업데이트를 수행하고 현재 예를 들어 post_count 값을 가져 와서 배지를 받아야하는지 확인해야합니다. 쿼리 COUNT (*) 하나만 필요할 수 있습니다. 나는 비록 필드를 추가하는 좋은 이유가있을 것보다 복잡한 데이터에 대한 동의
게리 그린

5
@Gary Green 이것은 쉬운 방법 일뿐만 아니라 확장 가능한 방법이며 문서 데이터베이스와 호환됩니다. 정확성에 관해서는 당신이 옳지 만, 배지 시스템의 경우 100 % 정확하고 느린 것보다 빠르고 정확할 가능성이 높습니다. 하나의 카운트는 빠르지 만 시스템이 확장되고 사용자가 많은 경우 해당 전략이 유지되지 않습니다.
Knubo 2010

1
저는 배지 정의 테이블과 사용자를 배지 및 현재 진행 상황에 연결하는 링크 테이블을 갖는 아이디어가 마음에 듭니다. noSQL을 수행하면 당시 어떤 스키마 에든 잠기 게되며 갑자기 배지에서 오타가 발견되거나 1000 개의 새 배지가 추가 될 때 유지 관리 할 수 ​​없습니다. 빠른 검색을 위해 항상 일괄 처리를 통해 더 많은 문서 저장소에 캐시 할 수 있지만 링크 된 상태로 두겠습니다.
FlavorScape 2015 년

2

UserInfuser는 배지 / 포인트 서비스를 구현하는 오픈 소스 게임 화 플랫폼입니다. 여기에서 API를 확인할 수 있습니다. http://code.google.com/p/userinfuser/wiki/API_Documentation

나는 그것을 구현하고 최소한의 기능을 유지하려고 노력했습니다. 다음은 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 구현은 http://code.google.com/p/userinfuser/source/browse/trunk/serverside/api/api.py 에서 찾을 수 있습니다 .


1
이 PHP 기반입니까? 문제는 PHP를 기반으로
레닌 라즈 Rajasekaran에게

1
PHP 바인딩이 있지만 서버 측 코드는 Python으로 작성됩니다.
Navraj Chohan 2011

0

성과는 부담 스러울 수 있으며, 잘 구성된 Event수업 이없는 한 나중에 추가해야하는 경우에는 더욱 그렇습니다 .

이것은 성취를 구현하는 나의 기술로 이어집니다.

나는 그것들을 먼저 '카테고리'로 나누고 그 안에 성취 단계가 있습니다. 즉, kills게임 의 카테고리는 첫 번째 킬에 대해 1, 10 킬, 10 만 킬 등의 상을받을 수 있습니다.

그런 다음 좋은 응용 프로그램의 척추로 이벤트를 처리하는 클래스입니다. 다시 살인 게임을 상상해보십시오. 플레이어가 무언가를 죽이면 상황이 발생합니다. 킬이 기록 Events되고 기타 관련 장소에 정보를 전달할 수있는 클래스 와 같은 중앙 집중식 위치에서 가장 잘 처리됩니다 .

적절한 방법으로 Achievements클래스를 인스턴스화 하고 플레이어가 올바른지 확인하는 것이 완벽하게 제자리에 있습니다 .

Achievements클래스를 구축하는 것은 간단합니다. 데이터베이스를 확인하여 플레이어가 다음 업적에 필요한만큼의 킬을 보유하고 있는지 확인하는 것입니다.

Redis를 사용하여 BitField에 사용자의 업적을 저장하는 것을 좋아하지만 MySQL에서도 동일한 기술을 사용할 수 있습니다. 즉, 플레이어의 업적을로 저장 int한 다음 and해당 업적으로 정의한 비트와 함께 해당 int를 저장하여 이미 획득했는지 확인할 수 있습니다. 이렇게 int하면 데이터베이스에서 단일 열만 사용 합니다.

이것의 단점은 그것들을 잘 정리해야한다는 것이고, 나중에 2 ^ 14가 무엇에 해당하는지 기억할 수 있도록 코드에 주석을 달아야 할 것입니다. 업적이 자체 테이블에 열거 된 pk경우 업적 테이블의 기본 키인 2 ^ pk를 수행 할 수 있습니다 . 그것은 수표를 다음과 같이 만듭니다.

if(((2**$pk) & ($usersAchInt)) > 0){
  // fire off the giveAchievement() event 
} 

이렇게하면 나중에 업적을 추가 할 수 있으며 이미 수여 된 업적의 기본 키를 절대 변경하지 마십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.