Using ActorHasTag to set bool but it wont return false

Hi I’m hoping someone can help me. I’m new to C++, been working in Blueprints for a while but i want to expand my knowledge.
Im currently trying to create a moving wall that checks to see if the overlapped actor has a specific tag but does not have another tag. The actor in question is a static mesh that can be picked up by the player. Im checking to see that it has the “Trigger” tag but does NOT have the “Grabbed” tag. The wall moves perfectly fine when im just checking for the trigger tag but then it immediately grabs the mesh and moves down. I want the player to first release the actor and then the wall moves.

Code for Overlapping actors:

AActor* UTriggerComponent::GetTriggerActor() const
{
TArray<AActor*> Actors;
GetOverlappingActors(Actors);

for (AActor* Actor : Actors)
{
		bool IsGrabbed;
	if (Actor->ActorHasTag("Grabbed")
	{
		IsGrabbed = true;
	}
	else
	{
		IsGrabbed = false;
	}
		bool HasTriggerTag = Actor->ActorHasTag(TriggerTag);
		if ( HasTriggerTag && !IsGrabbed)
		{
			return Actor;
		}
	
}
return nullptr;
}

Code for adding/ removing grabber tag (in a separate component):

void UGrabber::Grab()
{
UPhysicsHandleComponent* PhysicsHandle = GetPhysicsHandle();
if (PhysicsHandle == nullptr)
{
	return;
}

FHitResult HitResult;
bool HasHit = GetGrabbableInReach(HitResult);
if(HasHit)
{
	UPrimitiveComponent* HitComponent = HitResult.GetComponent();
	HitComponent->WakeAllRigidBodies();
	HitResult.GetActor()->Tags.Add("Grabbed");
	PhysicsHandle->GrabComponentAtLocationWithRotation(
		HitComponent,
		NAME_None,
		HitResult.ImpactPoint,
		GetComponentRotation()
	);
}
DrawDebugSphere(GetWorld(), HitResult.ImpactPoint, 10, 10, FColor::Green, false, 5);		
}

void UGrabber::Release()
{
UPhysicsHandleComponent* PhysicsHandle = GetPhysicsHandle();
if (PhysicsHandle == nullptr)
{
	return;
}
if(PhysicsHandle->GetGrabbedComponent() != nullptr)
{
	AActor* GrabbedActor = PhysicsHandle->GetGrabbedComponent()->GetOwner();
	GrabbedActor->Tags.Remove("Grabbed");
	PhysicsHandle->GetGrabbedComponent()->WakeAllRigidBodies();
	PhysicsHandle->ReleaseComponent();
}
}

Can you visualise the problem with a video? Problem space seems simple logic however I am having trouble to understand the gameplay logic.

Whats supposed to happen is that releasing the gargoyle makes the wall go down revealing a secret passage. If i removed the check to see if IsGrabbed is false the code works but then it happens immediately where im trying to create player interaction in releasing the gargoyle before the door goes down.

Where this is checked? What triggers it? I am assuming this is on door?

correct, the door has a UMover and UTrigger component. That sets a bool on tick ShouldMove to true

Then seems everyting is fine ( atleast logic wise) However I would recommend doing events rather than tick operations as common practice.

However on you code;

I wonder will it fix :slight_smile:

Also can you eject from player and click on gargoyle to see if the tag is removed or not. Since tag is problematic let’s be sure that tag is not there in the first place?

no that one didnt work. And i did do the eject both for grab and release and it does add and remove the tag which is why im confused as to why the boolean wont return false when Grabber tag isnt there

if (Actor->ActorHasTag("Grabbed")
	{
		IsGrabbed = true;
	}

I see one paranthesis is missing, actually the IDE should warn you about that but can you try, cause it should work.

if (Actor->ActorHasTag("Grabbed"))
	{
		IsGrabbed = true;
	}

yeah i think that happened when i copy and pasted its fine in the IDE my bad

Interesting one really, it should work. I am asking mainline questions to you but it is important to get over the baseline, when you log something like below you see actor and its tags right? Just to be sure actor is colliding, the name is there on the gate

UE_LOG(LogClass, Log, TEXT("Actors %s"), *Actor.GetName());

Yes i was using that message to make sure that the door was actually picking up on the overlaps. If i removed the Grabber if statement that message will print to the output log. so i know the issue is that its not setting isGrabbed to false when the gargoyle is released. Everything else in the code is working fine

const bool IsGrabbed = Actor->ActorHasTag(“Grabbed”);

1 Like

small suggestion, you can also break here:

        const bool HasTriggerTag = Actor->ActorHasTag(TriggerTag);
		if ( HasTriggerTag && !IsGrabbed)
		{
			KeyActor = Actor ;
			break; // here
		}

yeah I know that one, but didn’t seen the “const” incoming :smiley:

1 Like

adding const bool didnt work either

What would break; do?

it will avoid going through the full list unnecessarily. just get the first one.

yeah, it was not addressing the main issue, just a suggestion. i wasn’t meaning to correct grimnir, his code is good. was just a simple thing that crossed my mind.

the logic from grimnir seems good to me.
i think you’re using tick because otherwise you might “miss” the overlap event if it’s grabbed.

i haven’t tested this, and i only have a vague memory of the last time i’ve used the physhandler.
but afaik, when you grab a component with a physicshandle you reparent it, so the owner is the physhandle and not the original actor. hence youre removing the tag from the wrong actor.

try this instead:

 void UGrabber::Release() {
  UPhysicsHandleComponent* PhysicsHandle = GetPhysicsHandle();
  if (UNLIKELY(!PhysicsHandle)) return;

  UActorComponent* Cmp = PhysicsHandle->GetGrabbedComponent();
  if (UNLIKELY(!Cmp)) return;

// 1st release
	Cmp->WakeAllRigidBodies(); // you will probably need to use a different class than UActorComponent to do this, but i can't recall the exact one and i'm a bit busy to check myself. im sure you can find it.
	PhysicsHandle->ReleaseComponent();

// then reset
	AActor* GrabbedActor = Cmp->GetOwner();
if (UNLIKELY(!GrabbedActor)) {
  UE_LOG(LogTemp, Warning, TEXT("%hs Can't find the right owner of the last grabbed component. cmp=%s"), __func__, *GetNameSafe(Cmp));
  return;
}
	GrabbedActor->Tags.Remove("Grabbed");
}

good work testing. this hinted me.

just sharing something i’ve learned.
i’ve found that keeping the order of tasks when doing/undoing is always very helpful. always undo stuff in the reverse order you do them.
e.g. you unstack the opposite way you stack. you construct an object in the reverse order you destroy them. (e.g. call the super::Destroy) at end, and in this case, you release before untagging and tag before grabbing.

though, now that i think about it, you could have simply ignored objects owned by a physhandler or the pawn (assuming that the pawn owns the physhandler). instead of having to deal with tags.

please verify these asumptions by watching what happens with the actors on the outliner while PIE.

1 Like

UTriggerComponent is not a standard class. How does it work, when is this function called ?

What do you mean immediately ? As soon as you start PIE ? Or when you bring the mesh into the overlapping box ?

This is unclear, can you show this part of code maybe ?

  1. UTriggerComponent is a box component added onto the door BP in UE.

  2. Yes moves immediately when the mesh overlaps with the trigger component. but im trying to make it so that it only moves when you actually release the mesh. this logic is set by input actions in player character

  3. This is the actual mover component that handles the logic for the doors movement. this is the only place im using tick because its constantly checking whether the door can and cant move

    include “Mover.h”
    include “Math/UnrealMathUtility.h”

UMover::UMover()
{
PrimaryComponentTick.bCanEverTick = true;

}

void UMover::BeginPlay()
{
Super::BeginPlay();
OriginalLocation =  GetOwner()->GetActorLocation();
// ...

}


void UMover::TickComponent(float DeltaTime, ELevelTick TickType, 
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
FVector TargetLocation = OriginalLocation;
if(ShouldMove)
{
	TargetLocation = OriginalLocation + MoveOffset;
}
FVector CurrentLocation = GetOwner()->GetActorLocation();
float Speed = MoveOffset.Length() / MoveTime;

FVector NewLocation = FMath::VInterpConstantTo(CurrentLocation, TargetLocation, DeltaTime, Speed);
GetOwner()->SetActorLocation(NewLocation);
}

void UMover::SetShouldMove(bool NewShouldMove)
{
ShouldMove = NewShouldMove;
}