Je réfléchis à la meilleure façon de concevoir un système de réalisations à utiliser sur mon site. La structure de la base de données peut être trouvée à Meilleur moyen de dire 3 enregistrements consécutifs ou plus manquants et ce fil est vraiment une extension pour obtenir les idées des développeurs.
Le problème que j'ai avec beaucoup de discussions sur les badges / systèmes de réussite sur ce site Web est juste que - tout est parlé et pas de code. Où sont les exemples réels d'implémentation de code?
Je propose ici un design auquel j'espère que les gens pourront contribuer et j'espère créer un bon design pour coder des systèmes de réalisation extensibles. Je ne dis pas que c'est le meilleur, loin de là, mais c'est un point de départ possible.
N'hésitez pas à apporter vos idées.
mon idée de conception de système
Il semble que le consensus général soit de créer un "système basé sur les événements" - chaque fois qu'un événement connu se produit comme une publication est créée, supprimée, etc., il appelle la classe d'événements comme ceci.
$event->trigger('POST_CREATED', array('id' => 8));
La classe d'événements découvre alors quels badges "écoutent" cet événement, puis dans ce requires
fichier, et crée une instance de cette classe, comme ceci:
require '/badges/' . $file;
$badge = new $class;
Il appelle ensuite l'événement par défaut en transmettant les données reçues lors de l' trigger
appel;
$badge->default_event($data);
les badges
C'est alors que la vraie magie opère. chaque badge a sa propre requête / logique pour déterminer si un badge doit être attribué. Chaque badge est présenté par exemple dans ce format:
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
La fonction provient d'une classe étendue Badge
qui vérifie essentiellement si l'utilisateur a déjà reçu ce badge, sinon, mettra à jour la table de base de données des badges. La classe de badge s'occupe également de récupérer tous les badges d'un utilisateur et de les renvoyer dans un tableau, etc. (les badges peuvent donc être affichés par exemple sur le profil de l'utilisateur)
qu'en est-il lorsque le système est mis en œuvre pour la première fois sur un site déjà en ligne?
Il existe également une requête de tâche "cron" qui peut être ajoutée à chaque badge. La raison en est que lorsque le système de badges est mis en œuvre et lancé pour la première fois, les badges qui auraient déjà dû être gagnés n'ont pas encore été attribués car il s'agit d'un système basé sur des événements. Ainsi, un travail CRON est exécuté à la demande pour chaque badge afin d'attribuer tout ce qui doit l'être. Par exemple, le travail CRON pour ce qui précède ressemblerait à:
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);
}
}
}
Comme la classe cron ci-dessus étend la classe de badge principale, elle peut réutiliser la fonction logique try_award
La raison pour laquelle je crée une requête spécialisée pour cela est que nous pourrions "simuler" les événements précédents, c'est-à-dire parcourir chaque message d'utilisateur et déclencher la classe d'événements comme si $event->trigger()
elle serait très lente, en particulier pour de nombreux badges. Nous créons donc plutôt une requête optimisée.
quel utilisateur obtient le prix? tout sur l'attribution d' autres utilisateurs en fonction de l'événement
La fonction de Badge
classe award
agit sur user_id
- ils recevront toujours le prix. Par défaut, le badge est attribué à la personne qui a CAUSÉ l'événement, c'est-à-dire l'ID utilisateur de la session (cela est vrai pour la default_event
fonction, bien que le travail CRON boucle évidemment à travers tous les utilisateurs et attribue des utilisateurs séparés)
Prenons donc un exemple, sur un site Web de défi de codage, les utilisateurs soumettent leur entrée de codage. L'administrateur évalue ensuite les entrées et une fois terminé, publie les résultats sur la page de défi pour que tous les voient. Lorsque cela se produit, un événement POSTED_RESULTS est appelé.
Si vous souhaitez attribuer des badges aux utilisateurs pour toutes les entrées publiées, disons, si elles étaient classées dans le top 5, vous devez utiliser le travail cron (bien que sachez que cela sera mis à jour pour tous les utilisateurs, pas seulement pour ce défi. les résultats ont été publiés pour)
Si vous souhaitez cibler une zone plus spécifique à mettre à jour avec le travail cron, voyons s'il existe un moyen d'ajouter des paramètres de filtrage dans l'objet de travail cron et obtenez la fonction cron_job pour les utiliser. Par exemple:
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);
}
}
La fonction cron fonctionnera toujours même si le paramètre n'est pas fourni.
la source
Réponses:
J'ai implémenté un système de récompense une fois dans ce que vous appelleriez une base de données orientée document (c'était une boue pour les joueurs). Quelques points forts de mon implémentation, traduits en PHP et MySQL:
Chaque détail du badge est stocké dans les données des utilisateurs. Si vous utilisez MySQL, je me serais assuré que ces données sont dans un enregistrement par utilisateur dans la base de données pour les performances.
Chaque fois que la personne en question fait quelque chose, le code déclenche le code du badge avec un drapeau donné, par exemple flag ('POST_MESSAGE').
Un événement peut également déclencher un compteur, par exemple un décompte du nombre de publications. augmenter_compte ('POST_MESSAGE'). Ici, vous pouvez vérifier (soit par un crochet, soit simplement en effectuant un test dans cette méthode) que si le nombre de POST_MESSAGE est> 300, vous devriez avoir récompensé un badge, par exemple: flag ("300_POST").
Dans la méthode du drapeau, je mettrais le code pour récompenser les badges. Par exemple, si le drapeau 300_POST est envoyé, le badge récompense_badge ("300_POST") doit être appelé.
Dans la méthode des indicateurs, vous devez également avoir les indicateurs précédents des utilisateurs présents. vous pouvez donc dire que lorsque l'utilisateur a FIRST_COMMENT, FIRST_POST, FIRST_READ vous accordez le badge ("NEW USER"), et lorsque vous obtenez 100_COMMENT, 100_POST, 300_READ, vous pouvez attribuer le badge ("EXPERIENCED_USER")
Tous ces drapeaux et badges doivent être stockés d'une manière ou d'une autre. Utilisez une manière où vous considérez les drapeaux comme des bits. Si vous voulez que cela soit stocké vraiment efficacement, vous les considérez comme des bits et utilisez le code ci-dessous: (Ou vous pouvez simplement utiliser une chaîne nue "000000001111000" si vous ne voulez pas cette complexité.
Une bonne façon de stocker un document pour l'utilisateur est d'utiliser json et de stocker les données des utilisateurs dans une seule colonne de texte. Utilisez json_encode et json_decode pour stocker / récupérer les données.
Pour suivre l'activité sur certaines des données des utilisateurs manipulées par un autre utilisateur, ajoutez une structure de données sur l'élément et utilisez également des compteurs. Par exemple, lire le nombre. Utilisez la même technique que celle décrite ci-dessus pour l'attribution des badges, mais la mise à jour doit bien sûr être envoyée dans le message des utilisateurs propriétaires. (Par exemple, l'article lu 1000 fois le badge).
la source
UserInfuser est une plateforme de gamification open source qui implémente un service de badges / points. Vous pouvez consulter son API ici: http://code.google.com/p/userinfuser/wiki/API_Documentation
Je l'ai implémenté et j'ai essayé de garder le nombre de fonctions minimal. Voici l'API pour un client 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); }
Le résultat final est d'afficher les données de manière significative grâce à l'utilisation de widgets. Ces widgets incluent: cas de trophées, classement, jalons, notifications en direct, rang et points.
La mise en œuvre de l'API peut être trouvée ici: http://code.google.com/p/userinfuser/source/browse/trunk/serverside/api/api.py
la source
Les réalisations peuvent être lourdes et encore plus si vous devez les ajouter plus tard, à moins que vous n'ayez une
Event
classe bien formée .Cela rejoint ma technique de mise en œuvre des réalisations.
J'aime les diviser d'abord en «catégories» et à l'intérieur de celles-ci ont des niveaux de réalisation. c'est-à-dire qu'une
kills
catégorie dans un jeu peut avoir une récompense à 1 pour le premier kill, 10 dix kills, 1000 mille kills etc.Puis à la colonne vertébrale de toute bonne application, la classe qui gère vos événements. Imaginez à nouveau un jeu avec des victoires; quand un joueur tue quelque chose, des choses arrivent. Le kill est noté, etc. et il est préférable de le gérer dans un emplacement centralisé, comme une
Events
classe qui peut envoyer des informations à d'autres endroits impliqués.Il se met parfaitement en place là, que dans la bonne méthode, instanciez votre
Achievements
classe et vérifiez que le joueur en a une.En construisant la
Achievements
classe, c'est trivial, juste quelque chose qui vérifie la base de données pour voir si le joueur a autant de victimes que nécessaire pour la prochaine réalisation.J'aime stocker les réalisations des utilisateurs dans un BitField en utilisant Redis mais la même technique peut être utilisée dans MySQL. Autrement dit, vous pouvez stocker les succès du joueur sous forme d'un
int
, puisand
cet int avec le bit que vous avez défini comme cet exploit pour voir s'il l'a déjà gagné. De cette façon, il n'utilise qu'une seuleint
colonne dans la base de données.L'inconvénient est que vous devez bien les organiser et que vous devrez probablement faire quelques commentaires dans votre code pour vous rappeler ce à quoi 2 ^ 14 correspond plus tard. Si vos réalisations sont énumérées dans leur propre tableau, vous pouvez simplement faire 2 ^ pk où
pk
est la clé primaire du tableau des réalisations. Cela fait du chèque quelque chose commeif(((2**$pk) & ($usersAchInt)) > 0){ // fire off the giveAchievement() event }
De cette façon, vous pouvez ajouter des succès plus tard et cela s'enchaînera très bien, ne changez JAMAIS la clé primaire des succès déjà attribués.
la source