Advertisement

Suspiscious double call to GameMode member Function

Started by September 06, 2017 11:00 AM
8 comments, last by MarcusAseth 7 years, 2 months ago

I'm confused by this...

This is my function declaration and definition:


UFUNCTION(BlueprintPure, Category = "Spawn", meta = (BlueprintProtected))
		void RandomEmptySpawnLocation(FVector& Location);

void AWormGameMode::RandomEmptySpawnLocation(FVector& Location)
{
	TArray<int> RowWithSpaceID;
	TArray<int> ColumnWithSpaceID;
	for (auto rowID = 0; rowID < GridSlotsOccupiedMap.Num(); rowID++)
	{
		for (auto columnID = 0; columnID < GridSlotsOccupiedMap[0].BoolRow.Num(); columnID++)
		{
			if (!GridSlotsOccupiedMap[rowID][columnID])
			{
				RowWithSpaceID.Add(rowID);
				ColumnWithSpaceID.Add(columnID);
			}
		}		
	}
	int IDToUse = FMath::RandRange(0, RowWithSpaceID.Num() - 1);
	UE_LOG(LogTemp, Warning, TEXT("IDToUse = %i"), IDToUse)
	int MiddleColumnID = (GridWidth / GridBlockSize - 1) / 2;
	int MiddleRowID = (GridHeight / GridBlockSize - 1) / 2;

	Location.X = (ColumnWithSpaceID[IDToUse] - MiddleColumnID) * GridBlockSize;
	Location.Y = 50.f;
	Location.Z = (-RowWithSpaceID[IDToUse] + MiddleRowID) * GridBlockSize;
}

It simply check the bool map that contains the "Occupied/Empty" status for every grid slots, it stores the empty ones and then it picks one at random to be used as a spawn for pickup or player.

I am spawning 2 things, a pickup and a player, but as you can see from the console output, that function fired 4 times...

A row as ID ranging from 0 to 24 so ID 22 is the purple player, and ID 42 is the red pickup (24 + 18). But, there is nothing at ID 291 and 441...furthermore, I debugged step by step and only 2 spawn events happen, not 4... so why is my function called twice from every spawn event inside blueprint?!

I'll put below also the linkage of the function in blueprint, in case help to answer my doubt.

 

JEymwvO.png

image link

clMYMiN.png

Are you sure that your actual event (Event Spawn Player) isn't being called more than you think? You might be explicitly triggering it twice that you know of, but deeper UE processes (such as duplication for PIE, CDOs) might result in indirect triggering of your event for various reasons. Put a breakpoint in the code and examine the call stacks to see where -- you'll have to pass through several layers of indirection (the BP invokes) which may be tricky to untangle if you've never done it before, but at the bottom of the stack you might find enough different roots to tell what is going on.

BlueprintPure can have an impact, anecdotally, on how the BP compiler evaluates nodes. Try temporarily making the function BlueprintCallable instead and see if that has any impact.

Advertisement

Thanks jpetrie, I replaced the BlueprintPure with a BlueprintCallable, and indeed it solved it, it is run only once :)

But before that, with the BlueprintPure version I've also put a breakpoint and run the Debug tab as you suggested, image below, as you can see Event Spawn Player is called once, but the SpawnActor (engine default node) is being called twice consecutively (and thus summoning my function twice), is this normal behaviour? It has to do with the deeper UE processes you mentioned?

If so, it is safe to assume that BlueprintPure is less performant than just plain Blueprint Callable, based on the fact it runs my function(which internally calls FMath::RandRange and a for loop with roughly 600 iterations) twice at every spawn? 

Another question that arise is, is the second SpawnActor call actually used for something or is thrown away?! Because if it is used, I now have a bug since I'm providing 2 different (random) resoult to the same spawned actor, and I don't know how the engine is using those resoults for... o_O

 

nBEnomB.png

38 minutes ago, MarcusAseth said:

But before that, with the BlueprintPure version I've also put a breakpoint and run the Debug tab as you suggested, image below, as you can see Event Spawn Player is called once, but the SpawnActor (engine default node) is being called twice consecutively (and thus summoning my function twice), is this normal behaviour? It has to do with the deeper UE processes you mentioned?

 

I meant actually break in Visual Studio. That Blueprint Debug tab and associated stack trace is, in my experience, completely useless.

39 minutes ago, MarcusAseth said:

If so, it is safe to assume that BlueprintPure is less performant than just plain Blueprint Callable, based on the fact it runs my function(which internally calls FMath::RandRange and a for loop with roughly 600 iterations) twice at every spawn? 

 

It's not really safe to assume anything about the performance in general. A BP pure function is one where the invocation of the function has no side-effects on the invoking object. This means the compiler can optimize around the node, and organize it, with that assumption in mind. This can result in the compiler calling the pure function more often than it would "need," (for example, once per input pin its attached to, once per time it needs the value, et cetera) because it is safe to do so, and it means the compiler need not reserve any storage for the results of the function. But the BP compiler can't tell how complex the pure node is if the body is native code, so there's no way for it to tell if the pure function is a cheap getter or a complex calculation. If your function is complex and the compiler decides to call it one for every input pin attached to it, you might see performance issues.

The "purity" of pure functions isn't really enforced either. If you, for example, increment a global variable in a pure function, the BP compiler isn't really going to know about that and you might get some interesting results.

52 minutes ago, MarcusAseth said:

Another question that arise is, is the second SpawnActor call actually used for something or is thrown away?! Because if it is used, I now have a bug since I'm providing 2 different (random) resoult to the same spawned actor, and I don't know how the engine is using those resoults for... o_O

 

I don't quite understand this problem description, unfortunately.

I've just realized I'm seeying this "side-effect" a lot but I always fail to understand it, what does "side-effect" represent in this context? :/

Also I think I've managed to debug it from VS, though I don't know what to make of the resoult. Below the image, the two different screenshot are the first and the second time we enter that function, and the stack is the same in both cases (Are we jumping back up the stack?!), also to keep it simple I only spawned 1 thing, the player. 

Quote

I don't quite understand this problem description, unfortunately.

I mean, my function is returning a random Location. If I link it to 1 pin of a SpawnActor, I expect it to return 1 and only 1 resoult. But if behind the scenes it gets called twice, 1 random location is used to set the actor location in the scene, but I have no idea what the second is used for. For what I know, maybe the editor need to keep a copy of the value so that's why it calls it again, but instead of the current value it is getting a new random location, and that's why I'm asking if this is the case that this could create problems.

 

Also a thing interesting to notice:  Even though if I make it BlueprintCallable (thus getting the execution pin) it is called only once as one would expect, if I also make the function "const" (which remove the execution pin, making it identical to BlueprintPure in appearance inside the BPEditor) then it behave like the BlueprintPure, meaning it is called twice per spawn again :|

What should I make out of this? x_x

EDIT: side note: I am trying to give you a Thanks, but when I refresh the page the Thanks gets removed... is this a bug?

QpUZMiF.png

A "side-effect" means that a function changes something as well as just giving you a value back. In mathematics, a function usually has no side-effects. sqrt(x) returns the square root of x to you but nothing changes, no matter how many times I call that function. But if I call decreaseHitpoints(character), then something in that character object is likely to get changed, so it matters how often I call it.

Pure functions with no side-effects are great because the results can be cached forever, they are easily tested, they don't care which order they're called in, etc.

Arguably, functions that include random number generation cannot be pure functions, because they do have hidden side-effects - the random number generator changes state so that it can generate a new value next time. Whether this is relevant or not to UE4 or your situation is probably debatable, but I'd flag this function as Callable rather than Pure, and be done with it.

Just putting this out there: sometimes an event appears to get called multiple times in UE4 because it's running on both the client and the server. Do you have multiplayer enabled?

As for your call stack, obviously that's worthless, unfortunately. I'm used to running from a full source build so I can see where things are called from.

Advertisement

@Kylotan Thank you for the great explanation, much more clear now!

Regarding the multiplayer, I've started a new Blank Project with no starting content, so unless multiplayer is on by default, it should be off (I won't even know where to touch to enable it).

Also making it BlueprintCallable is the way I went, makes me nervous otherwise.

Well, that's being said, below is my first game ever, and is crap :D (I mean is boring)

 Moving on to something else :)

 

Nice job. Looks a lot better than my first game did.

3 minutes ago, Kylotan said:

Nice job. Looks a lot better than my first game did.

Thanks, though that is 95% thanks to the unreal engine team, not me xD

This topic is closed to new replies.

Advertisement