As was foretold, we've added advertisements to the forums! If you have questions, or if you encounter any bugs, please visit this thread: https://forums.penny-arcade.com/discussion/240191/forum-advertisement-faq-and-reports-thread/

[Game Dev] I don't have a publisher. What I do have are a very particular set of skills.

18485868890

Posts

  • Alistair HuttonAlistair Hutton Dr EdinburghRegistered User regular
    I'm branching out into hat based microtransactions:

    I have a thoughtful and infrequently updated blog about games http://whatithinkaboutwhenithinkaboutgames.wordpress.com/

    I made a game, it has penguins in it. It's pay what you like on Gumroad.

    Currently Ebaying Nothing at all but I might do in the future.
    DrovekKoopahTroopahasofyeunIanatordipuc4lifeElvenshaeGlalCornucopiist
  • CornucopiistCornucopiist Registered User regular
    Man! it's been a long time since anything got posted here. I'm in the post-release marketing chaos for my game FNS, which means finally getting a preview on the app store (funny thing: I edit loads of video for Tiktok but it's all portrait mode!), making updated screenshots (added some scenery post-release as I figured out where it needed some oomph) and updating the website (current project).
    It's all in between me being utterly exhausted by family life and just having my day job contract renewed, so there's a lot less work involved than the time it takes would indicate.
    However, downloads are low to non-existent. Them's the breaks.
    Which, incidentally leads me to ask; who's putting up the indie game posts with Tube gone? I messaged DJ Eebs but I don't know how regular he is.
    I'll be popping in in a bit to talk about my current WIP, but here are some screenshots of the latest FNS version:
    hww4xn9lbzx1.png
    25vs7cspdybz.png
    jeemhs2kk7k9.png
    57jyx2vkdvmm.png


    GlalasofyeunKupiIncenjucarHandkordipuc4lifeIanatorScooterLilnoobsElvenshaeDisruptedCapitalist
  • GlalGlal AiredaleRegistered User regular
    I was going to post about this DOS game I was making in Allegro for kicks. I sorted out asset loading, input, music and sound, loaded up a tiled level with parallax scrolling and implemented a menu system, then discovered that, on a 386 equivalent, VGA is really slow at a full screen redraw. You can speed things up by doing all the drawing on a virtual surface and only blit the display when it's all done drawing, but even just doing a full screen 320x240 blit without anything else will run you at sub-30fps. Add in all the other operations (drawing to a virtual surface isn't free either) and you can halve that.
    b29u21k3x3ea.png

    And that kind of took the wind out of my sails. Coding in C was a huge pain in the ass as it was (my hat off to developers who wrote complex engines before OO languages, good lord) and I didn't feel enthused enough to put in the time to change it to a flip-screen. Alas, the Allegro library can't do sub-8bit colours, so no real faster draw modes either.

    Oh well, back to Godot. I shan't miss C, I can tell you that.

    LD50CornucopiistElvenshaeDisruptedCapitalist
  • HandkorHandkor Registered User regular
    edited December 2022
    It wasn't too bad when that's all you had or knew. You'd write C and core graphics stuff with asm { } on interrupt 0x13 to set some pixels in memory. I remember trying to write some sprite rotation code instead of just making a larger sprite sheet with the car pre-rotated being a pain in the arse with only integer math.

    For fullscreen stuff in VGA once you got access to extended memory and HIMEM.SYS you suddenly could do crazy fullscreen double bufferingand such just cause you had the space.

    Handkor on
    GlalCornucopiist
  • KupiKupi Registered User regular
    Man! it's been a long time since anything got posted here.

    Hey, on that note today's the day I felt like finally getting off my ass and rendering...

    Kupi's.................. Friday Game Dev Status Report

    And since I've got something of a backlog of things to talk about, and I know I use fifty times as many words as necessary, I'm going to (attempt to) be especially terse this time around. Even with that it's become an epic, so out of courtesy I'm going to plop it all behind a spoiler tag.
    What's up?

    Depressed and getting worse. Taking the wins where I can get them.

    Nimsters

    Hilariously, my last KWFGDSR post was back in July and said "Next week, I'll tell you what decision I've made about Nimsters". Okay, so a bit of a delay there, but the short version is that I've more or less abandoned the Nimsters project. Made the same blunder I always do with RPG engines of letting the amount of game state get out of control and winding up with something that has too many rules to actually enjoy. (4th edition D&D suffered a similar problem where it seemed to be designed for its online aids, and consequently the actual playing of it required more low-level math than humans could do comfortably.)

    Since it's been six months, a quick summary: Nimsters is Pokemon, except there is a line of 21 randomly-generated numbers between 0 and 9 which is shared by both players. On their turn, each player may move "the timeline" up by between 1 and 3 numbers, and the number they land on determines which moves their Nimster can use. So, your choice of which move to use this turn affects which moves your opponent can use next turn, and vice-versa.

    On top of that core premise, I added the notion that defensive effects (equivalent to the move "Protect" from Pokemon) would also be tied to the timeline; "blocking" effects would prevent damage from attacks lower than the number they were activated from, and "evading" effects would prevent damage from attacks on numbers higher than the number they were activated from. There were also "Claim" effects which would attach a triggered effect to a particular number. e.g. "The number 3 is mine now; touch it and you take fire damage."

    Where I went wrong was allowing multiple defensive and claim effects to stack on top of one another, which enabled certain game rules to be implemented through common systems but turned the game board into an unreadable mess. My big takeaway is that at least as far as an RPG is concerned, if you have more game state than you can present in a single view, your game is carrying too many concepts and needs to be pared down.

    If (if) I return to the project, these are the revisions I plan to make before going back to Nimster and move design:

    - All defensive effects last until the beginning of the next turn, and have no requirement on how the numbers that the effect or the move they're defending against relate to one another. If you're blocking, you're blocking and that's the end of it. If you're evading, you're evading and that's the end of it. You (generally) can't be blocking and evading at the same time, because there's no way to put two guard effects up at the same time.
    - All buff/power-up effects are limited in duration, rather than having variable limitations (such as "until activated a specific number of times"). They can be stacked, but the turn limit naturally creates an inability to stack them indefinitely.
    - Claims are naturally mutually exclusive. Any new claim on a particular number replaces the old one.
    - Consequently, the Bind and Sleep status effects that had previously been implemented through Claim effects move (back) into an independently-tracked piece of game state.

    Binary Serialization

    One of the things I've wanted for a while is the ability to just send an object into a binary stream in a reversible way that's as convenient as it is with the JSON libraries in the .NET ecosystem. (Without using BSON, which is actually designed for traversibility rather than compression.) I took another tilt at that windmill and failed.

    Improvements to JSON Serialization

    ... so out of spite (for myself?) I went back to some of the integrations I'd written with the JSON serialization libraries in System.Text.Json and improved them. To wit:

    - Found some ways to avoid allocating objects during JSON serialization, such as loading incoming values into instances from the object pool and using the built-in forward-only stream to figure out what property is about to be loaded rather than doing string allocations.
    - My custom fixed-precision number type is now implemented as an actual JSON Number instead of a string that's parsed into a number.

    Self-Assembling System 2

    I'm going to skip the preamble on this one, other than to say that I did a significant rework of how my System type (in the ECS sense) uses reflection to create the series of "steps" (code that runs in parallel across the worker threads), with the end result that the functions you write look more like they're operating on objects rather than forcing you to use array indexers repeatedly, and involves no usage of C# attributes.

    A system that updates a velocity with an optional acceleration value in the first step, then updates the entity's position with its velocity in the second would look like this in the old way:
    class MovementSystem : System
    {
        [QueryBuffer]
        public Position[] position;
        
        [QueryBuffer]
        public Velocity[] velocity;
        
        [QueryBuffer(QueryBufferTypes.Optional | QueryBufferTypes.ReadOnly)]
        public Acceleration[] acceleration;
        public bool[] hasAcceleration;
        
        [Step(0)]
        public void Accelerate(int index)
        {
            if(hasAcceleration[index])
            {
                velocity[index].Value += acceleration[index].Value;
            }
        }
        
        [Step(1)]
        public void Move(int index)
        {
            position[index].Value += velocity[index].Value;
        }
    }
    

    In the new paradigm, it looks like this:
    class MovementSystem : System
    {
        protected override Queries Queries => new Queries
        {
            new Query
            {
                new QueryBuffer<Position>(),
                new QueryBuffer<Velocity>(),
                new QueryBuffer<Acceleration>(QueryBufferTypes.Optional | QueryBufferTypes.ReadOnly)
            }
        };
        
        protected override Steps Steps => new Steps
        {
            new Step(Accelerate),
            new Step(Move)
        };
        
        public void Accelerate(ref Velocity velocity, bool hasAcceleration, ref Acceleration acceleration)
        {
            if(hasAcceleration)
            {
                velocity.Value += acceleration.Value;
            }
        }
        
        public void Move(ref Position position, ref Velocity velocity)
        {
            position.Value += velocity.Value;
        }
    }
    

    Admittedly, in this contrived example the second version winds up being a bit longer than the first. But when the functions are actually doing real work, all that not typing "[index]" after everything makes it all a lot more readable.

    Rearchitecting Component Storage

    ECS game architecture assumes that there will be Entities, which are aggregations of Components, which are processed by Systems. It makes no statement about where exactly you get those components from. I've been storing them in a class called "ComponentStorage", appropriately enough, with every Component of a specific type existing in a single shared array. Entities are tracked by separate bookkeeping objects that I called "Archetypes", which stored the start index and length in each array covered by the Archetype. So, for those basic movement entities above, there would be two separate archetypes: one for Position-Velocity, and one for Position-Velocity-Acceleration. But the Positions and Velocities for both would be stored in a common array. I broke those out into separate arrays stored by the Archetypes themselves (now called EntityTypes). The main reason for doing so was to make it easier to expand or contract the buffers as entities enter and leave play, which was otherwise controlled by a bunch of impenetrable math that just wasn't worth whatever gains there were in cache coherency that keeping everything in the same array might have afforded.

    Performance Improvements In Shared Buffer Handling

    Speaking of growing, shrinking, and reusing buffers, I had a a minor epiphany related to how I was handling the reuse of arrays. In my insane quest to avoid letting the garbage collector run at all, I have a system in place to keep pre-allocated array instances in collections and re-issue them as necessary. As different threads may be requesting an array at the same time, this creates a locking-contention issue. Previously, this was all handled by a static "Buffers" class from which you could request an array of a particular type and size with a call like "Buffers.GetForCount<Position>(3)". Internally, there was a two-layer lookup table to get to the set of existing array indexes (Dictionary<Type, Dictionary<int, Stack<Array>>>). With three collections involved, this meant that there were three layers of locks to pass through. What I realized that was most of the time you already know what type you're looking for, and you can put static information in a generic type and it will be stored for each concrete type you create out of the generic type. Therefore, the call looks like "Buffers<Position>.GetForCount(3)" and you bypass the outermost layer of locking completely and only operate on the map from count to the stack of existing instances. Because buffer allocation and freeing is such a fundamental gesture, making that change took something like 15% off the frame time for my stress test.

    But wait, it gets better! I also realized that the vast majority of my engine's use of arrays is in collection types, which use the classic strategy of expanding to power-of-two sizes as they grow. The static constructor for a given type is thread-safe by default, so I could just pre-initialize an array of stacks of arrays of the given type, indexed by the power of two that the array size is. That is, the list looks like this:

    [Arrays of Length 0][Arrays of Length 1][Arrays of Length 2][Arrays of Length 4][Arrays of Length 8][Arrays of Length 16]... and so on

    So when you ask for an array of a specific size, the first thing the buffer manager does is check if your requested size is a power of two, and if so it just goes directly to the stack of instances for that length. It only goes to the lookup table (and incurs a second lock) if you ask for a non-power-of-two size, which is extremely rare. That optimization scored a second 15% speedup over the existing frame-time for the stress test.

    Looking Back At Performance

    Speaking of the stress test, it occurred to me to see just what my statistics say about how much all this perf micromanagement has accomplished. It took me a while to settle on a specific set of dimensions as my test case, so this isn't quite a fair comparison, but comparing the informal console-output timings from an early run of my "ants bouncing around a maze" stress test:

    Maze of 100k tiles, 100 ants
    Average frame time: 38.4996335
    Average frame time: 37.4829946666667
    Average frame time: 37.1140671666667
    Average frame time: 36.9742428333334

    To my current condition:

    Maze of 80k tiles, 1000 ants
    Average frame time: 2.8445058352784325 (5.505453853955371) (2958/2999)
    Average frame time: 2.56464313333334 (3.6042756666666653) (3000/3000)
    Average frame time: 2.5618532333333337 (1.8387009666666647) (3000/3000)
    Average frame time: 2.7304474999999946 (0.9402305666666674) (3000/3000)
    Average frame time: 3.0027220333333413 (0.8783654666666694) (3000/3000)
    Average frame time: 2.7745348000000005 (0.7663088000000007) (3000/3000)
    Average frame time: 2.666757766666662 (2.0859570856952327) (2999/3000)

    The engine handles ten times as many moving objects, in a denser physical space (thus more interactions) in a tenth of the time.

    Next Steps

    Now that I've learned how to use the table control in Windows Forms, I want to revise some of my object editors to make them more comfortable to look at and use. I have to live in these interfaces going forward, after all.

    Long-Term Goal

    I spoke to my sister about how I realized that a spare idea I'd had for a game narrative (involving a subversion of the typical Dr. Robotnik figure in which it turns out that the guy behind the robots taking over the world has actually just re-enacted The Sorcerer's Apprentice) matched up with the idea for a game (a rabbit with transforming ears that allow him to fly) that I couldn't figure out an appropriate villain for. My nine-year-old nephew overheard the phone conversation and is now intensely curious when he'll be able to play it. So now that I have an audience I can't afford to disappoint them.

    Can't promise I'll be back weekly but I'll try not to throw a novel at y'all next time.

    My favorite musical instrument is the air-raid siren.
    Glaldipuc4lifeLD50ScooterHandkorElvenshaePolaritieDisruptedCapitalist
  • ScooterScooter Registered User regular
    Man! it's been a long time since anything got posted here. I'm in the post-release marketing chaos for my game FNS, which means finally getting a preview on the app store (funny thing: I edit loads of video for Tiktok but it's all portrait mode!), making updated screenshots (added some scenery post-release as I figured out where it needed some oomph) and updating the website (current project).
    It's all in between me being utterly exhausted by family life and just having my day job contract renewed, so there's a lot less work involved than the time it takes would indicate.
    However, downloads are low to non-existent. Them's the breaks.
    Which, incidentally leads me to ask; who's putting up the indie game posts with Tube gone? I messaged DJ Eebs but I don't know how regular he is.
    I'll be popping in in a bit to talk about my current WIP, but here are some screenshots of the latest FNS version:


    I forget if I posted it here at the time, but there was a chart like half a year ago that was an analysis of indie games on steam post-2019, and the median lifetime earnings for most genres were only around the $5k range...even the most successful genre didn't earn enough on average to pay an annual salary for even one dev. I'm beating that average myself and it's not even close to what I'd need to quit the dayjob for. It really is kind of depressing to see not just myself but other folks put all their time into a project and have them get so little attention. It's no one's fault, really, but it definitely means there's got to be some enjoyment in the actual creation process to make it worthwhile most of the time.

    As for my project...geeze, have I really not posted here since August? I 'relaunched' my Patreon in October for the start of regular updates for my second game, and while I didn't get a huge boost at the time like I was somewhat hoping, things have been slowly crawling up since then, and support's currently a bit over what it was for my first game. While my general hope was to have most of the game's base functionality done during the year of non-releases, I did still spend a lot of the fall getting in a lot more systems, QoL fixes and so on. In particular I've been doing a lot of modifications to how my game and individual scenes track and use progress, so that rather than just stick to a smaller number of Big Choices I can track and use a lot more minor things, such as what attitude you use to answer a particular question with, which I may use again in a later scene.

    I am now finally in a state though where I can really start to focus on the writing part of my text-based game, and I've been doing a lot of experimentation with my systems which may also be slowing me down a bit. In my first game, scene structure was extremely fixed - scene text could vary a lot based on variables like gender and alien species, but there weren't very many branches to scenes, and a lot had none at all. Which did make it easy to schedule for myself, 'I can do a third of a scene a night, so in this release I'll have these three scenes done' and so on. Now, I've done things like a CYOA-wrestling match scene with something like a dozen different branches leading to four different types of outcomes, based on which moves a player decides to try in the fight, which both balloons the scene size (over 4k words for that one) and means every scene I make now is so different from the others that planning out a schedule for myself is getting really vague.

    Currently planning to start adding in my second alien faction to the game after December, and once players have the ability to hop around between star systems I'll be able to do the second big chunk of mechanics functionality, as pretty much anything tied in to resources sort of depends on keeping the player moving around. Particularly looking forward to getting the second tier of ships into the game, as that's when players will be able to start focusing their playstyle (ie, do they want to start working towards battlecruisers or party yachts for the ships they captain).

    Cornucopiistdipuc4life
  • CornucopiistCornucopiist Registered User regular
    QOL and nice editors are really important. The past four weeks my programming time was spent getting a manual wang tile mapper that used strings (long story) to use a custom class with ints instead.
    Replace a function, break everything, fix, repeat. Some sort of foresight would have have been great, but my mind always goes ‘you get bonus points for idiosyncrasy’.
    Hope to finish that today and report back.
    I’m happy all of you called in, keep at it guys!

  • PhyphorPhyphor Building Planet Busters Tasting FruitRegistered User regular
    Oh yeah my post early retirement plan is to just make some games but they won't provide anything beyond beer money. The market is too flooded and prices are too low, plus you'd have to chase the popular game types and where's the fun in that?

    dipuc4lifeHandkorIncenjucarGlal
  • KupiKupi Registered User regular
    Kupi's Second Consecutive Friday Game Dev Status Report

    Spent two hours reworking my all-purpose object editor, only to discover that when Microsoft says that nesting TableLayoutPanels is not recommended because it's extraordinarily prone to display errors, they mean it's extraordinarily prone to display errors. I already have an implementation that produces somewhat janky-looking but correct output, so I've abandoned the idea of revising my editor for aesthetics.

    As a side-project, now that I've been reminded that static constructors run separately for every new generic type you use, I created an event that types can assign a callback to, and had my object-pooling types wire up a callback that purges their object pools when invoked. The idea being that at some future time it may become necessary to let go of the created instances to make room for new ones.

    My project for the coming week is to write up a design document for either the platform game I mentioned at the end of my last post, or some far more restricted proof-of-concept that contains a title screen and a main gameplay mode with three levels. I really need to stop fucking around with engine code and demonstrate (or disprove) that what I've built will actually carry a real game of some kind.

    My favorite musical instrument is the air-raid siren.
    GlalHandkordipuc4lifeLilnoobs
  • KupiKupi Registered User regular
    Kupi's Optimistically-Described Weekly Friday Game Dev Status Report

    I had one task this week and I didn't finish it; I started the Game Design Document for my restricted proof-of-concept, but didn't finish it. Mostly I just managed to describe the title screen and options menu. What you see in this post will probably constitute more of a description of the actual game features than the GDD itself.

    My minimal proof-of-concept game is both codenamed and actually named "Attitude Mammal", a poke at the flurry of Sonic wannabes who flooded the 16-bit and early 32-bit era. With an eye toward keeping the feature set eminently manageable while still producing something you might actually want to play (if only for ten minutes), the game's intended feature set includes:

    - Two-button gameplay: you can jump and you can fire your rocket launcher. This should suffice to address all obstacles presented by the game, including...
    - Three kinds of stage hazards: buzzsaws (which hurt you on contact), spikes (which hurt you if you step on them), and moving platforms (which can be helpful but will also squish you if you stand underneath them).
    - Three kinds of enemies: a gun turret that aims at you and shoots intermittent streams of bullets, a rocket turret that moves up and down a wall while firing, and a flying robot that drops bombs on you from above.
    - Every level contains at least, but generally limited to, one goal flag that ends the level and proceeds to the next.
    - After the third level you have a boss fight with the evil Dr. Xtractionofsurplusvaluefromlabor, whose mobile combat platform can shoot intermittent streams of bullets, move up and down while firing rockets, or drop bombs on you from above. (Asset reuse!) Defeating him ends the game.

    Most of what kept me from doing much game dev work this weight was the emotional weight of bringing myself to turn in an application to volunteer at the local animal shelter. Which also gave me the idea for a simulation/management-type game where you run an animal shelter... for a fantasy world. If it were a Pokemon game, you'd try to find appropriate adopters for each monster that arrives at your shelter, while making sure you have the budget to keep the right kinds of food in stock and arranging the kennels/living quarters such that no monster gets too stressed out by having to sleep next to a losing type matchup.

    My goal for the coming week remains to work on that GDD. I am on vacation from my day job for the entire week, so I should actually be able to get around to it this time...

    My favorite musical instrument is the air-raid siren.
    HandkorLD50Glaldipuc4life
  • 21stCentury21stCentury Call me Pixel, or Pix for short! [They/Them]Registered User regular
    GUESS I'LL BE HERE.

    With my autoshooter attempt.

    IncenjucarasofyeunElvenshaeGlalIanatorMrBlarneyMadpoetdipuc4lifeHandkor
  • 21stCentury21stCentury Call me Pixel, or Pix for short! [They/Them]Registered User regular
    So, my checklist to Minimum Viable Product is...
    [X] Player Movement
    [X] Camera movement
    [X] Enemy Movement
    [X] Firing at nearest target automatically
    [X] Bullet Damage
    [X] Enemy HP
    [X] Contact Damage
    [X] Player HP
    [X] Player HP Bar
    [X] Timer
    [X] Critical hits
    [X] Special Move Bar
    [ ] Special Move Activation
    [ ] Random target cone pattern weapon
    [ ] Attribute system
    [ ] Map Background
    [ ] Enemy Spawning
    [ ] Melee Weapons
    [ ] Weapon Loadout system
    [ ] Item Pickups
    [ ] Enemy Drops
    [ ] Destructible objects
    [ ] XP System
    [ ] Level up system

    I am doing pretty well so far, honestly. I just need to keep going with this even as i go back to my dayjob tomorrow.

    Elvenshae
  • ZekZek Registered User regular
    I've posted here before about my ill-fated attempts to get into hobbyist game development - I always run out of steam somewhere along the way and abandon my idea at the prototype stage. The trouble is I'm not that creative and have a hard time getting excited about what ideas I do have. This year I'm changing up my tactics, and am just going to focus on learning stuff for the sake of learning it, with only very general goals. I work as a full stack web developer, and I think the best application of my skills would be something web-oriented. So first I'm working on starting a basic web server + database + React UI - then I'm planning to start learning Phaser and hooking up the two.

    After that I figure I'll just keep trying to learn more stuff and add each thing I learn onto the web app in some form. If I have an idea along the way I'll build it and maybe not do anything more with it, no big deal. Then over time I'll keep growing my feature set until I have the inspiration to do something more specific. That way if I do have inspiration, I'll have already cleared the largest hurdles to implementation.

    Elvenshae21stCentury
  • 21stCentury21stCentury Call me Pixel, or Pix for short! [They/Them]Registered User regular
    I did some good work today despite my dayjob getting in the way.

    Current Checklist progress went negative because i added more stuff i need to get done, ah well.
    [X] Player Movement
    [X] Camera movement
    [X] Enemy Movement
    [X] Firing at nearest target automatically
    [X] Bullet Damage
    [X] Enemy HP
    [X] Contact Damage
    [X] Player HP
    [X] Player HP Bar
    [X] Timer
    [X] Critical hits
    [X] Special Move Bar
    [X] Special Move Activation
    [ ] Random target cone pattern weapon
    [ ] Attribute system
    [ ] Map Background
    [ ] Enemy Spawning
    [ ] Melee Weapon
    [ ] Aura weapon
    [ ] AOE explosions
    [ ] Orbit projectiles
    [ ] Random enemy targeting projectile
    [ ] Proc activation
    [ ] Scaling amount of bullets
    [ ] Weapon Loadout system
    [ ] Item Pickups
    [ ] Enemy Drops
    [ ] Destructible objects
    [ ] XP System
    [ ] Level up system
    [ ] Pause Menu

    MechMantisAnon the FelonasofyeunKupiElvenshaeKoopahTroopahIanatordipuc4life
  • Anon the FelonAnon the Felon In bat country.Registered User regular
    Inspiring stuff!

  • 21stCentury21stCentury Call me Pixel, or Pix for short! [They/Them]Registered User regular
    My list keeps growing faster than i can deal with but that's ok, it's alright, i still learn a lot every day.

    Today i learned how to add deviation to an auto-aimed attack. I'm starting to get a better idea of how to google stuff.

    like, asking how to make a 2D shotgun doesn't work for my game... BUT asking how to randomly generate a vector? THAT works in my favor.

    Fun stuff. :) It's just a constant loop of "find problem, research problem, solve problem".

    Current Checklist Progress.
    [X] Player Movement
    [X] Camera movement
    [X] Enemy Movement
    [X] Firing at nearest target automatically
    [X] Bullet Damage
    [X] Enemy HP
    [X] Contact Damage
    [X] Player HP
    [X] Player HP Bar
    [X] Timer
    [X] Critical hits
    [X] Special Move Bar
    [X] Special Move Activation
    [X] Random target cone pattern weapon
    [ ] Block Activation of special move while previous one is active
    [ ] On-hit knockback to player
    [ ] Attribute system
    [ ] Map Background
    [ ] Enemy Spawning
    [ ] Melee Weapon
    [ ] Aura weapon
    [ ] AOE explosions
    [ ] Orbit projectiles
    [ ] Random enemy targeting projectile
    [ ] Proc activation
    [ ] Scaling amount of bullets
    [ ] Weapon Loadout system
    [ ] Item Pickups
    [ ] Enemy Drops
    [ ] Destructible objects
    [ ] XP System
    [ ] Level up system
    [ ] Pause Menu

    asofyeundipuc4lifeElvenshae
  • KupiKupi Registered User regular
    Kupi's Weekly Friday Game Dev Status Report

    For the week of the Holiday Forum, I have only one token effort to report: I separated the system that updates sprite animations into two parts: a first that does nothing but update the frame displayed, and a second that watches for changes in the animation/frame state and updates the entity's collision volumes with the data from the animation. I had previously decided to associate collision info with animation data because there really is a tight association between hitboxes and the sprite being displayed, but in writing out the game design document for Attitude Mammal, I realized that there's also a category of entities which animate without changing their collision data: animating world tiles like conveyor belts. It doesn't make sense to keep resetting their collision data to the same thing as their frames change, hence the separation of duties into different systems with a component to indicate "this entity should update its collision info from the animation when the animation changes".

    Speaking of the Attitude Mammal GDD, that's what I finished this week. The next step is to translate that into a checklist the way 21stCentury has done and get to work on it. However, this is AGDQ week, so I'm officially setting my target at "no progress". (Not that that'll stop me from feeling compelled to do something and report on it.)

    My favorite musical instrument is the air-raid siren.
    dipuc4life
  • Endless_SerpentsEndless_Serpents Registered User regular
    I like when you guys do the things. Keep it up!

    GlalElvenshae
  • GlalGlal AiredaleRegistered User regular
    Godot 4.0 is just around the corner, so I've made it my arbitrary "I'll work on non-game projects until then" point, letting ideas and enthusiasm build up until I have a new toy to play with to throw them at. And I know 4.0 will likely be a bit buggy and unreliable until the first stability release that follows, but there's only so much waiting I can do.

    It'd be nice if Spriter 2 also came out around the same time, buuuuut that project has been in the works for what, 5 years now? They say it'll come out early 2023, but I'm not holding my breath, it'll be finished when it'll be finished. Frankly, with them giving all owners of the original Spriter Pro the next one for free I'm surprised they have the funding to do this.

    Until then I sate myself reading about you good folks' accomplishments.

  • KupiKupi Registered User regular
    Kupi's Weekly Friday Game Dev Status Report

    Y'know, every time I say "I make no promises because it's GDQ week", and then it turns out that I'm more focused on game dev than I am under normal circumstances.

    I mentioned before that I had a number of revisions I wanted to make to my Nimsters prototype; with a vacation week to throw at it, I decided to go ahead and implement those and several more that I came up with in the meantime. Going down my Git commit history for the week...

    Because Nimsters started as (and remains) a fairly direct Pokemon derivative, I somewhat lazily copied the way monster stats are defined, with the exception of replacing "Speed" (which Nimsters doesn't use since the players trade turns back and forth) with "Vitality" (the equivalent of your HP for the accumulation of status effects). This means that every Nimster has a rating for Striking (physical) Attack and Defense, and Energy (special) Attack and Defense. The thing is, this produces the classic situation where, except in very rare circumstances, you don't care about one stat or the other. And just as a matter of personal philosophy, I hate the notion of dump stats. It's okay that Wizards have worse Strength than other characters, but they should feel its absence. And even though Nimsters has mechanisms for forcing the enemy to use specific attacks out of their moveset, the ability to actually force an enemy into using either a striking or energy attack is limited if they don't actually slot in one of those moves. Furthermore, I also already included a notion of the shape of the attack for the purposes of filtering guard effects; attacks are necessarily one of "contact", "projectile", or "blast". This was more concepts than the game really needed, so I decided to compress the game's concepts down to just its original ones (relative to Pokemon).

    Now, Nimsters all have six stats:

    Health: Amount of damage it can take before being forced to retreat.
    Vitality: Amount of status damage it can take before being afflicted with a status effect.
    Stamina: Total amount of available stamina points to use for moves.
    Power: Combined attack and defense stat for contact attacks.
    Skill: Combined attack and defense stat for projectile attacks.
    Focus: Combined attack and defense stat for blast attacks.

    Admittedly, this creates another, different undesirable scenario where every Nimster that's good at, say, melee attacks, is also necessarily highly durable against them. However, given the choice between that and just being able to ignore the influence of one stat or another, I prefer it. And if I decide to resurrect the notion of "Attributes" (that is, Abilities), then you might have one that raises contact attack damage without raising contact defense.

    This also brings Stamina into play as a variable stat; previously, I gave every Nimster a percentage stamina that was reduced according to the total number of times an individual move could be used-- again, taking inspiration too directly from Pokemon, where every move has a number of PP that determines how many times it can be used before having to refresh. I prefer it being a variable; it means that even without a traditional "speed" stat you can represent "nimble" monsters by giving them more Stamina that lets them attack more often.

    One potentially-controversial decision I've leaned into is making Nimster stats "objective". By which I mean, stats do not increase with level; all computations use the equivalent (in Pokemon terms) of the base stats plus EVs and IVs without the linear interpolation by level. The levels of the attacker and defender are now direct factors in the damage formula; damage increases if the attacker's level is higher than the defender's, decreases in the opposite case, and is unchanged at equal levels. This is mostly because I want to keep the numbers comparatively low so the player can keep up with the math. If "but number go up??? :<", that's what transforming your Nimsters into higher forms and the friendship system (the equivalent of grinding EVs) are for.

    I sharply reduced the number of rules involved in defensive and other triggered or contingent effects. Guards (personal defense effects, similar to Protect or Detect) and Claims (reactions associated with a particular digit on the timeline, e.g. "if you use a move on the number 5, you take fire damage") no longer have configurable durations or trigger counts. Guards last until the start of the user's next turn, and Claims last indefinitely. Only one Claim can be associated with a particular digit; if another Claim is used on the same digit, it replaces the old one. Furthermore, Guards no longer have the number-comparison rule that determine if they even go off. Previously, in order for a guard effect to work, the number you used it on had to be higher (if a Block) or lower (if an Evade) than the attack it was attempting to guard against. Now, Blocking and Evading are absolutes, and the only way around them is to use an attack with the Unblockable or Unavoidable flag (for their respective guard types). This makes it much, much simpler to judge whether or not one of your attacks will be likely to work at a certain point in the timeline.

    As a consequence of the above revisions, the Binding and Sleep status effects, which were previously implemented using the Claim system, have been moved into additional state on the individual Nimster affected by the status effect.

    On the same simplification tack, power modifiers (buffs and debuffs affecting specific attack types or Nimster types) no longer have the "until trigger count" duration limit. All buffs or debuffs last until their configured duration in terms of the user's turns.

    This one is invisible to the player, but I also rearchitected how response effects define the battle effects of their response. Previously, the response was stored as a completely separate move... i.e., for the move "Spiked Armor", the damage-on-contact effect was represented by a different move called "Spiked Armor Counter" referenced by Spiked Armor. The problem with that is that the power-balance calculator I built into my data-entry tools could only refresh its assessment after you navigated away to the separate move, making it difficult to see the influence of changing the counter without a lot of flipping back and forth between dialogs. And, though there were some cool things you could do with making a counterattack refresh itself, it was also subject to infinite loops and the occasional "oh, I forgot to make the counter's attack type match the type of the original move". So I've moved the response effects into the definition of the original move. I've realized as I was typing that I could just use non-modal dialogs and share information between windows, but the work is already done so there's no sense in going back on it.

    As of this moment, I'm in UI hell, revising the editors to reflect the changes to the underlying code. Forms are a pain in the ass.

    My favorite musical instrument is the air-raid siren.
    GlalIanatorLilnoobs
  • KupiKupi Registered User regular
    Kupi's Weekly Friday Game Dev Status Report

    I was supposed to start working on Attitude Mammal at some point, wasn't I? However, I have been nerd-sniped by Nimsters (including the Muse dumping a series of great animal+thing puns into my head), so work has continued on that.

    Last week I said I was in UI hell, and now... I might actually be out of it??? Thanks to deciding that a number of features weren't worth the effort of porting them over to the new data arrangement, there were fewer things to represent in custom-made controls than I thought there would wind up being. So after the week's work, I now have a version of the Nimsters Move Editor where every aspect of the move can be modified by controls on the form (as opposed to having to type in a string in a purpose-built input language, or open up a completely different move that's being invoked as the counter-attack). The principal advantage is that now everything can feed back into the automatic balance assessment instantly. ... of course, if it were really as simple as finding the right mathematical formula, game balance would be a solved problem, but it helps to have something that can at least tell you at a glance if what you're putting in for the power parameters is wildly off-curve.

    Naturally, having finally reached a comfortable point, I reflexively decided to make myself uncomfortable again and implemented a new move effect / Nimster state: speed. As an effect of a move, the user can be made either "fast" or "slow". If a Nimster is "fast", it gets another consecutive turn. (Naturally, this is weighed as an extremely valuable move effect for the purposes of the balance score.) Contrarily, if the Nimster is "slow", it loses the next turn it would have otherwise have taken. A simple enough concept, but it turns out that it has some screwy interactions when a Nimster is forced to retreat, since control has to pass to the other team for them to send out the replacement, without attacking, before the fast Nimster gets its next turn. I haven't worked out an implementation I like, but at this point the problem has irritated me enough that I feel compelled to solve it.

    Anyway, here's a screenshot of what the move editor looks like. The only thing left now is to use it!

    tyvcxc9wn3o0.png

    My favorite musical instrument is the air-raid siren.
    IanatorLilnoobsPeewiGlaldipuc4lifeElvenshae
  • PeewiPeewi Registered User regular
    Recently I had the idea that I wanted to try making a game with online multiplayer. Obviously, online multiplayer can be very complicated, so to make things easier I made a very simple turn based game. It's a memory game (flip over cards and find ones that match)

    It looks pretty basic:
    xl88cynyxedf.png

    I used Steamworks for the online stuff. I don't think you're really supposed to use it without paying the Steam publishing fee and getting an appID, but you can use the appID of the Spacewar test app and it works. Setting up game lobbies and invites was fairly straightforward. I used lobby chat messages to transmit game commands, which seems like something you shouldn't do, but it works, so for this tiny thing I don't care.

  • PhyphorPhyphor Building Planet Busters Tasting FruitRegistered User regular
    You won't be able to release it like that but using it for development is fine

    DrovekElvenshae
  • KupiKupi Registered User regular
    Kupi's Weekly Friday Game Dev Status Report

    I've been tooling around with my move editor for Nimsters. I'm almost done with the Fire-type moves, but it's been a bit of a process to get there, between getting distracted by other things (external to the project) and tripping over issues with my balance-assessment math.

    In the original rendition of the Nimsters game rules, guard effects that blocked moves blocked attacking moves that were used on numbers lower than the move that created the guard effect, and guard effects that evaded did the opposite, stopping attacks from moves used on higher numbers. Therefore, I established a convention where moves used on lower numbers were less costly and did less damage, while moves used on higher numbers cost more and did more damage. So blocking effects tended to be better for stopping weaker moves, while evading effects tended to be better for stopping stronger moves (presumably because of their longer wind-up time or whatever). However, blocking and evading effects are now absolutes no matter where they're used from, so I've changed up the convention for what numbers mean. Now, ranges of numbers correlate to the theme of the Nimster type to which they belong, in six-digit bands. For example, thematically "low" types, like Earth, Ice, and Darkness, tend to be usable in the range of 0 to 5 (that is, a range within those boundaries; most moves are usable on a three-digit range), while thematically "high" types like Fire, Air, and Light tend to useable in the range of 4 to 9. Structured or balanced types like Metal and Psychic are usable in the middle ranges, and so on. This helps produce an effect that I want, where it's hard to put together a moveset that lets you no-brain the management of the timeline by always having a move available.

    As I brainstorm moves, I find myself struggling with an old nemesis I call "design by matrix". Design-by-matrix is where you have some number of dimensions, and then you try to fill every intersection between them. In the case of Nimsters, this involves the intersection between 1) each individual Nimster type, and 2) the very broad families of move type like "direct attack" (for each individual attack type, being Contact, Projectile, and Blast), "guard", "claim", "star move" and "X move". My reflex is to try to fill all or most of those intersections. But, especially when I'm doing my best to avoid the [Element] [Anatomy] template frequently used by Pokemon (e.g. Fire Punch, Fire Fang, Blaze Kick...), there just... aren't that many ways to give, say, Electricity the same number of melee attacks as you can come up with for Metal. And I'm trying to remind myself that asymmetries produce character! Maybe Electric is just an attack with a lot of Blasts and not a lot of Projectiles!

    One of the Electric-type melee moves is called "Rolling Thunder", though. I'm proud of that one. :D

    Next week: continued provisioning of the new Nimster move data.

    My favorite musical instrument is the air-raid siren.
    IanatorGlalLilnoobs
  • Mc zanyMc zany Registered User regular
    edited January 31
    Well one result from the whole wizards of the coast D&D debacle was that the SRD for D&D was just released with a creative commons license, which means anyone can use the basic ruleset (with credit). The document they released even mentions Waterdeep, which means anyone can use the name (but not anything else)

    Which is great news for me, seeing as I working on a legally distinct game based off Eye of The Beholder, a D&D based game which is set in Waterdeep.

    Mc zany on
    GlalHandkordipuc4lifeElvenshaeFrem
  • KupiKupi Registered User regular
    Kupi's Weekly Friday Game Dev Status Report

    Plodding along on Nimsters's move data. Got almost all the way through three types: Water, Plant, and Electric.

    Somewhere along the way I decided that the character of the Water type would be "slowly building power". Water moves have broader usability ranges on average and lower-than-average stamina costs, so they don't have a lot of direct power. However, many of them provide buffs to subsequent Water moves or get power boosts against enemies afflicted with the status effects inflicted by the entry-level Water moves, so they ramp up over time. In designing the water moves, I decided to resurrect the "landing comparison" move usability type, where certain moves can be used depending on how the current state of the timeline compares to the state of the timeline on the last move used by either the current Nimster or its target. This was to enable a pair of physical attack moves, Dive and Breach, where Dive requires you to land on a lower number than the last one you used, attacks and raises the power of water moves for a few turns, and Breach is a stronger attack with no special effects that requires you to use it on a higher number than the last one you used. So there's a sort of dolphin-swimming shape to how you use those two moves.

    The Plant-type has about what you'd expect: a few life-draining moves, a few thematic status-effect inflicting attacks (Sleep being the one most unique to the type), and a bunch of moves that can heal either the user or their allies who come in after it. Like Water, Plant is a "slow build" type when it comes to buffing up, and strives to outlast the opponent rather than going for the throat.

    I also started working on the Electric-type moves. Electric-type moves tend to have small usability ranges and high stamina costs, with fittingly high power and frequently being unblockable, unavoidable, or even both. Most of the Electric-type buff moves have high intensity and low duration, lasting only to the user's next turn. There are several moves in the Electric-type with the "fast" flag, meaning they cause the user to take another turn after they're used. It burns through Stamina quickly, but hey-- who needs stamina if you win the fight by spending it all?

    This coming week, I'm going to see if I can beat my record for number of Nimster types covered. This project already has the feeling of a boondoggle and I want to keep a good pace. There's a lot of distractions available to cut from my life in service of it, at least. We'll see.

    My favorite musical instrument is the air-raid siren.
    GlalIanator
  • KupiKupi Registered User regular
    Kupi's Weekly Friday Game Dev Status Report

    Alas, did not beat my record this week. I came up with a sufficient number of moves for three new types (Ice, Earth, and Air), plus filled in a few gaps in previous types.

    Just like inspiration for a move made me decide to resurrect the landing-comparison-based move usability type, another pair of moves made me decide to resurrect the landing-comparison-based move power modifier, such that moves can be stronger if the number you use them on compares to the previous value in a specific way. The impetus for this was a pair of Electric-type physical attacking move names that occurred to me: "Circuit Breaker" and "Return Stroke". Both have the same mechanics, but function in opposite ways: Circuit Breaker can only be used on a number matching the last one the user landed on, and deals extra damage if that number also matches the opponent's last number, and Return Stroke can only be used on the same number that the opponent just used, with a power bonus if that number matches the user's last-used number.

    I said before that I was reluctant to do elemental pallet-swaps, but my principles are nothing if not malleable in the face of a dumb pun; the Ice-type therefore has physical attacking moves called "Frost Bite" and its advanced form "Cold Snap". As you might imagine, the Ice-type's primary status effect is Paralysis/Binding (haven't settled on a user-facing name), though several of its other moves can Weaken (reducing physical attack and defense) through the use of extreme cold. In another instance of mechanics-as-comedy, one of the strongest energy-based Ice moves, Absolute Zero, can only be used on one number... 0.

    The Earth type was where I really got invested in the positional power modifiers. Two of its physical attacks form a pair: Stalactite is a rock-based downward blow, while Stalagmite is a stony uppercut. Naturally, Stalactite gets a power bonus if the opponent's previous landing is below the user's, while Stalagmite gets a power bonus when used from below. On the energy side, the Earth type's attacks are themed around either earthquakes or petrification (which is just Earth-elemental Paralysis/Binding (haven't settled on a user-facing name)).

    Because I have an "Air" type instead of a "Flying" type, most of the obvious options for moves revolving around birdlike anatomy are out of the question. So, the Air-type physical attacks are mostly themed around using the wind to carry oneself forward, while the projectile attacks involve using breath as a weapon. My favorite move from a mechanical standpoint (which I suspect actually isn't going to be that useful in practice) is an X move (one of the ones you can only use on what is normally an "all moves fail" space) called Inflate. The user puffs themselves up to get a boost to their Air moves on the next turn, but also activates a guard effect whose "counter" assigns the user a x0 multiplier to Air power moves for the next turn-- in other words, if they get attacked, they get the wind knocked out of them.

    There are five types to go: Metal, Poison, Psychic, Light, and Dark. Doubtful I can get them all this week. But hey, hope springs eternal.

    My favorite musical instrument is the air-raid siren.
    Anon the Felon21stCenturyIanator
  • KupiKupi Registered User regular
    Kupi's Weekly Friday Game Dev Status Report

    I spent a large amount of this week's creative energy budget putting together a gift for my [It's Complicated], which cut into how much I was willing and able to work on Nimsters. Therefore, I sort of half-assed the move data entry for the next two types on my list, Metal and Poison, and filled in some gaps that I'd unintentionally left in the previous types (despite what I said before, I have fully embraced design-by-matrix in this case as ideas occur to me).

    The contact and projectile move families for the Metal type are about as you might expect: the contact attacks invoke the imagery of various weapons bladed and blunt, while the projectile moves involve shooting metal balls of various sizes at the foe (stopping short of ever admitting that something might be a "bullet"). "Energy blast" is a bit stranger of a thing to try to apply to the notion of "Metal", but I've settled on magnetism and radio waves as the theme. The character of the Metal type overall is to block; lots of otherwise-offensive moves have riders that block one incoming attack type or another.

    Probably the most interesting thing I can say about the Poison type is that it's absorbed a few aspects of what would have been the Bug type in Pokemon; Poison here stands not just for toxic materials but anything specifically biologically damaging, like blood draining. So, insect bites and stings are in scope as well. One line of poison blast attacks involves an entry-level move that inflicts poisoning on both combatants, with two related advanced moves that become more powerful based on how many stacks of poison the user has. One causes damage and cures the user of poison, while the other just straight up inflicts a lot of the poison status. Poison! It's nasty stuff.

    In coming up with special moves for the Metal type, I realized that I've created a weird interaction in my speed rules. Quick refresher: one element of a Nimster's current state is its speed, either Normal, Fast, or Slow. If a Nimster is Fast at the end of its turn, its speed returns to Normal and control of the turn stays with its team (that is, it takes another turn). If a Nimster is Slow when it would otherwise receive a turn, it becomes Normal speed and control stays with the opponent. I implemented speed changes as a move effect, rather than an attribute of the move, which means that they can be applied by, for instance, guard effects. One of the Air guard moves makes the user Fast as part of its response, so the counter-"attack" is really "if you try to hit me, I'll dodge it and then take two turns in a row".

    So, here's the weirdness: guard effects only end at the start of the user's next turn. Let's say that a guard effect has as one of its response effects to make the user Slow. So if that guard effect is triggered, the user passes their next turn. Which leaves the guard effect in effect. And if the opponent triggers the guard again, the user becomes Slow... again. I think this is actually an interesting interaction! One of the star moves (the especially powerful ones) for the Metal type blocks all damage, retaliates, and makes the user Slow, keeping the shield up until the enemy finally stops attacking or pierces the guard. But this could get even stranger: a guard effect can be completely unfiltered, meaning it goes off no matter what on the enemy's turn. If one of the response effects of such a guard is to make the user Slow, then once you activate it, the user will never actually get a turn, and just keep performing their counter on the enemy's turn. (Note: the Sleep status immediately ends all Guards, so this is one way to break the cycle. If the user also becomes Slow, then both Nimsters void their turn and the one with the slow-guard will be forced to take their turn.) The only problem I have with this, philosophically, is that (so far) I've limited the start-of-turn and end-of-turn events to turns you actually take. So if you go into this infinite-recursion mode, your monster becomes completely immune to poison, and their damage buff effects never expire. I feel like I could fix this by moving the trigger for a guard effect expiring to "attempting to use another move", and have a Nimster which is Slow still experience start-of-turn and end-of-turn events when they would otherwise have acted. However, that makes turn-change logic extremely thorny when poison damage results in a KO. But when has "that would be extremely complicated" ever stopped me?

    RPGs are deceptively difficult things to make.

    Anyway, the target is the same as it's been: get all the move definitions in and then start making Nimsters with them. I also realized that I've completely forgotten that I have to make moves for the Physical (Normal) type as well, so the list is a bit longer than I thought last time.

    My favorite musical instrument is the air-raid siren.
    GlalHandkorEvilOtakuIanatorDisruptedCapitalist
  • PeewiPeewi Registered User regular
    I was pretty happy with the collision detection I had made, but it had some major limitations. The collider used for moving objects had to be a circle and the world was made entirely of lines. You could make a platformer where the player's collider is a circle, but most of them have characters that are taller than they are wide.

    I am now making a system where any kind of collider can do swept collision against any kind of collider. The shapes I'm implementing right now are circle, capsule and axis-aligned bounding box. I'm not doing line because that's just a capsule with radius 0.

    I was unable to find any useful materials on how to do swept collision of two capsules. The solution I came up with was doing circle-capsule sweeps from the ends of the capsule and in the opposite direction from the other capsule, and using the shortest result. It works, but it feels a bit hacky.

    I have not yet actually implemented this new collision in game physics, but I have implemented the collision checks individually and they appear to work.

    Kupi
  • KupiKupi Registered User regular
    That's exactly the approach I wound up using for my engine. Everything ultimately decays to either a line segment intersect test or a line segment to circle intersect test, playing games with frame of reference, iteration, and min/max to get the necessary result.

    My favorite musical instrument is the air-raid siren.
  • zagdrobzagdrob Registered User regular
    I've been feeling bored and wanting to make something, so I installed Unity and spent a few days playing around trying to make something somewhat fun. Mostly at best buggy dev art level assets but it's at least sorta feeling like it could be like a game.

    I have all kinds of ideas about some Tyrian-like vertical scroller with all kinds of other elements but right now I'm just glad I have something that sorta kinda works. Guns track and shoot, space ship flies, missiles fire, etc.

    https://youtu.be/ltQCHrTIF1g

    IanatorGlalPeewiDisruptedCapitalistKoopahTroopahElvenshae
  • PeewiPeewi Registered User regular
    Alright, got my new colliders implemented in my player movement. I found and fixed some bugs along the way. And a few still left to fix.

  • KupiKupi Registered User regular
    Kupi's Weekly Friday Game Dev Status Report

    I spent the President's Day Weekend taking a file in Hades from the beginning to the end credits, which I'm putting in the evidence file for Why Kupi Is Not Allowed To Have Roguelikes. I spent the remainder of the week alternatively depressed and completely knackered, so there was basically no progress on anything. I created a few moves for the Metal and Poison types, but nothing on the scale of what I've been doing the previous two weeks. And I'm also leaning into the idea that maybe these moves don't always need to be mechanically or conceptually novel. Two of the moves I created for Poison were "Poison Gas", whose only effect is to inflict the Poison status, and "Toxic Cloud", which inflicts more Poison status damage than Poison Gas.

    So since I haven't got material results to share, here's something from the ephemeral side: this week I managed to sleepwalk my way into a damage formula for that Phantasy Star-like JRPG I've referred to in the past that I like a lot better than the previous ones I've come up with. I got there by way of trying to come up with a damage-reduction mechanic I liked for a roguelike instead (hey, I'm not allowed to play anybody else's, but if I make my own, that's play-testing). I wanted to make sure that armor could reduce incoming damage, but never totally deflect it. One way to accomplish this is to multiply incoming damage by some ratio. The problem is that linear increases in the damage reduction have a non-linear effect on the character's total durability. So, if you progress from 50% reduction to 55% reduction to 60% reduction, the 60% damage reduction is actually a bigger improvement over the 55% armor than the 55% armor is over the 50%. How to you know what multipliers the armor should apply to have each new piece/set of armor be about the same degree of improvement over the last step?

    The answer is a technique ("technique", like it isn't just basic math...) I've been applying when writing the balance-assessment functions for Nimsters: just invert the multiplier! To put it another way, instead of directly assigning the armor a multiplier to incoming damage, instead assign the armor its effective increase in your maximum HP. Let's say that each armor upgrade represents the equivalent of a 50% increase in your maximum HP-- applying the reduction lets you take that much more damage before you die. Then the armor's damage multiplier becomes a function of the effective increase in max HP like so:

    ArmorDamageMultiplier = 100 / (100 + ArmorMaxHPIncreasePercent)

    50% effective max HP increase: 100 / 150 = 2/3 = ~0.66
    100% effective max HP increase: 100 / 200 = 1/2 = 0.5
    150% effective max HP increase: 100 / 250 = 2/5 = 0.4

    Expressing the armor's value in this way is way more readable to both the player and the designer-- double the armor value and it's worth twice as much, simple as that.

    Bringing it back to a JRPG, it's trivial to add an attack stat that increases damage to the formula; the attack stat just adds on to the numerator instead of the denominator. So an attack stat of 50 means "add 50% more damage over base", an attack stat of 100 means 100% more damage over base, and so on. Increases in the Attack stat perfectly negate an equivalent increase in the Defense stat, if that's what you want. (If you want the same attacks to do more damage in the late game, you can multiply the Attack stat by some amount so its effect grows faster than Defense, which you might need to do since characters accrue more HP as they level up. There's a lot of nuances to RPG rules that go beyond the scope of a single message board post.) And, of course, there's no reason that Attack and Defense need to map 1:1 to the percent increase in damage or effective max HP; if you want a smaller stat scale, you apply another multiplier to the Attack and Defense stats to convert them to their effect on the percentages.

    This new formula (Damage = BasePower * ((100 + Attack) / (100 + Defense))) has a couple advantages over the share-of-sum (Damage = BasePower * (Attack / (Attack + Defense))) one that I'd been exploring:
    - Requires no special cases for either or both of Attack and Defense being 0.
    - Damage scales infinitely with increased Attack. (Share-of-sum never goes beyond the value of BasePower, even if Attack infinitely exceeds Defense.)
    - When Attack is 0, some damage still results, even if minuscule.
    - The effect of both Attack and Defense are comprehensible to the player and the result of changes to them can be predicted. (In share-of-sum, doubling your Attack results in a very difficult-to-predict increase in damage.)

    For the coming week: Nimsters work, presumably! I want to finish the Metal and Poison types, convert the brainstormed ideas I have for Light and Dark into actual game data, and then figure out how in the world the Psychic-type will produce contact attacks.

    My favorite musical instrument is the air-raid siren.
    GlalDisruptedCapitalistElvenshae
  • PeewiPeewi Registered User regular
    I'm adding graphics!

    sszdz58je610.png

    Actually making graphics takes time and my artistic skills are very limited, so I'm using free art assets.

    Rather than making my own level editor, I am using Tiled. It's pretty nice, though as the name implies, it is focused on making levels out of tiles and using arbitrary shapes isn't an option. Someone's made a C# library for reading Tiled files, but it turns out it's missing support for a lot of features.

    I added the option to draw colliders:
    75qvczvohi6a.png
    For now, all tiles are either empty, fully solid or in the foreground or background.

    I support arbitrary game resolutions and I ran into some graphical glitches when doing non-integer scaling. Long story short, I learned that there's good reason to leave gaps between sprites in a spritesheet.

    KupiKoopahTroopahGlalElvenshaeHandkorIanatorAnon the FelonIncenjucar
  • KupiKupi Registered User regular
    Kupi's Weekly Friday Game Dev Status Report

    Having experienced a total failure of self-discipline and abandonment by all other available mechanisms of getting help (it turns out that Steam has no mechanism to genuinely remove a game from your account), I spent a lot more of my free time playing Hades than I want to on an intellectual level this week. Ironically, this somehow resulted in my best week of progress on Nimsters in a while, which I can only guess is because I actually did the thing I wanted to do first rather than sit around in neutral wishing I were doing something else.

    I hit and exceeded the target set in my last post: the Metal and Poison types are now fully provisioned for moves, and from there I continued on to the Light, Psychic, and Fighting Force types. The Dark type is only half-implemented, but I managed to brainstorm moves for the empty slots this morning so that's within a half-hour of being done. From there, the only thing left is the Normal Physical type, which is closer to a type and half to two types' worth of work, since everything has Physical moves and therefore a bit more variety is called for.

    I did, in fact, come up with concepts I liked for contact/strength-based attacks for the Psychic type: one family of moves centers around using psychic power to suss out weaknesses or attack while evading, while the other is basically a series of variations on the good old-fashioned Star Wars Force choke. Psychic projectile moves involve either launching globs of psychic energy at the foe or using telekinesis to throw things at them. And the majority of the rest of the moves otherwise have some relationship to the Confusion status, which in Nimsters is the stat-modification effect for energy attacks (both weakening outgoing attacks and lowering defense against them).

    Many attacks in the Light type cure the user of some kind of status effect while performing the attack; for example, "Purification" uses holy light to attack the foe while curing the user of Poison. When it isn't invoking holy imagery, the Light type tends to involve just attacking with light, whether that's blinding flashes or laser rays.

    What I have so far of the Dark type, and what exists in the future plans, is, as you might expect, somewhat the mechanical opposite of the Light type: the Dark type likes to inflict a whole bunch of different status effects on the enemy and even itself. Many Dark-type moves have some kind of cost to the user, whether it's attack recoil or self-inflicted status effects, with the advanced moves gaining power while the user is afflicted with those same status effects. It's a spiteful, "I get hurt but you get hurt worse" kind of type. And the Fear status effect is splashed all over; because a Nimster that leaves the battlefield for any reason is not allowed to return in a regulation Nimster battle, "Fear" functions as the game's "instant death" status.

    Force is where I made probably the biggest mental stretch in this whole design exercise. The contact attacks are, naturally, mostly big unblockable straight hits, and the energy attacks are shockwaves and ki blasts, simple enough to come up with. For projectiles, though? Well, it may surprise you to know that karate is a form of projectile. ... at least, I'm saying that flying kicks, pressure-point jabs, and other Wuxia bullshit require sufficient precision that anything that would foul the trajectory of a projectile would keep it from working correctly.

    For the upcoming week, I plan to fill in the move list for Dark, come up with the moves for the Physical type, and do a final pass to see if there's anything in the design-by-matrix that still needs to be filled in. After that, it's on to making the first Nimsters, building out an in-game map, and discovering that the game is unsalvagably unfun and throwing the whole project in the garbage. :D

    (Nah, however it turns out, I am going to take this project to a completed state, even if that completed state is "text adventure that runs in the console". Nimsters's purpose remains "to give me something I can use to learn other programming languages by porting it to them".)

    My favorite musical instrument is the air-raid siren.
    GlalIanatorDisruptedCapitalist
  • GlalGlal AiredaleRegistered User regular
    Godot 4.0 is out, so time for me to start messing with it once more! There are still edges to polish on this release (some of the example demos not working because API names had changed since they were updated/written is pretty funny), but I'm fine with that for what I want from it and I like what I'm seeing from improvements to the physics engine.

    My last go at making a game in it ended predictably, that's to say I fell down the hole of dynamic character generation, animation, animation blending logic and building UI stuff because "well I want it to look interesting" and "I think I'll need this" with little else to show for it once it was done. This time my interest is much more focused on getting the core working, and figuring out rudimentary AI pathfinding in a 2d platformer space, so that should be fun. I'll deal with visuals when it feels like there's something there.

    DisruptedCapitalist
  • KupiKupi Registered User regular
    Kupi's Weekly Friday Game Dev Status Report

    Last week's goal was to finish the Dark type and provision the Physical type in its entirety. While everything after "After that," hasn't been started yet, I'm pleased to report that Nimsters now has a complete move list (for those moves which are not unique to specific Nimsters).

    The Physical type is the equivalent of Pokemon's "Normal" type, being those moves which have no particular elemental affinity and which is everyone's secondary type. As such, I treated it differently from the other types. For all of the other types, I (despite my protestations to the contrary) did a design-by-matrix in the following pattern:

    - Two families of Contact attacks consisting of one "entry-level" move (low stamina requirement and broad usability, but low effect) and two "advanced" moves (one or both of high stamina usage and narrower usability, and closer to baseline level of effect). Note that "attack" can be a move that only causes a status effect; the definition of "attack" is "immediately causes harm to the opponent".
    - Two families of Projectile attacks, arranged similarly.
    - Two families of Blast attacks, arranged similarly.
    - Three Guard moves, which have no other "root" effect than to establish a guard effect of some kind.
    - Three Claim moves, which place an ongoing effect triggered by landing on the number they're used on.
    - Three Support moves, which generally 1) raise the damage of moves of that type by some amount for some number of turns, and 2) have some other beneficial effect for the user.
    - Three Star moves, which can behave like any of the previous categories but can only be used on the Star. The balance function allows Star moves to be especially powerful because the Star is a rare space.
    - Three X moves, which can behave like any of the previous categories but can only be used on the X. The balance function assigns X moves a lower permissible power level, because they allow you to act on the space that normally causes you to lose your turn. Most X moves have some kind of drawback like recoil or inflicting a status effect on the user.

    For the Physical type, I extended the above matrix in the following ways:
    - Each attack type has three families, with one entry-level move and three advanced moves.
    - The Guard category includes some "entry-level" guards that do nothing but deflect specific types of attacks (instead of countering), plus a few "proper" guard moves themed after the attack families.
    - The Support moves, instead of raising the power of Physical-type moves, raise the power of moves of a specific type (as in, Contact, Projectile, or Blast).
    - All other categories have five moves instead of three.
    - Except for Claims, which don't exist. Claims are flavored as battlefield effects, and though I was willing to stretch that to the breaking point with the Force type (another punchy-touchy type in theme), I don't see how you can make your body extend to the battlefield.

    The attack families for Physical are:
    - Contact: Striking, Tackles/Charges, and Bites
    - "Projectile": Claws, Jumping and Spinning, Slaps/Whips
    - Blast: Sound, Voice, Intimidation By Way of Bodily Posture

    So, having made a backup of all my work so far, the agenda for this week Nimsters-wise is to revise the Nimster species editor, possibly coming up with a way to simply assign a set of moves to a Nimster and automate the arrangement of what levels they're learned at, and... well, honestly, I think that's enough of a project for one week, so that's all I'm going to write in. See you then!

    My favorite musical instrument is the air-raid siren.
    IanatorGlalDisruptedCapitalist
  • KupiKupi Registered User regular
    Kupi's Weekly Friday Game Dev Status Report

    Turns out that when you restrain yourself in terms of scope, you hit your targets. This week I set out to update the Nimster species editor to accelerate the process of creating species data (the data that, in Pokemon terms, every Bulbasaur shares which makes it distinct from a Squirtle).

    The biggest effort this week went into the learnset tooling. Every Nimster species has a set of moves that it learns at given experience levels. This is expressed as a lookup table from the move's name to the level that the move is learned at. In a previous rendition of the Nimster species editor, there was a control to manually assign levels to move names in the lookup table, with a button to "smear" the entries across a certain level range while retaining their current ordering. I've decided to automate this even further by creating a sort order that you can apply to an arbitrary set of moves, obviating the need for any intervention by the user in terms of ordering the moves in the set. To put it another way, as the designer all I have to do is pick which moves I want any given Nimster species to learn and between which levels (which 99% of the time will be the default of whatever range I settle on for the game's experience curve), and the tool automatically orders and spaces them out.

    A digression: this entailed creating a LINQ extension method for reordering a sequence. What I needed, for reasons I'll explain in a moment, was the ability to interleave the elements of an arbitrary number of categories with one another to produce an output sequence. I wound up calling this a "Rotational Sequence" and while I'm not happy with the name it's also not leaving my codebase so shrug. A rotational sequence is created by doing the following:

    1. Separate the input sequence into groups based on the group selector.
    2. Assign a sort value to each element of each group equal to ([its index in the group] + 1) / ([the number of elements in the group] + 1)
    3. Merge the groups back together and sort by the elements' sort values, breaking ties by preferring elements from the group with the largest number of elements originally.

    Here's a few examples of what sort values the elements of a group wind up with given a particular count of elements in the group:
    1 element: .5
    2 elements: .333, .666
    3 elements: .25, .5, .75
    4 elements: .2, .4, .6, .8

    So let's say we're performing a rotational sequencing on three groups, A, B, and C, with group A having four members, group B having three, and group C having two. Driving the sort with the sort values above (and noting that I've conveniently chosen an example with no ties to break), groupwise the output sequence looks like:

    ABCABACBA

    So you can sort of see visually how each group is nicely spread out through the whole range of the sequence.

    There are layers and layers of sort ordering, but in brief this is how Nimster moves are prioritized for learning:

    1. As an initial special case, the first entry-level attack according to the ordering is assigned to the minimum of the level range (level 0) and removed from the set. Likewise, the first entry-level Guard (of which all are Physical-type by design) is assigned to the minimum of the level range and removed from the set. This guarantees that every Nimster always has at least one attack and a basic guard, if any exist in its learnset.
    2. Attacks and Non-Attacks are rotationally sequenced.
    3. Within the category of "Attacks", all entry-level attacks must be learned before any advanced attacks are learned.
    4. Within the category of "Non-Attacks", Specials and Non-Specials are rotationally sequenced. ("Specials" are moves that can be used on the X or the Star. Non-Specials are not Specials.)
    5. Within all the categories named above, moves learned are rotationally sequenced based on move type. Physical moves are learned first, then moves matching the Nimster's first type, then moves matching the Nimster's second type.
    6. Within a given type, moves are rotationally sequenced based on which attack stat they use, preferring, in order, moves with no attack stat, moves that match the Nimster's highest attacking stat, then middle, then worst.
    7. Attack moves are rotationally sequenced by family.
    8. Moves with lower stamina costs are learned before moves with higher stamina cost, assuming all that rotational sequencing didn't somehow produce a preference.
    9. After that fucking whatever man I think they're just output in the order they come in.

    Unfortunately, submitting all of this to automation means that I don't have a lever for manual overrides such that, for example, Bubby the bubble-puppy Nimster might have the Bubble Shield guard move buried somewhere in the mid-40s instead of learning it as its first same-type non-attack move, but it's a small price to pay for not having to manually snap together everything.

    After all that got done, I revised a bit of how Nimster evolution Transformations are handled in the Nimster editor, making the same changes I made to the Move editor to enable defining collections of polymorphic objects through the UI instead of having to use the weird-ass token parsing system I'd originally designed for the purpose. There's a utility in there for creating new Nimster species from within the Nimster species editor itself, so, for example, Sogidogi doesn't have to exist before I can tell the editor that Bubby should transform into it-- I can click a button to say "Bubby transforms into Sogidogi, which I want you to create now". And there's also a button for "this Nimster's stats should be [1.5x / 2x] its base form" so I can make the later forms "the one it comes from but better" with one click.

    For the coming week, I'm going to dive into the list of Nimster ideas I've been keeping as they occur to me and start creating the game's monster list.

    Have a freebie as I go:

    The Static Orb Nimster, Balloohm (Electric/Air).
    And its transformed version, Zappelin.

    My favorite musical instrument is the air-raid siren.
    IanatorHandkorDisruptedCapitalist
  • KupiKupi Registered User regular
    Kupi's Weekly Friday Game Dev Status Report

    Didn't wanna. Still did, a bit.

    Picked a few bugs out of the move-spreading implementation after trying to put together legitimate movesets and seeing a few "why is that getting learned there?!" cases that led to me finding that I'd used a ThenByDescending when I meant ThenBy. Also realized that my create-transformation-of-this-Nimster button wasn't carrying over data like treat flavor preferences, so I patched that up.

    In the process of putting the token Fire/Water/Plant starter Nimsters together, I realized that I had to make a design decision when it comes to the power levels of the various last-form Nimsters, and didn't immediately come up with an answer that I liked. The competitive ideal (suggesting that this game would ever have a competitive mode) would be to have every final-form Nimster be at least nodded to for viability. However, Nimsters, like Pokemon, is also a single-player RPG, which suggests a slow increase in power over time. Rattata evolves into Raticate at level 28 (I think?), and while Raticate is stronger than a lot of stuff you can get at that point in the game, absolutely nobody has ever used Raticate in a serious competitive setting. Because it's one of those monsters you grow past, by design.

    While it would be nice to have every last-form Nimster be useful, I think ultimately Nimsters is more of a single-player experience... plus, part of the narrative of the Route 1 trash mammal (Squarrel) is that it either turns into something that got spoonfed its victories (Champmunk) or got beaten so often it learned to run away as a primary strategy (Retrat), so I guess just typing this up got me to make a decision.

    I have another problem, though: my ideas for Nimsters are wildly type-imbalanced. Psychic, Ice, Force, and Light in particular have a paucity of monsters in my ideas pile. That'll take some dedicated brainstorming sessions to make up.

    Some excerpts from the description blocks of the starter monsters:

    Bubby: This small puppy looks like it fled the bathtub after being shampooed. However, its fur is just like that.
    Sogidogi: This small terrier looks like it just got sprayed with a hose. However, its fur is just like that.
    Pouddle: This tall, thin dog looks like someone shaved it and then covered it in bubble bath. However, its fur is just like that.
    Briqitten: This small cat is so charcoal-black that it's impossible to make out its contours. It's easier to see when it covers itself in flame, which is any time it wants.
    Cougash: Because it is perpetually on fire, this cat leaves a trail of ashes in its wake.
    Tigurn: This towering feline loves the taste of raw meat. However, everything it catches winds up well-done.
    Bunja: Though this small green ball of fluff looks cute, it is both willing and able to kill you if need be.
    Ronibble: In the pursuit of power, it took up the vegetable weapon known as the Carrotana. However, its technique is unrefined.
    Samurabbit: Endless training has allowed it to unlock the power of Bunshido. It wields the Carrotana in endless pursuit of justice.

    My favorite musical instrument is the air-raid siren.
    GlalIanatordipuc4life
  • GlalGlal AiredaleRegistered User regular
    I'm working on a game that's a mix of platformer and town builder. No combat, the core concepts are exploration, community and cooperation, so I want as many mechanics as possible to build on those.

    For the platformer bit I've created a base platforming entity (handling movement, animations, etc), then extended that to a variant controlled by the player, and another to be controlled by the computer, letting me have a bunch of followers running around with you that can behave just like the player. Instead of implementing a full navigation AI that would require me to have a step that analyses the level and creates appropriate navigation points (I looked into 2d platformer navigation meshes and they're a lot) I went through a few iterations to settle on what feels Good Enough:
    - if the player is further than X away, walk in their direction
    - if the player jumped then create a Jump node that stores their motion vectors
    - if the player is in the air then create Air nodes that likewise store those vectors (to follow air movement, and also edge drops)
    - if there are nodes present then, instead of the player, move towards the next node instead, removing it once processed

    It looks a bit silly if the player does weird air wiggles while dropping, as they're replicated exactly, but necessary otherwise there's no guarantee that an air maneuver the player performed to avoid something won't get mucked up (they are affected by regular physics, same as the player).

    I then put in some additional logic to remove extraneous nodes (the player walking over their own Jump node will remove it and all the following nodes, on the assumption that the jump was not necessary, and moving through the follower will remove any existing nodes for the same reason) and that's good enough for now. Oh, and a "crap, we're clearly desynced, just teleport to player/next node" failsafe, nothing worse than escort NPCs getting stuck.
    I give the followers a random tint when they're spawned, so they can be distinguished, and the same for their nodes.
    x9oa9r8q9xdh.jpg

    With that done I forced myself to put Godot down and figure out what my main loop and mechanics are going to be- what the intended progression is, how the core concepts feed into that, etc.
    I spent the past week just writing down ideas, refining them, discarding or reworking them, etc., but I feel it's necessary, I don't want to put time developing something that isn't actually interesting/fun, doesn't feel like it works for the game, or building levels that will get scrapped. As I'm not making a game that's exactly mirroring a proven concept I can't directly copy mechanics and feel confident they'll work. Fingers crossed I guess!

    Prototypes will show if it's actually fun or not, but hopefully this way I'm minimising the rework necessary when things don't pan out.

    I was watching the two videos the developer of Patch Quest made about their process ("How NOT to make an indie game" on them spending years working on something that never cohered, and "How I Made A Game - from Start to Finish!" for the follow up they actually completed) and I do not want to go down the road of the former.

    KupiIanatordipuc4lifeasofyeunAndy Joe
Sign In or Register to comment.