How to use mouse click to change current pawn under control?


I’m trying to make a chess game where there are two players and 16 pawns per player. I’m under the impression that this should happen in PlayerController class by unpossessing & (re)possessing the pawns, but where/how can I get the reference of all the pawns in the playercontroller?


You have to iterate through all of the pawns in the world, unfortunately. You can look for subclasses and there are ways to modify your iterator to find specific classes, but in code its the only way.

for (TActorIterator<AActor> ActrItr(GetWorld()); ActrItr; ++ActrItr)
      ActorNameString = ActrItr->GetName();
      if (ActorNameString == something)
         ContainerForPawn = ActrItr;

Obviously, it needs more logic than this to get to your final project, but the for loop iterator I provided is a basis for what you need. More info on actor iterators is here:

To add a bit more to help you, since your top says mouse click.

What you can do is make a TArray of pointers to the correct pawns, iterate through the pawns in the world and add the correct ones using similar syntax as above to your array.

ActrItr has a GetLocation function, so each index in your array will have it as well. So you can call PawnArray[1]->GetLocation();, then on mouseclick get its location, see if it collides with one of the pointers in your TArray, then if it does, use that index in the array as the pawn to pass to the possess function.

So let’s say I am making a RTS game. Of course there are lot’s of units. Let’s say a total of 100 uits for 2 players on a multiplayer match. That means for each click first all these other pawns has to be went through ? Or is that a expensive thing to do ? How would performance fare in really crowded scenes ?

Thanks and sorry for interrupting :

Actor iterating is very expensive, you only really want to do it during a BeginPlay() or similar. This is because most things you place in the world are based off AActor, so a typical map has thousands of actors.

In the case of an RTS, where most units are spawned dynamically, I would create a pointer TArray<APawn> per player of their units at map start, and then every time you spawn a unit, add it to this list of pointers. This way you have your dynamic list of pawns of only units, and never have to iterate through the world. TArray also has some very nice features for iterating through the array quickly.

This would leave you with a TArray of 100 units, and one TArray per player. Since each of the indexes of the array is a pointer to a pawn, you should have all base functionality as if each one was the pawn, so you can do all of your AI logic, Movement, and other things using a base PlayerUnits[index]-> call.

More info on TArrays and Dynamic Arrays in general is here:

Oh thanks mate :smiley: So it is possible.

I am a happy panda now :smiley:

Always happy to help. I also added in a quick edit after your post there with some more info on TArrays and whatnot to hopefully help you along your journey.

Thank you kind ser :smiley:


Thanks. I’m wondering why the mouse (X & Z) coordinates are significantly different from the position of the actor if I click on top of the actor? On the other hand the Y coordinate is matching. I’m using the following code to get the positions:

FVector actorLoc, mouseLoc, mouseDir;
actorLoc = ActrItr->GetActorLocation();
DeprojectMousepositionToWorld(mouseLoc, mouseDir);

I’m now attempting to override ReceiveActorOnClicked() but the event appears to not fire. I’ve set bEnableClickEvents to true in the custom PC.

void AChessPawnCharacter::ReceiveActorOnClicked() {
    GEngine->AddOnScreenDebugMessage(-1,15.0f,FColor::Red,"Actor clicked");

Is there some obvious mistake?

bEnableClickEvents is a uint32 so instead of true set it to 1 instead. At least, thats what Rama mentions here:

It appears to have no effect. So just to summarize in order to print to editor screen when clicking on the character using ReceiveActorOnClicked() I’m using the top down template project and I’ve added these lines:


  void ReceiveActorOnClicked() override;


void AChessPawnCharacter::ReceiveActorOnClicked() {
    GEngine->AddOnScreenDebugMessage(-1,15.0f,FColor::Red,"Actor clicked");


AChessPawnPlayerController::AChessPawnPlayerController(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
  bEnableClickEvents = 1;

Is there anything else that should be done or is something wrong?