x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Respawn an Actor?

Lets say I pick up a sword on the ground called ASword. To remove it from the world I could call Destroy on ASword but that would prevent me from being able to re-spawn it into the world. I tried to call RemoveActor from UWorld but that did nothing.

What is the proper way to "Re-Spawn" an actor?

Below is my current implementation: This is how I add an item to my inventory, It gets called when NotifyActorBeginOverlap() is called by the item (a sword in this case). I first check to make sure it was successfully added to the inventory before destroying/removing the item.

 bool SEtherItemGrid::AddItem(ABaseItem* itemToAdd)
 {
     UWorld* theWorld = itemToAdd->GetWorld();
     bool itemWasAdded = false;
     int32 firstEmptySlotIndex = -1;
 
     if (itemToAdd != NULL)
     {
         //Get the index of the first available slot.
         firstEmptySlotIndex = this->FindFirstAvailableSlot();
 
         if (firstEmptySlotIndex >= 0 && firstEmptySlotIndex < this->GetNumSlots())
         {
             //Add the item to the empty slot we just found.
             itemWasAdded = this->AddItem(firstEmptySlotIndex, itemToAdd);
 
             //Check to see if the item was added successfully.
             if (itemWasAdded == true)
             {
                 //Remove item from the world since it was successfully picked up.
                 itemToAdd->Destroy();
             }
         }
     }
 
     return itemWasAdded;
 }

In my inventory, I have the option to right click an item and drop it. In other words this will remove the item from the inventory and then attempt to re-spawn it into the world. Here is what the code looks like for that:

 void AStandardCharacter::DropItemFromInventory(ABaseItem* item)
 {
     UWorld* theWorld = this->GetWorld();
     ABaseItem* removedItem = NULL;
     FRotator spawnRotation;
     FVector spawnLocation;
     
     //Remove the item from the inventory.
     if (item != NULL && myInventory.IsValid() == true)
     {
         removedItem = myInventory->RemoveItem(item);
 
         //Take the removed item and re-spawn it into the world.
         if (removedItem != NULL)
         {
             if (theWorld != NULL && itemToDrop != NULL)
             {
                 spawnRotation = this->GetActorRotation();
                 spawnLocation = this->GetActorLocation();
 
                 //Spawn the item at the spawnLocation.
                 theWorld->SpawnActor<ABaseItem>(itemToDrop->GetClass(), 
                                                 spawnLocation, 
                                                 spawnRotation);
             }
         }
     }
 }

When Attempting to drop the item, nothing happens and I get a warning saying "SpawnActor failed because the spawned actor IsPendingKill".

So what Is the proper way to do this?

Product Version: UE 4.10
Tags:
more ▼

asked Feb 01 '16 at 03:22 AM in C++ Programming

avatar image

Katianie
151 27 20 29

avatar image sulibarri17 Feb 05 '16 at 02:29 AM

Literally this is all I do: PrimaryWeapon->SetActorHiddenInGame(true); SecondaryWeapon->SetActorHiddenInGame(false); and if you want it to appear somewhere, you can use SetActorLocation(LocationVector).

But I need to know what you mean by "drop." Do you mean spawn a new item, with physics enabled? Because if you just make it invisible without physics or collision, it will be lying around somewhere in your world (which I guess is ok), but if you want to spawn a new one, you can do that too. But your "PendingKill" problem comes from trying to spawn an actor you've already called Destroy() on.

I don't know if SetActorHiddenInGame() has anything to do with the physics being on or off.

avatar image Katianie Feb 05 '16 at 12:42 PM

What I mean by "drop" is to take the item and make it appear in-front of the character. Since I don't want to create a new item, I just want to "re-spawn" it so yea just SetActorHiddenInGame would work but you also need to disable collision. If you don't disable collision you will be able to keep picking up the weapon even though its invisible.

The issue I'm having now is after re-enabling collision, the sword keeps falling though the floor. I try to spawn it above the floor but it insists on falling though the floor when I un-hide it and re-enable collision.

(comments are locked)
10|2000 characters needed characters left

4 answers: sort voted first

Could you make a temporary weapon variable? Like this:

ABaseItem* TemporaryItem; TemporaryItem = ItemToDrop; ItemToDrop->Destroy();

And then when you need to spawn it again, spawn the temporary item? You shouldn't get the pendingKill problem that way.

more ▼

answered Feb 05 '16 at 02:34 PM

avatar image

sulibarri17
71 1 3 6

avatar image Katianie Feb 06 '16 at 08:57 PM

That might solve the pendingKill problem but that's not necessarily what I want. If I destroy an item, lets say in the future that item has enchantments put on it. The enchantments would be lost because you would be creating a new item entirely.

My initial understanding of Destroy was slightly incorrect, now that I understand that destroy literally means destroy the item and remove it from memory, that's not exactly what "dropping" an item is.

avatar image sulibarri17 Feb 06 '16 at 10:49 PM

That's right, calling Destroy() will make pointers to that object null. But why don't you just create a function that creates a temporary and then populates any variables based on the item you're about to destroy? Like if you have a magical damage buff for instance, create a temporary and make its damage buff equal to the damage buff of the item you're about to destroy, then destroy it. Now you have an exact copy of that item stored in your character (or wherever you store it) class and then when you want to bring it back, simply spawn that temporary.

Edit: Actually, as soon as you copy all the variables from the item into the temporary, then destroy, simply set the pointer to the destroyed item equal to the temporary. Then you have an exact copy stored as your item variable. Like this:

ABaseItem* Temporary; Temporary = ItemToDestroy; Temporary->DamageBuff = ItemToDestroy->DamageBuff; ItemToDestroy->Destroy(); ItemToDestroy = Temporary;

You can do it this way. I do this in my project. Now your item will no longer be in the world, but you can re spawn it any time, with all the parameters and enchantments stored on it.

avatar image Katianie Feb 07 '16 at 12:28 AM

That doesn't sound like the proper way to do it. I thought the proper way was to hide it and then un-hide it like you suggested previously, that's what I've been trying to get working. Doing it this way might work but it seems like we're going around logic we should be enforcing.

avatar image sulibarri17 Feb 07 '16 at 01:33 AM

One way in't more proper than another. I just said you could do that because I understood what you meant by drop, meaning you wanted to re spawn. But as far as the actor falling through the floor, have you tried the following (after setting visible to true and setting the actor location):

 TheWeaponRootComponent->SetCollisionProfileName(TEXT("BlockAll"));

Or whatever component you have set up for collision. Be it a box or a capsule etc.

avatar image Katianie Feb 07 '16 at 02:21 AM

Nah I tried that, I create the bounding capsule sub-object in the constructor for the item but I don't think I should have to re-construct (call the constructor on) the actor just to un-hide the actor. I tried doing what you wrote but all that's happening now is it re-appears in the same spot I originally picked it up at so its not setting the location, and in addition to that its not detecting collisions after I do that.

avatar image Katianie Feb 07 '16 at 07:02 PM

I said screw it and tried what you suggested and it worked exactly as I want it to! My only concern is this; why does this work? Having juggler = itemToAdd takes the contents of itemToAdd and copies them into juggler, making juggler point to the same location. My line of thought was that since juggler is pointing to the same location, it would get destroyed anyway because we simply have two pointers pointing to the same memory location. But as we see below, my assumption is apparently wrong.

So what exactly is happening when we do "temporary = itemToAdd"? Below is my full code for when I remove an item from the world.

 bool SEtherItemGrid::AddItem(ABaseItem* itemToAdd)
 {
     bool itemWasAdded = false;
     int32 firstEmptySlotIndex = -1;
     ABaseItem* juggler;
 
     if (itemToAdd != NULL)
     {
         //Get the index of the first available slot.
         firstEmptySlotIndex = this->FindFirstAvailableSlot();
 
         if (firstEmptySlotIndex >= 0 && firstEmptySlotIndex < this->GetNumSlots())
         {
             //Add the item to the empty slot we just found.
             itemWasAdded = this->AddItem(firstEmptySlotIndex, itemToAdd);
 
             //Check to see if the item was added successfully.
             if (itemWasAdded == true)
             {
                 juggler = itemToAdd;
                 itemToAdd->Destroy();
                 itemToAdd = juggler;
 
                 //Clean up temporary variable copy.
                 juggler->Destroy();
             }
         }
     }
 
     return itemWasAdded;
 }
avatar image sulibarri17 Feb 07 '16 at 08:49 PM

Good! Yes I can see how you'd expect that to happen. But what's happening is you're declaring a new pointer to the class of ABaseItem or whatever class you want, and when you say juggler = itemToAdd, you're copying itemToAdd and you're making juggler point to the copy. I suspect it's an overload of the = operator. That's only my speculation. But I take advantage of it! Glad it worked. I really wanna know why you the item falls through the floor when visibility is toggled though. That's weird :/ I'll be looking into that when I get the time. Edit: does it un-make this an answer when I comment on it? Cuz it's not green anymore. Just wondering haha.

avatar image Katianie Feb 07 '16 at 09:10 PM

Yea it did make it un-green, that was weird, I'll make sure you get credit for it. Add me on Skype (Katianie9).

That's fine I just wanted to make sure I still understand pointers correctly, Because I printed out the memory addresses of the temporary and the itemToAdd and they were the same. I guess in the case of unreal engine, it makes a copy and has the pointer point to that....but then why would the addresses be the same? Perhaps unreal is doing some behind the scenes logic to accomplish this.

(comments are locked)
10|2000 characters needed characters left

Do you have a particular root component selected? I know there can be issues if you don't. Try these specific settings (I'm assuming your root component is something called capsule)

 Capsule->SetSimulatePhysics(true);
 Capsule->SetCollisionObjectType(ECC_WorldStatic);
 Capsule->SetCollisionProfileName(TEXT("BlockAll"));

These are literally the settings I have on weapon items that I drop in the world. They fall and tumble around on the ground. Sometimes they bounce and fall through the floor, so I have a special code to take care of it. Here's the header and the source file for a weapon called AProjectile, which spawns with AddForce and AddTorque so it flies and spins, and notice my timer triggered function that detects if it falls through the floor and stops it if it does so. You must select the mesh in the blueprint (I chose not to hardcode the mesh into the code).

Header:

 // Fill out your copyright notice in the Description page of Project Settings.
 
 #pragma once
 
 #include "GameFramework/Actor.h"
 #include "Projectile.generated.h"
 
 UCLASS()
 class MELEE_API AProjectile : public AActor
 {
     GENERATED_BODY()
     
 public:    
 
     FTimerHandle TimerHandle;
 
     void TurnOffCollision();
 
     bool bHasTimerTicked;
     
     UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = Projectile)
     USkeletalMeshComponent* Mesh;
 
     UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = MeleeWeapon)
     UBoxComponent* Capsule;
 
     
     // Sets default values for this actor's properties
     AProjectile(const FObjectInitializer& ObjectInitializer);
 
     // Called when the game starts or when spawned
     virtual void BeginPlay() override;
     
     // Called every frame
     virtual void Tick( float DeltaSeconds ) override;
 
     
     
 };
 

and source:

 // Fill out your copyright notice in the Description page of Project Settings.
 
 #include "Melee.h"
 #include "Projectile.h"
 
 
 // Sets default values
 AProjectile::AProjectile(const FObjectInitializer& ObjectInitializer) :
 Super(ObjectInitializer.SetDefaultSubobjectClass<UMovementComponent>(TEXT("Movement")))
 {
      // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
     PrimaryActorTick.bCanEverTick = true;
 
     Mesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Mesh"));
     
 
     Capsule = CreateDefaultSubobject<UBoxComponent>(TEXT("Capsule"));
     RootComponent = Capsule;
     Mesh->AttachTo(RootComponent);
     Capsule->SetSimulatePhysics(true);
     Capsule->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);
     Capsule->SetCollisionObjectType(ECC_WorldStatic);
     SetActorEnableCollision(true);
     Capsule->SetCollisionProfileName(TEXT("BlockAll"));
 
     bHasTimerTicked = false;
 
 }
 
 // Called when the game starts or when spawned
 void AProjectile::BeginPlay()
 {
     Super::BeginPlay();
 
     
     Capsule->AddForce(FVector(2000, -200, 20000), NAME_None, true);
     Capsule->AddTorque(FVector(20000, 20000, 0), NAME_None, true);
 }
 
 // Called every frame
 void AProjectile::Tick( float DeltaTime )
 {
     Super::Tick( DeltaTime );
 
     if (GetActorLocation().Z <= 3500.f)
     {
         if (Capsule->GetComponentLocation().Z <= -12.0f)
         {
             SetActorEnableCollision(false);
             Capsule->SetSimulatePhysics(false);
             Capsule->SetCollisionProfileName(TEXT("OverlapAll"));
 
             FRotator NewRotation = GetActorRotation();
 
             NewRotation.Yaw = 0.f;
             SetActorRotation(NewRotation);
             FVector NewActorLocation = GetActorLocation();
             NewActorLocation.Z = 0.f;
             SetActorLocation(NewActorLocation);
             return;
         }
         if (!bHasTimerTicked)
         {
             GetWorldTimerManager().SetTimer(TimerHandle, this, &AProjectile::TurnOffCollision, 3.5f, false);
         }
         bHasTimerTicked = true;
     }
 
 }
 
 void AProjectile::TurnOffCollision()
 {
     SetActorEnableCollision(false);
     Capsule->SetSimulatePhysics(false);
     Capsule->SetCollisionProfileName(TEXT("OverlapAll"));
     
 }
 

I hope this helps.

more ▼

answered Feb 07 '16 at 05:31 AM

avatar image

sulibarri17
71 1 3 6

avatar image sulibarri17 Feb 07 '16 at 05:37 AM

Correction: The tick function detects if it falls through the floor by seeing if its Z location ever goes past -12.f and if so it turns off physics. The timer function is what allows it to tumble realistically before turning off physics. This is so the character doesn't bump into its collision mesh when it's on the ground. Also, ignore that first if statement. Originally I was using it to test something but for our purposes here it doesn't do anything.

Another thing to note: I'm using a box component for the collision, and I'm calling it Capsule. Hope that doesn't confuse you. Before it was a capsule component and I changed it but kept the name.

Hope that helps.

(comments are locked)
10|2000 characters needed characters left

Make a Spawner that will spawn the object once it picked up and place it insted of placing ASword it self. you can make it universal by making UClass varable which you can set in details tab once you place it on the level.

Or keep ASword instance as inventory item in character, just deactivate the visual components make it spawn it self in spot it been picked up (so you need to keep that information).

more ▼

answered Feb 01 '16 at 06:42 AM

avatar image

Shadowriver
35.2k 921 166 1098

avatar image Katianie Feb 01 '16 at 02:31 PM

Can you provide an example of this or some reference? I get the idea but those two sentences alone are not enough for me to go on.

Currently I'm Destroying the item on pickup and then attempting to use world->spawn actor but I'm getting an error that tells me "IsPendingKill"

avatar image Shadowriver Feb 01 '16 at 02:34 PM

Can you show me the that bad code? Tell me where it is located too

avatar image Katianie Feb 04 '16 at 07:43 PM

Edited my original answer to include the code.

(comments are locked)
10|2000 characters needed characters left

In a project I'm working on, I have my character pick up a sword and then pick up a second, but on a key press he can switch weapons from the currently equipped weapon to the secondary weapon. Since I want to bring the weapon back after I've switched it, I don't want to destroy it, just make it invisible. So I use SetActorHiddenInGame(true) from within the weapon class to hide it, and SetActorHiddenInGame(false) to bring it back. Both weapons must be stored as separate variables and must exist in the world in some class (the character class is perfectly fine) just have in your character's .h file something like AWeapon* weapon1; AWeapon* weapon2; etc. or make blueprints of them and store UClass pointers. You can call Destroy() and spawn new ones each time, but when you do this, you're not actually brining the weapon back, but creating a new one altogether. So if you had any info stored in that weapon it would spawn with default values for everything, u less you stored those values somewhere like in the character and set them in the new weapon class upon spawning. If you wanna spawn new actors rather than toggle their visibility, use SpawnActor.

more ▼

answered Feb 02 '16 at 02:45 PM

avatar image

sulibarri17
71 1 3 6

avatar image Katianie Feb 03 '16 at 02:09 PM

I'm working on what you suggested, If it works I will accept your answer.

avatar image Katianie Feb 04 '16 at 02:23 PM

Please provide a code example of this if you want me to accept this as an answer. I did what you suggested (along with disabling actor collision) and it works for actors(items) that are NOT simulating physics. If I try to "drop" the simulated physics actor, it is invisible.

I thought that perhaps it was re-appearing under the floor but it wasn't, or it was re-appearing somewhere else on the map but that does not seem to be the case either.

Update: Above is the requested code, I kind of gave up on trying to do that after the Destroy work around worked perfectly for me.

(comments are locked)
10|2000 characters needed characters left
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question