Я думаю о том, как лучше всего разработать систему достижений для использования на моем сайте. Структуру базы данных можно найти в разделе " Лучший способ указать, что 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 расширяет основной класс значка, он может повторно использовать логическую функцию 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 по-прежнему будет работать, даже если параметр не указан.
источник
Ответы:
Однажды я реализовал систему вознаграждений в том, что вы бы назвали документно-ориентированной базой данных (это было грязью для игроков). Некоторые основные моменты моей реализации, переведенной на PHP и MySQL:
Каждая деталь о бейдже хранится в пользовательских данных. Если вы используете MySQL, я бы удостоверился, что эти данные находятся в одной записи для каждого пользователя в базе данных для повышения производительности.
Каждый раз, когда рассматриваемый человек что-то делает, код запускает код значка с заданным флагом, например flag ('POST_MESSAGE').
Одно событие также может запускать счетчик, например подсчет количества сообщений. увеличить_count ('POST_MESSAGE'). Здесь у вас может быть проверка (либо с помощью крючка, либо просто с помощью теста в этом методе), что если счетчик POST_MESSAGE> 300, вы должны получить значок, например: flag ("300_POST").
В методе флага я бы поместил код для вознаграждения значков. Например, если отправляется флаг 300_POST, то должен быть вызван значок reward_badge ("300_POST").
В методе флага вы также должны иметь предыдущие флаги пользователей. чтобы вы могли сказать, что когда у пользователя есть FIRST_COMMENT, FIRST_POST, FIRST_READ, вы предоставляете значок («НОВЫЙ ПОЛЬЗОВАТЕЛЬ»), а когда вы получаете 100_COMMENT, 100_POST, 300_READ, вы можете предоставить значок («EXPERIENCED_USER»)
Все эти флаги и значки нужно как-то хранить. Используйте способ, которым вы думаете о флагах как о битах. Если вы хотите, чтобы это сохранялось действительно эффективно, вы можете думать о них как о битах и использовать приведенный ниже код: (Или вы можете просто использовать пустую строку «000000001111000», если вам не нужна такая сложность.
Хороший способ хранения документа для пользователя - использовать json и хранить данные пользователей в одном текстовом столбце. Используйте json_encode и json_decode для хранения / извлечения данных.
Для отслеживания активности в отношении данных некоторых пользователей, которыми манипулирует другой пользователь, добавьте структуру данных в элемент и также используйте счетчики. Например, количество прочтений. Используйте ту же технику, которая описана выше для присуждения значков, но обновление, конечно же, должно быть внесено в сообщение о владельце. (Например, статья читается на значке 1000 раз).
источник
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
источник
Достижения могут быть обременительными, и тем более, если вам придется добавить их позже, если у вас нет хорошо сформированного
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 }
Таким образом, вы можете добавлять достижения позже, и все будет хорошо, просто НИКОГДА не меняйте первичный ключ уже полученных достижений.
источник