#OnSphereOverlap not being called if overlapping weapons in quick succession

233 messages Β· Page 1 of 1 (latest)

exotic mango
#

Me again 😊

I've had a bug for a while where a character is sometimes unable to pick up a weapon and the pickup widget doesn't show. I've narrowed it down and concluded that if the player overlaps with multiple weapons OR overlaps with different weapons in quick succession (if the spheres are so close they almost touch), the OnSphereOverlap() function isn't being called and they're unable to pick the weapon up, unless they exit and re-enter the sphere.
Any ideas on how to rectify this?

hearty pond
#

One thing is shrink the size of the overlap sphere. I've run into that issue before myself, so allow me to ask you this, how attached to the whole overlap system are you?

exotic mango
#

not particularly attached, should a player die and drop their weapon onto or next to another one, I'll have the same issue regardless of sphere size, so if there is a better way, teach me, master πŸ™‡β€β™€οΈ

hearty pond
#

Lol "master" I am not, but another method, and I've found great success with its using a line trace and interface calls. This way you can control what objects you're looking for and plus there's no overlapping issues. Idk how comfortable you're with interfaces

exotic mango
#

I've used a couple of times but couldn't set one up from scratch. We do have an interface in this project InteractWithCrosshairsInterface that the character implements, but it's empty and I honestly can't remember what we use it for πŸ˜…

hearty pond
#

Lol ok no worries. I'll post some code here in a few minutes. Now then I'll be honest this approach requires a little setup, but in all honesty it's really not bad. It's very very flexible and very very low overhead. Line traces are cheap

exotic mango
#

thank you ❀️

hearty pond
#

Lol ok massive code wall incoming πŸ˜‚

#
/* In the header **/
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnClearViewport);

public:
    UPROPERTY(BlueprintAssignable)
    FOnClearViewport Clear;

/* In BeginPlay() **/
void AYourCharacterClassHere::BeginPlay()
{
    Super::BeginPlay();
    
    /* I set InteractableTraceTimer in the ctor, and I've found that 0.25F is the "sweet" spot time wise **/
    GetWorldTimerManager().SetTimer(InteractableTraceTimerHandle, this, &AYourCharacterClassHere::ScanForInteractables, InteractableTraceTimer, true);
}

/* This ISN'T MANDATORY, but I highly recommend doing this. Always cleanup **/
void AYourCharacterClassHere::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    Super::EndPlay(EndPlayReason);

    GetWorldTimerManager().ClearAllTimersForObject(this);

    UKismetSystemLibrary::QuitGame(GetWorld(), 0, EQuitPreference::Quit, false);
}```
#
void AYourCharacterClassHere::ScanForInteractables()
{
    FHitResult HitResult;

    FVector Start = Camera->GetComponentLocation();
    FVector End = Start + (Camera->GetComponentRotation().Vector() * 400.F);

    TArray<TEnumAsByte<EObjectTypeQuery>> TraceObjects;
    TArray<AActor*> ActorsToIgnore;

    ActorsToIgnore.Add(this);
    ActorsToIgnore.Add(WeaponMap[WeaponSlot_01->GetCurrentWeaponEnumName()]);

    TraceObjects.Add(UEngineTypes::ConvertToObjectType(ECC_WorldStatic));
    TraceObjects.Add(UEngineTypes::ConvertToObjectType(ECC_WorldDynamic));

    const bool bIsInteractable = UKismetSystemLibrary::LineTraceSingleForObjects(GetWorld(), Start, End, TraceObjects, true, ActorsToIgnore, EDrawDebugTrace::None, HitResult, true);

    if (bIsInteractable)
    {
        if (HitResult.GetActor())
        {
            if (HitResult.GetActor()->GetClass()->ImplementsInterface(UInteractInterface::StaticClass()))
                IInteractInterface::Execute_InteractableFound(HitResult.GetActor());
        }
    }
    
    /* This is a multicast delegate. Its function will become clear. Yet again THIS ISN'T MANDATORY **/
    else 
        Clear.Broadcast();
}```
#
void AYourCharacterClassHere::InteractWithObject()
{
    FHitResult HitResult;

    FVector Start = Camera->GetComponentLocation();
    FVector End = Start + (Camera->GetComponentRotation().Vector() * 400.F);

    TArray<TEnumAsByte<EObjectTypeQuery>> TraceObjects;
    TArray<AActor*> ActorsToIgnore;

    ActorsToIgnore.Add(this);
    ActorsToIgnore.Add(WeaponMap[WeaponSlot_01->GetCurrentWeaponEnumName()]);

    TraceObjects.Add(UEngineTypes::ConvertToObjectType(ECC_WorldStatic));
    TraceObjects.Add(UEngineTypes::ConvertToObjectType(ECC_WorldDynamic));

    /* By using LineTraceSingleForObjects vs LineTraceChannel it stops the line trace as soon as the trace hits anything **/
    const bool bCanInteract = UKismetSystemLibrary::LineTraceSingleForObjects(GetWorld(), Start, End, TraceObjects, true, ActorsToIgnore, EDrawDebugTrace::None, HitResult, true);

    if (bCanInteract)
    {
        if (HitResult.GetActor())
        {
            if (HitResult.GetActor()->GetClass()->ImplementsInterface(UInteractInterface::StaticClass()))
                IInteractInterface::Execute_InteractWithObject(HitResult.GetActor());
        }
    }
}```
#
/* I use a Player Controller this once more ISN'T MANDATORY also I use Enhanced Input **/
void AYourCharacterClassController::SetupInputComponent()
{
    Super::SetupInputComponent();

    // Get the Enhanced Input Local Player Subsystem from the Local Player related to our Player Controller.
    if (TObjectPtr<UEnhancedInputComponent> PlayerEnhancedInputComponent = Cast<UEnhancedInputComponent>(InputComponent))
    {
        if (InteractAction)
            PlayerEnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Started, this, &AYourCharacterClassController::Interact);
    }
}

void AYourCharacterClassController::Interact()
{
    if (!IsValid(PlayerRef))
        return;
    
    else
    {
        PlayerRef->Clear.Broadcast();

        PlayerRef->InteractWithObject();
    }
}```
#
class IInteractInterface
{
    GENERATED_BODY()

    // Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
    UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
    void InteractableFound();

    UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
    void InteractWithObject();
};```
#
void APickupBase::InteractableFound_Implementation()
{
    if (IsValid(PickupWidgetComponent))
        PickupWidgetComponent->SetVisibility(true);
    
    else
        return;
}

void APickupBase::OnClearViewport()
{
    if (IsValid(PickupWidgetComponent))
        PickupWidgetComponent->SetVisibility(false);

    else
        return;
}```
#

@exotic mango

exotic mango
#

a little set up πŸ˜‚

hearty pond
#

LOL it's not that bad I condensed it quite a bit πŸ˜‚

exotic mango
#

hahah nah I really appreciate it ❀️ ❀️

#

I'll take a look at it properly tomorrow, I've been dying of a headache all day πŸ₯² I'll let you know how I get on! TYSM 😭

hearty pond
#

You're very welcome. I suffer chronic migraines myself so I feel your pain. Feel free to hmu anytime with questions I don't mind. Lol now I hope you see why most generally people teach the overlap system in beginner type courses. Lol I'm actually working on switching that code over to an asynchronous line trace system πŸ˜‚

exotic mango
#

okay headache decided to stick around for the ride, so I apologise in advance for any stupid questions I'm DEFINITELY going to ask πŸ˜‚
First of all, how tf do you just know this πŸ˜‚

hearty pond
#

Hey hopefully the migraine is finally getting better. I have to special meds when mine get that bad. Second, lol there's no stupid questions only stupid answers. Lol so how do I know this? My interact system, which btw is 100% my own code came from lots and lots of reading the docs and research and lots of T&E. Interfaces in C++ took me forever to learn and figure out how they work. Interfaces in C++ have a "hidden" parm no matter if you have any parms or not. So like in the example I showed you, you'll notice that void InteractableFound() has no parms, and yet it takes in a parm when I call it in PlayerCharacter. Lol that's because there's a hidden parm that UE creates called UObject* O which is real helpful πŸ˜‚. Anyway, Jolly Monster was a huge help on this journey. Lol I noticed you edited your question, but IIRC yes to the rest

solid trailBOT
#

@exotic mango

alarm Please refrain from Excessive use of blacklisted words!

exotic mango
#

damn bot, I don't even remember what I said now lol

#

slowly but surely, thank you πŸ˜„ I just have bog standard meds so I take as many as often as is medically allowed haha
Well colour me impressed, I tried googling simple stuff and didn't understand anything πŸ˜… I can only hope to know that much one day πŸ₯²
I haven't used EnhancedInput before, I'm a little confused as to what I need to put instead of InteractAction in the player controller? I have an equip action which is set up in the character, but is the normal action mapping. From what I've read, it looks like I need to add a UInputAction* in the header, but I'm not quite sure lol

hearty pond
#

Lol ignore Wick it's having personal issues, I just checked the log πŸ˜‚. Oh so back to the input, just add an interact action the same way you add any new input. I think something like BindAction("Interact", IE_Pressed, this, & AMyAwesomeController::MyInteractFunction)

exotic mango
#

okay yeah that I can do!

hearty pond
#

Lol well from that interact system I built a pickup system that is a single C++ class that spawns anything literally anything I want into the world all from a single class no BP

exotic mango
#

that sounds beautiful 😢

okay so, I've got it mostly working, the widget shows up, but only if it overlaps the sphere, not the weapon itself, but it doesn't seem to the removing the widget when I look away. I also can't pick it up but I'm assuming I need to add more code for that?

#

here's my thinking: we have a function to set the overlapping weapon in order to equip it, I think I should call this function in the Weapons InteractableFound_Implementation function, and call the same function in OnClearViewport but pass in nullptr to clear it

#

or maybe just move all of the code inside OnSphereOverlap into InteractableFound_Implementation

hearty pond
#

Move the overlap code into InteractableFound. OnViewportClear, should only contain the code to clear the widget from the viewport. And should be bound to the delegate from player

exotic mango
#

okay, 1 out of 2 isn't bad πŸ˜‚
neewwwww question, the code in Overlap casts to the character using the OtherActor param, how do I access the character without a param to cast to? I tried GetOwner() and crashed the engine πŸ˜‚ which makes sense because the weapon doesn't yet have an owner but i thought i'd try

hearty pond
#

Lol that's actually easy, use this Cast<YourChatacterClass>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0) you'll need #include "Kismet/GameplayStatics.h"

exotic mango
#

thank you, that's something that has been bugging me since I started to learn coding, as well as knowing how to cast to rando objects and such, but another time lol

#

okay lemme try this

hearty pond
#

Yeah UGameplayStatics has a few getters that guarantee you can get a pointer to certain classes. Such as GameInstance, ACharacter, and AController

exotic mango
#

noted!

#

I can't get it to work 😭

#

why am I so dumb πŸ₯²

hearty pond
#

What's wrong? Post code

exotic mango
#

literally about to give you my postcode πŸ˜‚

hearty pond
#

Lol. You're fine let's see what you got going on

exotic mango
#

void AWeapon::InteractableFound_Implementation()
{
BlasterOwnerCharacter = Cast<ACosmicBlasterCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
if (PickupWidget && BlasterOwnerCharacter)
{
ShowPickupWidget(true);
if (WeaponType == EWeaponType::EWT_Flag && BlasterOwnerCharacter->GetTeam() == Team) return;
if (BlasterOwnerCharacter->IsHoldingTheFlag()) return;
BlasterOwnerCharacter->SetOverlappingWeapon(this);
}
else return;
}

void AWeapon::OnClearViewport()
{
if (PickupWidget)
{
ShowPickupWidget(false);
}
else return;
}

#

void ABlasterPlayerController::Interact()
{
ACosmicBlasterCharacter* BlasterCharacter = Cast<ACosmicBlasterCharacter>(GetPawn());
if (BlasterCharacter == nullptr) return;

`BlasterCharacter->Clear.Broadcast();`
`BlasterCharacter->InteractWithObject();`

}

void ABlasterPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
if (InputComponent == nullptr) return;

InputComponent->BindAction("Quit", IE_Pressed, this, &ABlasterPlayerController::ShowReturnToMainMenu);
InputComponent->BindAction("Equip", IE_Pressed, this, &ABlasterPlayerController::Interact);
}

hearty pond
#
void AWeapon::InteractableFound_Implementation()
{
    BlasterOwnerCharacter = Cast<ACosmicBlasterCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
    if (PickupWidget && BlasterOwnerCharacter)
    {
        ShowPickupWidget(true);
        if (WeaponType == EWeaponType::EWT_Flag && BlasterOwnerCharacter->GetTeam() == Team) return;
        if (BlasterOwnerCharacter->IsHoldingTheFlag()) return;
        BlasterOwnerCharacter->SetOverlappingWeapon(this);
    }
    else return;
}

void AWeapon::OnClearViewport()
{
    if (PickupWidget)
    {
        ShowPickupWidget(false);
    }
    else return;
}```
exotic mango
#

how did you do that

hearty pond
#
void ABlasterPlayerController::SetupInputComponent()
{
    Super::SetupInputComponent();
    if (InputComponent == nullptr) return;

    InputComponent->BindAction("Quit", IE_Pressed, this, &ABlasterPlayerController::ShowReturnToMainMenu);
    InputComponent->BindAction("Equip", IE_Pressed, this, &ABlasterPlayerController::Interact);
}```
exotic mango
#

omg

hearty pond
#

magic πŸ˜‚

exotic mango
#

here's me putting fecking tilda signs lool

#

void ACosmicBlasterCharacter::ScanForInteractables()
{
FHitResult HitResult;

`FVector Start = FollowCamera->GetComponentLocation();`
`FVector End = Start + (FollowCamera->GetComponentRotation().Vector() * 400.F);`

`TArray<TEnumAsByte<EObjectTypeQuery>> TraceObjects;`
`TArray<AActor*> ActorsToIgnore;`
`ActorsToIgnore.Add(this);`
`ActorsToIgnore.Add(Combat->EquippedWeapon);`
`ActorsToIgnore.Add(Combat->SecondaryWeapon);`

`TraceObjects.Add(UEngineTypes::ConvertToObjectType(ECC_WorldStatic));`

TraceObjects.Add(UEngineTypes::ConvertToObjectType(ECC_WorldDynamic));

`const bool bIsInteractable = UKismetSystemLibrary::LineTraceSingleForObjects(`
    `GetWorld(),`
    `Start,`
    `End,`
    `TraceObjects,`
    `true,`
    `ActorsToIgnore,`
    `EDrawDebugTrace::ForDuration,`
    `HitResult,`
    `true`
);

if (bIsInteractable)
{
if (HitResult.GetActor())
{
if (HitResult.GetActor()->GetClass()->ImplementsInterface(UInteractInterface::StaticClass()))
{
IInteractInterface::Execute_InteractableFound(HitResult.GetActor());
}
}
}
else
{
Clear.Broadcast();
}
}

void ACosmicBlasterCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);

`GetWorldTimerManager().ClearAllTimersForObject(this);`

`UKismetSystemLibrary::QuitGame(GetWorld(), 0, EQuitPreference::Quit, false);`

}

hearty pond
#
void ACosmicBlasterCharacter::ScanForInteractables()
{
    FHitResult HitResult;

    FVector Start = FollowCamera->GetComponentLocation();
    FVector End = Start + (FollowCamera->GetComponentRotation().Vector() * 400.F);

    TArray<TEnumAsByte<EObjectTypeQuery>> TraceObjects;
    TArray<AActor*> ActorsToIgnore;
    ActorsToIgnore.Add(this);
    ActorsToIgnore.Add(Combat->EquippedWeapon);
    ActorsToIgnore.Add(Combat->SecondaryWeapon);

    TraceObjects.Add(UEngineTypes::ConvertToObjectType(ECC_WorldStatic));
    TraceObjects.Add(UEngineTypes::ConvertToObjectType(ECC_WorldDynamic));

    const bool bIsInteractable = UKismetSystemLibrary::LineTraceSingleForObjects(
        GetWorld(),
        Start,
        End,
        TraceObjects,
        true,
        ActorsToIgnore,
        EDrawDebugTrace::ForDuration,
        HitResult,
        true
    );```
exotic mango
#

void ACosmicBlasterCharacter::InteractWithObject()
{
FHitResult HitResult;

`FVector Start = FollowCamera->GetComponentLocation();`

FVector End = Start + (FollowCamera->GetComponentRotation().Vector() * 400.F);

`TArray<TEnumAsByte<EObjectTypeQuery>> TraceObjects;`
`TArray<AActor*> ActorsToIgnore;`

`ActorsToIgnore.Add(this);`
`ActorsToIgnore.Add(Combat->EquippedWeapon);`
`ActorsToIgnore.Add(Combat->SecondaryWeapon);`

`TraceObjects.Add(UEngineTypes::ConvertToObjectType(ECC_WorldStatic));`
`TraceObjects.Add(UEngineTypes::ConvertToObjectType(ECC_WorldDynamic));`

`const bool bCanInteract = UKismetSystemLibrary::LineTraceSingleForObjects(`
    `GetWorld(),`
    `Start,`
`    End,`
    `TraceObjects,`
    `true,`
    `ActorsToIgnore,`
`    EDrawDebugTrace::ForDuration,`
`    HitResult,`
`    true`
);

`if (bCanInteract)`
{
`    if (HitResult.GetActor())`
    {
    `    if (HitResult.GetActor()->GetClass()->ImplementsInterface(UInteractInterface::StaticClass()))`
        {
            `IInteractInterface::Execute_InteractWithObject(HitResult.GetActor());`
        }
    }
}

}

hearty pond
#
if (bIsInteractable)
    {
        if (HitResult.GetActor())
        {
            if (HitResult.GetActor()->GetClass()->ImplementsInterface(UInteractInterface::StaticClass()))
            {
                IInteractInterface::Execute_InteractableFound(HitResult.GetActor());
            }
        }
    }
else
    {
        Clear.Broadcast();
    }
}```
exotic mango
#

magic please πŸ˜‚

hearty pond
#
if (bCanInteract)
    {
        if (HitResult.GetActor())
        {
            if (HitResult.GetActor()->GetClass()->ImplementsInterface(UInteractInterface::StaticClass()))
            {
                IInteractInterface::Execute_InteractWithObject(HitResult.GetActor());
            }
        }
    }
}```
#

it's 3 tildas then cpp then new line code then 3 tildas

exotic mango
#

okay i think that's all of it

#

so the line trace works, I get the pickup widget, then nada 😦

hearty pond
#
 if (PickupWidget && BlasterOwnerCharacter)
    {
        ShowPickupWidget(true);

        if (WeaponType == EWeaponType::EWT_Flag && BlasterOwnerCharacter->GetTeam() == Team) 
        {
             GEngine-AddOnScreenDebugMessage(-1, 8.F, FColor::FromHex("#FF6700"), L"First if check");

             return;
        {
             
        if (BlasterOwnerCharacter->IsHoldingTheFlag()) return;
        BlasterOwnerCharacter->SetOverlappingWeapon(this);
    }```
#
 if (PickupWidget && BlasterOwnerCharacter)
    {
        ShowPickupWidget(true);

        if (WeaponType == EWeaponType::EWT_Flag && BlasterOwnerCharacter->GetTeam() == Team)
             return;
             
        if (BlasterOwnerCharacter->IsHoldingTheFlag()) 
        {
             GEngine-AddOnScreenDebugMessage(-1, 8.F, FColor::FromHex("#F87217"), L"Second if check");
            
             return;
        }

        BlasterOwnerCharacter->SetOverlappingWeapon(this);
    }```
#

Lets see where the logic is breaking down

exotic mango
#

okay lets gooo

#

no debug messages for either of those

#

πŸ‘

hearty pond
# exotic mango no debug messages for either of those

Ok then try this ```cpp
void AWeapon::InteractableFound_Implementation()
{
BlasterOwnerCharacter = Cast<ACosmicBlasterCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));

if (PickupWidget && BlasterOwnerCharacter)
{
    GEngine-AddOnScreenDebugMessage(-1, 8.F, FColor::FromHex("#FFD801"), __FUNCTION__);

    ShowPickupWidget(true);

    if (WeaponType == EWeaponType::EWT_Flag && BlasterOwnerCharacter->GetTeam() == Team) return;

    if (BlasterOwnerCharacter->IsHoldingTheFlag()) return;

    BlasterOwnerCharacter->SetOverlappingWeapon(this);
}

else return;

}```

exotic mango
#

debug message appeared πŸ‘

hearty pond
#

Awesome, we know the Cast is good, so in SetOverlappingWeapon() add GEngine-AddOnScreenDebugMessage(-1, 8.F, FColor::FromHex("#FFD801"), __FUNCTION__); and see if it fires off

#

Lol btw you can use w/e FColor you want. I'm just having fun with different colors πŸ˜‚

exotic mango
#

it's a fun surprise πŸ˜‚

#

yeah that called fine

hearty pond
#

I actually made an entire struct of custom FColor just because I got bored with the predefined ones

exotic mango
#

ooo that sounds like fun

hearty pond
# exotic mango yeah that called fine

Ok great, so we know it's not failing any checks and it's calling the correct function, now to figure out where it's breaking down and I'm assuming it's in SetOverlappingWeapon

#

Knock yourself out btw lol

exotic mango
#

okay so I added it before the if checks and separately in each and it fired off every time, but one if check is meant to turn the widget off

#

if (IsLocallyControlled() && OverlappingWeapon)
{
OverlappingWeapon->ShowPickupWidget(false);
GEngine->AddOnScreenDebugMessage(-1, 8.F, FColor::FromHex("#FFD801"), FUNCTION);
}

#

tbh this function has always confused me before we call the same if check but pass in true in one and false in another

#

probably has something to do with declaring overlapping weapon between the two if checks, but I still didn't quite get it

hearty pond
#
if (IsLocallyControlled() && OverlappingWeapon)
{
        OverlappingWeapon->ShowPickupWidget(false);
        GEngine->AddOnScreenDebugMessage(-1, 8.F, FColor::FromHex("#FFD801"), __FUNCTION__);
}``` sorry easier to read this way πŸ˜…
exotic mango
#

sorry that's my bad

hearty pond
#

No worries

#

Right so basically this if check turns off the widget if you're holding it

exotic mango
#

oh okay

#

easy enough haha

hearty pond
#

Anyway back to the issue at hand. So we know everything is firing off as expected, yes?

exotic mango
#

yes

hearty pond
#

Ok so now it's a matter of figuring out where it's going pear shaped. What exactly is supposed to happen?

exotic mango
#

when overlapping the area sphere it would set the overlapping weapon, pressing the E key would call EquipButton pressed, which calls ServerEquipButtonPressed, that calls EquipWeapon which then attaches the weapon to the hand etc etc

hearty pond
#

Ok, have you verified that chain of events occurs with your current code?

exotic mango
#

it worked with overlapping the sphere, and I assumed setting the overlapping weapon via the line trace would do the same thing?

#

could it be that I'm binding the same action mapping to two different functions now?

#

yeah i think that's where I'm gonig wrong

#

PlayerInputComponent->BindAction("Equip", IE_Pressed, this, &ACosmicBlasterCharacter::EquipButtonPressed); in the Character.cpp

InputComponent->BindAction("Equip", IE_Pressed, this, &ABlasterPlayerController::Interact); in the Controller.cpp

#

should I remove the character version but call EquipButtonPressed() within the new code?

hearty pond
#

Yeah you can't bind two different functions to the same key

#

Try it and see what happens, lol you can always change it back

#

Anyway, I'm so sorry to do this, but I have to go on duty and I'm teaching CPR class, so I'll be gone awhile. I'll check back in with ya when I get free time

exotic mango
#

no that's fine! I'll ramble on whilst you're gone dwπŸ˜‚

#

so pick up works now, but swapping weapons doesn't work because obvs not doing a line trace to swap weapons

#

SO, probably needs it's own binding to swap weapons?

hearty pond
exotic mango
#

LOL update, so I tried play testing and my game crashes πŸ˜… I commented out all of the code from today and it worked again πŸ˜…

hearty pond
#

What's the crash?

exotic mango
#

no idea, doesn't give me a log πŸ₯² just kicks both host and client out of the game, shuts down the game but doesn't actually say error

#

I think this is one of the logs, I deleted each build but pretty sure this is one that failed, but it's the only log type file

hearty pond
#

I'm pretty sure I know why. We're casting based on it being player index 0 so when the other player tries anything it's not player 0 thus causing the crash

exotic mango
#

Ahhh yes that makes sense!

exotic mango
#

would this work?

    class ABlasterGameMode* GameMode = Cast<ABlasterGameMode>(GetWorld()->GetAuthGameMode());
    BlasterOwnerCharacter = Cast<ACosmicBlasterCharacter>(GameMode->DefaultPawnClass->GetDefaultObject());
hearty pond
#

@exotic mango Give it a shot see what happens lol

hearty pond
#

@exotic mango Hey lol I'm sorry lost track of this. Did you get something working?

exotic mango
#

Hey don't worry! I haven't come back to trying this yet πŸ˜… I had to take a week off to actually work πŸ˜‚ I am going to look at it though, need to stop procrastinating... haha

hearty pond
#

Lol fair enough. I kept meaning to follow up and ask, and then I get side tracked πŸ˜‚

exotic mango
#

story of my life πŸ˜‚ I'm slowly giving up on other issues atm, so I'll get back to it next πŸ˜‚

exotic mango
#

So I decided I wanted all characters to play the Macarena dance once the match state changes to cooldown, I've got it to work absolutely fine, unless the character is eliminated moments before the match countdown ends, so if they respawn during cooldown, the server sees them do the dance but the client doesn't and is just idle πŸ₯²

#

also fireworks play for the winners, and if they respawn during cooldown, the fireworks play at the elim location and not the respawned location

hearty pond
#

Ah lol, well outside my scope πŸ˜‚. Sigh I really need to learn multiplayer someday

exotic mango
#

haha I'll figure it out eventually I'm sure πŸ˜‚ it's a total ballache, replicating stuff is so difficult 😭

hearty pond
#

I'll bet. Lol I'm stuck on multithreading, object pooling and data assets

exotic mango
#

my turn to be lost πŸ˜‚

fringe obsidian
#

Sorry for intervention, me too modifying the pick-up system to something more advanced.
And it’s of course include using line trace. I’m not yet got full implementation. Just wondering how you guys approach to this question

exotic mango
#

I attempted implementing Shadow's code but came across some snags, I am definitely going to try and work around the issues though. It will also mean moving all of the swap weapon functionality into their own functions, but it seems do-able πŸ™‚

hearty pond
fringe obsidian
hearty pond
fringe obsidian
exotic mango
# hearty pond <@538772036672028693> Give it a shot see what happens lol

Hey @hearty pond , so I decided to come back to this, and regretted my decision πŸ˜‚ got it all up and running again, except the ClearViewport still doesn't get called, but most importantly... I still can't seem to cast properly to the character in

void AWeapon::InteractableFound_Implementation()

I tried my previous suggestion and it flat out didn't work, keeping the original works for the server but not for clients...
Halp 😭

hearty pond
exotic mango
hearty pond
exotic mango
#

great minds πŸ₯Έ

#

my last idea is to make a TArray of all characters in the world...

#

and do the whole function within a for loop

exotic mango
#

I tried this and it didn't work 😦

void AWeapon::InteractableFound_Implementation()
{
    //BlasterOwnerCharacter = Cast<ACosmicBlasterCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));

    TArray<AActor*> FoundCharacters;
    UGameplayStatics::GetAllActorsOfClass(GetWorld(), ACosmicBlasterCharacter::StaticClass(), FoundCharacters);

    for (AActor* Actor : FoundCharacters)
    {
        ACosmicBlasterCharacter* BlasterCharacter = Cast<ACosmicBlasterCharacter>(Actor);
        if (PickupWidget && BlasterCharacter)
        {
            GEngine->AddOnScreenDebugMessage(-1, 8.F, FColor::FromHex("#FFD801"), __FUNCTION__);
            ShowPickupWidget(true);

            if (WeaponType == EWeaponType::EWT_Flag && BlasterCharacter->GetTeam() == Team) return;

            if (BlasterCharacter->IsHoldingTheFlag()) return;

            BlasterCharacter->SetOverlappingWeapon(this);
        }

        else return;
    }


}
exotic mango
#

okay so it worked for the server but not the client...

#

sorry i'ma just think out loud and you can have a fun read when you're around πŸ˜‚

#

okay so it works for the server but not the client...

#

okay had to make a server RPC for interact AND IT WORKS

hearty pond
#

@exotic mango That's awesome, I'm super happy to hear that it works!!!

exotic mango
#

and getting a hold of the character was a pain in the rear, but got there in the end! I have noticed it doesn't work on occasion (the line trace actually acknowledging the weapon, but it works 9/10 times so it's better than I had!) Thanks again for all of this! ❀️

hearty pond
exotic mango
#

ooh I have just noticed, that the pickup widget only clears if I look at another weapon, so if I look at one and run away, the widget stays visible

#

also the box collision is much more reliable than the sphere! gonna change them all lol

hearty pond
#
void AInteractableBase::OnClearViewport()
{
    if (IsValid(InteractableFoundWidget))
        if (InteractableFoundWidget->IsInViewport())
            InteractableFoundWidget->RemoveFromViewport();
}```
exotic mango
#

yeah so I created the delegate and I call it here

#
void ACosmicBlasterCharacter::ScanForInteractables()
{
    FHitResult HitResult;

    FVector Start = FollowCamera->GetComponentLocation();
    FVector End = Start + (FollowCamera->GetComponentRotation().Vector() * 450.F);

    TArray<TEnumAsByte<EObjectTypeQuery>> TraceObjects;
    TArray<AActor*> ActorsToIgnore;
    ActorsToIgnore.Add(this);
    ActorsToIgnore.Add(Combat->EquippedWeapon);
    ActorsToIgnore.Add(Combat->SecondaryWeapon);

    TraceObjects.Add(UEngineTypes::ConvertToObjectType(ECC_WorldStatic));
    TraceObjects.Add(UEngineTypes::ConvertToObjectType(ECC_WorldDynamic));

    const bool bIsInteractable = UKismetSystemLibrary::LineTraceSingleForObjects(
        GetWorld(),
        Start,
        End,
        TraceObjects,
        true,
        ActorsToIgnore,
        EDrawDebugTrace::None,
        HitResult,
        true
    );

    if (bIsInteractable)
    {
        if (HitResult.GetActor())
        {
            if (HitResult.GetActor()->GetClass()->ImplementsInterface(UInteractInterface::StaticClass()))
            {
                IInteractInterface::Execute_InteractableFound(HitResult.GetActor());
                //GEngine->AddOnScreenDebugMessage(-1, 8.F, FColor::FromHex("#FFD801"), __FUNCTION__);
            }
        }
    }
    else
    {
        Clear.Broadcast();
    }
}
hearty pond
#

Prefect!

exotic mango
#

but it isnt working unless i look at another weaponπŸ˜‚

hearty pond
exotic mango
#
void AWeapon::OnClearViewport()
{
    if (PickupWidget)
    {
        ShowPickupWidget(false);
    }
    else return;
}
hearty pond
#

That's probably why it's returning out of the function. That return is kind of redundant. What's inside ShowPickupWidget

exotic mango
#
void AWeapon::ShowPickupWidget(bool bShowWidget)
{
    if (PickupWidget)
    {
        PickupWidget->SetVisibility(bShowWidget);
    }
}
#

I removed the else return; and it didn't make a difference, which I get I guess haha

hearty pond
#

Yeah I just checked my code and I have mine setup the same as yours

exotic mango
#

I mean, I did copy your code πŸ˜›

hearty pond
#

Lol well I just have ```cpp
void APickupBase::OnClearViewport()
{
if (IsValid(PickupWidgetComponent))
PickupWidgetComponent->SetVisibility(false);

else
    return;

}```

exotic mango
#

I like curly braces, okay πŸ˜‚

hearty pond
#

Lol well fair enough

exotic mango
#

hey @hearty pond, I'm so sorry to be a pain, I know you're really busy, so please don't rush to respond!
I was popping some debugs in the code because the pickup widget just didn't seem to clear, I put a UELOG inside

    else
    {
        Clear.Broadcast();
    }

and the else case is being called, but I put one inside OnClearViewport() and that isn't being called... I don't think I've bound my delegate properly, but I'm not 100% sure on where I should be doing that?

hearty pond
#

@exotic mango Not a pain at all. Give me like 10 minutes and I'll be on

exotic mango
#

Honestly no rush! Thank you 😊

hearty pond
exotic mango
#

hahahaha πŸ˜‚ well if it helps, I noticed that I'm not binding the delegate to the OnClearViewport() function, which I think I need to do πŸ˜…

exotic mango
hearty pond
exotic mango
#

in the weapon BeginPlay()

hearty pond
#

Wherever you want to bind to that Delegate

#

With MultiCast you bind as many functions to it as you want

exotic mango
#

damn, yeah that I haven't done, but it puts me back to square one with how tf do I get a hold of the Character in the weapon class πŸ˜… my TArray thing worked, is that something I can do in begin play?

hearty pond
exotic mango
#

mmmkayyyy

hearty pond
#

Thanks Mr Mackey πŸ˜‚

exotic mango
#

who? πŸ˜‚

hearty pond
exotic mango
#

ahh, yeah never could get into South Park...

hearty pond
#

Ah lol he always says mkay so lol of course that's how I read that πŸ˜‚

exotic mango
#

haha fair πŸ˜‚

hearty pond
#

Lol anyway like I said I asked someone waaaaaay smarter than I am about this and he's getting back to me at some point

exotic mango
#

that's fine πŸ™‚

#

thinking aloud here...

hearty pond
#

Think away

exotic mango
#

but instead of having OnClearViewport, could I bind the delegate to within the if check of my SetOverlappingWeapon function, where we set the Widget to false? πŸ€”

#
void ACosmicBlasterCharacter::SetOverlappingWeapon(AWeapon* Weapon)
{
    if (IsLocallyControlled() && OverlappingWeapon)
    {
        OverlappingWeapon->ShowPickupWidget(false);
    }

    OverlappingWeapon = Weapon;

    if (IsLocallyControlled())
    {
        if (OverlappingWeapon)
        {
            OverlappingWeapon->ShowPickupWidget(true);
        }
    }
}
#
void ACosmicBlasterCharacter::SetOverlappingWeapon(AWeapon* Weapon)
{
    if (IsLocallyControlled() && OverlappingWeapon)
    {
        OverlappingWeapon->ShowPickupWidget(false);

        like in here?
    }

    OverlappingWeapon = Weapon;

    if (IsLocallyControlled())
    {
        if (OverlappingWeapon)
        {
            OverlappingWeapon->ShowPickupWidget(true);
        }
    }
}
solid trailBOT
#

@exotic mango

alarm Please refrain from Excessive use of blacklisted words!

exotic mango
#

fml

#

I swear Wick has a vendetta against me

hearty pond
exotic mango
#

but yeah, something along the lines of can I determine which character is looking at which weapon so everyone can see their own personal widget and not fight between themselves as to who can see the widget

#

or is that too multiplayer for you? πŸ˜‚

hearty pond
exotic mango
#

I'll find a way πŸ˜‚

hearty pond
#

I have faith in you

exotic mango
#

that makes one of us πŸ˜‚

#

so, with the execute interface function, we can add a param HitResult, can I add a param on the weapon function call to include a character as well?

exotic mango
#

I tried this and it threw a billion errors at me, but I might have just done it wrong and there might be a correct way?
weapon.h
void InteractableFound_Implementation(ACosmicBlasterCharacter* OverlappingPlayer);
weapon.cpp

void AWeapon::InteractableFound_Implementation(ACosmicBlasterCharacter* OverlappingPlayer)
{
    if (OverlappingPlayer == nullptr || OverlappingPlayer->IsLocallyControlled())
    {
        if (PickupWidget && OverlappingPlayer)
        {
            if (WeaponType == EWeaponType::EWT_Flag && OverlappingPlayer->GetTeam() == Team) return;

            if (OverlappingPlayer->IsHoldingTheFlag()) return;

            OverlappingPlayer->SetOverlappingWeapon(this);
        }
    }
#
                if (HitResult.GetActor()->GetOwner())
                {
                    ACosmicBlasterCharacter* OverlappingPlayer = Cast<ACosmicBlasterCharacter>(HitResult.GetActor()->GetOwner());
                    if(OverlappingPlayer)
                    {
                        IInteractInterface::Execute_InteractableFound(HitResult.GetActor(), OverlappingPlayer);
hearty pond
#

It would look something like this in the interface cpp UFUNCTION(BlueprintNativeEvent, BlueprintCallable) void InteractableFound(class ACosmicBlasterCharacter* OverlappingPlayer);

exotic mango
#
    UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
    void InteractableFound(ACosmicBlasterCharacter* OverlappingPlayer);
    virtual void InteractableFound_Implementation(ACosmicBlasterCharacter* OverlappingPlayer) override;
void AWeapon::InteractableFound_Implementation(ACosmicBlasterCharacter* OverlappingPlayer)
{
    if (OverlappingPlayer == nullptr || OverlappingPlayer->IsLocallyControlled())
    {
        if (PickupWidget && OverlappingPlayer)
        {
            if (WeaponType == EWeaponType::EWT_Flag && OverlappingPlayer->GetTeam() == Team) return;

            if (OverlappingPlayer->IsHoldingTheFlag()) return;

            OverlappingPlayer->SetOverlappingWeapon(this);
        }
    }
}
    if (bIsInteractable)
    {        
        if (HitResult.GetActor())
        {
            if (HitResult.GetActor()->GetClass()->ImplementsInterface(UInteractInterface::StaticClass()))
            {
                if (HitResult.GetActor()->GetOwner())
                {
                    ACosmicBlasterCharacter* OverlappingPlayer = Cast<ACosmicBlasterCharacter>(HitResult.GetActor()->GetOwner());
                    if(OverlappingPlayer)
                    {
                        IInteractInterface::Execute_InteractableFound(HitResult.GetActor(), OverlappingPlayer);
                        //GEngine->AddOnScreenDebugMessage(-1, 8.F, FColor::FromHex("#FFD801"), __FUNCTION__);
                    }
                }

            }
        }    
    }
#

omg it actually compiled that time

hearty pond
#

Yay!!! πŸ₯³

exotic mango
#

awh it didn't work 😦

#

we got too excited too early πŸ₯²

#

I think I've spotted the issue:

HitResult.GetActor()->GetOwner()

this isn't going to return the owner of the line trace is it, it's returning the owner of the weapon...

hearty pond
#

Ah, yes that's correct

exotic mango
#

okay okay okay

#

I have it working mostly

#

my only issue now is for the client the widget turns on and off quickly

#

the client is turning it on, but the server then turns it off πŸ€”

hearty pond
#

Lol this is where I'm totally useless πŸ˜…

exotic mango
#

again, you and me both πŸ˜‚

#

on the plus side

#

I got your super awesome code to work

#

yaaaaaaaaaaay

#

although I didn't use the delegate πŸ˜…

hearty pond
#

Hey that's ok, I always share my code with the caveat that one should modify it to their needs and not just copy / paste it

exotic mango
#

Well I super duper appreciate it, although it's still a little finicky and I assume that's because it's multiplayer, it's a way better solution than overlapping spheres and I'll be able to implement this in the future 😊

hearty pond
#

I promise the more you mess with it and learn you'll tweak it so it fits your needs

exotic mango
#

and tweak I shall!

#

Thank you again, I do not promise I won't be back with further questions 😊 ❀️

hearty pond
#

Please do, I mean that anytime hmu

hearty pond
#

Lol also wanted to share that code came from a lot of research and reading the docs. I plan to actually change it over to run on asynchronous line trace instead

tropic chasm
#

This is a good solution. Another option, that I'm considering trying, is to maintain an array of currently overlapped items and default to either the closest, or the closest to the targeting vector. Defaulting to last overlap is an easy option that I’ll probably try first. These solutions also have the advantage of not having to precisely target the item with a line trace - although there are scenarios where that might be preferable.

tropic chasm
#

I quickly implemented the array solution where I select the last overlapped item. This solution seems to work really well, and is pretty simple. It would be easy to select the item closest to either the aim vector or character's forward vector instead. No line traces required!

// .h file
    TArray<AProItem*> OverlappingItems;

    UPROPERTY(ReplicatedUsing = OnRep_OverlappingItem, VisibleInstanceOnly)
    AProItem* OverlappingItem;

// .cpp file
void AProEchoCharacter::SetOverlappingItem(AProItem* Item)
{
    OverlappingItems.AddUnique(Item);
    UpdateOverlappingItem();
}

void AProEchoCharacter::RemoveOverlappingItem(AProItem* Item)
{
    OverlappingItems.Remove(Item);
    if (Item == OverlappingItem)
    {
        UpdateOverlappingItem();
    }
}

void AProEchoCharacter::UpdateOverlappingItem()
{
    AProItem* Item = nullptr;
    if (OverlappingItems.Num() > 0)
    {
        // Select last item added
        Item = OverlappingItems[OverlappingItems.Num() - 1];
    }

    if (Item != OverlappingItem)
    {
        if (OverlappingItem)
        {
            OverlappingItem->ShowPickupWidget(false);
        }

        OverlappingItem = Item;
        if (IsLocallyControlled() && OverlappingItem)
        {
            OverlappingItem->ShowPickupWidget(true);
        }
    }
}