- Home /
Who Killed Who - Scrolling GUI Text
Hello,
I'm working on a 'who killed who' (wow) script for my game. It's basically text that comes up on the corner of the game window telling all the players who killed who, every time someone dies/get a kill, and then disappears after a few seconds.
I'm just trying to figure out what approach I need. What I have so far:
Every time someone gets a kill, the information of the killer, the victim and the weapon used is passed to a function that inserts it into a string ("Timmy was axed by John"), and stored into an Array. This is then displayed via GUI.Label on the side of the screen for ever, and eventually the list of 'who killed who' builds up that it goes off the screen.
I'm looking for a scroll and fade (maybe) effect, where one wow appears for a few seconds, then fades out. If another wow occurs, then it moves the previous wow scrolls up a little until it fades out, I made a crummy little GIF animation as an example:
My GUI script is pretty simple so far:
//show who killed who
GUI.BeginGroup(new Rect(20, 100, 400, 500));
for(var who = 0; who < whoKilledWho.length; who++){
GUI.Label(Rect(0, 40 + (who * 30), 400, 20), "" + whoKilledWho[who]);
}
GUI.EndGroup();
Like I said, whoKilledWho is called every time someone kills someone as is the killer, victim and weapon used. So how would I approach this then? Is it possible to control individual values in an Array (in terms of position, and fade)?
Thanks guys
Answer by whydoidoit · May 27, 2012 at 07:52 PM
Hey here's my script for something like that - you might want to change what is being displayed in the Message.Draw function
Installation
If you want to call this code from C# then put it in a plugins folder. Call the file Messages.js and copy in the code. You can put it anywhere if you are going to call it from Javascript.
In Use
Note the whole thing is driven off statics so you don't need references you can just access the features through Messages.Message from your script.
Configuration
Configure the screen location you want by changing Messages.Message.messageStartPosition either in the code or at runtime. Note: start positions will be rounded to a multiple of verticalSpacing on start up. If you move it, perhaps to account for screen resolution, make sure that it is a multiple of verticalSpacing or the results will not be pretty.
Set the Y coordinate where fades will start irrespective of time. It's Messages.Message.fadeY
Set the spacing of messages. Messages.Message.verticalSpacing
Choose your GUI skin/style for Label in the OnGUI call.
Displaying a Message
To display a message:
Call Messages.Message.QueueMessage("Your message"); from any script
Messages support multiple color strings by using some custom formatting in the string:
Either a plain string or red,green,blue:My string*|red,green,blue:Another string*
You can have any number of those combinations: an example is "1,0.4,0.5:Hello |1,1,1:World"
You can also display a multi colour string from any OnGUI by using
Messages.Message.DrawGuiString(message, position)
Where position is a Vector2
I've included some examples to see it working...
Messages.js
/************************************
* *
* Scrolling GUI Status *
* (Who Killed Who?) *
* by Mike Talbot 2012 *
* www.whydoidoit.com *
* *
* FOR THE AVOIDANCE OF DOUBT *
* public license, no warranties *
* use as you see fit *
* *
************************************/
import System.Collections.Generic;
import System.Linq;
import System;
class Message
{
//Holds the messages being displayed
static var messages : List.<Message> = new List.<Message>();
//Holds the messages waiting for display
static var queue : List.<Message> = new List.<Message>();
//Internally used to space the messages in time
private static var lastTime : float = 0;
//The screen Y coordinate that causes a fade
static var fadeY : float = 300;
//The amount of time before fading
static var fadeTime : float = 10;
//The position that the bottom message starts at
static var messageStartPosition : Vector2 = Vector2(10,400);
//The amount of pixels the message scrolls per second
static var movePerSecond : float = 40;
//The minimum amount of time between messages
static var spacingTime : float = 1;
//The vertical spacing of messages
static var verticalSpacing : float = 25;
//Static constructor, fixes the start Y position
private static function Message() {
messageStartPosition.y -= messageStartPosition.y % verticalSpacing;
}
//Creates a new message
private function Message(newMessage : String) {
message = newMessage;
position = Vector2(messageStartPosition.x, messageStartPosition.y - (messageStartPosition.y % verticalSpacing));
}
//Call this to display a message
static function QueueMessage(message : String) {
var m : Message = new Message(message);
queue.Add(m);
}
//Draws the current messages
static function Display() {
for(var m : Message in messages.ToList()) {
m.Draw();
//If the message has become invisible
//delete it - note the .ToList() above means
//that this for loop won't fail
if(!m.visible)
messages.Remove(m);
}
//Check if we have a new message and the requisite amount of time
//has passed
if(queue.Count > 0 && Time.time - lastTime > spacingTime) {
//See if we are displaying messages
if(messages.Count > 0) {
//Is there a message in the bottom slot?
if( Mathf.Floor((messages[0].position.y + verticalSpacing-1) / verticalSpacing)
== Mathf.Floor(messageStartPosition.y / verticalSpacing)) {
//Move the bottom message
messages[0].position.y -= Time.deltaTime * movePerSecond;
//Reposition the other messages
for(var i = 1; i < messages.Count; i++) {
messages[i].position.y = messages[i-1].position.y - verticalSpacing;
}
//Return to stop a new message being added
return;
}
}
//get the message at the front of the queue
var n = queue[0];
//update the queue
queue.RemoveAt(0);
//add the new message to the start of the running ones
messages.Insert(0,n);
//update the times
n.startTime = Time.time;
lastTime = Time.time;
}
}
//Current position of this message
var position : Vector2;
//This message text
var message: String;
//Current alpha value
var alpha : float = 0;
//Is the message visible?
var visible = true;
//The time the message was displayed
var startTime : float;
//Draw this single message
private function Draw() {
//Store the current GUI color
var color = GUI.color;
//adjust for alpha fading
GUI.color = Color(GUI.color.r, GUI.color.g, GUI.color.b, alpha * color.a);
//Draw the actual message
DrawGuiString(message, position);
//Reset the color
GUI.color = color;
//Is it time for a fade?
if(position.y < fadeY || (Time.time - startTime > fadeTime) ){
//Fade out
alpha = Mathf.Clamp01(alpha - Time.deltaTime * 0.25);
if(alpha == 0)
visible = false;
}
else {
//Fade in
alpha = Mathf.Clamp01(alpha + Time.deltaTime * 5);
}
}
//A section of coloured string!
class ColorPair
{
public var text : String;
public var color : Color;
}
//Call this to draw a coloured string somewhere from an OnGUI
static function DrawGuiString( guiString : String, position: Vector2) {
//Do we have anything fancy?
if(guiString.IndexOf(":")==-1) {
//No? Then draw a standard label
GUI.Label( Rect(position.x, position.y,1000,1000), guiString);
return;
}
//Save the colour
var color = GUI.color;
//Create an array of string parts - first split on |
var labels = guiString.Split(["|"], System.StringSplitOptions.None).Select(function(s) {
//Now split the colour from the text on the :
var parts = s.Split([":"], System.StringSplitOptions.None);
//Extract the color elements, by splitting on "," and make an array of floats
var colorParts = parts[0].Split([","], System.StringSplitOptions.None).Select(function(c) {
//Convert it to a float
return Convert.ToSingle(c);
}).ToArray();
var np = new ColorPair();
np.color = Color(colorParts[0], colorParts[1], colorParts[2], color.a);
np.text = parts[1];
return np;
}).ToArray();
//Draw the sections
for(var label : ColorPair in labels) {
//Current color (already adjusted for alpha)
GUI.color = label.color;
//Draw the string section
GUI.Label(Rect(position.x, position.y, Screen.width,100), label.text);
//Move the X by the width of what we just drew
position.x += GUI.skin.GetStyle("Label").CalcSize(GUIContent(label.text)).x + 2;
}
//Put the colour back
GUI.color = color;
}
}
function Start() {
Message.QueueMessage("Hello");
Message.QueueMessage("World");
Message.QueueMessage("From");
Message.QueueMessage("1,0.4,0.5:WhyDoIDoIt|0.3,0.4,1: Can you tell me?|1,1,1: Thought not...");
}
function OnGUI() {
//Choose your skin of styles for Label here
//Only update when we are painting
if(Event.current.type == EventType.repaint)
Message.Display();
//Dummy button for demo
if(GUI.Button(Rect(40,40,200,20), "Push Me"))
Message.QueueMessage("Another message");
}
This is brilliant $$anonymous$$ike! Do you know a way of getting the Label to have different colours for the killer and the victim? I was thinking about just having 3 labels, each with a unique colour and shifting them over, but it would look weird if someone with a short name, or a long name was displayed, the text would over lap right? Any ideas?
Well you can do what you are thinking, you can use the GUIStyle to calculate the size of each label and then you know how to position them so that they don't overlap.
Just make your message structure have 3 bits, 2 for the names and one for the messages.
Then layout your first label, work out how wide it is, then your next and so on... This is the doc for calcsize http://unity3d.com/support/documentation/ScriptReference/GUIStyle.CalcSize
Or you could just have one string and split it up to work out various bits and colours - how about this:
class ColorPair
{
public var text : String;
public var color : Color;
}
function DrawGuiString( guiString : String, position: Vector2) {
if(guiString.IndexOf(":")==-1) {
GUI.Label( Rect(position.x, position.y,200,40), guiString);
return;
}
var color = GUI.color;
var labels = guiString.Split(["|"], System.StringSplitOptions.None).Select(function(s) {
var parts = s.Split([":"], System.StringSplitOptions.None);
var colorParts = parts[0].Split([","], System.StringSplitOptions.None).Select(function(c) {
return Convert.ToSingle(c);
}).ToArray();
var np = new ColorPair();
np.color = Color(colorParts[0], colorParts[1], colorParts[2], 1);
np.text = parts[1];
return np;
}).ToArray();
for(var label : ColorPair in labels) {
GUI.color = label.color;
GUI.Label(Rect(position.x, position.y, 200,40), label.text);
position.x += GUI.skin.GetStyle("Label").CalcSize(GUIContent(label.text)).x + 2;
}
GUI.color = color;
}
I'll insert it into the code in my answer
You could extend that to allow switching styles I guess - but I thought that the string format would start getting hard to parse and I figure color would be as much as I would use. But an interesting concept - could do some kind of skin name thing in there too.
Hi. Trying to use this code. I've copy / pasted it into $$anonymous$$essages.js and that's in my root scripts folder. Unit is complaining that $$anonymous$$essage is not a member of $$anonymous$$essages (line 180-185). I noticed the class is called "$$anonymous$$essage". What am I doing wrong? If you need more information please let me know. Thanks much!
Answer by aldonaletto · May 27, 2012 at 08:10 PM
Yes, you can control color and alpha per label with GUI.color - but as far as I know, you can't control it per word.
I would do something like this:
var maxWow = 10; // max messages private var wowMessage: String[] = new String[maxWow]; // messages array private var wowTimer: float[] = new float[maxWow]; // timers array
// adds a new message to the array:
function AddWow(message: String){ for (var i = 1; i < maxWow; i++){ wowMessage[i] = wowMessage[i-1]; // shift messages and timers in direction wowTimer[i] = wowTimer[i-1]; // to the end of the arrays } wowMessage[0] = message; // and add message at the beginning wowTimer[0] = 5; // also start its timer }
function Update(){ // decrement active wow timers each Update: for (var i = 0; i < maxWow; i++){ if (wowTimer[i] > 0) wowTimer[i] -= Time.deltaTime; } }
var groupHeight = 500;
function OnGUI(){ GUI.BeginGroup(new Rect(20, 100, 400, groupHeight)); for(var who = 0; who < maxWow; who++){ // set alpha according to the timer value clamped to 0..1: GUI.color.a = Mathf.Clamp(wowTimer[who], 0, 1); // message[0] is displayed at the group bottom GUI.Label(Rect(0, groupHeight - (who * 30) - 40, 400, 20), wowMessage[who]); } GUI.EndGroup(); } This basic idea could be extended to 3 message parts (like who1, how, who2) - which would generate three different labels per line, so you could use different colors per label like in your nice gif.
Your answer
Follow this Question
Related Questions
Fade In/Out Login GUI? 1 Answer
How would i add an animation to GUI < Im a noob 0 Answers
Fading an image drawn with GUI.DrawTexture 1 Answer