gdquest-demos / godot-open-rpg

Learn to create turn-based combat with this Open Source RPG demo ⚔
https://youtube.com/c/gdquest/
MIT License
2k stars 245 forks source link

OpenRPG rewrite: Godot 4.X #207

Closed food-please closed 1 year ago

food-please commented 1 year ago

OpenRPG Rewrite:

I'm submitting a feature request.

I'm proposing a re-write of the OpenRPG codebase so that it: a) Is updated to Godot 4.X. b) Is made to conform to GDQuest's up-to-date code/structure/art guidelines. c) Is sufficiently differentiated from the JRPG combat tutorial included in the 2D Secrets course. d) Is simplified to better serve as a starting point for those diving into the project.

With regards to point d, this would probably include documentation of the project structure and major design decisions. In particular I'm interested in project architecture, as I find most tutorials are not written in a project scope but rather as small stand-alone demos.

On the discord channel I had pitched the idea of a simple "first person", turn-based battle similar to Dragon Quest (1 or 2) or Pokemon. Considering the discussion that followed I do not necessarily think that sweeping changes need to be implemented. However, I do think simplicity is key and the demo priority should be showing how systems interact (in this case, transition to and from the combat state while modifying player data, perhaps running an event post-combat, etc.).

I had originally included a few concept images as brainstorm fodder and I've included them here for posterity. Sample screen4_0

Concept images by Kenney (kenney.nl) and Hyptosis (https://opengameart.org/content/simple-battles), respectively.

The following points of discussion were recommended by xananax over discord: https://discord.com/channels/245092069415190528/644112672878493707/1082396543639891968

Project Scope

Ideally, the demo would be a polished & trimmed down version of the previous OpenRPG project. With this in mind I think that the feature list should be built from features that are already included in the Godot 3 version. In no particular order:

There may be a handful of extra features to consider adding at a later date. In particular, I’m interested in random encounters as well as moving between different field maps. Full feature list TBD.

First Steps:

The first step beginning the project in earnest would be to draw up the feature list (probably in Github projects. Kanban style?) and break down into milestones and immediate tasks.

Before doing this, however, I would like to put together a small demo in the next day or two to verify that the project is headed in the correct direction. I would propose a rough draft of the player moving around in the field/exploration state. This would include the following:

Anyways, lots of words for a simple demo. Let me know what you think and I’ll slap something together. I can also draw up a rough architectural diagram if that would help, though that might be more useful for review after the fact.

Xananax commented 1 year ago

Thank you for this!

I think your proposal of an initial project is good. Let's start on that basis, and let's be willing to iterate on it until we feel it's a satisfying architecture, after which we can move on to the next system (combat, inventory, ...).

I don't think an architectural diagram is necessary, unless you want it. For me, directly intervening on code without abstractions is generally preferrable (Of course, this implies sometimes tearing the code down and rebuilding to a large degree. I'm personally fine with that).

However, an architectural diagram can serve as a thinking tool for some people. If that is how you proceed and you'd rather have feedback on that first, that is completely fine by me.

At the moment, I do not have specific opinions about all the points you've made; they all seem good to me! Some reserves about "not componentizing", but it really depends on the implementation.

So then, I suggest the next step is to open a WIP PR and work there. If you want to discuss things before implementation, feel free to follow up in this issue, open new issues, or comment in the PR.

food-please commented 1 year ago

I apologize for this long post. If there is a better way to run design by the folks at GDQuest, please let me know. For the first prototype I wanted to verify design with the team to make sure that I'm not completely out to lunch.

I agree that iterating over code/structure will be more productive than trying to "structure" everything from the beginning. I DO find laying things out helps at first and I may be able to head off some issues (or raise red flags when you question what in the world I was thinking) by blocking them out within Godot and then iterating on top of that.

This is the rough structure I've laid out for the "first steps" prototype. Please note that I've added the player controller in two scenes to get feedback on which method would be preferable. I'm open to changing anything and I like the idea of componentizing as much as possible.

GodotArchitecture

This may be more complex than it needs to be at first, so I'll try to explain what I was thinking with each scene:

Main serves as the point of entry to the program. Main would have a few utility objects (Session - explained below, audio elements that should only have one track running at a time such as music and ambience, a screen overlay - which I forgot to picture here - for fading the screen, etc). Each game state would have an initialize method where it could be fed references to these systems so, for example, the combat state can quickly transition to combat music.

I'm imagining that - down the road - Main will be a finite state machine (probably pushdown so that we can just pop the combat state, re-activate everything in the field/explore state, run a post-combat event and let the player keep going) so I've set it up as such. If that's more complicated than necessary we could rejig it to just route events to the active game state.

Within Main, Session probably needs explanation. My thinking is that we'll want a place to cache a) player data and b) world data. Regarding player data, we would want a place to store party info, inventory (if it were ever implemented), the player scene during combat/area map transitions, etc.

The Field gamestate is where the player runs around a grid/map purchasing gear, talking to NPCs, etc. The terrain layer is background terrain and what is currently labelled "TileMap" would have grid-based obstacles (trees, houses, maybe cliffs, etc.). The obstacle grid would by y-sorted as well as all of its children. It seemed more clear to add the gamepieces to a node dedicated to parenting them, but they could be children of the obstacle tile map just as easily.

I had originally been thinking that there would be a Player node dedicated to tracking the player. I had been thinking of calling a "set_focus" method that could follow any given gamepiece and the player node would handle setting up a controller, disabling ai, following with the camera, etc. My reason for doing so was that I did not think you would want to control two different characters at a time, as that seems confusing. Please see below for an alternative.

Edit: Down the road the map itself would be dynamically loaded from one of several. The player would be injected to the map when it is loaded.

A Gamepiece scene is a base scene for any given object that will occupy a space on the gameboard. I had been planning a limit of one gamepiece per gameboard cell. I had been thinking that characters, interactive elements such as treasure chests and signs, events, and such would all be gamepieces. The Path2D and PathFollow2D can be used to easily path along the grid. Hopefully the graphics nodes are pretty self explanatory, but I was thinking that Sprite and its children would handle any height-based movement (such as jumping). Finally, I've roughed in that controller components could be attached to any gamepiece to control them. Please note that the Controller node + children would not exist in the gamepiece base scene but would probably be added dynamically.

Edit: Forgot to mention that the gamepiece GFX/Animation would be a swappable component. It's laid out here to show the full structure.

Anyways, I would love to hear any feedback you may have and, hopefully, I'll have this up and running by the weekend.

Thanks for taking the time to read.

Edit: I couldn't seem to find this in the GDScript style guide. Is there a preference for connections being made in editor or in _ready via code?

Xananax commented 1 year ago

No problem about long posts! Long discussions require details.

Before answering, I'll first posit some assumptions (@NathanLovato correct me if you disagree). These aren't direct answers to your suggestions; they're more general guidelines that I'll use to discuss the project.

Goals The goal of this repository is to offer an as simple as possible (but not simpler) example of RPG mechanics. To this end: - We want the code to be production-like, but readable above all. In the event that a trade off has to be made between convenience, performance, or readability, readability should come on top. - We want to limit flexibility a lot. Flexible interfaces require documentation, and changing a flexible interface requires to follow dependencies and branching flags deep into an application. Conversely, simple code can easily be scavenged. - Idiomatic architectures (e.g. widely used by the community) should be preferred, even if a different one would seem more appropriate Of course, we evaluate those guidelines on a case by case basis. Feel free to challenge those guidelines, especially if (but not only if) your own goals for making this differ. With this out of the way, let's jump in!

In general, I very much like your proposal. I have a few comments/ things to discuss.

Main serves as the point of entry to the program [...]

That's completely fine. Keep in mind though that you want to maximize the possibility of running single independent scenes with F6, for easier testing or demonstration. There are three major architectures that lead to that:

  1. Making sure the scenes never, or rarely, depend on Main, and run as quasi-complete even if it isn't there
  2. Making Main an autoload
  3. Explicitly and verbosily require dependencies in each scene (scene loads Main, or composes it from smaller nodes)

My personal preference is towards 3, but the community, or docs, seem to favour 2, so that is what I would recommend.

I'm imagining that - down the road - Main will be a finite state machine

I'm not sure of what you mean by state machine here. If you mean a set of distinct and easily identifiable states that cannot overlap, yes, completely.
But if you mean a specific generic "State Machine" implementation, then I'd probably warn against adding indirection and complexity to something that can remain very simple in my mind. But we can cross that bridge when we get to it.

Session [would] store party info, inventory, the player scene during combat/area map transitions, etc.

Sure, that seems good. Maybe consider using a resource instead.

The Field gamestate [...]

All good

I had originally been thinking that there would be a Player node dedicated to tracking the player. [...] Please see below for an alternative.

This is a valid idea, and the composition based setup is also valid. I suggest to start with your initial proposition, as it is simpler, and switch to composition if we notice the simple implementation is blocking.

Sorry for not answering before, I've had a busy day.

Regarding signals: we connect them in code. In general, we want everything to be as explicit as possible.

Feel free to comment back on anything you disagree with or find unclear.

Good work!

NathanLovato commented 1 year ago

It all sounds good. Regarding the state machine part, it's good too, just seeing the screenshots, I would recommend avoiding one node per state in this project for things like the Main node/script.

Having taught people to make state machines like that I know that it looks simpler, but for simple code like what Main will likely become, you tend to end up with more boilerplate than code actually doing something useful, and you fragment code, which are big tradeoffs for just seeing the node names in the scene dock.

I don't know if you intended to make state machines with one node and one script per state, but just in case I prefer to mention I think it'd add unnecessary complexity to the Main scene at least.

@Xananax while people tend to use autoload, I prefer to stick to Main being a plain old scene and nothing depending on it. It's not complicated in my experience and it makes the project easier to explore than using an autoload as this scene will automatically open when importing the project.

food-please commented 1 year ago

Thanks for the feedback, this is great. Exactly what I'm looking for.

I've been plodding along slowly, trying to unlearn some "solo dev" habits and reworking initial attempts to require less dependencies. I'm impressed at how difficult it is to write simple code, I hope this will not be too far off the mark.

It's hard to setup a small MVP as there are so many moving parts. There are many elements from the TRPG course here and I've changed the architecture some (as expected, including removing the state machines and other boilerplate code). The biggest notable change (and this is something that will require feedback) is that I have tried to pull apart the program data into several resources. For example, in the TRPG course the gameboard has methods dealing with many different pieces of data (paths, terrain, characters, etc.). In the FTL-like in 2D secrets, Rooms seem to place the occupants and handle interactions there. In the old OpenRPG project each pawn would request movement from the gameboard which would then call methods on the pawns. Etc.

I'm not sure that I have reduced cross dependencies, but I've tried to isolate them more. I have a pathfinder class that deals with terrain and which cells are walkable/empty/occupied. A gamepiece "directory" performs all lookups you could ever want on the pawns/gamepieces (by cell, uid for use in events or finding the player, etc.). The grid class handles conversions between units and placing things on the grid. These are RefCounted objects created on the fly (the exception is the grid which is a resource) so that there is only a single copy of the databases. They're still passed around quite a bit but hopefully all of the connections are explicit and prevent coupling different systems. For example, the cursor can do it's thing completely independent of the gameboard.

pathfinder_progress

Please note that Kenney's stuff is placeholder (it's so good!).

I have to work in character movement, at which point I will then clean up what I have, do a write up of the current architecture and then put through the initial PR. I'm hoping to have something to show off at the end of the week, but life is busy so we'll see.

Thanks again.

food-please commented 1 year ago

Just a quick question as I wrap up the initial PR in the next few days, what would you like included for assets? Do you want something from a previous version of the demo (might be tricky, I was hoping to have an animated character to demonstrate changing directions)? Does all GDQuest art need to be unique, or are appropriate CC0 assets good enough for the first iteration?

Thanks.

Xananax commented 1 year ago

Feel free to work in any way that you feel is sensible and allows you to move without being blocked.

CC0 assets are fine! We can figure out what to change later.

food-please commented 1 year ago

Sorry for the confusion with that pull request. Do you want the full commit history including 3.0 versions of the project? Or should the 4.0 rewrite restart the commit history? I can't imagine wanting to bounce between the two.

Once I've got that setup properly I'll put a pull request through for review and we can maybe chat about the demo.

Thanks.

Xananax commented 1 year ago

It's fine to have a clean history for this work. We'll either keep the previous one in its own branch, or move your PR to its own repository. It's something to be decided later, for the moment it's fine to keep working in your repo and ping us through PRs/Issues.

We're travelling for the next two days, so there'll be no time for review, but I'll give you feedback around the beginning of next week.

Thank you!

food-please commented 1 year ago

Ok, sounds good. I'll keep communicating through this issue for the moment.

I'm not sure if it's because I don't know what I'm doing, but Github isn't interested in setting up a PR for either branch on the OpenJRPG repository since I've restarted from commit 0 - as they are "are entirely different commit histories".

I had a write-up done on the initial PR that may explain the general architecture of the project. It appeared to have pinged several old contributors so it seemed best to restart the commit history since the two are so incompatible.

For now, the demo can be grabbed from the following repository. I'll hold off until I've heard back from someone on the team to verify that I'm headed in the right direction with the project. Movement works great except the characters' animation components don't seem to be set correctly on mobile exports.

Thanks and safe travels!

food-please commented 1 year ago

Ok, I fixed the disappearing-characters-on-mobile-export issue. I'm not entirely sure what was going on as the animation component is set as an exported packed scene and the setter wasn't even being called. Works nicely on Android now, even on my slow Kindle Fire.

@Xananax, I was thinking about how involved this first milestone is and how it might be more to review than is possible given everyone's time constraints. I wondered if you would like me to break it apart component-by-component and slowly tack it together so that we could look at things piece-by-piece? If you would like I can strip it back to a simpler starting point.

Xananax commented 1 year ago

No, thank you, but it's okay. I prefer to go over a coherent whole; the only thing is that I might have to ask you to wait, depending on when you're available.

Is it ready for review now? Can I review it in the next few days?

food-please commented 1 year ago

No, thank you, but it's okay. I prefer to go over a coherent whole; the only thing is that I might have to ask you to wait, depending on when you're available.

I'm happy to wait; I'm not in a rush at all and will work at your schedule. I just wanted to make sure that I couldn't make your life (well, the review anyways) easier.

I think it's a good idea to do the review now, as it will give me an idea on the state of the code and if I need to make any big architectural/code style shifts before getting too far into the project. Please see the (closed) PR for a brief description of the architecture. Until we have a 4.0 branch/repository it probably makes sense to just pull the code from the following repository.

I've tried to stick as close as possible to more recent GDQuest tutorials. I've also attempted to make the architecture more clear (who does what, things only have one main purpose, names make sense). Also, exports should work though I haven't tested IOS or web.

Please let me know if you have any questions or want more clarification on something. Thanks again for your time.

food-please commented 1 year ago

For posterity's sake, I setup a review-ready PR to be merged to the feature/code-rewrite branch (they don't share code, obviously). It also includes a few minor updates (consolidating debug tools, facing target characters after following a path, some decoupling/robustifizing the cursor).