Відокремлення світової держави та анімації у грі на основі покроків


9

Як ви справляєтеся з відокремленням анімації від світової держави в рамках гри по черзі? Зараз я працюю над грою на 2D сітці. Нижче наведений код спрощений для кращого пояснення.

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

Що я зробив, це ввести два типи анімацій, які я називаю блокуючими анімаціями та неблокуючими анімаціями. Коли гравець хоче рухатися, виконується код, який виконується

class Actor {
    void move(PositionInfo oldPosition, PositionInfo newPosition) {
        if(isValidMove(oldPosition, newPosition) {
             getGame().pushBlockingAnimation(new MoveAnimation(this, oldPosition, newPosition, ....));
             player.setPosition(newPosition);
        }
    }
}

Тоді основний цикл оновлення робить:

class Game {
    void update(float dt) {
        updateNonBlockingAnimations(dt); //Effects like particle explosions, damage numbers, etc. Things that don't block the flow of turns.
        if(!mBlockingAnimations.empty()) {
            mBlockingAnimations.get(0).update(dt);
        } else {
             //.. handle the next turn. This is where new animations will be enqueued..//
        }
        cleanUpAnimations(); // remove finished animations
    }
}

... де анімація оновлює положення екрана Актора.

Ще одна ідея, яку я реалізую, - це створення одночасної анімації блокування, де декілька блокуючих анімацій будуть оновлюватися одночасно, але наступна черга не відбудеться, поки всі вони не будуть виконані.

Це здається розумним способом робити речі? Хтось має якісь пропозиції чи навіть посилання на те, як інші подібні ігри роблять подібне.

Відповіді:


2

Якщо ваша система працює на вас, я не бачу причин не робити цього.

Ну, одна причина: може стати безладним, коли ви не можете легко судити про наслідки "player.setPosition (newPosition);" більше.

Приклад (лише вигадуючи речі): Уявіть, що ви переміщуєте гравця за допомогою свого setPosition поверх якоїсь пастки. Сам виклик "player.setPosition" викликає інший виклик .. сказати код слухача пастки, який, в свою чергу, підштовхне блокуючу анімацію, яка відображає "болісний сплеск" поверх себе.

Ви бачите проблему: сплеск відображається одночасно з переміщенням гравця у пастку. (Давайте припустимо, що ви цього не хочете, але хочете, щоб анімація пастки була відтворена одразу після закінчення переміщення;)).

Тож поки ви зможете мати на увазі всі наслідки своїх дій, які ви робите після тих "дзвінків pushBlockingAnimation", все добре.

Можливо, вам доведеться почати перетворювати логіку гри на «блокування чогось» - класи, щоб їх можна було поставити в чергу для виконання після закінчення анімації. Або ви передаєте зворотний виклик "callThisAfterAnimationFinishes" до pushBlockingAnimation та переміщуєте setPosition у функцію зворотного виклику.

Іншим підходом було б написання мов. Наприклад, у Lua є "coroutine.yield", який повертає функцію з особливим станом, щоб її можна було викликати пізніше, щоб продовжити в наступному операторі. Таким чином, ви можете легко дочекатися закінчення анімації, щоб виконати ігрову логіку, не захаращуючи "нормальний" вигляд програмного потоку вашого коду. (означає, що ваш код все-таки буде трохи схожий на "playAnimation (); setPosition ();" замість того, щоб передавати зворотні дзвінки навколо та захаращувати логіку гри через декілька функцій).

Unity3D (і принаймні ще одну ігрову систему, яку я знаю), має C # "прибутковість", щоб імітувати це безпосередньо в C # коді.

// instead of simple call to move(), you have to "iterate" it in a foreach-like loop
IEnumerable<Updatable> move(...) {
    // playAnimation returns an object that contain an update() and finished() method.
    // the caller will not iterate over move() again until the object is "finished"
    yield return getGame().playAnimation(...)
    // the next statement will be executed *after* the animation finished
    player.setPosition(newPosition);
}

Звичайно, в цій схемі заклик переїхати () зараз отримує всю брудну роботу. Ви, ймовірно, застосовуєте цю техніку лише для конкретних функцій, де ви точно знаєте, хто звідки їх викликає. Тому сприймайте це лише як основне уявлення про організацію інших двигунів - це, ймовірно, занадто складне для вашої поточної конкретної проблеми.

Я рекомендую дотримуватися ідеї pushBlockingAnimation, поки у вас не виникнуть проблеми в реальному світі. Потім змініть його. ;)


Дуже дякую, що знайшли час, щоб написати цю відповідь, я дуже ціную це.
mdkess
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.