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"

How can I implement delay into my project?

I'm so lost in timers and delegates.. I can't find a clear description on how to use timers, specially the GetWorldTimerManager().SetTimer(....) function and what the arguments inside it refer to or how they are related to each other.

I'm trying to accomplish a simple delay between spawning Actors. In my BeginPlay() function, I have a loop from 0 to 8, inside the loop body it spawns a new actor that is stored in an array (i.e myActor[0] to myActor[8]). I just want to add a delay after each actor is spawned so that not all the actors get spawned at the same time.

-Where do I implement the GetWorldTimerManager().SetTimer(...)

-What are the arguments inside SetTimer(...)

-Does SetTimer() provide delay? How?

-Do I need to create my own Delay function outside BeginPlay()? How does it relate to SetTimer()?

Product Version: Not Selected
Tags:
more ▼

asked Sep 16 '14 at 08:53 AM in C++ Programming

avatar image

CGZee
43 4 8 11

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

2 answers: sort voted first

It seems like you are setting a new timer every time you spawn a cell, and that timer will after one second spawn the actors again.

Instead you need spawn cell to spawn one actor and then set a timer and when the timer executes SpawnCell it will create the next actor, working through the loop one step at a time.

Maybe something like this?

 // in BoardGrid.h
 
 int32 i = 0;
 int32 j = 0;
 
 // .cpp
 
 void ABoardGrid::SpawnCells()
 {
     if(j >= mNumBoardColumns)
     {
             j=0;
             i++
     }
     if( i >= mNumBoardRows)
     {
         return;
     }
     
     if (SpawnedCell != NULL)
     {
         UWorld* const World = GetWorld();
         if (World)
         {
             FActorSpawnParameters SpawnParams;
             SpawnParams.Owner = this;
             SpawnParams.Instigator = Instigator;
 
             const float XOffset = j * CellSpacing;
             const float YOffset = i * CellSpacing;
             const FVector CellLocation = 
                             FVector(XOffset, YOffset, 0.f) + GetActorLocation();
 
             FRotator CellRotation;
             CellRotation.Yaw = 0.f;
             CellRotation.Pitch = 0.f;
             CellRotation.Roll = 180.f;
 
             MainBoardGrid.Rows[i].Columns[j] =
                             World->SpawnActor<ABoardCell>(SpawnedCell,
                                                           CellLocation,
                                                           CellRotation,
                                                           SpawnParams);
             
             MainBoardGrid.Rows[i].Columns[j]->OwningBoard = this;
             MainBoardGrid.Rows[i].Columns[j]->iCellValue = 0;
             
             j++;
             GetWorldTimerManager().SetTimer(this,
                                             &ABoardGrid::SpawnCells,
                                             false,
                                             1.0f);
         }
     }
     else 
     {
         MainBoardGrid.Rows[i].Columns[j] = NULL;
         j++;
         SpawnCells();
     }
     
 }
 
  void ABoardGrid::BeginPlay()
  {
      Super::BeginPlay();    
      SpawnCells();
      
  }




more ▼

answered Sep 17 '14 at 08:17 PM

avatar image

james0x0A
269 10 9 26

avatar image CGZee Sep 18 '14 at 06:46 AM

Aha thank you, much appreciated! That worked!

I'm running into the pitch rotation bug now, discussed here:

https://answers.unrealengine.com/questions/21168/pitch-limited-to-90-90.html

avatar image james0x0A Sep 18 '14 at 12:29 PM

Every time you set an actor's rotation it must recalculate them to an equivalent orientation in the domain of Euler angles. For any given orientation only only one set of angles exist. That means that Pitch will go up to 90 and then any value above that has an equivalent value in inside the Euler domain that is a combination of a new pitch, roll and yaw.

  1. (P: 89, Y:0, R: 0) is great.. everything in the domain.

  2. (P: 91, Y:0, R: 0) is outside the domain

  3. (P: 89, Y:180, R: 180) is equivalent to #2 and inside the domain.

#3 is the value that the actor will use when you give it P:91, 0 , 0. Then if you try to get the pitch and add one you will end up locked around P:90 as yaw and roll swap back and forth from +/- 180 to 0. It is not a bug. This is a property of the equations used for rotations and has been the bane of game developers and 3D artists for a long time.

If you want to post another question, I would be glad to give a more specific answer to whatever issue you might be having, but I do recommend reading up on the maths. Geometry, Trigonometry and Linear Algebra important to understanding the how and why of game engines like UE4. Some of it may be beyond the scope of the UE4 answerhub, but I think we should try to clear up misconceptions about the implementation.

avatar image CGZee Sep 19 '14 at 07:37 AM

Makes sense...thanks for the information!

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

You'll want to do something like:

 GetWorldTimerManager().SetTimer(this, &AActorFunctionBelongsTo::SpawnActor, 1.0f);

This will call spawn actor after one second. You can also add a 'true' parameter after your 'time to wait' variable that will loop the call as well.

Therefore, you could create a recursive function for a simple approach:

 int nNumSpawned = 0; // Probably needs to be in header or something
 GetWorldTimerManager().SetTimer(this, &AActorFunctionBelongsTo::SpawnActor, 1.0f);
 
 void SpawnActor()
 {
     // Spawn Actor here
     nNumSpawned++;
     if (nNumSpawned < 8)
         GetWorldTimerManager().SetTimer(this, &AActorFunctionBelongsTo::SpawnActor, 1.0f);
 }

with SpawnActor being a void with no parameters -- void SpawnActor(); -- Obviously, you can get a lot more in-depth with the timing methods, but this is a simplistic approach which should suit your needs hopefully?

more ▼

answered Sep 16 '14 at 09:47 AM

avatar image

Dune
1.9k 53 40 200

avatar image CGZee Sep 16 '14 at 08:25 PM

Thank you Dune for your answer!

If I understand correctly, SetTimer() will just call a function and delays execution of everything else for a specified period of time (for example 1.0 seconds), correct? Does this stop Tick() from executing as well?

I'm trying to spawn my actor and then have it immediately rotate using the Tick() function.

I also have few questions regarding the code provided above:

  • The first line of code you have in your comment

    GetWorldTimerManager().SetTimer(this, &AActorFunctionBelongsTo::SpawnActor, 1.0f);

Where do I put that line of code? Is it in the BeginPlay() function? Top of the function? Inside the loop body?

  • The overridden BeginPlay() and the SpawnActor functions I have, they both belong to the same class. How will SetTimer() look like...is it something like this: GetWorldTimerManager().SetTimer(this, this::SpawnActor, 1.0f);

  • In the last block of code in your comment, you have SetTimer() called twice...one before the SpawnActor() function and the other is inside it, what's the difference and impact?

I apologize for not being able to test your code yet...will do when I'm in front of my personal PC tonight.

Thanks a lot!

avatar image CGZee Sep 17 '14 at 01:03 AM

When I put SetTimer() inside my SpawnActor() function like so:

 void AMyActorClass::SpawnActor(int rows, int columns)

 {
 
 //Code for two nested for loops to Spawn Actor here
 
 GetWorldTimerManager().SetTimer(this, &AMyActorClass::SpawnActor, 1, true);

 }


I get the following error:

error C2664: 'void FTimerManager::SetTimer(FTimerHandle &,float,bool,float)' : cannot convert argument 2 from 'void (__cdecl AMyActorClass:: )(int,int)' to 'void (__cdecl AMyActorClass:: )(void)'

Looks like SetTimer() is not accepting a function with arguments. Do I need to create a delegate? how? or is there an existing overloaded SetTimer() version of function that accepts functions with arguments?

Still not sure whether I need to use SetTimer() in SpawnActot() or BeginPlay()

avatar image CGZee Sep 17 '14 at 02:16 AM

I changed the SpawnActor(int i, int j) function to SpawnActor() with no parameters then moved all the nested loops required for spawning inside the SpawnActor() so it's not dependent on arguments any more.

I also added the below line inside SpawnActor() function: GetWorldTimerManager().SetTimer(this, &AMyActorClass::SpawnActor, false, 1.0f);

Still no delay....all actors spawn at the same time.

Code looks like this now:

 void AMyActorClass::SpawnActor()
 
  {
  
  //Code for two nested for loops to Spawn Actor here
  
  GetWorldTimerManager().SetTimer(this, &AMyActorClass::SpawnActor, false, 1.0f);
 
  }
 
 void AMyActorClass::BeginPlay()
 {
 Super::BeginPlay();
 SpawnActor();
 }
 

I tried moving SetTimer() to BeginPlay() before calling SpawnActor() but still no delay. The only thing I noticed is Actors keep spawning forever when I change the (false) argument in SetTimer() to (true)

avatar image james0x0A Sep 17 '14 at 04:16 AM

The boolean tells the timer to loop by creating a new identical timer at the end of execution. It will call the function referenced and then reset itself to call again. You can set a looping timer in BeginPlay event and it will call the function until you unset the timer. You never need to explicitly call the function.

The example provided above spawns an actor and keeps a reference to the total number spawned. If that is fewer than 8 it will create a new timer to call SpawnActor again. You should only ever have to call spawn actor once in this example and it will stop on its own.

Remember, all the timer does is wait a specified time and then call the provided function.

avatar image CGZee Sep 17 '14 at 06:33 AM

Thank you james0x0A, makes a lot of sense

Problem is I can't seem to make it work with my code below..I have a dynamic 2D array. I have to loop through rows (i) and columns (j) and spawn the [i][j] element Actor each loop cycle

The problem I'm having is that I can't seem to get delay/SetTimer() to work after each element gets spawned due to loops (most likely)...hopefully code below makes more sense

I modified the code and changed the naming of the class, functions, and variables to make it more readable

 void ABoardGrid::SpawnCells()
 {
     for (int32 i = 0; i < mNumBoardRows; i++)
     {
         for (int32 j = 0; j < mNumBoardColumns; j++)
         {
             if (SpawnedCell != NULL)
             {
                 UWorld* const World = GetWorld();
                 if (World)
                 {
                     FActorSpawnParameters SpawnParams;
                     SpawnParams.Owner = this;
                     SpawnParams.Instigator = Instigator;
 
                     const float XOffset = j * CellSpacing;
                     const float YOffset = i * CellSpacing;
                     const FVector CellLocation = FVector(XOffset, YOffset, 0.f) + GetActorLocation();
 
                     FRotator CellRotation;
                     CellRotation.Yaw = 0.f;
                     CellRotation.Pitch = 0.f;
                     CellRotation.Roll = 180.f;
                     
                     MainBoardGrid.Rows[i].Columns[j] = World->SpawnActor<ABoardCell>(SpawnedCell, CellLocation, CellRotation, SpawnParams);
                     MainBoardGrid.Rows[i].Columns[j]->OwningBoard = this;
                     MainBoardGrid.Rows[i].Columns[j]->iCellValue = 0;

 
                     //GetWorldTimerManager().SetTimer(this, &ABoardGrid::SpawnCells, false, 1.0f);
                     
 
                 }
             }
 
             else
                 MainBoardGrid.Rows[i].Columns[j] = NULL;
         }
 
     }
     
     
 }
 
 void ABoardGrid::BeginPlay()
 {
     Super::BeginPlay();    
     SpawnCells();
     
 }
 

(comments are locked)
10|2000 characters needed characters left
Viewable by all users
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