Announcement

Collapse
No announcement yet.

std::call_once isn't called once

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

    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:

    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::calnce approach, you can the yellow phrases inside the output window, there are three of them:

    Click image for larger version

Name:	UE4-img-1.jpg
Views:	1
Size:	402.1 KB
ID:	1663996

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

    Click image for larger version

Name:	UE4-img-2.jpg
Views:	1
Size:	491.8 KB
ID:	1663997

    In both cases, what I'm doing wrong?

    Any help will be greatly appreciated!

    Thanks!

    #2
    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
    https://wiki.unrealengine.com/Delega...and_BP_Exposed

    Comment


      #3
      Originally posted by andrea.b View Post
      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
      https://wiki.unrealengine.com/Delega...and_BP_Exposed
      Thanks for your reply! I'll modify the code and then i'll update this thread later =)

      Comment

      Working...
      X