Bring back a way for easy access to scene variables in extensions

A recent update added extension variables, but it also removed the ability to directly change scene variables from our extensions. I think that it was an extremely useful feature, especially for extensions that are only planned to be used in 1 project.


Being able to compile big chunks of code and clumsy calculations for some of the in-game numbers, and also being able to change them for your whole project from one menu is great. Extensions dont necessarily have to be globally usable.

I suggest making an expression like SceneVariable() that would allow to use scene variables in extensions, and also making “for each element of a structure or array” work with scene variables inside of the extensions. And, of course, make the “external variables” thing work in general

I think the new feature of extension variables is great but we dont have to remove previous functionality.

4 Likes

To my knowledge, nothing has been removed. What is your issue exactly?

I believe they are needing to access the in-scene specific or game global variables from within an extension rather than just the extensions variables. If there is still a way to do this, I cannot find it either.

Even if it’s not recommended to use it, the exact same system has been kept for compatibility and the old actions and conditions are still listed to allow users to maintain old extensions (all extensions from the repository have been migrated to the new way, so it’s something that should disappear at some point).

To communicate with scene events, expressions and parameters must be used.

I haven’t checked since the most recent release, but I wasn’t able to find the external variables section when I checked a few weeks back, even when searching for external. I wonder if that’s related to OPs issue?

Edit: I just checked and am showing the external variable options are now available in search on the same extension I couldn’t before.

Separately; I would caution against removing the external variables as a concept, specifically for project-specific behaviors or functions. There is definitely going to be need to interact with game-level globals (and sometimes scene variables) for a lot of internal-use only functions.

E.g.
Control mapping will need to to be a project global variable for ensuring the correct button prompts are displayed throughout the game, but then you’ll need those same mappings in your control extension (for both keyboard/gamepad/touch) so you don’t have to put Or statements throughout your main event sheets.

You could create expressions and actions to present that information back and forth, but that is a lot of extra effort for no gain, and only serves to bloat the event sheet and source code.

There are a lot of potential ways around this scenario, but most will be less efficient than just being able to directly interact with external variables. Those will always be project specific though, and I strongly agree that extensions for the engine/in the main list should not have externals.

1 Like

I guess you didn’t find it before because you didn’t have the exact labels in mind, but nothing has changed from when the new variable system was introduced months ago.

The big issue is that you need to use:

  • Variable(), VariableString(), GlobalVariable() or GlobalVariableString() expressions
  • 8 actions instead of the unified one
  • 8 conditions instead of the unified one

It’s really different to the new way variables are used know. It will confuse users when they search for the action to change a variable value and if they don’t choose the right one, I wish them good luck to understand why it doesn’t work like it should.

I wonder if it could be similarly consolidated into a single ExternalV() or ExtVariable() expression to make it closer to how the new variable system works. It’d still require stuff to be predeclared, and let folks utilize logic as they’d expect, but have it in a way that still lets it be identifiable as external.

1 Like

yes those exist but there is no way i found to easily call them inside expressions

2 Likes

It still uses the old way with Variable() and GlobalVariable().

If I understand correctly you have:

  • a condition “The Action button is pressed”
  • that uses a structure variable like this: ButtonNames[Device][Action]

You can expose the structure values with something like this:

  • action “Change the button name for device: Device and action: Action to Value”
  • expression InputMapper::ButtonName(Device, Action)

The parameters names are good reminders of the meaning of each level of the structure. Here, it’s easy to keep track, but imagine a similar use-case where the structure has more than 4 levels.

And, you can go even further by:

  • Switching between Xbox and PlayStation button names automatically
    • always storing Xbox names values in the variable
    • automatically mapping the names given by the expression to PlayStation ones if a PlayStation controller is plugged
  • Handle presets with an action “Change the mapping of device: Device to preset: PresetName”

The automatic switching would be impossible to do if the main events were using the variable directly.

Unless I’m misunderstanding your method, I would now have to pass through the bound button/key variable into the function every time I remap, when I’m saving/loading settings, or when I’m resetting defaults, correct?

If so, that’s still more events/complexity than what I have to do today by directly accessing the global variables.

I have a lot of detail on my current setup below if you want to look through it, but the above question is really the more important part as it’s a noticeable amount of extra effort when rebinding.

Also note as the Gamepad extension automatically converts button prompts between controller types (even if you’re having it check for XBOX A, if you have a PSX gamepad connected it’ll successfully check for Cross), I don’t have to do much (if anything) to change/automatically switch between gamepad types as the gamepad extension already converts buttons between controllers.


Big grouping of events below. 
Putting this here as a separator to  allow spacing for the above question

Consolidation of conditions into a function condition

  • This is the “confirm is pressed or held” condition’s events:
  • Here’s the resulting condition in the event sheet
    image
  • This is done for each button (confirm, cancel, menu, up/down/left/right, special attack, etc).

Button prompts:

  • Here’s the entire event for keeping the button prompts updated.This single event handles all of the animation updates regardless of keyboard or gamepad or type of gamepad. (no conditions)
    image

  • The button prompt object is a single object with every possible gamepad button in it, and they’re named the same except prefixed by PS4 or XBOX. A, so I don’t need to do that elsewhere. The animations are just named XBOXA and PS4A. Keyboard bindings include all 100+ keys as animations. e.g. keyT, keyLCTRL, etc.

  • This object has a variable that says which action it is for, used in the above event to pull the right prompt.

  • This event setup means I’m setting gamepadtype to either “key” or “xbox” or “psx”, which is the prefix on those animations in the object. It’s set up in such a way that I could also add an “other” for non PS4/XBOX pads and just add another set of prompt animations in the object. Here’s how I swap between keyboard and gamepad, along with xbox and PS4 prompts.


    (The gamepad extension returns the full gamepad name/ID string, so I do some manual string settings)

Saving, binding, and restoring defaults

  • For saving, I just need to save the single controls structure (or the parent settings structure, or top level gamedata structure), which is one event.

  • Loading is the same, one event.

  • For restoring defaults I have a separate global variable structure copy of the original default bindings that I can just override the GameData controls with via one event.

  • For rebinding, all I have to do is the child in the global structure for the type of input (gamepad or keyboard). If I’m rebinding it then applies to all button prompt objects for that action, and applies to the function automatically, as they’re all working off the same variables. While the conditions can be complex (to ensure everything is paused/only one button press counts/etc), only one action is needed which updates that global variable to the last button (or key) pressed.

1 Like

No, these features should be part of the extension.

I would use a parameter with a drop-down list to avoid duplicating the action code for each action.

This should be an expression like: InputMapper::ButtonName(Device, Action)

This should be an action like “Change the input device type to DeviceType”.

These should be actions:

  • “Save input mapping”
  • “Load input mapping”
  • “Restore input mapping to default”
  • “ Change the button name for device: Device and action: Action to Value”

Every access of the variables related to input mapping are inside the extension which mean that the feature is encapsulated.

Sorry for the delay, I didn’t get to dive into this deeper until today. Also apologies for the long post, I put in the info from testing to ensure I’m covering the bases. Please let me know if there’s something I missed or misunderstood.

Both this

and these

do work fine, but they lead to two scenarios, both of which are adding complexity (total event counts both within and without the function, and extra setup for use) to the project.

Scenario 1: Matching your setup above
At the start of the scene:

  • I now have to add an additional action to load input mapping, rather than just doing the full data load into the project global variable.

When rebinding inputs:

  • I now have to add an additional action to rebind inputs rather than just update the single global variable.

When saving settings

  • I have no need to “save” the inputs from within the function, since they’re already matched to global at the start of the game, or when rebound in settings, so there’s no net difference here.

Restoring defaults:

  • I now have to add an additional action in addition to restoring the project global variable defaults.

Within the function:

  • Assuming I’m understanding your recommendation correctly, I set up each button merged into one large condition with a parameter that determines which event branch it goes down (button).
    • Note: This unfortunately seems to run into the same scenario as the main event list IDE running into performance issues with large event lists even though the final result isn’t super large compared to larger event lists that run into this issue, so I’m not sure this is a safe alternative, although it (in theory) should only have to be accessed once or twice while setting it up.
  • This also means that the resulting condition is longer to set up in the project, with having to select button type each time after adding the condition.
    • My main function it’s just add condition > type the button you’re looking for > add.
    • With this new recommended setup, it’s Add condition > search “Button” > set up parameters > add. Which is an extra step for every time you need to use the condition.

Scenario 2: Having the variable passed at the time of use.

  • I also tried having the button mapping in each condition themselves, requiring passing the variable every time “Button is pressed: Device and action: Action with Value”.
  • I know this isn’t your recommendation above, but I wanted to see if I could reduce the quantity of events. This requires less duplication of events on saving/mapping/in the function, but dramatically increases the amount of time to actually set up the conditions in the event sheet every time I need them.

Overall, both of these are more complex:

  • More time to set up (initially)
  • More complex to set up (in use).
  • They both add more events required to the source code
    • Scenario 1 increases the source code by at least one line for each item listed
    • Scenario 2 increases source code by a line every time the condition is used

Summary:
I truly want a better solution if there is one for the engine, and understand your goals in trying to clean things up for extensions for the main extension list. This test seems to reiterate to me that restricting access to global variables makes both sides of the process more complex. (building the function and using it)

  • The extra encapsulation does not seem to simplify my project nor make it easier for me to make new events compared to the original design I was using. Event actions/conditions and number of source code lines is increased, time of set up is increased from setting up the additional actions in the function.
  • The global variable access/set up gave me the same visibility to what was being used within the condition as a parameter would, the only difference this new method does is having to check the event sheet at the time of rebinding, where as the original I could see the variable setups by name in the global variable list.

All of this testing just reiterates to me that Global Variables should be accessible directly via functions (for project specific functions). The actions/conditions should probably be renamed to “External Global Variable”, and the expressions renamed to ExtGlobalVariable() as appropriate. This keeps the simplicity compared to the method you suggested (my primary concern), and keeps usage of functions more in line with the rest of the engines and languages out there (not a concern of mine in this case, but can help with adoption)

For the actual PR, I’d think that for the extension lists, a requirement should be made to not allow any of the above (Conditions/Actions/Expressions for External Global)

(Note, I do think pulling the scene variable access overall makes sense to me. The scene is a different scope than any function, and those shouldn’t be accessible from a different scope.)

1 Like

I’m not sure to understand what you mean by “add an additional action”. Do you mean:

  • declare/implement an action in the extension

or:

  • add an action in the main events

The unified variable action can already be used for:

  • scoped global variables
  • scoped scene variables
  • properties
  • parameters
  • local variables

“project global variables” could be added on top of the list. It’s not an issue technically. It’s just that it would allow what I think are bad practices.

Add an action into the main events. I still have to load/update/etc the project global variables (for purposes of the button prompt objects and other things using that data that aren’t related to the function), so with the new event logic I would still have to save/load those project global variables, and then also apply those variables down to the function.

The buttons can use an expression like this: InputMapper::ButtonName(Device, Action). They don’t need a direct access to the variables.

Either I’m not understanding on where you’d put that, or that is potentially even more time intensive as I’m having to pass the variable every time, manually.

I meant that you don’t need to access the variable directly from the main events. You should add the feature inside the extension, something like this:

I approved the controversial-but-no-breaking-because-you-can-always-copy-paste changes for the actions/conditions that allowed to access scene/global variables from an extension.
If we want to bring something like this back, I would really love a concrete use case/example where passing variable content as parameter is either undoable (because it would require 40+ parameters, if a structure somehow does not work) or would hurt performance or anything else.

The idea of parameters of actions/conditions/expressions is that they are explicit dependencies (when you add one, you declare that your function needs something) whereas global/scene variables are implicit (you don’t see them, they creep around in your no-code until the time you forget them).

Worst case, remember you’re not forced to migrate your global variables, but it should be an interesting exercice to rethink “how can I make my functions of my extension have all the context they need or expose expressions so that the outside world can get the result without sharing implicitly a well-named global variables”.

Hello, I’m a very amateur developer with no technical or under the hood knowledge. I used to make custom extensions for individual projects to streamline my events into neat, one line actions and conditions so my Event sheets didn’t get too cluttered, and also to reduce the chances of making hard-to-find mistakes.
Since we can no longer use extensions to interact with the regular scene and global variables, does this mean it’s no longer possible to do this? I’m aware this streamlining isn’t the primary use of extensions but it was a really useful feature.
I hope I expressed what I was trying to convey clearly.

1 Like