Add Review
Subscribe
Nominate
Submit Media
RSS
Input Processing Theory (11/18)
Pasty- 11/19/2013 03:46 AM
- 1270 views
Hey, y'all.
This update is a doozy, so I'm gonna hide the commits at the bottom for reference and broad-stroke it for you. I realize it's a day earlier than I usually update, but I have to present a progress report for this project tomorrow and I'd like to have all my ducks in a row.
So, quite a lot went into the engine over the last week. Let's take a look.
The ActionQueue
There is a new processing context for ActionQueue actions, and it's one that fixes one of the fundamental problems with the AC ActionQueue system: the inability to process input through the queue.
A history lesson: AC's ActionQueue objects worked in either a LOCAL or a GLOBAL context, and with those contexts came queue behavior. Or: a LOCAL queue needed to be triggered and a GLOBAL queue ran automatically whenever Actions were dropped in. The problem with this system, besides the whole system being constructed essentially backwards, was that it was horrifically prohibitive with regard to LOCAL queue processing. LOCAL queues needed the GLOBAL queue's "permission" to run their own queue, which was a waste of actions and processing time.
So, when it came time to do things like issue attack, item, or skill commands through the LOCAL queue, not only was an extra command needed, but these commands needed to be placed at the front of the queue. And sometimes they could back up if a command was forced too many times, leading to terrible queue interaction bugs that had me pulling on my hair in more than one instance. If issuing one-shot input commands through the ActionQueue was this painful, funneling low-level immediate input like player movement through it was out of the question.
The system eventually became a pile of hacks that used some sort of arcane sorcery to stay operational; if even one part of the underlying queue behavior was changed, the whole system would come crashing down.
Naturally, I wanted to approach this system again and build it in a way that makes sense. Here are the rules:
Input Processing
So, the system overhaul described above was done in preparation to handle everything, including player input processing, through the ActionQueue. Why bother? If it's simpler to do:
...then why not do that?
It's uncontrollable: something like this cannot be referenced in the gmRPG IDE. It's important to note that there are three different facets to this very simple code: there's the gameplay verb ("I want to MOVE UP"), there's the action ("When I decide to MOVE UP, I decrease my y-value by my movement speed value"), and the control set ("In order to MOVE UP, I need to press the UP ARROW on the KEYBOARD."). Of course, I can't harness any of this information if I write the player movement functions like that, so I generalize that code to:
This is better, but, still...
It makes dangerous assumptions: As the developer of this software, I have no right to assume that when the user wants their character to move up, that they want to press the Up Arrow, or W, or NumPad8, or Space or whatever. This is fine, and that's why configurable controls exist, but there's a problem.
As the developer of this software, I cannot make the assumption that when the user wants their character to move up, they actually want their character to move up. What is "up"? Is up "negative 2 pixels"? Is it "positive 2 pixels"? Do they actually want their character to move down, and they're just saying they want to move their character up because they're confused about the nature of their floor and ceiling? So a "command" structure is necessary, and that's where the ActionQueue comes in.
Finally, with this specific project, I've already made clear that I cannot make the assumption that the user knows how to write even simple code like this. So, the user will be able to define gameplay verbs and tie ActionQueue actions to them event-style, and when the IDE auto-generates that GML code, it'll probably look something like this:
This code is now completely generalized: it'll roll through the inputQueue (a SERIAL queue) and it'll be handled immediately. It won't interfere with the ActionQueue, and it'll do what it says it's going to do. A minor drawback to this system is that loading too many Actions into a gameplay verb may have some slowdown effects, but with responsible use and proper safeguards, it'll be fine.
As an aside: I ran tests for both pixel-based movement and grid snapping using this system and they both worked flawlessly at a solid 60FPS. This is here to stay.
Let's move on.
Modifier Stats
This one's easier to explain. There's now a type of stat called a Modifier Stat. This is something you can tack on to a base stat in order to affect it somehow - think buffs/debuffs, equipment, stuff like that.
With the Modifier Stat comes the ability to get a composite of the base stat and any Modifiers that affect it, like so:
GameManager
Most of the GameManager logic has been implemented. I'm starting to focus on serialization of important stuff like whether a puzzle has been completed, whether a cutscene has been seen, that kind of thing. I rolled all these persistent variables into one structure so that they can be easily serialized and saved to a save file.
That's pretty much it. I know this was long, but I hope it was at least marginally interesting. I'm going to start working on the ControlSet and Gameplay Verbs before the next update. Sorry there's not much to play with (or that there's not a demo by now); I really need to assemble the infrastructure before I get to cool things like tile editors and cutscene stagers.
Take care.
Changelog:
This update is a doozy, so I'm gonna hide the commits at the bottom for reference and broad-stroke it for you. I realize it's a day earlier than I usually update, but I have to present a progress report for this project tomorrow and I'd like to have all my ducks in a row.
So, quite a lot went into the engine over the last week. Let's take a look.
The ActionQueue
There is a new processing context for ActionQueue actions, and it's one that fixes one of the fundamental problems with the AC ActionQueue system: the inability to process input through the queue.
A history lesson: AC's ActionQueue objects worked in either a LOCAL or a GLOBAL context, and with those contexts came queue behavior. Or: a LOCAL queue needed to be triggered and a GLOBAL queue ran automatically whenever Actions were dropped in. The problem with this system, besides the whole system being constructed essentially backwards, was that it was horrifically prohibitive with regard to LOCAL queue processing. LOCAL queues needed the GLOBAL queue's "permission" to run their own queue, which was a waste of actions and processing time.
So, when it came time to do things like issue attack, item, or skill commands through the LOCAL queue, not only was an extra command needed, but these commands needed to be placed at the front of the queue. And sometimes they could back up if a command was forced too many times, leading to terrible queue interaction bugs that had me pulling on my hair in more than one instance. If issuing one-shot input commands through the ActionQueue was this painful, funneling low-level immediate input like player movement through it was out of the question.
The system eventually became a pile of hacks that used some sort of arcane sorcery to stay operational; if even one part of the underlying queue behavior was changed, the whole system would come crashing down.
Naturally, I wanted to approach this system again and build it in a way that makes sense. Here are the rules:
- A "Manual" (or LOCAL) queue cannot run without being told to by a QUEUE_RUN command. Fortunately, it can run without asking a global queue for permission because a QUEUE_RUN command placed at the top of a Manual queue auto-runs the queue until it is empty or a QUEUE_BREAK statement is issued due to the new self-starting command whitelist.
- An "Auto-Sequential" or (GLOBAL) queue runs automatically. It processes one action per game step until the queue is emptied.
- Finally, an "Auto-Serial" queue runs automatically, but it processes all actions placed into the queue during that step, something I'll cover in more detail in the next section.
Input Processing
So, the system overhaul described above was done in preparation to handle everything, including player input processing, through the ActionQueue. Why bother? If it's simpler to do:
if(keyboard_check(vk_up))
{
y -= moveSpeed;
}
...
...then why not do that?
It's uncontrollable: something like this cannot be referenced in the gmRPG IDE. It's important to note that there are three different facets to this very simple code: there's the gameplay verb ("I want to MOVE UP"), there's the action ("When I decide to MOVE UP, I decrease my y-value by my movement speed value"), and the control set ("In order to MOVE UP, I need to press the UP ARROW on the KEYBOARD."). Of course, I can't harness any of this information if I write the player movement functions like that, so I generalize that code to:
if(gscrVerbActive("MOVE_UP"))
{
y -= moveSpeed;
}
...
This is better, but, still...
It makes dangerous assumptions: As the developer of this software, I have no right to assume that when the user wants their character to move up, that they want to press the Up Arrow, or W, or NumPad8, or Space or whatever. This is fine, and that's why configurable controls exist, but there's a problem.
As the developer of this software, I cannot make the assumption that when the user wants their character to move up, they actually want their character to move up. What is "up"? Is up "negative 2 pixels"? Is it "positive 2 pixels"? Do they actually want their character to move down, and they're just saying they want to move their character up because they're confused about the nature of their floor and ceiling? So a "command" structure is necessary, and that's where the ActionQueue comes in.
Finally, with this specific project, I've already made clear that I cannot make the assumption that the user knows how to write even simple code like this. So, the user will be able to define gameplay verbs and tie ActionQueue actions to them event-style, and when the IDE auto-generates that GML code, it'll probably look something like this:
if(gscrVerbActive("MOVE_UP"))
{
//Auto-generated by the gmRPG IDE
gscrActionAddToQueue(gscrActionCreate(entity, ENTITY_MOVE, x, y - moveSpeed, moveSpeed), inputQueue);
}
...
This code is now completely generalized: it'll roll through the inputQueue (a SERIAL queue) and it'll be handled immediately. It won't interfere with the ActionQueue, and it'll do what it says it's going to do. A minor drawback to this system is that loading too many Actions into a gameplay verb may have some slowdown effects, but with responsible use and proper safeguards, it'll be fine.
As an aside: I ran tests for both pixel-based movement and grid snapping using this system and they both worked flawlessly at a solid 60FPS. This is here to stay.
Let's move on.
Modifier Stats
This one's easier to explain. There's now a type of stat called a Modifier Stat. This is something you can tack on to a base stat in order to affect it somehow - think buffs/debuffs, equipment, stuff like that.
With the Modifier Stat comes the ability to get a composite of the base stat and any Modifiers that affect it, like so:
var total_atk = gscrActorGetStatComposite("Attack Power", "Equipment Power", "Attack Status");GameManager
Most of the GameManager logic has been implemented. I'm starting to focus on serialization of important stuff like whether a puzzle has been completed, whether a cutscene has been seen, that kind of thing. I rolled all these persistent variables into one structure so that they can be easily serialized and saved to a save file.
That's pretty much it. I know this was long, but I hope it was at least marginally interesting. I'm going to start working on the ControlSet and Gameplay Verbs before the next update. Sorry there's not much to play with (or that there's not a demo by now); I really need to assemble the infrastructure before I get to cool things like tile editors and cutscene stagers.
Take care.
Changelog:
Engine
ActionQueue
Fixed bug in ENTITY_MOVE: it was missing assignment of the behavior script.
Fixed some ActionQueue behavior:
an Action must now have an owner attached when it is created. This uses up an available argument. Up to 14 arguments are now allowed.
Action command scripts will now directly reference the Action owner whenever possible.
Added "null" value to engine (-999999999999), since GM has no null value.
Added ActionQueue commands SYSTEM_CALLSCRIPT and SYSTEM_WAIT. These call a user-defined script and halt the caller's queue for x milliseconds, respectively.
Fixed bugs in all stages of queue and action execution.
Performed pretty intensive testing of the ActionQueue. It took a while to get everything in order, but the two implemented actions work like a charm now. The test consists of a sequence of actions that spell out "Hello World" with SYSTEM_WAIT statements in between each letter.
Added ActionQueue commands QUEUE_BREAK and QUEUE_RUN. These commands manipulate pulling of queue actions from an ActionQueue.
Added the ability for certain queue actions to pull themselves from the queue if at the front, regardless of the queue's autorun setting. This allows queues that don't auto-run to run when issued a QUEUE_RUN command without needing the assistance of an autorunning queue to start themselves.
Added script gscrActionQueueGetFront - this returns the minimum index of the ActionQueue (in an ActionQueue, the minimum is the front).
Fixed a bug in gscrActionQueueRun where repeated QUEUE_RUN commands would speed up queue execution if SYSTEM_WAIT commands were present.
Fixed an error in gscrActionQueueGetFront: queue cannot pull an action when empty.
Added skippable property to gobjAction. If the action is skippable, it will be terminated before execution when a cutscene is being skipped.
Spent part of the day experimenting with rolling immediate player input into the ActionQueue structure. There's a problem with using the main sequential ActionQueue for this - I'll explain below.
Prepared the ActionQueue for immediate player input with the delineation of processing methods. Now, instead of autorun or manual, there's manual, auto-sequential, and auto-serial. Auto-serial is a new type of queue processing that will be used to process player input through the ActionQueue.
Tested all three processing methods with a test RPGEntity object that adds one character per action to a string that eventually spells "Hello World".
Ran input tests with both free pixel movement and grid snapping. Works great.
Fixed jitter issues with movement. As a general rule: all input in a step for an entity should be handled before the execution of that entity's InputQueue. (So, old problem, new clothes.)
Fixed issues with interpolation where ENTITY_MOVE would occasionally push p-values over 1.
Stats
Added support for Modifier Stats. These are stats that modify other stats (for instance, an equipment-HP stat that modifies a base-HP stat)
Added an ID field to the Stat data structure. This stores the stat's constant so that the stat can be easily referenced and compared with other stats and properties.
Modified all scripts that reference the Stat data structure accordingly.
Added scripts to access the ID and "modifies" field of a stat
Added gscrActorStatGetComposite - this returns the composite of a base stat and the provided modifier stats.
Tested gscrActorGetStatComposite. The user can currently make it return a composite where a modifier stat is invoked more than once, but other than that, it works fine.
Added invoke-once protection to gscrActorGetStatComposite
GameManager
Implemented most of the GameManager logic.
Added non-persistent GameManager settings
Made RPGDatabase part of the GameManager flow (finally)
AC's cutscene flags, puzzle booleans, cursor memory variables, and every other persistent variable have been rolled into one central persistent variable map for easy serialization. This map allows the user to set a name and category to reduce the possibility of collisions.
Added the ability for RPGEntities to register themselves with a central map that allows for easy entity ID lookup (a big improvement over AC's scrFindActor, which would loop through every entity in order to find the right one)
Added UserDefined_Menus stub.
General
Fixed gscrEntityGetID to use the new entity lookup map.
Fixed a bug where RPGActor was a child of RPGEntity for the sake of one inheritance property (creation of inventories)
Modified gscrDirectionToCardinal in order to accept eight directions (and to be a bit cleaner)
Added Animated master object. This is the parent of all RPGEntities (and anything else that can be animated) and the child of the Movable master object. As a result, RPGEntity gets all animation system and linear interpolation properties.
Added stub for gscrAnimationStart - this cannot be implemented until there's a means for storing animation sheets and properties.
IDE
Added null value to generateUserConstants.
Added user control for Modifier Stats.
Added GML generation for Modifier Stats.
Edited GML Generator for stats to add ID constant to each stat.
Renamed "actor_id" to "handle" in actor GML generation.
Changed database structure creation to also accommodate database settings.
Added constants for eight directions instead of four
Added QUEUE_ constants
ActionQueue
Fixed bug in ENTITY_MOVE: it was missing assignment of the behavior script.
Fixed some ActionQueue behavior:
an Action must now have an owner attached when it is created. This uses up an available argument. Up to 14 arguments are now allowed.
Action command scripts will now directly reference the Action owner whenever possible.
Added "null" value to engine (-999999999999), since GM has no null value.
Added ActionQueue commands SYSTEM_CALLSCRIPT and SYSTEM_WAIT. These call a user-defined script and halt the caller's queue for x milliseconds, respectively.
Fixed bugs in all stages of queue and action execution.
Performed pretty intensive testing of the ActionQueue. It took a while to get everything in order, but the two implemented actions work like a charm now. The test consists of a sequence of actions that spell out "Hello World" with SYSTEM_WAIT statements in between each letter.
Added ActionQueue commands QUEUE_BREAK and QUEUE_RUN. These commands manipulate pulling of queue actions from an ActionQueue.
Added the ability for certain queue actions to pull themselves from the queue if at the front, regardless of the queue's autorun setting. This allows queues that don't auto-run to run when issued a QUEUE_RUN command without needing the assistance of an autorunning queue to start themselves.
Added script gscrActionQueueGetFront - this returns the minimum index of the ActionQueue (in an ActionQueue, the minimum is the front).
Fixed a bug in gscrActionQueueRun where repeated QUEUE_RUN commands would speed up queue execution if SYSTEM_WAIT commands were present.
Fixed an error in gscrActionQueueGetFront: queue cannot pull an action when empty.
Added skippable property to gobjAction. If the action is skippable, it will be terminated before execution when a cutscene is being skipped.
Spent part of the day experimenting with rolling immediate player input into the ActionQueue structure. There's a problem with using the main sequential ActionQueue for this - I'll explain below.
Prepared the ActionQueue for immediate player input with the delineation of processing methods. Now, instead of autorun or manual, there's manual, auto-sequential, and auto-serial. Auto-serial is a new type of queue processing that will be used to process player input through the ActionQueue.
Tested all three processing methods with a test RPGEntity object that adds one character per action to a string that eventually spells "Hello World".
Ran input tests with both free pixel movement and grid snapping. Works great.
Fixed jitter issues with movement. As a general rule: all input in a step for an entity should be handled before the execution of that entity's InputQueue. (So, old problem, new clothes.)
Fixed issues with interpolation where ENTITY_MOVE would occasionally push p-values over 1.
Stats
Added support for Modifier Stats. These are stats that modify other stats (for instance, an equipment-HP stat that modifies a base-HP stat)
Added an ID field to the Stat data structure. This stores the stat's constant so that the stat can be easily referenced and compared with other stats and properties.
Modified all scripts that reference the Stat data structure accordingly.
Added scripts to access the ID and "modifies" field of a stat
Added gscrActorStatGetComposite - this returns the composite of a base stat and the provided modifier stats.
Tested gscrActorGetStatComposite. The user can currently make it return a composite where a modifier stat is invoked more than once, but other than that, it works fine.
Added invoke-once protection to gscrActorGetStatComposite
GameManager
Implemented most of the GameManager logic.
Added non-persistent GameManager settings
Made RPGDatabase part of the GameManager flow (finally)
AC's cutscene flags, puzzle booleans, cursor memory variables, and every other persistent variable have been rolled into one central persistent variable map for easy serialization. This map allows the user to set a name and category to reduce the possibility of collisions.
Added the ability for RPGEntities to register themselves with a central map that allows for easy entity ID lookup (a big improvement over AC's scrFindActor, which would loop through every entity in order to find the right one)
Added UserDefined_Menus stub.
General
Fixed gscrEntityGetID to use the new entity lookup map.
Fixed a bug where RPGActor was a child of RPGEntity for the sake of one inheritance property (creation of inventories)
Modified gscrDirectionToCardinal in order to accept eight directions (and to be a bit cleaner)
Added Animated master object. This is the parent of all RPGEntities (and anything else that can be animated) and the child of the Movable master object. As a result, RPGEntity gets all animation system and linear interpolation properties.
Added stub for gscrAnimationStart - this cannot be implemented until there's a means for storing animation sheets and properties.
IDE
Added null value to generateUserConstants.
Added user control for Modifier Stats.
Added GML generation for Modifier Stats.
Edited GML Generator for stats to add ID constant to each stat.
Renamed "actor_id" to "handle" in actor GML generation.
Changed database structure creation to also accommodate database settings.
Added constants for eight directions instead of four
Added QUEUE_ constants
Posts 

Pages:
1
too long; read anyway. This was an interesting read even though I didn't truly understand a lot of it.
Pages:
1













