Re: Reworking the control flow for my tactical role-playing game
On Saturday, 23 March 2024 at 22:37:06 UTC, Liam McGillivray wrote: ...a tick system would go hand-in-hand with making animations happen in a separate thread, but it sounds like you're talking about the same thread. Are you suggesting a fixed framerate? I have done both ways. If you're new to programming, I think the single thread would be better since your game logic is fairly simple. Simply update your world, then draw everything then sleep. Steven Schveighoffer does a lot of entry-level games so hopefully he will respond if he disagrees. You can have a bool flag you set when an action happens that requires the win condition to be checked and then if that flag is set, check the win condition at the end of the loop, assuming only one player can achieve a win condition at a time. This is during the Unit's constructor, where it gives the `Tile` object a reference to itself. What exactly is wrong with this? Can memory addresses change when a constructor completes? I assumed that objects come into existence at the beginning of the constructor function. I think the idea is the object is not ready to use until the constructor is complete and if you leak a reference, someone else who doesn't know could use it and they might have expectations about it's state that are not upheld midway through the constructor. I used to do this a lot and never had a problem with it. In an application where you're the only developer and it's single threaded, it won't be a problem. It's just bad practice because on a team you want to be able to trust that a reference to an object is of a completely constructed object and you don't want to have to check it's constructor every time. Anyway, I can change this by calling `Unit.setLocation` after creating a new `Unit` object. That's a good plan I suppose the current system for setting up objects from a `JSONValue` is messy because much of the data applies to the base `Tile` & `Unit` objects, but then some of it needs to be in `VisibleTile` & `VisibleUnit` because it has sprite information. That's fine. I would move the code out because I only care about it when I'm working on loading from JSON, so it wouldn't be helpful to see it all the time when I'm working on game logic. The value comes in having less to see and think about at a time but it will work just the same (assuming you don't break it when you move it :). If you like it there, then leave it. Do you really think it would be better if I removed `Unit.currentTile`, and just used `Unit.xlocation` & `Unit.ylocation` as parameters for `Map.getTile` instead? Go with your gut. If you start having problems with them getting out of sync, then I would separate them. Most of my suggestions boil down to: if you have problems and the fix breaks something else and that fix breaks something else or it's just hard to understand, then my suggestion will probably help. If it ain't broke, don't fix it. I have considered doing a rewrite of the rendering system, in which all the rendering, and possibly input during game are handled by a new `Renderer` object, which may have it's own array with all the units in it. This sounds a little like my idea of the `Renderer` object, in which the state of what's on screen would be updated by calling it's methods, but having a log of the UI wasn't what I had in mind. I like the renderer idea. I would keep it in the same thread for now unless you really want to make a separate thread. Before going with the current approach, I intended to have a module called `loadData` which would read JSON files and make objects out of them I think you made the right call. A generic json file loader is overkill at this point. If you make a second game, make a generic and reusable json loader. I try to never make things generic unless I have multiple actually use cases that I can design against or if it's pretty obvious. //Always set destroy to false when calling from the Unit destructor, to avoid an infinite > loop. I was just doing some work on the AI system, and I had a segfault every time the enemy was in reach to attack a unit. It turns out this was because the destructor was called. I replaced destroy(unit) with map.deleteUnit(unit), and it solved the problem. Nevermind. It turns out this was because the call to the Unit destructor was missing in Map.deleteUnit. The segfault happens whenever a unit is destroyed. This is exactly the kind of problem circular references cause. You're not quite sure what makes the bug or when to consider it done, especially when there's multiple entry points into the cycle. In this case, how should I handle safe destruction of units, given that there are multiple arrays that contain references to it? Here are my options, in inverse preference order (neglecting the time it would take): 1. Keep it like you have it 2. Switch to the tick system. At the end of your updating the world phase, clean up and for each
Re: Reworking the control flow for my tactical role-playing game
On Saturday, 23 March 2024 at 04:32:29 UTC, harakim wrote: * You should probably not do this, but it might give you some ideas for later. What I would do is make a separate thread for managing the UI state and push events to that thread through the mailbox. I have done this once (on my third version of a program) and it was by far the cleanest and something I was proud of. The benefit is you can publish those same events to a log and if something in your UI goes wrong, you can look at the log. I was thinking about how I can have one thread doing the rendering and another doing everything else, given that the "everything else" thread would be idle most of the time. I thought about giving the "everything else" thread a queue; an array of functions that it's tasked with going over. Every UI interaction would add one to the queue. It just occurred to me that this must be what you were suggesting here.
Re: Reworking the control flow for my tactical role-playing game
On Saturday, 23 March 2024 at 23:59:18 UTC, Liam McGillivray wrote: I replaced `destroy(unit)` with `map.deleteUnit(unit)`, and it solved the problem. Nevermind. It turns out this was because the call to the Unit destructor was missing in `Map.deleteUnit`. The segfault happens whenever a unit is destroyed. In this case, how should I handle safe destruction of units, given that there are multiple arrays that contain references to it? - Just call the unit destructor directly. The unit destructor will call functions in `Map` & `Faction` to delist the unit from it's arrays. - Call a function in `Map` which will delist the unit from `allUnits`, call a function in it's faction to delist it, and then call the unit's destructor. - Just call the units destructor, which will be rather plain. `Map` & `Faction` will have functions that frequently go over it's list of units, and delete any null references found.
Re: Reworking the control flow for my tactical role-playing game
On Saturday, 23 March 2024 at 04:32:29 UTC, harakim wrote: This comment points to a symptom of the circular dependency: //Always set `destroy` to false when calling from the Unit destructor, to avoid an infinite loop. I was just doing some work on the AI system, and I had a segfault every time the enemy was in reach to attack a unit. It turns out this was because the destructor was called. I replaced `destroy(unit)` with `map.deleteUnit(unit)`, and it solved the problem.
Re: Reworking the control flow for my tactical role-playing game
On Saturday, 23 March 2024 at 04:32:29 UTC, harakim wrote: You should engineer the system in a way that makes sense to you. The currency of finishing programs is motivation and that comes from success and believing you will succeed. If you're implementing XYZ pattern from someone else, if you don't get it then you will get unmotivated. I have seen some pretty horrible programs make it to all the way done and have almost never seen someone's first run at a problem be both pretty and get finished. Don't beat yourself up about keeping it clean. If you feel like it or it's getting out of control where you can't understand it, then you should go look for a solution. The motivation is in moving forward, though. This is probably the piece of advice I wish I had in the beginning! You can always refactor later when it's done. Alright. I suppose this is largely what I'm doing. Thank you very much for looking through my code and making the effort to understand it. So far you are the first person to comment on my code, beyond the little excerpts I've pasted directly onto this forum. Move to the tick system. You make a loop and update each object based on the time that has passed. If your tick is 1/60th of a second then you make a loop. In the loop, you update the map state knowing 1/60th of a second has passed. Then you update all the items and move them or deplete them or whatever however much would happen in 1/60th of a second. Then you update all of the units. Then you can update the factions or check win conditions. At the end of the loop, you draw everything. When you said "tick system", I thought you meant that it would be asynchronous to the framerate, but then you say "At the end of the loop, you draw everything". Right now the function that moves units across the screen moves them each frame a distance proportional to the duration of the previous frame. I was thinking that a tick system would go hand-in-hand with making animations happen in a separate thread, but it sounds like you're talking about the same thread. Are you suggesting a fixed framerate? Because this is a turn-based game, I don't need to update the factions and win conditions every frame, but at most every time a unit makes a move or when a new turn starts. Your verifyEverything method is awesome. I call that strategy fail-fast. It means you fail right away when you have a chance to identify what went wrong. That's nice. I thought it might be an amateurish solution to something that better programmers would do differently, similar to how I use `writeln` to print variables for debugging. It looks like I don't yet have any calls to this function, so perhaps I should add one under the debug configuration. Construct the unit and then call map.setOccupant(unit) after the unit is constructed. I would not do anything complicated in a constructor. It's also generally frowned upon to pass a reference to an object to anything before the constructor completes. Most of the changes I mention are things to think about, but this specifically is something you ought to change. I didn't immediately understand what you were saying, but then I looked at what line `unit.d:44` was at the time you wrote this. ``` map.getTile(xlocation, ylocation).setOccupant(this); ``` This is during the Unit's constructor, where it gives the `Tile` object a reference to itself. What exactly is wrong with this? Can memory addresses change when a constructor completes? I assumed that objects come into existence at the beginning of the constructor function. Anyway, I can change this by calling `Unit.setLocation` after creating a new `Unit` object. That's unless if there's a particular reason why you think I should make this a function of the `Map` object. I suppose the current system for setting up objects from a `JSONValue` is messy because much of the data applies to the base `Tile` & `Unit` objects, but then some of it needs to be in `VisibleTile` & `VisibleUnit` because it has sprite information. I would also remove the unit from the map and then delete the unit rather than removing the unit from within the map class. Sure. I'll change that. Even when I wrote the Unit destructor I was thinking that perhaps this was a bad way to implement this. I suppose I should just call the `Map.deleteUnit` whenever a unit should be deleted, right? I was also thinking of replacing `Map.deleteUnit` with a function that removes all null references from `this.allUnits` which can be called after destroying a unit, but unless if I need to hyper-optimize, that probably won't be any better than the function I have. Another reason I would switch that line is that it's best to avoid circular dependencies where you can. It will make it hard to reason about either in isolation. It relates to that line because your map has units in it and your unit takes Map in the constructor. That is a red flag that you are too c
Re: Reworking the control flow for my tactical role-playing game
I am not an expert but I would second the tick system. That is pretty solid advice. Just iterate through your events or, in your case, units and such objects and update them all. Then draw. Then go to the next tick. You should engineer the system in a way that makes sense to you. The currency of finishing programs is motivation and that comes from success and believing you will succeed. If you're implementing XYZ pattern from someone else, if you don't get it then you will get unmotivated. I have seen some pretty horrible programs make it to all the way done and have almost never seen someone's first run at a problem be both pretty and get finished. Don't beat yourself up about keeping it clean. If you feel like it or it's getting out of control where you can't understand it, then you should go look for a solution. The motivation is in moving forward, though. This is probably the piece of advice I wish I had in the beginning! You can always refactor later when it's done. Solicited advice: * Move to the tick system. You make a loop and update each object based on the time that has passed. If your tick is 1/60th of a second then you make a loop. In the loop, you update the map state knowing 1/60th of a second has passed. Then you update all the items and move them or deplete them or whatever however much would happen in 1/60th of a second. Then you update all of the units. Then you can update the factions or check win conditions. At the end of the loop, you draw everything. (maybe raylib just draws whenever it wants, which is fine) In order to know what to update, you will have to save what action they are doing. ex. an arrow is flying toward a target. you will need to keep track of what it is doing so when you iterate, you can continue that train of thought. This will require a bit of a rewrite but I have worked on a game server that was used by thousands of people for years and it was based on this simple system. It scales really well and is pretty easy to understand. Unsolicited advice: * Your verifyEverything method is awesome. I call that strategy fail-fast. It means you fail right away when you have a chance to identify what went wrong. * Construct the unit and then call map.setOccupant(unit) after the unit is constructed. I would not do anything complicated in a constructor. It's also generally frowned upon to pass a reference to an object to anything before the constructor completes. Most of the changes I mention are things to think about, but this specifically is something you ought to change. I would also remove the unit from the map and then delete the unit rather than removing the unit from within the map class. unit.d:44 map.getTile(xlocation, ylocation).setOccupant(this); * Another reason I would switch that line is that it's best to avoid circular dependencies where you can. It will make it hard to reason about either in isolation. It relates to that line because your map has units in it and your unit takes Map in the constructor. That is a red flag that you are too coupled. That concept is not a rule but just something to think about when you get stuck. This comment points to a symptom of the circular dependency: //Always set `destroy` to false when calling from the Unit destructor, to avoid an infinite loop. * In your game loop, I would keep track of the units separately from the map, if you can. Go through the map and do updates and go through the units and update each one. If the logic is too tied together, don't worry about it for now. * I would break the json loading into separate classes (eg FactionLoader, Unit loader) instead of being included in the map and unit class. I like to have code to intialize my programs separate so I don't have to look at it or think about it or worry about breaking it when working on my main code. * You said //Change this later so that the faction with the first turn is determined by the map file. Comments like that are perfect. Jot down all your ideas while you're working on the main functionality. Once you have something working, tweaking it will be so much fun. Take side quests when you want to stay motivated, but I would stray away from the bigger ones until you have the basic functionality working. It's often the fastest way to get the side quest done anyway since you can test it. * You should probably not do this, but it might give you some ideas for later. What I would do is make a separate thread for managing the UI state and push events to that thread through the mailbox. I have done this once (on my third version of a program) and it was by far the cleanest and something I was proud of. The benefit is you can publish those same events to a log and if something in your UI goes wrong, you can look at the log. Better than that is the ability to replay your log. Instead of sending the events from a game engine, you have a module that just reads from the file and s
Re: Reworking the control flow for my tactical role-playing game
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 unde
Re: Reworking the control flow for my tactical role-playing game
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. ## 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! ### The Library: All sounds good ### The Raylib Front-end: After looking at many libraries and taking a shot at [ae](https://github.com/CyberShadow/ae) & [godot-D](https://github.com/godot-d/godot-d) but not really figuring it out, I was recommended [raylib-d](https://github.com/schveiguy/raylib-d), a binding for [raylib](https://www.raylib.com/) by @Steven Schveighoffer. Raylib is a rather simple graphical library written in C. I ended up sticking with it because the website has so many easy-to-follow examples that make it easy as my first graphical library. They're written in, but I adapted them to D rather easily. Of course, being written in C has limitations as it isn't object-oriented. This is front-end is in the [`oe-raylib/`](https://github.com/LiamM32/Open_Emblem/tree/master/oe-raylib) directory. For this front-end, I've made the classes `VisibleTile` and `VisibleUnit`, which inherit `Tile` & `Unit`, but add sprite data and other graphics-related functionality. I then have the `Mission` class which inherits the `MapTemp` class. This class dominates the program flow in it's current state. It handles loading data from JSON files, switching between different game phases and does most of the function calls related to rendering and input. The way I have it currently, there's a `startPreparation` function and `playerTurn` function, each of which run a once-per-frame loop that renders all the necessary objects and takes user input. They each have a rather messy set of if-statements for the UI system. Any UI elements that may pop-up are declared before the loop begins, with if-statements to determine whether they should be visible this frame. For UI elements, I currently have `version` flags for either `customgui` (which I started writing before discovering raygui) and `customgui`, which you can select between using `dub --config=`. Having both makes the code messier, but I haven't yet decided on which I prefer. They are both currently achieve equivalent functionality. 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. To get a glimpse of a flaw with the current approach (which may be simpler to fix with an overhaul), try asking one of the units to move during your turn, but then try moving the other unit while the first one hasn't reached their destination. The first unit will stop. So when doing video game development with a main loop that needs to refresh the screen every frame, you need to componentize long-running operations into frame-size bits. 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. ## Should I rework things? So now I am thinking of reworking the rendering system, but also changing some of my approach to how the Open Emblem library works. I've been thinking of adopting an event-driven approach, using signals and slots, for both the library and the front-end (and between the two). I'm curious if more experienced programmers think this is the right 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 a
Reworking the control flow for my tactical role-playing game
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'm now at a point where I have trouble figuring out the next step to making the game playable. The complexity may have just reached a point where I find it harder to keep track of everything that I have written. There is probably a fair amount of unused code that I abandoned after deciding on a different solution, but forgot to delete. There are probably also some amateur decisions I've made in structuring the program, given that I largely figured it out myself. For some time now I've thought that I may later want to overhaul how the whole rendering and UI system work. Perhaps now is a good time since my productivity under the current system is slowing down. ## 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. When starting, I decided to structure it this way so that I can experiment with different graphics and UI libraries. This may have been a good move, even if it complicates some aspects, as the first library I tried wasn't the one I've stuck with. I also thought that this library may also be usable as a platform for others to make their own tactical RPG games, though that's unlikely with the current direction of the project. ### The Library: The most important modules here are `map`, `tile`, & `unit`, which contain the classes `Map`, `Tile`, & `Unit`. There is nothing here specific to any particular graphics or game library. Well, `Map` is now longer actually a class, as it's been replaced by the `Map` interface and `MapTemp` template which implements it, but for simplicity, I'll refer to `Map` as a class. This class is meant to serve as the master that controls the flow of a single game mission. Only one instance is meant to exist at a time. It holds a 2-dimensional array of `Tile` objects which represents the grid that the game is on (like a chessboard) and an array of all `Unit` objects. `Unit` represents a character in the game that can be moved on the map (like a chess piece). It has some stats stored as variables, and some functions to do various things a player (or AI) may ask the unit to do during their turn. Each unit occupies a tile object. `Tile` is a square on the map, which has it's own *x* & *y* coordinate. The `Faction` class currently only serves to store a set of units belonging to a certain player or AI, but is planned to play a bigger role later. ### The Raylib Front-end: After looking at many libraries and taking a shot at [ae](https://github.com/CyberShadow/ae) & [godot-D](https://github.com/godot-d/godot-d) but not really figuring it out, I was recommended [raylib-d](https://github.com/schveiguy/raylib-d), a binding for [raylib](https://www.raylib.com/) by @Steven Schveighoffer. Raylib is a rather simple graphical library written in C. I ended up sticking with it because the website has so many easy-to-follow examples that make it easy as my first graphical library. They're written in, but I adapted them to D rather easily. Of course, being written in C has limitations as it isn't object-oriented. This is front-end is in the [`oe-raylib/`](https://github.com/LiamM32/Open_Emblem/tree/master/oe-raylib) directory. For this front-end, I've made the classes `VisibleTile` and `VisibleUnit`, which inherit `Tile` & `Unit`, but add sprite data and other graphics-related functionality. I then have the `Mission` class which inherits the `MapTemp` class. This class dominates the program flow in it's current state. It handles loading data from JSON files, switching between different game phases and does most of the function calls related to rendering and input. The way I have it currently, there's a `startPreparation` function and `playerTurn` function, each of which run a once-per-frame loop that renders all the necessary objects and takes user input. They each have a rather messy set of if-statements for the UI system. Any UI elements that may pop-up are declared before the loop begins, with if-statements to determine whether they should be visible this frame. For UI elements, I currently have `version` flags for either `customgui` (which I started writing before discovering raygui) and `customgui`, which you can select between using `dub --config=`. Having both makes the code messier, but I haven't yet decided on which I prefer. They are both currently achieve equivalent functionality.