GUITypeWriter for TorqueX
by Josh Butterworth · 07/06/2007 (5:34 pm) · 1 comments
Here's a simple custom GUI control you can use to make a cool type writer like effect on your screens, it takes text in as a list of lines, then writes out each one in turn. Sorry there aren't more comments, I'll come back and tidy it up in a bit.
So to initialise one you'd write something like this:
Quick Tip:
To insert a 'wait' period in your text you can use a character that doesnt appear in your character map (the prime symbol works well for this).
Future Work:
Add an optional sound effect to the typing effect for extra impact, I had this working but removed it to make sure it was generic enough for everyones uses.
//-----------------------------------------------------------------------------
// Component Written by Josh Butterworth www.grassrootsgames.co.uk
// Notes:
// Make sure your textStyle.SizeToText property equals false or the multiline
// feature will not work
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using PointF = Microsoft.Xna.Framework.Vector2;
using RectF = GarageGames.Torque.MathUtil.RectangleF;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using GarageGames.Torque.Core;
using GarageGames.Torque.GFX;
using GarageGames.Torque.MathUtil;
using GarageGames.Torque.Sim;
using GarageGames.Torque.GUI;
namespace SpaceShooter.GUI.CustomControls
{
/// <summary>
/// Called when the GUITypeWriter has finished typing
/// </summary>
public delegate void OnTypeWriterFinished();
/// <summary>
/// Renders a string to the GUICanvas.
/// </summary>
public class GUITypeWriter : GUIControl, IAnimatedObject
{
//======================================================
#region Public properties, operators, constants, and enums
/// <summary>
/// The Character displayed after the current text to show
/// the text is being written Default none
/// </summary>
public char Cursor
{
get { return _cursor; }
set { _cursor = value; }
}
/// <summary>
/// The current character in the sequence, a number between 0
/// and the length of the string
/// </summary>
public int CurrentChar
{
get { return _currentChar; }
set { _currentChar = value; }
}
/// <summary>
/// The full text to be written
/// </summary>
public List<string> FullText
{
get { return _fullText; }
set { _fullText = value; }
}
/// <summary>
/// The height of each line, for best results set to slightly larger than your font size
/// Default 20
/// </summary>
public float LineHeight
{
get { return _lineHeight; }
set { _lineHeight = value; }
}
/// <summary>
/// The Numbers of characters to add per step. This is the only way to make the typewriter faster
/// than it's default settings. Default 1
/// </summary>
public int CharactersPerStep
{
get { return _charactersPerStep; }
set { _charactersPerStep = value; }
}
/// <summary>
/// Defines length of time the cursor should blink on/off for
/// Default 0.25f
/// </summary>
public float BlinkDelay
{
get { return _blinkDelay; }
set { _blinkDelay = value; }
}
/// <summary>
/// Defines Whether or not the Cursor should blink on and off
/// Default False
/// </summary>
public bool BlinkCursor
{
get { return _blinkCursor; }
set { _blinkCursor = value; }
}
/// <summary>
/// Defines the time between the GUI screen being activated and TypeWriter starting
/// Default 0
/// </summary>
public float StartDelay
{
get { return _startDelay; }
set { _startDelay = value; }
}
/// <summary>
/// Defines an extra delay between each step, this will add on to the 0.03 defined by the
/// ProcessList callback. Default 0
/// </summary>
public float StepDelay
{
get { return _stepDelay; }
set { _stepDelay = value; }
}
/// <summary>
/// Pauses the component, this is useful if you want to trigger a type writer to start at
/// a certain event.
/// </summary>
public bool Paused
{
get { return _paused; }
set { _paused = value; }
}
/// <summary>
/// Sets a method to run when the type writer finishes typing
/// </summary>
public OnTypeWriterFinished OnTypeWriterFinishedDelegate
{
get { return _finishedDelegate; }
set { _finishedDelegate = value; }
}
/// <summary>
/// Opacity of the text to be written, default 255 (max)
/// </summary>
public byte Opacity
{
get { return _opacity; }
set { _opacity = value; }
}
/// <summary>
/// The current text on the screen
/// </summary>
public string Text
{
get { return _text; }
set
{
if (_style == null)
return;
Assert.Fatal(!_style.Font.IsNull, "GUIText::Text: Font resource is not valid!");
if (_style.Font.IsNull)
return;
_text = value;
if (_style.SizeToText)
SetSize(new SizeF(_style.Font.Instance.MeasureString(_text), _style.Font.Instance.Height + 4.0f));
}
}
#endregion
//======================================================
#region Public methods
public override void OnRender(PointF offset, RectF updateRect)
{
DrawUtil.ClearBitmapModulation();
RectF ctrlRect = new RectF(offset, _bounds.Size);
// fill the update rect with the fill color
if (_style.IsOpaque)
DrawUtil.RectFill(ctrlRect, _style.FillColor);
// if there's a border, draw the border
if (_style.HasBorder)
DrawUtil.Rect(ctrlRect, _style.BorderColor);
float yPos;
//Create a new color using the Opacity parameter, this allows parent controls to manage the opacity of this one
Color thisColor = new Color(_style.TextColor.R, _style.TextColor.G, _style.TextColor.B, _opacity);
for (int i = 0; i < _lineNo; i++)
{
yPos = (_lineHeight * (i + 1)) - (_bounds.Size.Y/2);
//Console.WriteLine(i + " " + Position.Y + " - " + (_bounds.Size.Y) + " + " + (_lineHeight * (i))+ " = " + yPos);
string thisLine = _fullText[i];
DrawUtil.JustifiedText(_style.Font, new PointF(0.0f, yPos), _bounds.Size, _style.Alignment, thisColor, thisLine);
}
yPos = (_lineHeight * (_lineNo + 1)) - (_bounds.Size.Y / 2);
DrawUtil.JustifiedText(_style.Font, new PointF(0.0f, yPos), _bounds.Size, _style.Alignment, thisColor, _text);
// render the child controls
_RenderChildControls(offset, updateRect);
}
#region IAnimatedObject Members
public void UpdateAnimation(float dt)
{
if (!_paused)
{
UpdateCursor(dt);
UpdateText(dt);
}
}
private void UpdateText(float dt)
{
if (_startDelay > 0)
{
_startDelay -= dt;
}
else
{
_startDelay += _stepDelay;
string thisLine = _fullText[_lineNo];
if (_currentChar < thisLine.Length)
{
_currentChar += _charactersPerStep;
//Check the new addition doesnt send the character marker over the length of the string
if (_currentChar > thisLine.Length) _currentChar = thisLine.Length;
//Create the new line of text
Text = thisLine.Substring(0, _currentChar);
//If the Cursor is supposed to be visible add it to the new string
if (_cursorVisible) Text += _cursor;
}
else
{
if (_lineNo < _fullText.Count - 1)
{
_text = "";
_lineNo++;
_currentChar = 0;
}
else
{
//We're done.
if (_finishedDelegate != null)
_finishedDelegate();
}
}
}
}
private void UpdateCursor(float dt)
{
//If the cursor is supposed to blink then manage its visible state according to time past.
if (_blinkCursor)
{
if (_cursorTimer > 0)
{
_cursorTimer -= dt;
}
else
{
_cursorTimer += _blinkDelay;
_cursorVisible = _cursorVisible ? _cursorVisible = false : _cursorVisible = true;
}
}
}
/// <summary>
/// Skip the typewriting method and just write all the lines, assign this method to an input.
/// </summary>
public void Skip()
{
_text = "";
_lineNo = _fullText.Count -1;
_currentChar = 0;
if (_finishedDelegate != null)
_finishedDelegate();
}
#endregion
//======================================================
#region Private, protected, internal methods
protected override bool _OnNewStyle(GUIStyle style)
{
_style = (style as GUITextStyle);
Assert.Fatal(_style != null, "GUIText::_OnNewStyle: control was assigned an invalid style!");
if (_style == null || !base._OnNewStyle(style))
return false;
if (_style.SizeToText && _text != String.Empty)
SetSize(new SizeF(_style.Font.Instance.MeasureString(_text), _style.Font.Instance.Height + 4.0f));
return true;
}
protected override bool _OnWake()
{
if (!base._OnWake())
return false;
_currentChar = 0;
ProcessList.Instance.AddAnimationCallback(this);
return true;
}
protected override void _OnSleep()
{
ProcessList.Instance.RemoveObject(this);
base._OnSleep();
}
#endregion
//======================================================
#region Private, protected, internal fields
string _text = "";
private List<string> _fullText;
private int _currentChar = 15;
private char _cursor = '|';
private float _lineHeight = 20;
private int _charactersPerStep = 1;
private float _stepDelay = 0f;
private float _startDelay = 0;
private bool _blinkCursor = true;
private float _blinkDelay = 0.25f;
private bool _paused = false;
private byte _opacity = 255;
int _lineNo = 0;
bool _cursorVisible = true;
float _cursorTimer = 0;
GUITextStyle _style = null;
private OnTypeWriterFinished _finishedDelegate;
#endregion
#endregion
}
}So to initialise one you'd write something like this:
List<string> note = new List<string>();
note.Add("NOTE: Manual weapon switching is enabeled for the turret system.");
note.Add("This will allow the pilot to initiate a magazine change in realtime ");
note.Add("while engaged in combat, without the need for activating the ");
note.Add("ordinance assignment system.");
_arrowNoteText = new GUITypeWriter();
_arrowNoteText.Style = _textStyle;
_arrowNoteText.FullText = note;
_arrowNoteText.Position = new Vector2(650, 525);
_arrowNoteText.Size = new SizeF(400, 250);
_arrowNoteText.LineHeight = 16.0f;
_arrowNoteText.StartDelay = 5.0f;
_arrowNoteText.StepDelay = 0.01f;
_arrowNoteText.CharactersPerStep = 1;
_arrowNoteText.Visible = true;
_arrowNoteText.Folder = this;Quick Tip:
To insert a 'wait' period in your text you can use a character that doesnt appear in your character map (the prime symbol works well for this).
Future Work:
Add an optional sound effect to the typing effect for extra impact, I had this working but removed it to make sure it was generic enough for everyones uses.

Torque 3D Owner Jonathon Stevens