On Thursday, 21 March 2024 at 16:48:39 UTC, Steven Schveighoffer
wrote:
On Sunday, 17 March 2024 at 00:14:55 UTC, Liam McGillivray
wrote:
As many of you know, I have been trying to write a tactical
role-playing game (a mix of turn-based stategy & RPG) in D.
This is the furthest I have ever gotten in making an
interactive program from the main function up. Right now, it
is not yet playable as a game, but you can interact with it
and get a rough idea of what I'm going for. Feel free to
download and run it to see what I have so far.
https://github.com/LiamM32/Open_Emblem
I got it to run on my mac, I had to do a few updates to the
dub.sdl file, but it did work, though I couldn't figure out how
to make attacks work.
Great. You may be the first person to download and run it. I
would appreciate if you committed and pushed the changes to the
`dub.sdl` file. So far I've only tested it on Linux.
Attacks don't fully work yet. When selecting "Attack" in the
menu, you should see the tiles in range marked in red, but
clicking one will as far as you can tell, do nothing but bring
you back to the previous menu. Right now it's programmed to lower
the enemy HP on attack, but there's no way for the player to see
that it worked. Nothing currently happens when HP goes to zero or
lower.
## The Current Structure:
The code for Open Emblem (name subject to change) is split
between a source library, which handles the internal game
logic, and a graphical front-end program which uses that
library, but makes it usable.
This is kind of cool, I like the idea!
Good to hear. It makes some things difficult when I don't allow
myself to put anything specific to any graphics or UI library in
the library, but it also may make it easier if I rework the
graphics and UI.
Everything here is single-threaded. Despite that, I still get
thousands of frames-per-second when disabling the cap on
framerate.
Note that disabling the cap on framerate just avoids the
sleep-per-frame that raylib does. I always recommend setting a
cap of something like 60 unless you are going to use vsync.
The "framerate-test" configuration only exists as a benchmark to
give me an idea of how far I am from the CPU's limit. It's not
for playing.
In the default configuration and the others, it currently uses
the `getRefreshRate` function to set the target framerate to the
monitor's refresh rate on Linux. It's supposed to do that too on
Windows, but I haven't tested it. On Mac it just sets it to 60
FPS.
So for instance, an animation that needs to move an object from
A to B, should be captured into a temporary item (class object,
struct, member of the sprite, etc), where you tell it every
time you are drawing a frame (or even better yet, each game
tick), and let it make the changes necessary for one tick of
time.
For instance, build an object that takes start position, end
position, time to animate, and maybe a path function (like
linear, ease-in/ease-out, etc), and then each frame calculates
where the position should be based on the current time vs. the
start time. Encapsulating all this into an object makes things
easy to deal with. Then you just need to call it every
frame/tick.
Interesting. This is a different approach from how I imagined it.
I never thought I would add a whole new object just to move
another object around. Instead I was thinking of having a
function in `VisibleUnit` that moves itself to the next location
on a path (represented as an array of either directions or
locations). Either this would be called for every unit every
frame (but it would do nothing if they don't have a path), or
there would be an array of moving units in which this function
would be called in the rendering loop.
Perhaps if I had just used the plain `Unit` class from the
library instead of making the derived `VisibleUnit` class, I
would consider this approach.
I'm not sure if you want to do event driven here. It's a
possibility. But I find a game-tick system, where each tick,
you update each object according to its wishes to be pretty
easy to develop with. I will add the caveat that I am also a
pretty novice game developer, and have never actually built a
complete game.
If I were to recommend a system here, I'd create a linked list
of items to "tick" every frame, with something like:
```d
interface GameObj {
// returns false if this object is done
bool tick();
}
```
Then basically, you go through your list every frame, and call
the tick function, which will make needed changes, and if it
returns false, then you remove from the list while iterating.
This means you can do N things at once, and you don't need
multiple threads to do it.
Well this sounds not so different from signals and slots. My
current understanding is that every time the signal is called,
that same thread goes through a list of functions connected to
that signal.
Back when I wrote this post, I had a worse understanding of
signals and slots, and thought that it might be a thing for
inter-thread messaging.
I think you are better off not using threads. Threads make
things very difficult to synchronize, and you have no
guarantees that your animations will run at any specific time.
I don't think you would like the results.
Yeah. I can see how it may make things unpredictable. My current
thought is to have only one general-purpose thread. If I
experiment with others, they will probably be created just for
one particular task before being ended. It might still end-up
being single-threaded until things start to slow down.
But even with single-threading, I will need to figure out a way
to make animations happen at the right times without the library
dictating when the animations happen.
If I redo the rendering and UI system, I will probably start
using [`Fluid`](https://git.samerion.com/Samerion/Fluid),
which is a Raylib-based UI system written in D.
As for the rendering loop, how should that work? I don't know
how it works in other 2D games. Should it be much like the
current approach, with a loop for every game phase containing
everything it might need to render during that phase, and
using logical statements for things that only *sometimes*
appear? As an alternative, I was thinking of making a
`Renderer` object that runs the rendering loop in it's own
thread, and it has variables to keep track of what's currently
visible. Another thread would access functions of this object
to change what must be rendered. I don't know what's the best
approach.
One thing to consider is doing game ticks separate from frames.
That is, your game tick timer is not locked to the frame rate.
This way if you drop frames, the game doesn't change its timing.
I thought about that, but if I do this, then I would probably
want to make it multi-threaded. The thread that updates things
once per tick would be separate from the one that updates things
once per frame. It would be easier than doing this
single-threaded.
An update on Fluid; I realized that it doesn't have a feature to
do scrolling. I also don't see anything for buttons with images.
I have an idea of how I can implement these things with Raylib.
I'm not sure if it would be better/easier to just do that with my
custom GUI system, or to make derivatives of Fluid classes, and
possibly make a contribution to Fluid. There's also the issue
that the theme system in Fluid is going to be overhauled.
Right now I'm going through a trilemma of doing a fully custom
GUI, using raygui, or Fluid. This becomes of quadrilemma if I add
a fourth option of ditching Raylib, switching to SFML, and using
one of their GUI libraries (TGUI or DIMGUI).
You might notice that in the current "raygui" configuration, the
menu at the bottom of the screen in the preparation phase isn't
using raygui. That's because I don't know how I would make the
Unit cards with raygui. The rectangle around it doesn't use the
raygui theme, because the `GuiDrawRectangle` function that raygui
uses is private. While in some ways it's more appealing than the
custom GUI system I have, it still isn't capable enough that I've
decided to ditch my custom GUI system. Trying to load in a style
from RayGuiStyler resulted in the buttons being invisible, so I
used a different method to make and load a style.
Perhaps it would be nice to have a GUI system that uses SVG or
HTML to make things customizable. I don't know if such a library
exists. I've thought about trying to make something like that
with SVG, but only after getting more experience with programming
in D. Of course, it may be hard to not make it very heavyweight.
No, I think you are in pretty good shape! I don't know much
about signals and slots, so I'll leave that unanswered.
-Steve
Thank you. Making the enemy AI is difficult, but overall things
aren't going too badly. This is still a very amateur project by
someone who has never made a GUI application from the main
function up, so there may be some amateur design decisions that
haven't been pointed out to me.