2015-10-26 3 views
0

ОбъяснениеКак я могу создать систему Quest Quest, которая легко расширяется?

Допустим, у нас есть несколько квестов, которая вознаграждает игрока после завершения что-то. Приведенные ниже примеры показывают задания пользователя:

Name: Get a high score of 100 
Requirement: Score of 100 
Reward: 10 Coins 

Name: Get a high score of 5000 
Requirement: Score of 5000 
Reward: 10 Diamonds 

Name: Destroy 20 ships 
Requirement: Destroy 20 ships 
Reward: 5 Diamonds 

Name: Reach level 5 
Requirement: Level 5 
Reward: 20 Coins, 10 Diamonds 

Затем я создаю следующую логику:

Создать суперкласс с именем «Quest». Создайте подкласс для каждого квеста; наследуя суперкласс «Квест». Используя полиморфизм, мы вызываем метод CheckForCompletion, чтобы проверить, выполнено ли задание квеста. Если это так, пользователь получает вознаграждение.

Вопрос

Мой вопрос я могу создать таблицу базы данных под названием «Задания» и хранить каждый из перечисленных выше заданий в таблице?

Задания Пример таблицы данных: Имя: Уничтожить 20 кораблей Требование: { «Destroy», «20»} Награда: { «Бриллианты», «5»} Если да, то это лучший способ для загрузки каждый квест в соответствующий класс?

ИЛИ

ли я создать класс для каждого квеста, не создавая таблицу квестов? Если да, то как я узнаю, когда пользователь выполнил квест, чтобы пользователь дважды не награждал награду?

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

Благодаря

Редактировать 1:

Каждый квест может иметь несколько требований. Например: Уничтожьте 5 кораблей, Убейте 10 единиц, Оценка 1000 и т. Д.

Чтобы объяснить, хочу ли я хочу, чтобы это было исключение, если заявления. Я не хочу делать, если такие утверждения;

if (score == 100) 
    //process 
else if (ships_destroyed == 5) 
    //process 
else if (level == 5) 
    //process 
else if (ships_destroyed == 5 && units_killed == 10 && score == 1000) 
    //process 

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

ответ

1

У меня были схожие требования: добавление достижений/значков в игру.

Вы должны думать о том:

  1. какой тип предусловий вы собираетесь иметь
  2. какой тип наград у вас есть.

Предпосылки: Если предварительное условие прост, например, «уничтожить 5 кораблей», что его легко хранить в db.Если это сложно, как «уничтожить 5 кораблей и 10 единиц», это не плоская структура предварительных условий.

Таким образом, я могу предложить вам

  1. таблицу для описания квеста: ID, название, описание и так далее.
  2. стол для предварительных условий: квест_ид, тип («уничтожить магазин», «выиграть битву» ...), количество (5, 10).
  3. стол наград: quest_id, money_type, количество. Если у вас будет только один тип вознаграждения за каждый квест, то какие данные можно перенести на первую таблицу

Как проверить каждое условие также является проблемой. Специально, если у вас их много. Одно из возможных решений, добавьте некоторые определения триггеров. Как «пользователь просто уничтожил корабль», «пользователь просто выиграл битву». И определите для каждого квеста, какой тип триггера может вызвать его. Эта информация также может храниться в отдельной таблице

UPD: Чтобы проверить каждое предварительное условие, для каждого типа должен быть один класс java.

interface Precondition { 
    String type() 
    boolean check(Map<String, Object> config, Long userId); 
} 

class DestroyShipPrecondition{ 
    public String type() { 
     return "destroy_ship" 
    } 

    public boolean check(Map<String, Object> config, Long userId){ 
     Long expectdAmount = config.get("amount"); 
     Long realAmount = getDestroyedShipsByUser(userId); 
     return expectedAmount<=realAmount; 
    }  
} 

Некоторые ваши услуги должны иметь карту всех предусловий по их типу. Поэтому, когда вам нужно проверить задание, вы загружаете свои предпосылки, а затем:

public boolean checkQuest(Quest quest, Long userId){ 
    for (PreconditionConfig preconditionConfig : quest.getPrecondidtionConfigs()){ 
     Precondition precondition = precondititons.get(preconditionConfig.type); 
     if (!precondition.check(preconditionConfig.getContext(), userId)){ 
      return false; 
     } 
    } 
    return true; 
} 
+0

Каждый квест может иметь несколько предварительных условий. Сначала квест мог иметь только одно предварительное условие, подобное «уничтожить 5 кораблей», но по мере продвижения пользователя квесты становятся сложнее и могут иметь 5 или более предварительных условий. Я все еще не понимаю, как проверить, выполняется ли каждое предварительное условие с помощью триггеров. Можете ли вы объяснить дополнительные или любые ссылки, пожалуйста? – Beardslapper

+0

@Beardslapper ищет обновления. Надеюсь, это понятно – Natalia

3

Ваш вопрос имеет несколько взаимосвязанных аспектов - и, как обычно, ответ «это зависит».

Во-первых, я бы рекомендовал не наследуя свой класс «Quest» - это не черно-белый, конечно, но в целом, я предпочитаю composition over inheritance

Если вы уверены, что механика игры выиграл» t change, я бы не стал беспокоиться о таблице базы данных - я бы использовал константы на вашем языке программирования (Java или PHP). Поиск статических данных в базе данных является дорогостоящим с точки зрения развития и производительности.

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

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

И, наконец, большинство игровых движков include a scripting language для этого требования - может быть, излишним для того, что вы планируете, но стоит рассмотреть.

+0

Игра - многопользовательская игра. Я посмотрю на композицию над наследством. У вас есть материал, связанный с составом над наследованием, похожий на мою проблему? – Beardslapper

0

TL; DR Храните свою логику как лямбда в перечислениях и используйте свое состояние игры как вход.

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

Ради плотности я положил все это в одном классе, но вы, вероятно, можно увидеть, где я собираюсь с ним ...

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

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

public class Game { 
    public static class GameState { 
    public int playerscore = 1750; 
    } 

    public enum Quest { 
    FIVE_HUNDRED_PT(
     (GameState gamestate) -> gamestate.playerscore > 500, 
    () -> Game.trigger500pointHurray() 
    ), 
    FIVE_THOUSAND_PT(
     (GameState gamestate) -> gamestate.playerscore > 5000, 
    () -> Game.trigger5000pointHurray() 
    ); 

    private final Function<GameState, Boolean> trigger; 
    private final Runnable action; 

    Quest(Function<GameState, Boolean> trigger, Runnable action) { 
     this.trigger = trigger; 
     this.action = action; 
    } 

    } 

    public static void gameloop(GameState state) { 
    // Do all game logic. 

    for (Quest q : Quest.values()) 
     if (q.trigger.apply(state)) 
     q.action.run(); 

    } 

    public static void trigger500pointHurray() { 
    System.out.println("Hurray x500"); 
    } 

    public static void trigger5000pointHurray() { 
    System.out.println("Hurray x5000"); 
    } 

    public static void main(String[] args) { 
    gameloop(new GameState()); 
    } 
}