| Home Page | Recent Changes | Preferences


Displaying Rotated Text on a Canvas

The Canvas class provides methods for drawing text and textures, but unfortunately there is no way to rotate this text or those textures when they're rendered on the Canvas. Occasionally such a feature would be handy, though — when a lot of information has to be rendered on limited screen space, for instance.

With a bit of imagination, however, it's in fact possible to render rotated text on the Canvas:

  • You can draw a mesh on a Canvas in any orientation you like using the DrawClippedActor method.
  • Use a flat, quadratic mesh, and skin it with a ScriptedTexture.
  • Render the text you want to display on that ScriptedTexture.

The RotatedText actor implements just this functionality.


You can download the RotatedText actor, its source code and all related files here: http://mb.link-m.de/download/RotatedText.zip

Unzip this archive into a subdirectory of your Unreal Tournament base directory. Then copy everything from the System subdirectory in that directory into your actual System directory.

If you want to have a sneak preview of how it looks, start a game and select the "RotatedText Test Mutator" mutator. You'll see two strings revolving around your crosshair, one of them showing your player name, the other one the elapsed time.


How It Works

First Step

Create a simple, two-dimensional, quadratic mesh in MilkShape (or another 3D modelling program), consisting of two triangular faces, and skin it with a single 256x256 pixel texture.

Second Step

See ScriptedTexture (UT) for how to create your own custom ScriptedTexture from a given base texture.

Third Step

The RotatedText source code describes how the mesh and the ScriptedTexture (UT) are used to display rotated text on the Canvas:

// ============================================================================
// RotatedText
// Copyright (c) 2002 by Mychaeel <mychaeel@planetunreal.com>
// Displays rotated text on a Canvas, using DrawClippedActor, a simple mesh
// and a ScriptedTexture.
// Create a RotatedText actor at some point in your script and call its
// DrawRotatedText method (see below for details on its parameters) to render
// rotated text on a Canvas.
// Limitations:
// * Text cannot be wider than 256 pixels, the ScriptedTexture's width.
// * Not more than 32 different strings can be rendered within a single frame.
// * Color space is limited to the palette of the base texture of the
//   ScriptedTexture. The engine uses the closest matching color.
// Free for use, modification and distribution. Credit is appreciated.
// ============================================================================

class RotatedText extends Info;

// ============================================================================
// Compiler Directives
// ============================================================================

// Import RotatedTextTextures.utx created in UnrealEd into the RotatedText.u
// package we're currently creating. That saves us from having to bundle that
// texture package with our release and keeps it neat and self-contained.

#exec obj load file=Textures\RotatedTextTextures.utx package=RotatedText

// Import the mesh.

#exec mesh import mesh=RotatedTextMesh anivfile=Models\RotatedTextMesh_a.3d datafile=Models\RotatedTextMesh_d.3d mlod=0
#exec meshmap new meshmap=RotatedTextMesh mesh=RotatedTextMesh
#exec meshmap scale meshmap=RotatedTextMesh x=1.0 y=1.0 z=2.0

// ============================================================================
// Variables
// ============================================================================

var int IndexScriptedTexture;
var ScriptedTexture ScriptedTextures[32];

var Font DrawFont;
var Color DrawColor;
var string DrawText;

// ============================================================================
// PostBeginPlay
// The scripted texture uses a callback mechanism; when it is about to be drawn
// on the screen, it calls RenderTexture in the actor pointed to by its
// NotifyActor property. Thus, we set NotifyActor to ourself in PostBeginPlay.
// ============================================================================

simulated event PostBeginPlay() {

  for (IndexScriptedTexture = 0; IndexScriptedTexture < ArrayCount(ScriptedTextures); IndexScriptedTexture++)
    ScriptedTextures[IndexScriptedTexture].NotifyActor = Self;

// ============================================================================
// RenderTexture
// RenderTexture is called by the scripted texture before it is drawn on the
// screen. See above.
// ============================================================================

simulated event RenderTexture(ScriptedTexture ScriptedTexture) {

  if (DrawColor.R == 0 && DrawColor.G == 0 && DrawColor.B == 0)
    ScriptedTexture.DrawText(0, 0, DrawText, DrawFont);
    ScriptedTexture.DrawColoredText(0, 0, DrawText, DrawFont, DrawColor);

// ============================================================================
// DrawRotatedText
// This is the main function that is called by your UnrealScript code. It
// takes the Canvas to draw on, a rotation angle and the text to be drawn.
// Color and other rendering properties are taken from the given Canvas.
// ============================================================================

simulated function DrawRotatedText(Canvas Canvas, float Angle, string Text) {

  local float AngleRadian;
  local float AngleFovSave;
  local float SinAngle;
  local float CosAngle;
  local rotator RotationMesh;
  local vector LocationMesh;

  // Memorize the given text for use in RenderTexture.

  DrawText = Text;

  // Copy draw style and other propeties from the given Canvas to ourself.
  // The render style can't be directly copied, alas, since it's a byte
  // property in Canvas and an ERenderStyle property in Actor, we have to do it
  // this rather cumbersome way.

  DrawColor = Canvas.DrawColor;
  DrawFont  = Canvas.Font;

  switch (Canvas.Style) {
    case ERenderStyle.STY_None:         Style = ERenderStyle.STY_None;         break;
    case ERenderStyle.STY_Normal:       Style = ERenderStyle.STY_Normal;       break;
    case ERenderStyle.STY_Masked:       Style = ERenderStyle.STY_Masked;       break;
    case ERenderStyle.STY_Translucent:  Style = ERenderStyle.STY_Translucent;  break;
    case ERenderStyle.STY_Modulated:    Style = ERenderStyle.STY_Normal;       break;

  bNoSmooth = Canvas.bNoSmooth;

  // The location and rotation of the actor holding the mesh we want to draw
  // determines how and where it is drawn by DrawClippedActor, so we adjust
  // our rotation and location to accommodate that.

  RotationMesh.Yaw   = 16384;
  RotationMesh.Pitch = 32768 * Angle / 180.0;
  RotationMesh.Roll  = 0;

  LocationMesh.X = 4.0 / tan(Pi / 4.0);
  LocationMesh.Y = 0.0;
  LocationMesh.Z = 0.0;

  // Save the player's current field-of-vision angle since we're changing it
  // below.

  AngleFovSave = Canvas.Viewport.Actor.FovAngle;

  // Select the next ScriptedTexture from our array and skin the mesh with it.
  MultiSkins[1] = ScriptedTextures[IndexScriptedTexture++ % ArrayCount(ScriptedTextures)];

  // Set the player's field-of-vision angle to a defined value, draw the mesh
  // on the canvas, and reset the field-of-vision angle to its original value
  // that we memorized above. For convenience, we draw the mesh so that its
  // upper-left corner is at the current drawing position (rather than its
  // center).

  AngleRadian = Angle * Pi / 180.0;
  SinAngle = sin(AngleRadian);
  CosAngle = cos(AngleRadian);

  Canvas.DrawClippedActor(Self, false, 400, 400,
    Canvas.CurX - 200 + 128 * (CosAngle + SinAngle),
    Canvas.CurY - 200 + 128 * (CosAngle - SinAngle), true);

// ============================================================================
// Default Properties
// ============================================================================

defaultproperties {

  // Set ourself to use the RotatedTextMesh mesh imported above. Set a couple
  // of other display properties to make the mesh show up properly.
  Mesh=Mesh 'RotatedTextMesh'

  // Set RemoteRole to keep this actor from being replicated to other clients
  // when it is created on a listen server.


  // Make our scripted textures available to the script. Cumbersome, but the
  // only way I'm aware of to work around the limitation that a given
  // ScriptedTexture is rendered only once per tick.
  ScriptedTextures(0)=ScriptedTexture 'RotatedTextTexture1'
  ScriptedTextures(31)=ScriptedTexture 'RotatedTextTexture32'

Category Class (UT)
Category Custom Class

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Image Uploads

Random Page

Recent Changes

Offline Wiki

Unreal Engine

Console Commands


Mapping Topics

Mapping Lessons

UnrealEd Interface


Scripting Topics

Scripting Lessons

Making Mods

Class Tree


Modeling Topics


Log In