Classic turn based workflow, how to?

#1

Hi,

I really like roguelikes games, and want to create one. Since we’re already far into the XXI century, I want to have a good graphics support, and since I want to write in rust, Amethyst seems like an excellent choice.

But.

I don’t want game to be played in real time, I want it to be turn based, with every “monster” taking turn based on its speed. So a fast monster can move for example every 5 turns, which a slow one once every 8 turns (just an example). And I have hard time with grasping how to make it in amethyst. In a simple and naive implementation I would have just turn counter, iterate over all entities, and compare turn counter with internal entity variable and do (or don’t) do something, Then at the beginning of next iteration increment the turn counter, and so on.

How to write system for that in amethyst? My concerns are, that running system once per frame is gonna be probably too slow, on the other hand system should not run, when waiting for user input, and screen should update only whenever something really moved on screen. While drawing is less of a problem, I’m not sure how to make system run as fast as possible, and then wait for user input.

1 Like
(OvermindDL1) #2

Rendering should happen per frame regardless, after all you will want to render things like idle animations and mouse cursors and menu’s and such while thinking about a turn. Otherwise just queue up actions via components, then when you end turn a system iterates over those components and resolves them into actions. :slight_smile:

3 Likes
#3

The problem is that even with 60, 120, or even 240 fps, it can take lot of seconds, or minutes until there will be player turn. If turn will have a resolution of say 1/100 of a second, and a player action, like crafting something, takes 10 minutes, with 60 fps the real life player will have to wait over 16 minutes, instead of almost instantly getting text that it passed 10 minutes in game. I simply don’t know if it’s even possible to have one system running as fast as possible with pauses for user input, and second, graphic one, running at X fps. And how to stop that first system so it can wait for data from the Input system.

PS. I thought the internet is much bigger. Nice to see you here to, knowing how insightful person you are, back from elixir forums :slight_smile:

(doomy) #4

Correct me if I’m wrong understanding the issue but I think I know what should happen here.

You do not want to tie anything to actual game time, since the time a turn takes is arbitrary, as you already noted. Your original assumption looks to be how I would set things up. I would have your entity keep track of its internal turns, and devise a system to handle what happens whenever the turn increments. If the turn hasn’t changed since the last system run, just do nothing.

The additional question is “why should I run this system constantly, when it only needs to update on a very specific action?”. I’m not actually too sure about that one, or if it would cause a performance hit in any way (likely not) so I’ll let someone more knowledgable respond to that part. You may also want to take a look at Events.

4 Likes
#5

I tried to write app that run one system with unlimited fps, second just running at 10 FPS, and third, running at 1 FPS, last one showing on screen how many “turns” elapsed for each system. And all I’m able to get is about 60 fps for the unlimited system. I run the app with FrameRateLimitStrategy::Unlimited code is here:

Did i miss something or am I really not able to run it faster? I got 8 core CPU (AMD Ryzen 1700X) with AMD RX480 so it’s rather fast machine.

(Azriel Hoh) #6

vsync is on by default, you need to disable it usually in the graphics driver settings, and maybe also in display_settings.ron.

#7

Can you help me with how to disable vsync in amethyst config? When i disable vsync in console with export vblank_mode=0, running glxgears from console gives me 9065.723 FPS, so I firmly believe it’s turned off, but still my litlle “test” with amethyst give me ~60 FPS. I added vsync: false, to display config.ron, but I didn’t do much.

(Azriel Hoh) #8

hm, I’m also convinced. But could you check if there’s a driver settings app that has a vsync setting? Otherwise I’m out of ideas

#9

I did this: https://wiki.archlinux.org/index.php/ATI#Turn_vsync_off and on driver level, in /etc/X11/xorg.conf.d/20-amdgpu.conf I set those variables

Section "Device"
     Identifier "AMD"
     Driver "amdgpu"
     Option "TearFree" "off"
     Option "EXAVsync" "no"
     Option "SwapbuffersWait" "off"
EndSection

glxgears gives me 10607.813 FPS stright after restart without messing with any env variables, or running anything else prior to it, beside Konsole, from which i start glxgears.

Amethyst app, i linked earlier still gives me only 60 fps.

(Gray Olson) #10

Amethyst does not allow disabling vsync right now I believe. We’ll need to change the presentation mode of the swapchain in rendy in order to do so. Right now we just always use mailbox (triple buffering) mode, which is vsynced.

#11

Thank you for the details. I’ll keep checking up on vsync status.

(Kel) #12

Your game logic in a turn-based game should not be contingent on framerate, let alone frame limiting strategies and vsync. If it is, your code is wrong. Either use the fixed update mechanism or build your own dispatcher and dispatch it at the rate needed per-frame.

1 Like
#13

That exactly my problem. I don’t know how to use amethyst so that game logic is not run in the same “loop” as the display logic. I tried to write something based on that: https://book.amethyst.rs/stable/controlling_system_execution/custom_game_data.html but even this example from book is not up to date with the latest amethyst release, and I was unable to update parts not working (stuck at implementing DataDispose trait for CustomGameData).

But I totally accept the fact that I’m unable to write what I won’t because lack of my experience and also understanding how it should work, and not that there’s something wrong with amethyst though :wink:

But still I’d like to ask if you have or know about any fully working example of code that runs two or few independent dispatchers? With at least one of them not being “contingent on framerate, let alone frame limiting strategies and vsync”?

(Marco Alka) #14

I think, Amethyst is great for rendering and doing frame-to-frame tasks efficiently, however a turn-based game does not build on frame-to-frame logic or mathematical calculations. It is based on user input and timers, mostly. That’s why I would not build the game logic into a system, which runs every frame, but instead use events prominently. A user clicks “attack other field”, and that’s an event which is broadcasted by the UI and can be handled independently. It might trigger changes to components, so that your unit moves and plays an animation, however I’d keep the whole game logic fairly event-based.

3 Likes
(Erlend Sogge Heggen) #15

The most prominent turn-based Rust game I’m aware of is Zemeroth by @ozkriff

It’s made with ggez, not Amethyst.

1 Like
#16

I think I tried to bite too much at once. I never wrote complex game, and I probably shouldn’t start with complex ECS like Amethyst. Guess I stay with a simple “how I see it” design without the introduced complexity of components and systems. Drawing stuff with SDL2 and simply running inner loop inside main loop for when simulation run without the need of screen updating or waiting for a user input is super easy when you use basic stuff. If I won’t lose interest, and have a big chunk written I’ll try to convert it to Amethyst.

Thank you all for replies, and patience. :slight_smile:

1 Like
(doomy) #17

While it is of course up to you to decide what is best for development, I’d just like to interject that - while specs has some up-front difficulty - once you get the basics it’s not bad at all. I’ve been playing around with Godot and Xenko in my free time, and I actually prefer the systems based approach specs has, as it more easily organizes code for composition.

I agree with @maruru in that a turn-based game would work well using events, etc. without the need for a custom dispatcher. I hope you continue interest in the future and know that the #help channel on discord is always open and pretty active :slight_smile:.

2 Likes
#18

The only problem is that I don’t know how to force dispatcher to run the “AI” turns not being limited by framerate. Between player turns there can be thousands or millions (in edge cases) of turns for the “AI”, background simulation etc.

Using SDL2 and Specs I run in a loop dispatcher.dispatch(&mut world); world.maintain(); to run everything with updating screen only when needed I achieve what I want with single dispatcher.

With Amethyst, I’m unable to do that, because Amethyst bonds rendering system to monitor refresh rate. Or at least I don’t know how to write it.

(Andrey Lesnikov) #19

Hey there. I’m thinking of writing a prototype of an Amethyst turn-based game somewhere in the future (once Zemeroth is either finished or abandoned). I’ll most likely go with something like my current architecture:

…where the “core” is completely separated from the visualizer and communicates with it using commands for input and events for its output.

On the high level it works like this (I guess this should be added to the readme):

All this commands/events/etc stuff feels like a lot of infrastructure, but I find it to be really expressive and flexible (works well with networking, replays, etc).

Most of this system is abstracted away from the visualization, obviously except for the “Visualizer”.
For Zemeroth I’ve implemented the scene/animation manager zscene on top of ggez, but something like shouldn’t be that hard to implement as Amethyst’s system.

Hope this helps! :slight_smile:

6 Likes
(Yann Asset) #20

Hi there, reading @ozkriff post, I remember I made some time ago a little schematic of the architecture of my game, and I think posting it here may give an alternative (but relatively close) way of handeling a maybe-networked turn-based game.

The main idea is the following, the game completely separate game-state from game-representation-state.

The first containing only the gameplay-related stuff, the other only the displayed representation of it from one particular player point of view (but we can imagine it being a spectator).

The Representation can ask the gameplay information on the current state, or modification of it, through a Trait unifying network and local player->gamestate interaction.

The goal is to avoid cheating (the networked player client will only know what the player is allowed to know), and allow the game state to advance in a quantic way:

(gamestate, generation).next([active-player-commands]) -> (gamestate, generation+1)

(the next(…) method dispatching the required systems to the gameworld to make it the next generation gameworld)

As a consequence of such a separation, I have two world, a representation world, local, and a gameworld, maybe distant. It allow me to have a good separation of concern, and to easyly cache current-turn-data, as the trait implementor can decide if it have enough data to handle a request localy (like real-time move cost display under the pointer) or if it must ask the gamestate directly for it (like radar echo display on the map).

I hope it wasn’t too confuse.

have a nice day :slight_smile:

4 Likes