draw in notify state

Hello all,

I’m having trouble with a notifyState

I want to build a melee combat system (multiplayer), When I trigger a melee ability (with GAS), the ability play a given montage, which trigger a custom AnimNotifyState

void UAnimNotifyStateCanHit::NotifyTick(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float FrameDeltaTime, const FAnimNotifyEventReference& EventReference)
{
	Super::NotifyTick(MeshComp, Animation, FrameDeltaTime, EventReference);

	AMORPGCharacter* BaseCharacter = Cast<AMORPGCharacter>(MeshComp->GetOwner());
	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("hit !")));
	if (UKismetSystemLibrary::IsDedicatedServer(GetWorld())) {
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("i'am server !")));

	}
	if (BaseCharacter && HitLocation != EMORPGHitLocation::None)
	{

		FVector location;

		switch (HitLocation)
		{

		case EMORPGHitLocation::FistLeft:
			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("hit with left fist")));
			location = MeshComp->GetSocketLocation("middle_02_l");
			break;

		case EMORPGHitLocation::FistRight:
			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("hit with right fist")));
			location = MeshComp->GetSocketLocation("middle_02_r");
			break;

		default:
			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("this should not happen")));
			location = BaseCharacter->GetActorLocation();
			break;
		}
		
		UKismetSystemLibrary::DrawDebugSphere(GetWorld(), location, 150.0f, 10, FColor::Red);


		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("hit at %s"), *location.ToString()));
		TArray<AActor*> OutActors;

		FHitResult OutHit;
		TArray<TEnumAsByte<EObjectTypeQuery>> ObjectTypesArray;
		ObjectTypesArray.Add(UEngineTypes::ConvertToObjectType(ECC_WorldDynamic));

		/*
		bool HasHitOne = UKismetSystemLibrary::SphereTraceSingleForObjects(GetWorld(), location, location, 500.f, ObjectTypesArray, true, {}, EDrawDebugTrace::ForDuration, OutHit, true);
		if (HasHitOne) {
			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("yeah")));
		}
		*/

		bool HasHit = UKismetSystemLibrary::SphereOverlapActors(GetWorld(), location, 150.0f, ObjectTypesArray, APawn::StaticClass(), { BaseCharacter }, OutActors);
		DrawDebugSphere(GetWorld(), location, 150.0f, 10,  FColor::Red);

but the drawdebugsphere function never show on screen, no matter if I’m in standalone mode or multiplayer mode.

I get the first log message “hit!”, the second “hit with left fist” and the third one “hit at {location}”, the location seems good because when I move the caractere I can see the location changing. but the message “i’m a server” never show nor the debugsphere,

here is the anim montage screenshot

Is there something I could be missing ?
Can I rely on notify state like this or should I use something else ? I tried to use GAS gameplay cue where i see the debug sphere, but I don’t think GAS gameplayCue are designed to build such mechanism

Seems you using wrong world for IsDedicatedServer() and DrawDebug*()

Just checked: NotifyState’s GetWorld() always returns nullptr for me in pie. You should use the world your mesh is in: MeshComp->GetWorld().

As a side note, your NS will be run not only in game worlds, but in editor’s utility worlds as well. You can filter such cases with if(MeshComp->GetWorld()->WorldType == EWorldType::Type::EditorPreview)

Thanks for the hint !

I finally moved thid code to a UFUNCTION(server, reliable) on my Character Object (APawn)

void AMORPGCharacter::MakeHit_Implementation(EMORPGHitLocation HLocation, USkeletalMeshComponent* MeshComp, const TArray<TSubclassOf<class UGameplayEffect>> &HitEffects)
{
	FVector location;

	switch (HLocation)
	{

	case EMORPGHitLocation::FistLeft:
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("hit with left fist")));
		location = this->GetMesh()->GetBoneLocation("middle_02_l");
		break;

	case EMORPGHitLocation::FistRight:
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("hit with right fist")));
		location = this->GetMesh()->GetBoneLocation("middle_02_r");
		break;

	default:
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("this should not happen")));
		location = GetActorLocation();
		break;
	}

	UKismetSystemLibrary::DrawDebugSphere(GetWorld(), location, 20.f, 12, FColor::Red);


	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("hit at %s"), *location.ToString()));
	TArray<AActor*> OutActors;

	FHitResult OutHit;
	TArray<TEnumAsByte<EObjectTypeQuery>> ObjectTypesArray;
	ObjectTypesArray.Add(UEngineTypes::ConvertToObjectType(ECC_WorldDynamic));

	/*
	bool HasHitOne = UKismetSystemLibrary::SphereTraceSingleForObjects(GetWorld(), location, location, 500.f, ObjectTypesArray, true, {}, EDrawDebugTrace::ForDuration, OutHit, true);
	if (HasHitOne) {
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("yeah")));
	}
	*/

	bool HasHit = UKismetSystemLibrary::SphereOverlapActors(GetWorld(), location, 150.0f, ObjectTypesArray, APawn::StaticClass(), { }, OutActors);

	if (HasHit)
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("%d actors hit"), OutActors.Num()));
		if (HitEffects.Num() > 0)
		{
			for (AActor* HitActor : OutActors)
			{
				AMORPGCharacter* Character = Cast<AMORPGCharacter>(HitActor);
				if (Character)
				{
					GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("bim !")));
					Character->AddGameplayEffect(HitEffects);
					
				}
			}

		}
	}
}

now I see the trace, but definitely not on the good location, like the GetBoneLocation does not return what I expect

Depends on the implementation, but i assume you want to trace on server by client’s locations. If it’s so, then it’s a debatable decision.

And if my assumption is wrong and you want only trace on server by server locations, then you don’t need RPC for this, only a check before call to return immediately if it runs on the client

You should be aware that:

  1. your montages plays both on the server and the client, and due to network latency they will be in a different phases at the same moment of time.

  2. if you debugging in pie-‘play as client’ mode, both your draw debug (server and client version, if there is one, which in your case is not present) will be shown in client world.
    In standalone-‘play as client’ mode server’s drawdebug will only be visible on the console server, hence, invisible.

In my case i’m running such code inside of NotifyState on both client and server: hit detection, then on server - the actual ge_damage application, on the client - playing sfx\vfx. This approach does have its own minor problems though.

now I see the trace, but definitely not on the good location, like the GetBoneLocation does not return what I expect

Now the question is does “not on the good location” means “just a bit phase desync between server&client” or “they are WAY off of all the possible locations they could occured”?