2010-11-16 4 views
83

Я думаю о лучшем способе разработки системы достижений для использования на моем сайте. Структура базы данных может быть найдена в Best way to tell 3 or more consecutive records missing, и эта тема действительно является расширением для получения идей от разработчиков.Лучший способ кода Система достижений

Проблема, с которой я сталкиваюсь с большим количеством разговоров о знаках/системах достижений на этом веб-сайте, - это все разговоры и отсутствие кода. Где примеры реализации кода?

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

Пожалуйста, не стесняйтесь вносить свои идеи.


мой дизайн системы идея

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

$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, который в основном проверяет, чтобы увидеть, если пользователь уже присуждаются этот жетон, если нет, то будет обновлять таблицу значок дб. Класс значков также заботится о том, чтобы получить все значки для пользователя и вернуть его в массив и т. Д. (Так что значки могут отображаться, например, в профиле пользователя)

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

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

} 

Как выше класс хрон расширяет основной класс значок, он может повторно использовать логическую функцию try_award

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

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

Badge В классе 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 будет работать, даже если параметр не указан.

+0

Связанный (может быть, дубликат): http://stackoverflow.com/questions/1744747/achievements-badges-system – Gordon

+2

Это связано, но не дублируется. Прочтите второй абзац. «Проблема, с которой я сталкиваюсь с большим количеством разговоров о системах значков/достижений на этом веб-сайте, - это все - разговоры и отсутствие кода. Где примеры реализации кода?» –

+1

хорошо, письменный рабочий код возможен только в определенной степени. Я бы сказал, что для людей достаточно просто дать вам теорию, только когда любая реализация будет слишком сложной. – Gordon

ответ

9

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

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

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

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

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

  • В методе флага также должны присутствовать предыдущие флаги пользователей. так что можно сказать, когда пользователь имеет FIRST_COMMENT, FIRST_POST, FIRST_READ вы предоставляете знак («Новый пользователь»), и когда вы получите 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

Классический тренд в системах значков - это добавить новое поле для новой статистики в таблицу. Для меня это похоже на легкий путь и плохую идею, потому что вы сохраняете зеркальные данные, которые можно вычислить из данных, уже находящихся в таблице (возможно, простой COUNT(), который очень быстро работает на таблицах MyISAM, будет на 100% точный). Если производительность была вашей целью, вам нужно сделать обновление и выбрать, чтобы получить текущий, например. post_count, чтобы проверить, должен ли быть отмечен значок. Вам может потребоваться только один запрос, COUNT (*). Я соглашаюсь на более сложные данные, поэтому было бы неплохо добавить поле, но –

+5

@Gary Green Это не только простой выход, но и масштабируемый способ и совместимый с базами данных документов. Что касается правильности, вы правы, хотя для системы значков я бы предпочел бы, чтобы она была быстрой и, скорее всего, правильной, чем 100% правильной и медленной. Один единственный счет, вероятно, быстрый, но когда ваша система масштабируется, и у вас много пользователей, эта стратегия не предоставляется. – Knubo

+1

Мне нравится идея просто иметь таблицу определения значка и таблицу ссылок, чтобы связать пользователей с значками и их текущий прогресс. Выполнение этого noSQL блокирует вас во все схемы в то время и не поддерживается, когда обнаруживаются опечатки в значках или добавляются 1000 новых значков. У вас всегда может быть пакетный процесс кэширования в хранилище документов для быстрого извлечения, но я бы оставил вещи связанными. – FlavorScape

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

+1

- это PHP? Вопрос основан на PHP – emaillenin

+1

Он имеет привязки PHP, но код на стороне сервера написан на Python. –

0

Достижение может быть обременительным и даже больше, если у вас есть, чтобы добавить их в дальнейшем, если у вас есть хорошо сформированная 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 
} 

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

Смежные вопросы