Floating Damage Text in UI (using Slate or Canvas)

Here is the problem. I want to create floating damage text. When target takes damage, around it there will be floating numbers indicating the damage done by causer (player).

So far I using Slate I got it the point, where the Widget is attached at more or less right place, and show damage. But there are number of issues with my current implementation, which I’m trying to solve:

  1. I want text to automatically disappear after set amount of time, so it can make space for incoming numbers.
  2. When radial damage is applied, only last damaged target have displayed text.

Should I pack damage into single widget (which will do nothing expect display text and maybe animate it), and spawn new widget every time target takes damage, and then destroy it after set amount of time ?
If so, how do I spawn new widget at runtime ?

I personally think it would be much easier to do using canvas since you can use Project/Deproject functions. In Slate its fairly difficult to map 3D coordinates to 2D viewport but I assume you’ve already done it.

To animate widgets in Slate (opacity in this case) you can use FCurveSequence. Take a look at SNotificationList.cpp to see how it works. You can also find there dynamic widgets spawning management.


Projection part was actually the easiest part of it (;

Now I’m facing more complicated issues like:

  1. When I damage one actor I want text to float over it. Now When I damage one actor and then another, text is displayed over another actor.
  2. Even worse with Radial Damage. Text is displayed over last actor to which damage has been applied.
  3. Widget currently are not automatically destroyed. Is there a way to setup widget to automatically destroy it self after set time ?

For 1 and 2 my idea was to draw widget directly from target. But there is problem, that you can’t use Project outside of Draw Loop in HUD, and without it, I can just as well draw in middle of screen from HUD anyway.

I’m not sure how do you display your floating text right now, a code snippet would be helpful. But I think that if you have your projection done then everything else you need (fade in/out of a widget and dynamic adding/removing widgets from screen) can be found in SNotificationList.cpp


	HUDWidget->FCTPosition.BindUObject(this, &AARHUD::GetFCTPosition);

	if (OwnerChar.IsValid())
		if (Canvas)
			//FVector temp = Canvas->Project(OwnerChar->Attributes->ChangedAttribute.DamageTarget->GetActorLocation());
			FVector temp = Canvas->Project(OwnerChar->Attributes->UIDamage.Location);
			FCTPosition.X = temp.X;
			FCTPosition.Y = temp.Y;
FVector2D AARHUD::GetFCTPosition() const
	return FCTPosition;

This is where I get position for widget to display.

FVector2D SARHUDWidget::GetFCTPosition() const
	return FCTPosition.Get();

void SARHUDWidget::CreateFCTWidget()
	FVector2D pos = FCTPositionVector.Get();
	TSharedPtr<SCanvas> can = SNew(SCanvas)
		+ SCanvas::Slot()
		//.Position(TAttribute<FVector2D>(this, &SARHUDWidget::GetFCTPosition))
		.Size(FVector2D(400, 400))

And this is where I actually draw it.

The issue is, that I updated position of widget on every frame, and this is a reason why it is moving each time there is new position.

I tried to use fixed position, but it wasn’t working at all is widget has been spawning all over screen.

So from what I can see here is that you store only one FCTPosition all the time. You should have multiple values of positions if you want to display many floating texts.
The other thing is that I dont know what FCTBox is but I’m pretty sure its not a SCanvas you’re creating above. Maybe you should try creating one SCanvas, store reference (MyCanvas) to it and use MyCanvas->AddSlot() .Position(<<UniquePositionForThisFloatingtext>>). Right now it looks like you’re creating canvas with one slot every time you want to spawn floating text and then you add this canvas to some container.

And one more thing, I assume the first code snippet is from HUD’s postRender function. If so, you’re binding FCTPosition every frame by this line

HUDWidget->FCTPosition.BindUObject(this, &AARHUD::GetFCTPosition);

which is just silly ; )

Actually I have if() which check if HUDWidget is valid, If it is, it doesn’t create new one and bind everything every frame (;.

Actually the reall issue is getting the current position of actor, and projecting it directly to screen inside Slate widget. It’s farily trivial to do so in HUD, but not so easy outside of it.

I think using DrawElements to draw text is much better, rather than text widget

Here is solution. More or less.

If you are curious you can check code on my github repo.