Overall, I’d say things are going well right now! I do wish I was getting things done faster, but that’s always going to be true. The important thing is that I’ve kept working pretty steadily, and I’ve been getting better at minimizing downtime, making sure to take care of myself emotionally and not try to overwork myself. Development for the prototype of Project Wild One has been immensely challenging, but it’s mainly been challenges I expected, and most of it I’ve enjoyed, even when it brought things grinding to halt for a day or two.
For my last public Dev Journal I talked a lot about the math I was tackling. It forced me to really think in depth about matters of anatomy (or more accurately, the fantasy anatomy of supernatural sex fiends like furries) and try to turn really vague ideas of how things should be into a precision-engineered set of rules to interpret, store, and express the consequences of just about any possible action across a huge spectrum. It was tough! And a lot of it is still placeholders I’ll be coming back to later. But I got all of that to a place where I think it’ll work for now!
And since then… pretty much exactly what I described above has been what I’ve been doing, over and over, for a dozen different aspects of the game. I’m trying to make it future-compatible within reason, and sometimes that means that in the process of mentally building a system, I have to basically reinvent how it’ll work half a dozen times before I come to a system that should “work” for all the foreseeable possibilities– or at least, can be altered and expanded to meet them without breaking.
So it’s been a lot of mental gymnastics, and probably the sort of thing real programmers have to tackle all the time. When I first put MVOL together it was very “throw together whatever works, however it makes sense at the time,” and the code behind the scenes turned into quite a mess. Thankfully the game wasn’t too complicated for the most part, but I did commit a lot of “sins” of poor programming– and that’s just the ones I know about by now.
I’ve been trying to be better about that with Project Wild One since it’s going to be much more complicated and gameplay-focused, and sure enough, that’s forced me to learn a lot about how to build things. I’ve already started circling back to things I built early on and saying “what was I thinking when I wrote this? I can throw out two-thirds of this, I’ve built much more elegant solutions elsewhere for that by now.” And really, I’ll probably keep having moments like that as development moves forward. I like to think that’s partially an indication that I’ve already become a better programmer than I was, but it also means there was a lot in terms of design needs and features to meet them that I hadn’t foreseen when I was laying the groundwork. Some of that is unavoidable I think, but it does tell me I’ve still got a lot to learn about planning things out from the very start to work well.
Well. It’s been a ton of strange mental challenges, and it’s been pretty fun, but it’s taking a long time. I knew it’d be tough to get the game to a presentable state in two months, and I knew this project would have pretty much the most front-loaded work out of all my plans, but it’s turned out to be more work and take longer than I’d hoped. It’s been more than two months now since I shipped Project Matchmaker v0.01 to supporters, and that means I’ve already gone past the deadline I’ve set for myself. On my last supporter Dev Journal on Patreon and SubscribeStar, I explained that things were going long, and that it’d take at least a few more weeks to get the game to a playable state. There was only one possible shortcut I could see to take, so I put the question to them.
Project Wild One is all about interaction between characters, and I think it’ll be an important part of getting a feel for that to have some basic AI behind the NPCs. But that’s going to be loads more work even once everything else is running– I have some pretty ambitious long-term goals there, but even the “starter AI” is going to have so many options available it’ll need to be pretty robust. So my question was, would you guys prefer to just get the basics to play around with ASAP, but just control both of the characters yourself; would you like a painfully simple, one-track AI I can slap together quickly; or would you like me to build a “proper” starter AI for v0.01, even if it delays things by another month? I put that to a vote for the supporters.
I tried to present it impartially, but I have to admit, I’ve been having so much fun building all this craziness that I was pretty happy when I saw a resounding call for the full AI from you guys! I’m hoping to give a good first impression of Project Wild One’s potential before I shelve it for other projects again, so I’m also really glad to have the chance to present a more complete picture of at least the very basics of the gameplay. I feel like a lot of the subtleties will only really come out when you’re playing “with” or “against” someone.
So I’m glad for the decision, but I do feel a little crappy delivering the news that it’s going to be at least another month before Project Wild One is ready to go out to supporters. A big part of the transition to this new production phase was supposed to be shipping new releases every “one to two months,” and so far I haven’t delivered on the one month idea– and this is the first time in years I haven’t released some kind of playable update in over two months.
The best excuse I can offer there is that this is a pretty unique situation, so hopefully it shouldn’t come up again anytime soon. Once the basics are in place for a game it should be much more practical to bundle additions/improvements into lumps of work I can accomplish in two months or less, but building this engine from the ground up just doesn’t have any good stopping point in it partway. Hell, I already feel like what you’re going to get to play is going to be such a tiny, shallow reflection of what this game could be, what I want it to be, it hurts putting in all these placeholders just to get things “working in the most basic way possible.”
But the fact remains that this is supposed to be a Side Project, and Project Matchmaker is my Main Project right now! I don’t really know how people will like Wild One, and I still don’t know if it will really “work” in the end as a game experience. I have a lot of plans, ideas, and hopes, but sometimes these things just don’t work, so it’s important to keep things stripped down to their core for early testing, moreso with gameplay than anything else.
Right now… Hmmm. So much of the work has been purely abstract engine stuff that I only recently came back to expand the GUI to more than the most basic “does this run at all” functionality. Most of the framework is in place for all the elements in the gameplay to “exist,” and I’ve been building all the stuff for how things “interact,” which I think I’m just about putting a cap on now. I still need to build out some stuff for “consequences” so you don’t just keep losing HP forever or building arousal forever and so on, and I still need to flesh out the system for tracking everything that’s happened, both to present it properly as text and to refer to for new events. Once all that’s done, I can get the GUI into a semi-final state for basic gameplay.
After that, it’ll finally be time to get cracking on the AI. Since the vote I’ve started laying the groundwork for that more extensively, so I’m hoping it won’t be too terrible, but since this may well be the most complicated piece of the whole prototype, it’s pretty dang hard to predict how much work it’ll turn into.
Once all of that is done, it’ll just be a question of how to turn “the bare, beating heart of a game to be” into something that’s actually fun to play. Ideally I’ll throw together a real basic progression system where you’ll get rewards like experience and be able to choose upgrades, with some kind of system for randomly encountering characters and choosing what to do between encounters. A lot of that will be pure placeholder for what’s coming later, though, so it’s going to be a tough call between making the prototype fun vs. throwing away too much time on stuff I won’t use in the long term.
Then I just need to build the secondary GUI for stuff like choosing what content you want to see and configuring your character (unless I awkwardly reuse the regular GUI for it I guess) and build some super basic save functionality, and I think the prototype will be ready to put in you guys’ hands. It’s still a ton of work left to do, but it’s exciting just to have this much behind me and this little left before I can hopefully have something to actually show people instead of just some really vague descriptions.
Honestly, I let this journal delay because I kept working until I was just fried and needed to get my mind off it altogether, then as soon as I was ready to work again I couldn’t resist diving back in to tackle the next problem. I do really hope that when I can finally share this all with you guys, it’ll be fun and worthwhile. But either way, it’s a project that’s been gnawing away at the back of my head for years now, and it just feels great to finally make something out of it.
Thank you, sincerely, for letting me make games for a living, and for letting me make things that I can be passionate about, and excited about, even when they’re kicking my butt. It’s thanks to your support that I can be here today, doing something as crazy as this. I often feel like I don’t deserve this much trust and support, but as long as you guys are backing me up, I’ll keep doing my best. Thank you.
Sounds good. Most people never really arrive at the “I can build it so much better now” phase. Next step will be the “I cannot believe I designed this so well while knowing so little” phase. As long as there is ongoing improvement, you will surely arrive there eventually.
Be wary of the trap of *always* thinking “I can build it so much better now” – it might not always be true. Sometimes new things are simply more shiny than the old ones.
On danger of not qualifying as a “real programmer”, I’ll say that most of us definitely do the “throw together whatever works, however it makes sense at the time” thing all the time (it’s called “Rapid Prototyping” or “Agile” nowadays) and most of the brains go towards doing that in such a way, that allows to later alter or replace it *without* breaking everything around it. There is a lot of theoretical treatises about that. You have probably read of ‘SOLID’ or ‘Loose Coupling’ or ‘Modules’ or current buzzword ‘Microservices’ (if not, they are all search terms).
As you already have included the caveat “[…] or at least, can be altered and expanded to meet them without breaking” I think you are on the right track here. As long as you include opportunity cost in your considerations what be expedient and what be not, things should mostly work out (aka: “I’ll take a shortcut here now and I do realise it will cost me more investment in total, but that is worth reaching the milestone sooner”; ‘Technical Debt’ – works just like normal debt in your favour if you can manage it… and not if not).
Backseat programming can be annoying, so I’ll (try to?) keep it down this time: When creating your AI, keep in mind first and foremost, how to provide it with a sufficiently clear view about the game state. This is (I dare say) more important than how exactly the AI itself shall work internally.
All game AIs I have analysed from outside and sometimes inspected from within, have had at least one of two issues:
– they were just very simple and more of the same work could have vastly improved them
– the game engine did not provide them with the necessary information to act competently and neither quantity nor quality of work on the AI could have improved it much
There probably is no game out there that really is sufficiently complicated as to prevent AI from being “somewhat competent” at it. But many games nevertheless have very bad AI, because they fail to present to the AI their game state in a way that facilitates meaningful decisions.
Example: The old Warcraft 3 AI cannot know what units its opponents field (or indeed which faction they pick). The game offers the tried and trusty rock-paper-scissors approach to unit balance – but the AI is simply blind to which units are and indeed can be on the field. Even though both humans and *the map itself* obviously know. The AMAI mod had thence created a much better AI – by entirely ignoring the built-in AI system and instead using only environmental triggers. Those had all the information necessary. The scripts to make sensible use of that information were then not too complicated (but of course there were many… the point being: *with* the necessary information, sheer tenacity will eventually produce an AI rule set complete enough to handle most situations; *without*, no amount of work will bear fruit).
There are very basic rules for an AI in that game to play effectively, such as “for every X of [list of unit types] you see, maintain Y of [list of countering unit types]” but no way to express them.
… Similar issues from the same game: There are mercenary camps on some maps, which can be used to compensate for the weaknesses of a player’s faction – to the AI they are invisible. No AI code to employ them can thence be written, altough in terms of logic it would be easy to formulate something that works better than nothing: Just provide a big map between available faction and mercenary unit types and when there are matching entries and the AI wants to build a unit anyway, pick a mercenary one. Not smart at all – yet utterly impossible.
In more general terms, here is a Database Engineer proverb I have to paraphrase because I seem to have forgotten its source:
“When the data is suitably structured, most problems are easy to solve.”
Including a few more abstract but (hopefully) more to the point examples:
– a game has a 2D map with units on it
=> the game must provide to the AI spatial information about where key units are *in relation to each other*; the AI needs not understand the geometry behind it, but it must be able to see semantic information like “there are X paths away from this position”, “the goal is in this direction”, “two goals are somehow ‘close’ together”
=> the game must provide to the AI temporal information about how expensive moves are: “how long would it take to move there”, “if those units all move there, how much additional time is lost waiting to have them arrive simultaneously”, “how close must a hostile unit be to be a tactical/strategic threat”
– a game has a unit with needs
=> the game must provide to the AI information about *how* to meet those needs: “what actions are available”, “how do (direct) costs and benefits of each action compare”
=> if actions might have indirect costs (or benefits), temporal (and again spatial) information must be provided: “what indicators exist in the game world for risk of consequences / chance of additional gain”, “how long must one be wary of late effects of an action” and of course “what additional actions are viable (available *and* possible *and* not ruinous) to mitigate/exploit those additional effects”
Basically, the game engine itself must tell the AI what the “obviously stupid” and “obviously sensible” actions are and then further improvements to the AI choosing “intermediate” actions will always be possible (and, at some point, no longer be desirable – it might outperform the human in competitive play, it might be too infallible to be immersive, it might be too predictable).
… And because I absolutely cannot resist, here is a search term for a very promising AI architecture when dealing with agent-oriented game worlds: ‘Goal-Oriented Action Planning (GOAP)’
I’ll better stop now, before I become too bothersome a guest.
Hello again! x3 Yeah, I’ve known the frustration of working with programmers that are constantly wanting to stop everything and do a “complete overhaul” of how things work behind the scenes. That’s part of why I really want to “get it right the first time” on this, so I can avoid that temptation as much as possible down the line, but at some point I’ll probably just have to draw a line and say “this is good enough for THIS game, I’ll save these lessons for another game or a sequel or something.”
That does sound crazy that some designers would give the AI access to less information than even the player would get. I usually get frustrated because it feels like AI opponents are built like they know much more than I do, rather than just acting like another player, so I’d been more concerned with building ways for the AI to “only know what it should.”
A lot of what you describe sounds pretty similar to things I’ve been figuring out getting ready to dive into this. That sounds like a pretty interesting term to take a look into, but I’ll admit, sometimes I try to avoid looking too much at what others have built early on when it comes to things like this, just because I want to tackle things my own way before the way others look at it “taints” how I see the problem and its potential solutions. It’s great to stand on the shoulders of giants, but it can be harder to find new solutions if you start from the same point as everyone else, you know?
At this point, I’m not even too worried about making the AI “good at the game.” It’s more about making them good at “acting like people” with their own flaws, priorities, and desires. Maybe some will be more conscious of long-term consequences, maybe some won’t care about anything but instant gratification. I want them to feel like they have some personality, you know? Past that, yeah, I want to make sure they’re not too “stupid” about their choices, and that’ll be tough, but I’m not worried about them getting “too good” at the game. Hell, if they can teach the player how to play better, that’s a bonus.
Anyway, thanks for the thoughts and feedback! You’ve given me a few fresh things to think on.
Ah, this might be an important distinction: Sometimes, what is “information” might be a bit blurry:
In RTS games, where the AI ignores Fog of War and sees the whole map, one can often “kite” it – change its behaviour in a deterministic way – by moving units a human opponent could not see. These units – due to the distance involved – are perfectly safe from retaliation, the plan cannot *fail*, its worst outcome is being ineffective.
Company of Heroes suffers from this to comical effect: Two opposing AIs will always move their main forces along opposing sides of the map, attack each other’s base without ever encountering one another, and the AI who destroys that unprotected base first wins.
So, this may look like giving the AI “too much” information, but first and foremost: It is giving the AI *not the information it needs* – and ends up hurting the AI, not helping it.
There is a term for that, of course: ‘Data’, all is at first but data. Only *actionable* data really is information – when you have no clue what to do with it, are you really “informed”?
Games generally dump some semi-random selection of game state data on the AI and leave it to the AI to refine (in every sense of the word) that data into information. Imagine having to re-teach calculus to a human player before every single game. Imagine a board game manual, entirely written in some distinct fantasy language and coming with supplement dictionary and grammar training. Imagine an RTS game without a map and merely a list of coordinates attached to identifiers, all in some random and every-changing order. People would be quite poor players under such conditions, but somehow AI is expected to “just deal with it”.
*How much* information is generally not the point – *too much* information will eventually regress into mere data “under its own weight”.
Let’s see… consider an online shop. At some point, it might have so many articles, that you can never find the item you’re actually be looking for – even though they *do offer it*. The option exists but *might as well not*, because you cannot find it. This is giving the AI “enough information”, without helping any. It burdens the AI with “thinking about” general problems unrelated to the actual game… no AI developer (and no AI runtime) can deal with that.
… In case of the online shop, a useful filtering tool will generally only display very few items out of the many items availabe – and when it offers criteria *actually interesting to you*, you quite easily find the item you are looking for. This is then not your merit – it is the merit of the filtering tool. Substitute [your/the AI’s] and [filtering tool/game].
In RTS games and tactical shooters, that filtering tool is often a ‘Heat Map’: It automatically abstracts single units into “blobs” of “thread”, “opportunity” and “capability”, and then the AI only has to deal with these few blobs, instead of considering every unit an individuum. The ‘Heat Map’ also serves as memory for unit movements, such that the AI itself does not need to have one.
Yet another (quite jarring) example: The Warlords Battlecry RTS games (and many others) offer to human players to move units as a group. *The game* takes care of making them move together, not splitting up too much and waiting if necessary for stragglers to catch up. The human player only has to say “go there” and can then concern themselves with other things.
… The AI player controls every single unit individually. It also creates groups of units, but it has to deal with all these considerations itself. Humans (and even other AIs) can easily “snipe” single units from those groups. Attacks are very awkward – the human just says “attack-move” and each unit *figures out on its own* which unit to attack. The AI assigns targets to every single unit… poorly, too slowly – human units attack twice or thrice before AI units “get it” and retaliate… inefficiently.
Using what the game already offers to the human player would *completely eliminate* a lot of AI logic here *and simultaneously* improve its capability.
Win-win… -win-win-win.
Now, I have culled some 2000 characters from the first draft of this post, so I’ll try to squeeze in some clarification about the general development side:
Extreme solutions are a luxury of simple problems. I do not think “this is good enough for THIS game, I’ll save these lessons for another game or a sequel or something” is all that much better than trying to shine up everything constantly. It is a necessary approach for those working on a budget, but the donation-fueled development cycle popularised by Patreon mostly alleviates this issue. It is now more important to keep producing visible content, each small feature on a reasonable time scale. Being feature-complete at any one point in time has become… mostly irrelevant, from what I can tell.
It would be a shame to have to drop design goals because some initial implementation did not bear fruit. But neither must all things work perfectly at all times. Often a refactoring (or outright replacement) of some module can wait for months, if not years.
But it is a good idea to plan for *capability* of refactoring or replacing things.
To that end, I’ll mention my favourite design pattern: The ‘Strangler Pattern’ works just as well on wetware as it does on software and it always works.
There are only two fail states:
– You use it on too simple an issue, spending more time and code than you would have without… and then some more time to clean up the superfluous code.
=> This is pretty much always fine, because the effort was small to begin with. Feels bad but does not really hurt.
– You try to do to much in one step and end up having to throw away the attempt.
=> This happens just as well without. This pattern only helps to build a new way, when the only way left would surely lead into this dead end.
Now, I mentioned SOLID. The ‘Strangler Pattern’ is all about the “I” in ‘SOLID’: “Interface segregation”.
– You notice that some module A does not offer the capabilities you need to implement your next feature.
– You notice that extending A would be very troublesome and might likely fail.
=> extract an interface I between A and the rest of your program; at first, it will be nothing but declarations of A’s functionality (and thence superfluous; I generally advise not to do this before you do need it, because IDEs will provide good automation support and basically nothing can go wrong)
=> write a new module B fulfilling your new needs outside of A, even though the functionallity really belongs in A
=> when A does not offer the access points for B to do its work, provide them in A but do not include them in I (this is often a good place to use the “D” of ‘SOLID’: “Dependency inversion”; I generally advise against using it outside of such use cases, because it often complicates code unnecessarily and is hard to spot doing so before everything has already become a tangled mess)
=> unlike everything else in your application, allow B to access all of A, not only I
=> write dispatching code into I such that the capabilities of A and B appear through I as those of a single system
– To the rest of your program, it now looks as if you had already updated A, and as if B did not exist.
– You are now done with this feature.
– Future extensions of A will often be easier to implement in B, because B is smaller (and later: because B fulfills your requirements better than A), so you do them in B.
– Eventually you can discard A because it is now empty or all remaining content can be copied into B without changes. (I generally advise to leave I in place now – it may as well serve again, once the cycle begins anew.)
… Finally, it might be helpful to refer to Martin Fowler’s ‘Catalog of Refactorings’ (as always with single quotes, this is a search term). The man has written a lot of very… disputable content, but this catalog is very valuable in its entirety (and free to read online) because it lists a lot of code transformations, *which maintain semantics*. Aka. unless you make a structural misstake applying them, the result is guaranteed to (observably) work exactly like the original.
More and more of these refactorings become available as fully automated actions for IDEs – so the existing room for errors is also continously shrinking.
Aand that’s some 4000 characters more than before I started editing. I’ll see myself to the door now. Sorry for failing to be more succinct.
When coaching my own developers I have found that stating one thing in many different ways is a great way (or rather: the only way that seems to really work) to ensure that *only the intentioned meaning* has been understood… but of course it looks and feels a lot like blathering, both to you and to me.
Looking forward to being able to play as a rampaging, big-dick monster (at least I hope it’s somewhat close to that fantasy from what I’ve been reading so far).