Лучший способ кодирования системы Достижения

Я думаю о лучшем способе разработки системы достижений для использования на моем сайте. Структуру базы данных можно найти в Лучший способ рассказать о 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, который в основном проверяет, будет ли пользователь уже присвоен этот значок, если нет, обновит таблицу dbbbge. Класс значка также выполняет поиск всех значков для пользователя и возвращает его в массив и т.д. (Поэтому значки могут отображаться, например, в профиле пользователя).

как насчет того, когда система будет впервые реализована на уже существующем сайте?

Существует также запрос задания "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

Причина, по которой я создаю специализированный запрос для этого, заключается в том, что мы могли бы "имитировать" предыдущие события, то есть проходить через каждую запись пользователя и запускать класс событий, например $event->trigger(), это было бы очень медленно, особенно для многих значков. Поэтому мы создаем оптимизированный запрос.

какой пользователь получает награду? все о награждении других пользователей на основе события

Функция Badge class award действует на user_id - они всегда получат награду. По умолчанию значок присваивается лицу, которое ПРИЧИНА события, которое произошло, то есть идентификатор пользователя сеанса (это верно для функции default_event, хотя задание CRON, очевидно, проходит через всех пользователей и наделяет отдельными пользователями)

Итак, давайте возьмем пример, на веб-сайте, посвященной кодированию, пользователи отправляют свою запись в кодировку. Затем администратор оценивает записи и по завершении отправляет результаты на страницу запроса, чтобы все могли видеть. Когда это происходит, вызывается событие POSTED_RESULTS.

Если вы хотите награждать значки для пользователей за все опубликованные записи, скажем, если они были ранжированы в пятерку лучших, вы должны использовать работу cron (хотя, конечно, это будет обновляться для всех пользователей, а не только для что вызов результаты были опубликованы для)

Если вы хотите настроить таргетинг на более конкретную область для обновления с помощью задания 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 будет работать, даже если параметр не указан.

Ответ 1

Я реализовал систему вознаграждения один раз в том, что вы назвали бы ориентированной на документ базой данных (это была грязь для игроков). Некоторые основные моменты моей реализации, переведенные на PHP и MySQL:

  • Каждая информация о значке сохраняется в данных пользователей. Если вы используете MySQL, я бы удостоверился, что эти данные в одной записи для каждого пользователя в базе данных для производительности.

  • Каждый раз, когда этот человек что-то делает, код запускает код значка с заданным флагом, например, флаг ( "POST_MESSAGE" ).

  • Одно событие может также запускать счетчик, например счетчик количества сообщений. increase_count ( 'POST_MESSAGE'). Здесь вы можете проверить (либо с помощью крючка, либо просто пройти тест в этом методе), что если счетчик POST_MESSAGE равен > 300, то вы должны получить награду за значок, например: flag ( "300_POST" ).

  • В методе флага я бы поместил код для награждения значков. Например, если флажок 300_POST отправлен, тогда должен быть вызван значок награды_бадге ( "300_POST" ).

  • В методе флага вам также должны быть представлены предыдущие флаги пользователей. поэтому вы можете сказать, когда пользователь имеет 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-кратный значок).

Ответ 2

UserInfuser - платформа для игр с открытым исходным кодом, которая реализует службу бейджинга/точек. Вы можете проверить его API здесь: http://code.google.com/p/userinfuser/wiki/API_Documentation

Я реализовал его и пытался ограничить количество функций. Вот API для клиента php:

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

Ответ 3

Достижения могут быть обременительными и даже более того, если вам нужно добавить их позже, если у вас нет хорошо сформированного класса Event.

Это переходит в мою технику реализации достижений.

Мне нравится сначала разбивать их на категории, и внутри них есть уровни достижения. то есть категория kills в игре может иметь награду в 1 для первого убийства, 10 десяти убийств, 1000 тысяч убийств и т.д.

Затем к позвоночнику любого хорошего приложения, класс, обрабатывающий ваши события. Снова представляю себе игру с убийствами; когда игрок убивает что-то, все происходит. Убийство отмечено и т.д., И это лучше всего обрабатывать в централизованном месте, например, и Events класс, который может отправлять информацию в другие места.

Он отлично вписывается в это место, что в правильном методе создайте экземпляр класса Achievements и проверьте его, чтобы игрок получил его.

Как создание класса Achievements, это тривиально, просто что-то проверяет базу данных, чтобы увидеть, имеет ли игрок столько убийств, сколько требуется для следующего достижения.

Мне нравится хранить пользовательские достижения в BitField, используя Redis, но тот же метод можно использовать в MySQL. То есть вы можете сохранить достижения игрока как int, а затем and, которые int с бит, который вы определили как это достижение, чтобы узнать, уже ли они его получили. Таким образом, в базе данных используется только один столбец int.

Недостатком этого является то, что вы должны хорошо организовывать их, и вам, вероятно, потребуется внести некоторые комментарии в свой код, чтобы вы помнили, что соответствует 2 ^ 14. Если ваши достижения перечислены в их собственной таблице, вы можете просто сделать 2 ^ pk, где pk является первичным ключом таблицы достижений. Это делает проверку что-то вроде

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

Таким образом, вы можете добавить достижения позже, и это будет хорошо, просто НИКОГДА не измените первичный ключ уже достигнутых достижений.