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:

1024-scrollText.gif

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

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*.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");*
    

}

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 *= wowMessage[i-1]; // shift messages and timers in direction*
 _wowTimer *= 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 _> 0) wowTimer *-= 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.