Download

std::call_once isn't called once

Hi guys!

I’m really stuck here with this problem.

You see, I have a AActor class here where I’m trying to use std::call_once inside the constructor. In the editor, I have created a BP based on this class, and I have placed three instances of it. But when I start the game, the procedure that was given to std::call_once isn’t called once. I tried to do something else, created a macro and used atomics to do the same thing, but with no success. What I’m doing wrong here?

here is the class code:


FPSObjectiveActor.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "FPSCharacter.h"
#include <functional>
#include <mutex>
#include <atomic>
#include "FPSObjectiveActor.generated.h"


typedef std::function<void()> CallbackFunction;
class USphereComponent;
class AFPSCharacter;


UCLASS()
class FPSGAME_API AFPSObjectiveActor : public AActor
{
GENERATED_BODY()

public:
// Sets default values for this actor's properties
AFPSObjectiveActor();

protected:

AFPSCharacter* _playerCharacter;
CallbackFunction _callback;

/** <GAMEDEV> Static mesh component */
UPROPERTY(VisibleAnywhere, Category = "Components")
UStaticMeshComponent* _meshComp;

/** <GAMEDEV> Collision component */
UPROPERTY(VisibleAnywhere, Category = "Components")
USphereComponent* _sphereComp;

// <GAMEDEV> Particle system component
UPROPERTY(EditDefaultsOnly, Category = "Effects")
UParticleSystem* _pickUpFX;

// Called when the game starts or when spawned
virtual void BeginPlay() override;

void PlayEffects(); /

void setCallback(CallbackFunction _cb);

public:
// Called every frame
virtual void Tick(float DeltaTime) override;

// <GAMEDEV> Method used to notify when this actor overlaps another one
virtual void NotifyActorBeginOverlap(AActor* OtherActor) override;

};



FPSObjectiveActor.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "FPSObjectiveActor.h"
#include "Components/StaticMeshComponent.h"
#include "Components/SphereComponent.h"
#include "Kismet/GameplayStatics.h"
#include "FPSCharacter.h"
#include <mutex>
#include <atomic>

//#define CALLBACK_CALLED (]() {static bool is_first_time = true; auto was_first_time = is_first_time; is_first_time = false; return was_first_time; })
#define CALLBACK_CALLED ]()->bool {static std::atomic<bool> first_time(true); return first_time.exchange(false); }

// Sets default values
AFPSObjectiveActor::AFPSObjectiveActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;

_meshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComp"));
_meshComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
RootComponent = _meshComp;

_sphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComp"));
_sphereComp->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
_sphereComp->SetCollisionResponseToAllChannels(ECR_Ignore);
_sphereComp->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
_sphereComp->SetupAttachment(_meshComp);

/*if(CALLBACK_CALLED) {
auto& _tmp = _playerCharacter;
setCallback(&_tmp]() { return _tmp->PrintObjectiveMessage(); });
}*/

std::once_flag _wasCallbackExecuted;

std::call_once(
_wasCallbackExecuted,
[this]() {
auto& _tmp = _playerCharacter;
setCallback(&_tmp]() { return _tmp->PrintObjectiveMessage(); });

}
);

//auto& _tmp = _playerCharacter;
//setCallback(&_tmp]() { return _tmp->PrintObjectiveMessage(); });

}

void AFPSObjectiveActor::PlayEffects()
{
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), _pickUpFX, GetActorLocation());

}

void AFPSObjectiveActor::setCallback(CallbackFunction _cb)
{
_callback = _cb;
UE_LOG(LogTemp, Warning, TEXT("Callback bound"));

}

// Called when the game starts or when spawned
void AFPSObjectiveActor::BeginPlay()
{
Super::BeginPlay();

PlayEffects();

}

// Called every frame
void AFPSObjectiveActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);

}

void AFPSObjectiveActor::NotifyActorBeginOverlap(AActor* _otherActor)
{
Super::NotifyActorBeginOverlap(_otherActor);

_playerCharacter = Cast<AFPSCharacter>(_otherActor);
if (_playerCharacter) {
PlayEffects();
_playerCharacter->_bIsCarryingObjective = true;
_callback();
Destroy();
}

}

Here is an image containing the result after using the std::cal:once approach, you can the yellow phrases inside the output window, there are three of them:

And here is an image containing the result after I uncomment the macro and the IF statement:

In both cases, what I’m doing wrong?

Any help will be greatly appreciated!

Thanks!

First of all, as good practice, you should never use anything outside the UE4 C++ API to do gameplay programming in C++, so please do not use std functions.

Second, in Blueprint you have access to the “DoOnce” node that does exactly what you need. So if you have a BP class based on this native C++ class you can do that there in the ActorBeginOverlap BP event.
As for callback I suggest you to use UE4 delegates

Thanks for your reply! I’ll modify the code and then i’ll update this thread later =)