See what? Setting one event to multiple timers doesn’t mean that a new timer is created each time you call SetTimerByEvent node. Is there a certain way to check if timer handles from successive calls are not the same?
Yes, I also played with this. So it doesn’t reset a timer, but cancels the previous timer and sets a new one… Is it a memory leak? Will the number of timers just bloat forever? Or they are just garbage-collected when no variable holds them? If it’s not a memory leak, then my blueprint is all right.
Anyway, my blueprint should work. After all old timers are cancelled, the new one should fire, and the event should be called. Why it isn’t?
No.
It is a little strange. But you can get the same effect with a retriggerable delay…
Ok, it’s a pity that I didn’t understand what’s wrong with timers, but retriggerable delay works here. I didn’t know about this node, thank you! And it even seems to pause its delay countdown when the widget is removed from viewport, which makes it safe
Well, it doesn’t fail with retriggerable delay. And if it would fail, then it would be a dereferencing of invalid object, and I would see an error, wouldn’t I?
You’re right, I jumped too soon. But what else can it be?..
Maybe a UE bug:) Maybe it could be solved by reparenting blueprint to Actor and back or something like that, who knows…
Timers literally reset when called.
When a timer is triggered it starts the countdown. If the same timer is called before the count down ends it simply resets the clock. A “New” timer is not being created.
e.g. I have a timer set for 10 seconds. It ticks for 8 (2 secs remaining) and is called again… Timers clock resets to 10 and starts ticking down again.
The Timer Handle is a reference to the Timer itself. It’s used to clear (cancel the timer), check if it’s (Active, Paused, UnPaused, Exists), Get Remaining or Elapsed Time, etc and so forth.
@ClockworkOcean is correct, using a timer here is not the right approach.
Overall you’ll need to use varying approaches for each element and or section of your settings UI.
For direct feedback settings such as volume controls (e.g. In Game Music Volume) You’ll want to adjust the actual Volume directly, so there’s input responsiveness. You’d then Set the config value on section or settings UI exit.
- Apply directly to audio component on change.
- Save input value to Float var, Set VolumeChanged? bool to true.
- On UI Exit, Widget unload call Audio changes function.
- Audio Changes Function checks Bool and updates settings as needed.
For Keybinding sections or others that do not need instant feedback you do not need to update settings config (save) immediately after a change.
You’d simply need to create a struct and have an element for each input axis/action. Make the change in the struct, set a bool (Keys_HasChanges?). On UI exit check the bool and execute the settings save functionality.
For Graphics settings you typically have some settings that require a restart and some that don’t.
For those that do not require restart to take effect you’d apply them just as keybinds. For the others you’ll need to use an “Apply Changes and Restart” approach.
Get game Instance should be a set reference in the controller with an Is Valid. Otherwise you’d need to do an is valid on it per raw request… BUT you know this.
But game instance exists throughout the whole game lifetime. Why do we need to check if it is valid?
And wait, you’re saying that SetTimerByEvent node reuses the same timer on each call, then why does it give a new timer handle each time it is called? (As it is seen in ClockworkOcean’s post here: Set Timer By Event node doesn't cause the event to be triggered - #22 by ClockworkOcean)
The original pic makes sense to me, it should absolutely work out of the box providing it’s set up right. I can see a couple of scenarios where this could fail:
- we forgot to actually use our custom Game Instance:
- the cast somehow fails - can we see what’s inside that pure getter function -
Get My Game Instance
? - can we see what’s inside the
Save Settings
function? - can you explain how and when this widget is added to the viewport / panel container (although I’d struggle to find a scenario where this would affect the outcome. On the other hand, I’ve seen (and done) some crazy things like creating widgets on Tick…)
@rfcvp Any chance you could shed some light on the above?
- In that case, the debug check should fail (see below)
UMyGameInstance* UMyGameInstance::GetMyGameInstance(const UObject* WorldContextObject)
{
auto GameInstance = Cast<UMyGameInstance>(UGameplayStatics::GetGameInstance(WorldContextObject));
check(GameInstance);
return GameInstance;
}
void UMyGameInstance::SaveSettings()
{
check(Settings != nullptr);
Settings->MusicVolume = GetMusicVolume();
Settings->GameSoundVolume = GetGameSoundVolume();
Settings->SaveConfig();
}
float UMyGameInstance::GetMusicVolume()
{
return Settings->MusicVolume;
}
float UMyGameInstance::GetGameSoundVolume()
{
return Settings->GameSoundVolume;
}
Here is USettings class:
UCLASS(Config = Game)
class MYFIRSTGAMEINUE5_API USettings : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(Config)
float MusicVolume = 1.f;
UPROPERTY(Config)
float GameSoundVolume = 1.f;
};
- It is not done on Tick. Widget is added to viewport when a pause menu is opened, or main menu level is loaded, or the previous widget is asked to show another widget (e.g. when we press button “Sound” in Options widget). Do you need to see the code of AMenuLogic?
Yes the Game Instance exists the whole game lifetime. Yet when you call it (Get Game Instance) you should be casting to your specific GI class.
To save on the cast cost you’d save the cast result to a variable. Then use the variable reference.
When you “use” the reference you should be checking if the reference is valid (not empty/null). Especially if you get it from another class.
e.g. You create the reference in the controller and then use it in another class …say pawn.
Get Controller → MyGame Instance (is Valid): [TRUE] → do something with it.
Per node info in docs: Setting an existing timer will reset that timer with updated parameters.
Timer Handle is updated every time you call the timer.
Apologies for reviving an old thread, but I thought I would add this in case anyone else finds this and is scratching their head.
In this case the logic is running from a pause menu. I believe it is the fact that the game is paused that is preventing the timer from running.
Hope this helps
And this is exactly correct. I had the same situation and it is in fact the pause that’s causing the timer to not start. Sadly for UE beginners so many of these threads devolve into picking at someone’s code, answering incorrectly, and insulting the OP in a bizarre effort to build themselves up. Thanks for posting the correct resolution clearly and concisely.