Zastanawiam się nad najlepszym sposobem zaprojektowania systemu osiągnięć do wykorzystania w mojej witrynie. Strukturę bazy danych można znaleźć w Najlepszym sposobie, aby stwierdzić brakujące 3 lub więcej kolejnych rekordów, a ten wątek jest naprawdę rozszerzeniem umożliwiającym uzyskanie pomysłów od programistów.
Problem, który mam z wieloma rozmowami o odznakach / systemach osiągnięć na tej stronie jest po prostu taki - wszystko to mówi i nie ma kodu. Gdzie są rzeczywiste przykłady implementacji kodu?
Proponuję tutaj projekt, do którego, mam nadzieję, ludzie mogliby się przyczynić i mam nadzieję, że stworzy dobry projekt do kodowania rozszerzalnych systemów osiągnięć. Nie mówię, że to jest najlepsze, dalekie od tego, ale to możliwy blok startowy.
Prosimy o zgłaszanie swoich pomysłów.
mój pomysł na projekt systemu
Wygląda na to, że panuje powszechna zgoda co do stworzenia „systemu opartego na zdarzeniach” - za każdym razem, gdy występuje znane zdarzenie, takie jak tworzenie, usuwanie postów itp., Wywołuje klasę zdarzenia w ten sposób.
$event->trigger('POST_CREATED', array('id' => 8));
Następnie klasa zdarzenia sprawdza, jakie odznaki „nasłuchują” tego zdarzenia, a następnie przechodzi do tego requires
pliku i tworzy instancję tej klasy, na przykład:
require '/badges/' . $file;
$badge = new $class;
Następnie wywołuje domyślne zdarzenie, przekazując dane odebrane, gdy trigger
zostało wywołane;
$badge->default_event($data);
odznaki
To wtedy dzieje się prawdziwa magia. każda odznaka ma własne zapytanie / logikę w celu określenia, czy należy przyznać odznakę. Każda plakietka ma np. Następujący 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
function pochodzi z klasy rozszerzonej, Badge
która zasadniczo sprawdza, czy użytkownikowi przyznano już tę odznakę, a jeśli nie, zaktualizuje tabelę baz znaczników. Klasa badge zajmuje się również pobieraniem wszystkich identyfikatorów dla użytkownika i zwracaniem ich w tablicy itp. (Tak więc identyfikatory mogą być np. Wyświetlane w profilu użytkownika)
co się stanie, gdy system zostanie wdrożony po raz pierwszy w już działającej witrynie?
Istnieje również zapytanie o zadanie „cron”, które można dodać do każdej odznaki. Powodem tego jest to, że gdy system odznak jest po raz pierwszy wdrażany i uruchamiany, odznaki, które powinny już zostać zdobyte, nie zostały jeszcze przyznane, ponieważ jest to system oparty na wydarzeniach. Dlatego zadanie CRON jest uruchamiane na żądanie dla każdej odznaki, aby nagrodzić wszystko, co ma być. Na przykład zadanie CRON dla powyższego wyglądałoby następująco:
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);
}
}
}
Ponieważ powyższa klasa cron rozszerza główną klasę badge, może ponownie użyć funkcji logicznej try_award
Powodem, dla którego tworzę wyspecjalizowane zapytanie, jest to, że mogliśmy "zasymulować" poprzednie zdarzenia, tj. Przejrzeć każdy wpis użytkownika i wywołać klasę zdarzenia tak $event->trigger()
, jakby była bardzo powolna, szczególnie w przypadku wielu odznak. Dlatego zamiast tego tworzymy zoptymalizowane zapytanie.
jaki użytkownik otrzyma nagrodę? wszystko o nagradzaniu innych użytkowników na podstawie wydarzenia
Funkcja Badge
klasowa award
działa user_id
- zawsze otrzymają nagrodę. Domyślnie odznaka jest przyznawana osobie, która spowodowała wystąpienie zdarzenia, tj. Identyfikator użytkownika sesji (dotyczy to default_event
funkcji, chociaż zadanie CRON oczywiście przechodzi przez wszystkich użytkowników i nagradza oddzielnych użytkowników)
Weźmy więc przykład, w przypadku wyzwania z kodowaniem, użytkownicy witryny przesyłają swój kod. Administrator następnie ocenia zgłoszenia i po zakończeniu publikuje wyniki na stronie wyzwania, aby wszyscy mogli je zobaczyć. W takim przypadku wywoływane jest zdarzenie POSTED_RESULTS.
Jeśli chcesz przyznać odznaki użytkownikom za wszystkie opublikowane wpisy, powiedzmy, że jeśli znaleźli się w pierwszej piątce, powinieneś użyć zadania cron (chociaż pamiętaj, że zaktualizuje się to dla wszystkich użytkowników, a nie tylko dla tego wyzwania wyniki zostały opublikowane dla)
Jeśli chcesz wybrać bardziej konkretny obszar do aktualizacji za pomocą zadania cron, zobaczmy, czy istnieje sposób na dodanie parametrów filtrowania do obiektu zadania cron i uzyskanie funkcji cron_job, aby z nich korzystać. Na przykład:
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);
}
}
Funkcja cron będzie nadal działać, nawet jeśli parametr nie zostanie podany.
źródło
Odpowiedzi:
Wdrożyłem kiedyś system nagród w czymś, co można by nazwać bazą danych zorientowaną na dokumenty (to było błoto dla graczy). Niektóre najważniejsze informacje z mojej implementacji, przetłumaczone na PHP i MySQL:
Każdy szczegół dotyczący identyfikatora jest przechowywany w danych użytkownika. Jeśli używasz MySQL, upewniłbym się, że dane te znajdują się w jednym rekordzie na użytkownika w bazie danych w celu zapewnienia wydajności.
Za każdym razem, gdy dana osoba coś robi, kod uruchamia kod odznaki z daną flagą, na przykład flagą ('POST_MESSAGE').
Jedno zdarzenie może również wyzwolić licznik, na przykład liczbę postów. zwiększ_liczbę ('POST_MESSAGE'). Tutaj możesz sprawdzić (albo przez hak, albo po prostu mając test w tej metodzie), że jeśli liczba POST_MESSAGE jest> 300, powinieneś otrzymać odznakę, na przykład: flag ("300_POST").
W metodzie flag umieściłbym kod nagradzający odznaki. Na przykład, jeśli zostanie wysłana flaga 300_POST, wówczas należy wywołać odznakę nagrodę_nagroda („300_POST”).
W metodzie flag powinieneś również mieć obecne flagi użytkowników. więc możesz powiedzieć, że gdy użytkownik ma FIRST_COMMENT, FIRST_POST, FIRST_READ, przyznajesz plakietkę („NOWY UŻYTKOWNIK”), a gdy otrzymasz 100_COMMENT, 100_POST, 300_READ, możesz przyznać plakietkę („EXPERIENCED_USER”)
Wszystkie te flagi i odznaki muszą być w jakiś sposób przechowywane. Użyj sposobu, w którym myślisz o flagach jako bitach. Jeśli chcesz, aby było to naprawdę wydajnie przechowywane, myśl o nich jako o bitach i użyj poniższego kodu: (Lub możesz po prostu użyć czystego ciągu znaków „000000001111000”, jeśli nie chcesz takiej złożoności).
Dobrym sposobem na przechowywanie dokumentu przez użytkownika jest użycie json i przechowywanie danych użytkowników w jednej kolumnie tekstowej. Użyj json_encode i json_decode do przechowywania / pobierania danych.
Aby śledzić aktywność niektórych danych użytkowników, którymi manipulował inny użytkownik, dodaj strukturę danych do elementu i tam również użyj liczników. Na przykład liczba odczytów. Użyj tej samej techniki, co opisana powyżej, do przyznawania odznak, ale aktualizacja powinna oczywiście zostać umieszczona w poście ich właścicieli. (Na przykład artykuł przeczytany 1000 razy znaczek).
źródło
UserInfuser to platforma grywalizacji typu open source, która implementuje usługę oznaczania / punktów. Możesz sprawdzić jego API tutaj: http://code.google.com/p/userinfuser/wiki/API_Documentation
Zaimplementowałem to i starałem się, aby liczba funkcji była minimalna. Oto API dla klienta 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); }
Efektem końcowym jest przedstawienie danych w znaczący sposób za pomocą widżetów. Te widżety obejmują: skrzynię trofeów, tabelę wyników, kamienie milowe, powiadomienia na żywo, rangę i punkty.
Implementację API można znaleźć tutaj: http://code.google.com/p/userinfuser/source/browse/trunk/serverside/api/api.py
źródło
Osiągnięcia mogą być uciążliwe, a tym bardziej, jeśli będziesz musiał je później dodać, chyba że masz dobrze uformowaną
Event
klasę.To wpisuje się w moją technikę realizacji osiągnięć.
Lubię najpierw dzielić je na „kategorie”, które mają różne poziomy spełnienia. tj.
kills
kategoria w grze może mieć 1 nagrodę za pierwsze zabójstwo, 10 dziesięciu zabójstw, 1000 tysięcy zabójstw itd.Następnie do kręgosłupa każdej dobrej aplikacji, klasy obsługującej Twoje zdarzenia. Ponownie wyobrażając sobie grę z zabójstwami; kiedy gracz coś zabije, coś się dzieje. Zabójstwo jest odnotowywane itp. I najlepiej jest
Events
to zrobić w scentralizowanej lokalizacji, takiej jak i klasa, która może wysyłać informacje do innych zaangażowanych miejsc.Idealnie pasuje do tego, że we właściwej metodzie utwórz instancję swojej
Achievements
klasy i sprawdź, czy graczowi należy się jeden.Podczas budowania
Achievements
klasy jest to trywialne, po prostu coś, co sprawdza bazę danych, aby zobaczyć, czy gracz ma tyle zabójstw, ile potrzeba do następnego osiągnięcia.Lubię przechowywać osiągnięcia użytkownika w BitField przy użyciu Redis, ale ta sama technika może być używana w MySQL. Oznacza to, że możesz zapisać osiągnięcia gracza jako wartość,
int
a następnieand
tę wartość z bitem, który zdefiniowałeś jako to osiągnięcie, aby sprawdzić, czy już je zdobył. W ten sposób używa tylko jednejint
kolumny w bazie danych.Wadą tego jest to, że musisz je dobrze zorganizować i prawdopodobnie będziesz musiał wprowadzić kilka komentarzy w swoim kodzie, abyś pamiętał, co odpowiada 2 ^ 14 później. Jeśli twoje osiągnięcia są wyliczone we własnej tabeli, możesz po prostu zrobić 2 ^ pk, gdzie
pk
jest klucz główny w tabeli osiągnięć. To sprawia, że czek wygląda jakif(((2**$pk) & ($usersAchInt)) > 0){ // fire off the giveAchievement() event }
W ten sposób możesz później dodać osiągnięcia i wszystko będzie dobrze pasować, po prostu NIGDY nie zmieniaj klucza głównego już przyznanych osiągnięć.
źródło