Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

some kinda jackal posted:

My goal right now is just to absorb information. I'm not even sure if I want to try writing a game, but at this point I'm curious enough to just want to know how it's done for its own sake. I'm CERTAIN this HAS to exist in some form.
A short answer that's particularly relevant to old-school dev is that generally your "plot" advancement is broadly represented by a bunch of single-bits, like "has the player pulled this lever yet", which comprise the global game state, and get saved in your tiny save file. If you can represent the playing out of your plot in the form of a number of things that either have or have not been done, then every time the player enters a room, you have an initialize bit that checks the "has that lever been pulled" bit and changes the map to "door is open" or "lever is down" or "bridge exists" or whatever.

You *can* represent things the same way in a modern engine, but people mostly don't. It does make for an easy way to consider your design though, with boolean values representing whether areas are reachable and such. (In platform game terms, "does the player have double jump" functions as a "key", too.)

Adbot
ADBOT LOVES YOU

OneEightHundred
Feb 28, 2008

Soon, we will be unstoppable!

roomforthetuna posted:

A short answer that's particularly relevant to old-school dev is that generally your "plot" advancement is broadly represented by a bunch of single-bits, like "has the player pulled this lever yet", which comprise the global game state, and get saved in your tiny save file. If you can represent the playing out of your plot in the form of a number of things that either have or have not been done, then every time the player enters a room, you have an initialize bit that checks the "has that lever been pulled" bit and changes the map to "door is open" or "lever is down" or "bridge exists" or whatever.
Most RPGs, even old ones, represent primary progress with a progression counter that is only allowed to increase.

This has two very important advantages:

1. It makes skips to specific story points during development way easier.

2. It makes it less likely that the progression tracking will wind up in an unexpected state, which can brick the save file.


So e.g. if there's a drawbridge that can be raised or lowered, then instead of having a bit for it being lowered, you make it only lowered if your story progress is, say, 2 or higher.

But then if for SOME REASON the player finds a way past the drawbridge at story point 1, and they do something on the other side that advances the story to point 3, then all of the stuff that was only supposed to be active during story point 1 will disappear at that point because it's set to only be active until story point 1 ends, instead of leaving you in some bizarre state where stuff designed to only exist during point 1 and point 3 exist at the same time.

(This isn't perfect, but it is WAY more resilient generally.)

OneEightHundred fucked around with this message at 15:45 on May 23, 2024

some kinda jackal
Feb 25, 2003

 
 
Thanks gang! That's all super useful info. Very much agree I'll have a better time starting on something less constrained; This was really me kind of fleshing out the theory of game design, this was just the first big mental hurdle I had to overcome. I'm not married to NES, though selfishly I am doing this to get more comfortable with developing a low level language so I might scale down my aspirations and keep working with the limited resources I have.

As always, appreciate the assist!

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

OneEightHundred posted:

Most RPGs, even old ones, represent primary progress with a progression counter that is only allowed to increase.
Only RPGs that I hate for being gross and linear. :colbert:

Raenir Salazar
Nov 5, 2010

College Slice
LOL, my co-worker face a problem in Unreal engine by default you can't Ray trace (i.e Line Trace by Channel) a Skeletal Mesh (with Per Poly Collision enabled etc etc) that's been scaled; if its translated or rotated you can hit test against its mesh faces fine, but if you scale it you gotta in C++ trick it into thinking you changed its scale in the Editor and clicked save. Largely because Per Poly hit testing on a skinned/skeletal/rigged mesh is unsupported behaviour; Unreal just doesn't think you'll ever need or want the ability to accurately hit test against a animated mesh in circumstances where using the physics assets or collision hulls won't suffice.

In SDF news, my code works (with our project's setup) but I have no idea why it works! Terrific.

In our setup, we have saved to a Render Target the flattened Unwrapped Mesh (how Painting is usually done in Unreal because of some of the above issues) which we pass in as the coordinates for our Sphere Mask node in a different shader (for having more complicated behaviour, this is what our tech artist decided on); which okay, the pixel data of this render target should be the local space coordinates of the interpolated vertices of the unwrapped/flattened/uv mesh.

However when I test this in the preview it's just totally wrong, the sample/test Hit positions I pass in just aren't resulting in a sensible result that I would expect like with my earlier tests in UV or World Space. When I render the result to a Render Target and sample the pixels I have very absurd values, while each pixel is in like a gradient which is what I'd expect, and some values are negative, which I'd expect, the values don't match up with the actual Hit Locations from my Line Trace. Like (-8, 13, 133) would be the hit, presumably in Unreal units (cms so probably relative to the mesh to the world origin) but the values in the same spot on the exported texture might be like 150, 200, -500. So it seems like the rgb values are being encoded from the world space coordinates of the flattened mesh which makes sense, the mesh is flattened out to like a 1024 by 1024 sized plane in the World; but all of this actually works perfectly when its actually used, this doesn't make any sense! It shouldn't work, but it does! But not when I actually LOOK at it and try to test it in a clean environment; quantum-rear end bullshit.

I don't know if its just like, a massive inaccuracy in the pixel values when loaded in GIMP as a result of a maybe nebulous process to export the test result of the material to a texture and saving it for viewing; or if there's a implicit or explicit transformation that's occurring thats transforming the values into something sensible, or something else entirely I dunno; I just decided that this is a BLACK BOX, and thus my only option is to PRAISE THE OMNISSIAH, I HUMBLY BOW IN GRATITUDE TO THE MACHINE SPIRIT; and move on to integrating my SDF optimization to our painting code; which after a silly goof dividing 1/LARGE NUMBER without making it a float first! Finally works! By accident I've discovered that there is in fact a width size limit on Unreal textures and it WILL crash if you exceed it, so I changed my storage of Hit positions from a single long row texture to a square texture that acts as a 2D array of the hits; now I've got basically unlimited storage for the hits, yay!

I think if/when we're past the prototype stage and I can redo this, I'll make it so we're only dealing with positive linear colour values so I can more thoroughly test things and be more confident its working how it's supposed to, because this is silly.

Raenir Salazar
Nov 5, 2010

College Slice
Line SDF Segments have been CONQUERED.

KillHour
Oct 28, 2007


Raenir Salazar posted:

Line SDF Segments have been CONQUERED.

:hfive:

I should add those next...


On my side, I finally implemented a sane way to handle generic configurations, so I no longer have to hardcode everything. It's hideous because I haven't done any styling yet, and I'm going to need to make it collapsible so it doesn't take up half the screen, but it works.



It's all configuration based and serializes correctly too:

Raenir Salazar
Nov 5, 2010

College Slice
I've had to do a surprising amount of bit twiddling in Unreal now; particularly because it makes branchless programming a little easier to think about, but in the custom HLSL node because Unreal Material nodes don't seem to have a bit twiddling built in? There's some sort of bitmask node but I don't know if it does what I want it to do; primarily to check if two vector's are the same; but also do like bitwise OR and bitwise AND operations.

Where like, I wanted do:

code:
if (Flag) {
    return result * weight;
}
else {
    return result;
}


But without branching. Where weight is either 0 or 1 and thus is always 1 when Flag is false, but no change if Flag is 1.

Aaaaaaaand midway through writing this I probably could've just used LERP. :mad:

What I came up with:
code:
return (weight | 1 - Flag) * result;
Based off of the truth table I sketched out, when Flag is true, we default to the value of weight * result; and when it is False, the OneMinus of it makes it that regardless of the value of weight, we always return 1 * result.

But lerp is functionally identical if the input is either only ever 0 or 1. :sigh:

KillHour
Oct 28, 2007


Avoiding branching at all costs is honestly premature/micro optimization unless you've identified that branch as a bottleneck through profiling. If the driver can guarantee a branch will be the same for each unit in a wavefront, it's totally free on a modern desktop GPU.

Raenir Salazar
Nov 5, 2010

College Slice

KillHour posted:

Avoiding branching at all costs is honestly premature/micro optimization unless you've identified that branch as a bottleneck through profiling. If the driver can guarantee a branch will be the same for each unit in a wavefront, it's totally free on a modern desktop GPU.

Yeah I know that but I figured there was probably a reasonable way and I did it out of curiosity.

Nolgthorn
Jan 30, 2001

The pendulum of the mind alternates between sense and nonsense
I figured it was a stylistic choice, statement, or a challenge.

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe
I'm working through how I want "modifiers" to work in my roguelike game. For a reminder, this is a match-3 + tactics combat kind of setup, where the player plays match-3 to generate mana to power their spells:



These would be things like:

- An item that has the passive ability to block the first instance of fire damage you take in each fight.
- A status that causes you to take 1 poison damage each turn
- An enemy that reacts to being hit by water damage by casting a spell back at the attacker

What I'm imagining right now is that, every time some remotely notable action takes place, an event is published. It'll say things like "This unit is attacking this other unit", "this spell is being cast by this unit at this other unit", "this token in the match-3 board was just matched", et cetera. It will include type/category information (MeleeEvent, SpellEvent, TokenMatchedEvent, etc) the parameters for the action (e.g. amount of damage being inflicted, the damage element, the spell being cast, units hit by the spell), and a list of "riders", which are follow-on events that will occur when the first one occurs. Various parts of the code can subscribe to listen for these events, and then can modify them either by changing the parameters, or by adding riders. So for our 3 examples above:

- The passive fire block is achieved by changing the damage-dealt value to 0, and then setting an internal flag on the item to disable it until the next time a StartFightEvent occurs
- The status is achieved by attaching a rider to EndTurnEvent that casts a weak poison damage spell on the unit that has the status
- The enemy reaction is achieved by attaching a rider to the TakeDamageEvent which casts a spell at the unit that inflicted the damage

I would also need to track which bits of code have responded to events already in a given chain, to avoid infinite recursion. For example, if somehow the enemy-reaction ability was triggered by the enemy hurting themselves, then they shouldn't infinitely cast attack spells on themselves. This is mostly to avoid softlocks.

Now for the tricky bit: I want all of these capabilities to be modifiable by changing data files, not by editing my (UnrealEngine) source code. Practically speaking, I believe this means embedding Lua or some other scripting language in the game. So, let's say I have an enemy JSON record that looks like this:
code:
  {
    "dbName": "goblin",
    "displayName": "Goblin",
    "flavorText": "If you're seeing this, I hope like hell you're poking around in my data files, because this little guy isn't supposed to be in any official content.",
    "graphic": "goblin",
    "health": 4,
    "meleeDamage": 4,
    "ai": [
      "ASTAR"
    ]
  },
and I want to add the "reacts to taking damage by casting a spell" capability to this enemy. Conceivably, that could look something like this:

code:
  {
    "dbName": "goblin",
    "displayName": "Goblin",
    "flavorText": "If you're seeing this, I hope like hell you're poking around in my data files, because this little guy isn't supposed to be in any official content.",
    "graphic": "goblin",
    "health": 4,
    "meleeDamage": 4,
    "ai": [
      "ASTAR"
    ],
    "triggers": [
      {
        "filter": "thisTakesDamage",
        "response": {
          "funcName": "castSpellOnDamage",
          "caster": "self",
          "target": "damageOrigin",
          "spellName": "meteor"
        }
      }
    ]
  },
The idea being:
- The "filter" parameter is the name of a Lua function that returns a boolean indicating whether or not the trigger cares about a particular event.
- The "response" parameter provides a Lua function to mutate or attach riders to the event. In this case, we would invoke the Lua function "castSpellOnDamage". It would be handed the origin TakeDamageEvent, as well as the other parameters in that JSON map (the "caster", "target", and "spellName" fields), which it could use to set up its rider.

Then I just need to have some Lua files elsewhere in my data, that define all of these functions, and I need to decide on the signatures for the filter and response functions. I also need to implement an API that the functions can lean on to gain more information about the world and to create side-effects.

Does this sound remotely sane? Any advice in general?

leper khan
Dec 28, 2010
Honest to god thinks Half Life 2 is a bad game. But at least he likes Monster Hunter.

TooMuchAbstraction posted:

I'm working through how I want "modifiers" to work in my roguelike game. For a reminder, this is a match-3 + tactics combat kind of setup, where the player plays match-3 to generate mana to power their spells:



These would be things like:

- An item that has the passive ability to block the first instance of fire damage you take in each fight.
- A status that causes you to take 1 poison damage each turn
- An enemy that reacts to being hit by water damage by casting a spell back at the attacker

What I'm imagining right now is that, every time some remotely notable action takes place, an event is published. It'll say things like "This unit is attacking this other unit", "this spell is being cast by this unit at this other unit", "this token in the match-3 board was just matched", et cetera. It will include type/category information (MeleeEvent, SpellEvent, TokenMatchedEvent, etc) the parameters for the action (e.g. amount of damage being inflicted, the damage element, the spell being cast, units hit by the spell), and a list of "riders", which are follow-on events that will occur when the first one occurs. Various parts of the code can subscribe to listen for these events, and then can modify them either by changing the parameters, or by adding riders. So for our 3 examples above:

- The passive fire block is achieved by changing the damage-dealt value to 0, and then setting an internal flag on the item to disable it until the next time a StartFightEvent occurs
- The status is achieved by attaching a rider to EndTurnEvent that casts a weak poison damage spell on the unit that has the status
- The enemy reaction is achieved by attaching a rider to the TakeDamageEvent which casts a spell at the unit that inflicted the damage

I would also need to track which bits of code have responded to events already in a given chain, to avoid infinite recursion. For example, if somehow the enemy-reaction ability was triggered by the enemy hurting themselves, then they shouldn't infinitely cast attack spells on themselves. This is mostly to avoid softlocks.

Now for the tricky bit: I want all of these capabilities to be modifiable by changing data files, not by editing my (UnrealEngine) source code. Practically speaking, I believe this means embedding Lua or some other scripting language in the game. So, let's say I have an enemy JSON record that looks like this:
code:
  {
    "dbName": "goblin",
    "displayName": "Goblin",
    "flavorText": "If you're seeing this, I hope like hell you're poking around in my data files, because this little guy isn't supposed to be in any official content.",
    "graphic": "goblin",
    "health": 4,
    "meleeDamage": 4,
    "ai": [
      "ASTAR"
    ]
  },
and I want to add the "reacts to taking damage by casting a spell" capability to this enemy. Conceivably, that could look something like this:

code:
  {
    "dbName": "goblin",
    "displayName": "Goblin",
    "flavorText": "If you're seeing this, I hope like hell you're poking around in my data files, because this little guy isn't supposed to be in any official content.",
    "graphic": "goblin",
    "health": 4,
    "meleeDamage": 4,
    "ai": [
      "ASTAR"
    ],
    "triggers": [
      {
        "filter": "thisTakesDamage",
        "response": {
          "funcName": "castSpellOnDamage",
          "caster": "self",
          "target": "damageOrigin",
          "spellName": "meteor"
        }
      }
    ]
  },
The idea being:
- The "filter" parameter is the name of a Lua function that returns a boolean indicating whether or not the trigger cares about a particular event.
- The "response" parameter provides a Lua function to mutate or attach riders to the event. In this case, we would invoke the Lua function "castSpellOnDamage". It would be handed the origin TakeDamageEvent, as well as the other parameters in that JSON map (the "caster", "target", and "spellName" fields), which it could use to set up its rider.

Then I just need to have some Lua files elsewhere in my data, that define all of these functions, and I need to decide on the signatures for the filter and response functions. I also need to implement an API that the functions can lean on to gain more information about the world and to create side-effects.

Does this sound remotely sane? Any advice in general?

i'm not a fan of structuring all ai as event driven through config. i'd instead go with a fixed set of base triggers, ai scripts [in external lang driven from data if you want], and for significant number of effects/types an ECS approach to the logic (though i expect you wont do this at this stage of the project unless you already are). that is: your generic term "funcName" is a bit frightening to me, but without understanding the processing not in a terribly meaningful way.

mechanically, this looks like splitting the ai logic into separate scripts from your unit definitions. which will let you have shared logic between units. you have an "ai" field, but i think this is just movement? i'd give actions similar treatment to allow sharing behavior across units. and allow composing multiple scripts as the definition. potentially parametric scripts, with prefabs [also parametric] to spawn as one of the core actions.

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe

leper khan posted:

i'm not a fan of structuring all ai as event driven through config. i'd instead go with a fixed set of base triggers, ai scripts [in external lang driven from data if you want], and for significant number of effects/types an ECS approach to the logic (though i expect you wont do this at this stage of the project unless you already are). that is: your generic term "funcName" is a bit frightening to me, but without understanding the processing not in a terribly meaningful way.

mechanically, this looks like splitting the ai logic into separate scripts from your unit definitions. which will let you have shared logic between units. you have an "ai" field, but i think this is just movement? i'd give actions similar treatment to allow sharing behavior across units. and allow composing multiple scripts as the definition. potentially parametric scripts, with prefabs [also parametric] to spawn as one of the core actions.

If I understand your concerns, you want the entire logic associated with a given creature to be its own script, possibly calling out to utility functions? I can see how that would make sense, but it would be a substantial overhaul to how the game is architected. For the record, I feel that actual creature AI is pretty much solved at this point. The "ai" block in the goblin's description, where it says "ASTAR" currently, is technically not part of this problem. That's where the creature decides what direct action to take each turn, and I want it to be fairly simple and constrained, so that players can predict what AI units will do. At the moment, they're pretty much just limited to "do nothing", "pathfind to the player", and "cast a spell", and I honestly think that that will suffice for their direct actions each turn.

I don't actually anticipate using reactive triggers on AI units very much, but the potential is there so I felt I should mention it. It's more the kind of thing that could be used to throw some curveballs at the player in rare circumstances.

The real reason I want this stuff is for the player's items and status effects, which are much more central to gameplay. This is going to be a modern roguelike, which means that most of the player's progression in-game is by amassing a collection of passive bonuses from items, and by improving their spell selection. And the status effects are the primary way that the tactical combat interacts with the match-3 combat: spells and attacks can attach statuses to match-3 tokens, and then those statuses are active so long as the token they're associated with remains active. So e.g. you might be faced with "I could match the water and wind mana I need to cast Hurricane, buuuut then I'd take damage from these two poison statuses, so maybe I should match the Earth mana they're attached to so they get cleared?" Or alternately "This Wind token has a shield on it, so I want to avoid matching it for as long as possible because it's reducing the damage I take."

But yes, the intent is that different records in my JSON could mix-and-match bits of logic from the Lua code. Like, everything that triggers off of the owning unit taking damage can use the same "filter" function. Everything that wants to add a spell rider can use the same "response" function, just with different parameters.

Also, just to be clear: I do plan to have a completely fixed set of base triggers, which would be hardcoded in the C++ code. These would be the stepping-off points for calling into the Lua code. Each would be a different class in C++ code, implying a different table structure when the data is handed off to Lua. But they'd all have the ability to trigger riders.

Off the top of my head, they would be:
- Unit takes damage (possibly with a special subtype for "player takes damage")
- Unit melee attacks
- Unit moves
- Spell is cast
- Unit is targeted by spell
- Match-3 token is cleared
- Player gains mana
- Unit is killed
- Unit is pushed
- Player enters/exits a room (unit of combat gameplay)
- Floor is cleared
- Player gains an item
- Player gains a spell

There'll probably be a few more I'll need to add as the game's content gets filled out, but you get the idea, hopefully.

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe
Alright, I'm gonna walk through an example in detail and make sure I understand it. This may not be fully accessible to y'all, for which I apologize; there's context in terms of how the rest of the game works that isn't really worth fully explaining.

The most basic example I can think of is: the player is poisoned. At the end of their turn, they take 1 damage from the poison.

In statuses.json, where data on status effects is defined:
code:
   {
    "dbName": "poison1",
    "displayName": "Poisoned",
    "icon": "POISON",
    "spawnVFX": "PoisonForm",
    "spawnSFX": "PoisonApply",
    "spawnDuration": 1.25,
    "durationTurns": 3,
    "flavorText": "<text style=\"Default\">Take 1</><image id=\"water\"/><text style=\"Default\">/turn</>",
    "triggers": [
      {
        "filter": "playerEndTurn",
        "response": [
          {
            "action": "castSpell",
            "spellName": "poison1",
            "spellCaster": "origin
          },
          {
            "action": "spawnVFX",
            "vfxName": "PoisonForm",
            "vfxPosition": "origin"
          }
        ]
      }
    ]
  }
This presupposes the following Lua functions: playerEndTurn, castSpell, and spawnVFX.

A poison status emblem is attached to one of the tokens in the match-3 board, e.g. as a side-effect of the player having been attacked.

In the C++ code, when TacMap::EndPlayerTurn is invoked, the following happens:
code:
1. Create an EndTurnEvent. This is a subclass of the base Event class, with an internal type of END_TURN. It also records whose turn has ended (i.e. the player).
2. Pass the EndTurnEvent off to the event bus.
3. The event bus asks all registered listeners if they care about the event; if so, they are given the opportunity to mutate it (changing parameters or attaching riders).
4. Each status emblem is a registered listener.
5. The status emblem does something like this:[code]foreach (trigger in triggers) {
  if (trigger->filter->InvokeLua(event)) {
    foreach (var response in trigger->responses) {
      response->InvokeLua(event);
    }
  }
}
6. The "filter" and "response" objects are Lua wrappers, based on the JSON pasted earlier. They know the name of the Lua function they want to invoke, as well as the other JSON parameters like spellName and vfxName.
7. When InvokeLua is called, the C++ code constructs a table containing the event data and other JSON parameters. It calls into the Lua code.
7b. The "filter" Lua code is only responsible for returning a boolean.
7c. The "response" code returns a modified event table which the C++ code uses to modify the C++ event object (so basically I will have serialization/deserialization code for events that translates them to/from Lua tables). The response code can also invoke the C++ API, to generate rider events or cause other side-effects to occur.
8. After all responses have been processed, the event is executed. The EndTurnEvent is a no-op, but e.g. if it were a MeleeEvent, the attacker's attack animation would be performed, and damage would be assigned.
9. Any rider events are then handed to the event handler, to run through the same pipeline. In our example, a CastSpellEvent would be processed, with the "poison1" spell being cast by the player. This is a self-damage spell.

Our Lua code would then look something like this (my syntax is almost certainly off, it's been years since I wrote Lua):
code:
-- filters.lua
function playerEndTurn(event)
  -- All events have a `type` which is a stringification of an enum
  -- Should correspond 1:1 to the event's subclass in C++, which in turn
  -- determines how the event is serialized to a Lua table.
  -- In this case, because the type is "END_TURN" we know that it must have
  -- an "originName" which is the name of the unit whose turn just ended.
  if event.type == "END_TURN" and event.originName == "player"
    return true
  end
  return false
end

-- responses.lua (assuming we don't decide to further subdivide the lua files)
-- params is the extra JSON data attached to the response
function castSpell(event, params)
  -- Invoke a C++ API function to create a CastSpellEvent and attach it to `event` as a rider.
  AttachCastSpellRider(event, params.spellName, params.spellCaster)
end

function spawnVFX(event, params)
  -- Invoke a C++ API function to spawn a Niagara particle effect immediately.
  SpawnVFX(params.vfxName, params.vfxOrigin)
end

Raenir Salazar
Nov 5, 2010

College Slice
I think it could be fun if there was an immortal snail that slowly (like *really* slowly) chases the player across the entire course of the game, and if it touches you you die instantly.

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe

Raenir Salazar posted:

I think it could be fun if there was an immortal snail that slowly (like *really* slowly) chases the player across the entire course of the game, and if it touches you you die instantly.

If you're talking about my game, that's unlikely to be relevant, because I have other forcing functions to push the player forwards. In particular, the character automatically walks towards their destination each turn if possible. If their path is blocked, it's presumably by an enemy, who is using their turns to hurt the player, so you can't just stall out indefinitely.

Raenir Salazar
Nov 5, 2010

College Slice

TooMuchAbstraction posted:

If you're talking about my game, that's unlikely to be relevant, because I have other forcing functions to push the player forwards. In particular, the character automatically walks towards their destination each turn if possible. If their path is blocked, it's presumably by an enemy, who is using their turns to hurt the player, so you can't just stall out indefinitely.

I was originally going to joke about giving a goblin a glock but then I remembered the famous internet meme: https://www.youtube.com/watch?v=HINYhLtaaxc

OneEightHundred
Feb 28, 2008

Soon, we will be unstoppable!

KillHour posted:

Avoiding branching at all costs is honestly premature/micro optimization unless you've identified that branch as a bottleneck through profiling. If the driver can guarantee a branch will be the same for each unit in a wavefront, it's totally free on a modern desktop GPU.
I think they have conditional-set or similar type ops too so any code that just selects between two values (which could be 1 and weight in this case) is pretty cheap.

Tunicate
May 15, 2012

Raenir Salazar posted:

I've had to do a surprising amount of bit twiddling in Unreal now; particularly because it makes branchless programming a little easier to think about, but in the custom HLSL node because Unreal Material nodes don't seem to have a bit twiddling built in? There's some sort of bitmask node but I don't know if it does what I want it to do; primarily to check if two vector's are the same; but also do like bitwise OR and bitwise AND operations.

Where like, I wanted do:

code:
if (Flag) {
    return result * weight;
}
else {
    return result;
}


But without branching. Where weight is either 0 or 1 and thus is always 1 when Flag is false, but no change if Flag is 1.

Aaaaaaaand midway through writing this I probably could've just used LERP. :mad:

What I came up with:
code:
return (weight | 1 - Flag) * result;
Based off of the truth table I sketched out, when Flag is true, we default to the value of weight * result; and when it is False, the OneMinus of it makes it that regardless of the value of weight, we always return 1 * result.

But lerp is functionally identical if the input is either only ever 0 or 1. :sigh:
Too readable. Try

Flag = (1- weight)*flag
Return result- result*flag

xzzy
Mar 5, 2009

Needs a couple lambdas for true next level code

KillHour
Oct 28, 2007


Tunicate posted:

Too readable. Try

Flag = (1- weight)*flag
Return result- result*flag

It's shader code, so all the variable names should be single letters and completely meaningless.

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe
I have Lua integration working in my game, as a very crude proof-of-concept.

1. Download the Lua 5.4.6 source code and drop it into my game directly
2. Write this code:

code:
// All content copyright 2023 TMA Games LLC. All rights reserved.


#include "LuaWrap/LuaWrapper.h"
#include "lua-5.4.6/lua.hpp"

void ULuaWrapper::Init() {
	lua_State* L = luaL_newstate();
	luaopen_math(L);
	luaopen_string(L);
	luaopen_table(L);

	lua_pushcfunction(L, ULuaWrapper::Log);
	lua_setglobal(L, "Log");
	const auto code = "Log('Hello, world')";
	if (luaL_loadstring(L, code) != LUA_OK) {
		UE_LOG(LogTemp, Error, TEXT("Invalid lua code!"));
		return;
	}
	const auto error = lua_pcall(L, 0, 0, 0);
	if (error != LUA_OK) {
		const auto message = lua_tostring(L, -1);
		UE_LOG(LogTemp, Error, TEXT("Failed to call lua code: error %d, message `%hs`!"), error, message);
		return;
	}
	lua_pop(L, lua_gettop(L));
	lua_close(L);
}

int ULuaWrapper::Log(lua_State* state) {
	const auto message = luaL_checkstring(state, 1);
	UE_LOG(LogTemp, Error, TEXT("Lua message: `%hs`"), message);
	return 0;
}
3. Run the "Init" function, behold logged message

Next steps: figuring out how to get it to run a specific function from a file full of Lua functions, and passing more complicated state to the Lua code.

Magnetic North
Dec 15, 2008

Beware the Forest's Mushrooms
I've put it off for a while, but I am considering getting into purely hobbyist video game development. I am a professional software developer who has worked mostly in C#, but I don't want to use Unity because of their assorted bullshittery. I don't have to write it in C#, and I'm unsure if I'd prefer to learn something new or just exercise my existing skills. I'll probably just do whichever ends up working for other reasons.

What I have in mind is fully 2D and from a cursory search, it sounds like I should use GameMaker since it seems to be the best for 2D stuff, more so than Godot (which I might normally go to because I like to try and support permissive software licenses). It looks like Adventure Game Studio has a way to write extensions in C# but I've heard it's a bit janky to use AGS if you haven't been using it for a long while. There's no real OP to check for this type of basic question, so I'm curious to see what people suggest.

leper khan
Dec 28, 2010
Honest to god thinks Half Life 2 is a bad game. But at least he likes Monster Hunter.

Magnetic North posted:

I've put it off for a while, but I am considering getting into purely hobbyist video game development. I am a professional software developer who has worked mostly in C#, but I don't want to use Unity because of their assorted bullshittery. I don't have to write it in C#, and I'm unsure if I'd prefer to learn something new or just exercise my existing skills. I'll probably just do whichever ends up working for other reasons.

What I have in mind is fully 2D and from a cursory search, it sounds like I should use GameMaker since it seems to be the best for 2D stuff, more so than Godot (which I might normally go to because I like to try and support permissive software licenses). It looks like Adventure Game Studio has a way to write extensions in C# but I've heard it's a bit janky to use AGS if you haven't been using it for a long while. There's no real OP to check for this type of basic question, so I'm curious to see what people suggest.

https://www.stride3d.net/

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe
Go with Godot. It's popular, which means better community support (e.g. searching "how do I X" is likely to turn up results). It's perfectly capable of doing good 2D games in any genre. It supports C#. And it's actively being developed, so the scope of potential projects you can do will increase with time (especially if you decide to dabble in 3D games, where the only realistic alternatives are Unity and Unreal).

Pollyanna
Mar 5, 2005

Milk's on them.


Godot is Godotnough.

Moskau
Feb 17, 2011

HEY GUYS DON'T YOU LOVE ANIME?! I LOVE ANIME SO MUCH ESPECIALLY ALL THE PANTY SHOTS AND FAN SERVICE AND MOE MOE MOE! I JUST CAN'T GET ENOUGH!

Magnetic North posted:

I've put it off for a while, but I am considering getting into purely hobbyist video game development. I am a professional software developer who has worked mostly in C#, but I don't want to use Unity because of their assorted bullshittery. I don't have to write it in C#, and I'm unsure if I'd prefer to learn something new or just exercise my existing skills. I'll probably just do whichever ends up working for other reasons.

What I have in mind is fully 2D and from a cursory search, it sounds like I should use GameMaker since it seems to be the best for 2D stuff, more so than Godot (which I might normally go to because I like to try and support permissive software licenses). It looks like Adventure Game Studio has a way to write extensions in C# but I've heard it's a bit janky to use AGS if you haven't been using it for a long while. There's no real OP to check for this type of basic question, so I'm curious to see what people suggest.

GameMaker is quite capable for 2D even though the language is not necessarily the greatest. If you've done any programming it's quite easy to pick up and the concepts are quite easy to internalize, and it has a lot of support and tutorials available.

Personally I didn't like Godot very much when I tried it, and I'm big on C# development in my adult job too.

KillHour
Oct 28, 2007


Decided to lean extra hard into the glitchy retro look today.

(Epilepsy Warning)
https://youtu.be/uNyWQUZ1vUs
https://youtu.be/CEZnk0N9UAU
https://youtu.be/03V-VkvEkUc

KillHour fucked around with this message at 06:28 on Jun 3, 2024

Raenir Salazar
Nov 5, 2010

College Slice

KillHour posted:

Decided to lean extra hard into the glitchy retro look today.

(Epilepsy Warning)
https://youtu.be/uNyWQUZ1vUs
https://youtu.be/CEZnk0N9UAU
https://youtu.be/03V-VkvEkUc

Nice, kinda looks like a music visualizer! :)

KillHour
Oct 28, 2007


Raenir Salazar posted:

Nice, kinda looks like a music visualizer! :)

Maybe it's early and I don't get the joke but... that's exactly what it is?

Edit: Oh, I see - I almost always share videos in the other game dev thread, so you might not have seen them. It's a VJ tool I've been building for the last ~3 years.

quiggy
Aug 7, 2010

[in Russian] Oof.


Other game dev thread?

KillHour
Oct 28, 2007


quiggy posted:

Other game dev thread?

There are people in this thread who don't know about the other thread? I figured they were basically interchangeable at this point. :psyduck:

https://forums.somethingawful.com/showthread.php?threadid=3506853

Chillmatic
Jul 25, 2003

always seeking to survive and flourish
Had no idea either lol

Raenir Salazar
Nov 5, 2010

College Slice

KillHour posted:

Maybe it's early and I don't get the joke but... that's exactly what it is?

Edit: Oh, I see - I almost always share videos in the other game dev thread, so you might not have seen them. It's a VJ tool I've been building for the last ~3 years.

Yeah I have the other thread bookmarked but I've almost never been there, so I didn't know that was the intention :)

quiggy
Aug 7, 2010

[in Russian] Oof.


Nope, had no idea. In my defense I'm merely an out-of-work gamedev trying to find any gig in this hell industry that doesn't suck while trying (and failing) to wrangle my ADHD enough to make something of my own, so I don't actually post much in this thread.

Adbot
ADBOT LOVES YOU

KillHour
Oct 28, 2007


Oh boy, I have a whole new audience to pester with my insane experiments and delirious ramblings about signed distance fields!

(FYI: Practically everything I post is an existential threat to anyone with photosensitive epilepsy)
https://www.youtube.com/watch?v=QiCtOeEN9SM
https://www.youtube.com/watch?v=TBzt5f8fHCc
https://www.youtube.com/watch?v=sUDg0K9oO-g
https://www.youtube.com/watch?v=JYlnQL1p4Ns

KillHour fucked around with this message at 16:17 on Jun 3, 2024

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply