Basic Dynamic HUD

EDIT:
Forgot to mention, sorry about my extremely organized formatting and unnecessary “(null)” comments. That’s just my OCD, enjoy.

Thought I might share this, I felt pretty proud of myself for some reason, although this isn’t a big accomplishment, but it was fun to code and I hope people have fun using it. I’m open to comments and suggestions and please feel free to use it, read the copyright notice first.


/*
 * Copyright (c) 2014, Oliver Yasuna. All rights reserved.
 * 
 * You may use this for all non-commercial purposes only,
 * unless given permission by the one author (Oliver Yasuna).
 * No code in this document was copied and pasted from an
 * external source. Some content may have been influenced by
 * the work of others, but this whole document was written
 * from scratch, by only the one author.
 * 
 * Contact me at oyasuna8@gmail.com for more questions.
 */

/*
 * PROJECT: Basic Dynamic HUD
 * AUTHOR: Oliver Yasuna
 * 
 * NOTES:
 *  (null)
 */

// The table for the HUD.
local HUD = {
	// Data of the HUD and bar values.
	//  - I choose to make this separate from "VARS"
	//    beacause these are variables that the code
	//    requires the changes of, if that makes sense.
	DATA = {
		// Current bar index.
		//  - This is incremented everytime a bar is
		//    added (see HUD:DrawBar()), and reset to 0
		//    after every bar has been drawn (see HUD:Update()).
		barIndex = 0,
		
		// Bar values.
		//  - Add more, you can really name them whatever you want.
		//    I just named them data0, data1, and data2 for the
		//    purpose of understanding my tutorial.
		data0 = 0,
		data0Goto = 0,
		data0Min = 0,
		data0Max = 0,
		data1 = 0,
		data1Goto = 0,
		data1Min = 0,
		data1Max = 0,
		data2 = 0,
		data2Goto = 0,
		data2Min = 0,
		data2Max = 0
	},
	
	// Variables of the HUD.
	//  - Mainly constant variables.
	//  - x, y, and width will change.
	VARS = {
		// X value of the hud.
		x = 0,
		// Y value of the hud.
		y = 0,
		
		// Width percent on screen.
		widthPercent = 0.25,
		// Real calculated width.
		width = 0,
		
		// Inner padding.
		padding = 5,
		// Outer margin.
		margin = 5,
		
		// Vertical spacing between bars.
		bar_spacing = 5,
		
		// Height of a bar.
		bar_height = 16,
		
		// Panel border width.
		panelBorderWidth = 1,
		// Bar border width.
		barBorderWidth = 1,
		
		// Amount of bars.
		//  - YOU MUST CHANGE THIS TO THE AMOUNT YOU HAVE.
		barCount = 3;
		
		// Font for text.
		//  - This is never used, but feel free to implement code.
		font = "TargetID"
	},
	
	// Colors of the HUD.
	//  - Pretty self explanatory.
	COLORS = {
		panel = {
			border = Color(255, 255, 255, 255),
			background = Color(128, 128, 50, 255)
		},
		
		health_bar = {
			border = Color(255, 255, 128 + (128 / 2), 255),
			background = Color(128, 128, 100, 255),
			fill = Color(255, 128, 128, 255)
		},
		suit_bar = {
			border = Color(255, 255, 128 + (128 / 2), 255),
			background = Color(128, 128, 100, 255),
			fill = Color(128, 128, 255, 255)
		},
		stamina_bar = {
			border = Color(255, 255, 128 + (128 / 2), 255),
			background = Color(128, 128, 100, 255),
			fill = Color(128, 255, 128, 255)
		}
	}
};

/*
 * FUNCTION:
 *  HUD:Initialize()
 * 
 * ARGUMENTS:
 *  (null)
 *
 * PURPOSE:
 *  - Set and process things before the HUD is drawn.
 * 
 * NOTES:
 *  - Be aware that this function is only ran
 *    once per player upon first time spawn.
 */
function HUD:Initialize()
	// Get the player.
	local ply = LocalPlayer();
	
	// Make sure the player is valid and alive.
	if(!IsValid(ply) or !ply:Alive()) then
		return;
	end
	
	// Setup health data.
	data0 = ply:Health();
	data0Goto = data0;
	data0Min = 0;
	data0Max = ply:GetMaxHealth();
	
	// Setup armor data.
	data1 = ply:Armor();
	data1Goto = data1;
	data1Min = 0;
	data1Max = 100;
	
	// Setup Stamina data.
	data2 = ply:Stamina();
	data2Goto = data2;
	data1Min = 0;
	data2Max = ply:GetStamina();
end

/*
 * FUNCTION:
 *  HUD:Update()
 *
 * ARGUMENTS:
 *  (null)
 *
 * PURPOSE:
 *  - Reset the barIndex to 0.
 *  - Update anything else.
 *
 * NOTES:
 *  - There is no smooth movement code for stamina
 *    because that is already smooth.
 */
function HUD:Update()
	// Get the player.
	local ply = LocalPlayer();
	
	// Make sure the player is valid and alive.
	if(!IsValid(ply) or !ply:Alive()) then
		return;
	end
	
	// Update minimum and maximum health.
	data0Min = 0;
	data0Max = ply:GetMaxHealth();
	
	// Update miniumum and maximum armor.
	data1Min = 0;
	data1Max = 100;
	
	// Just set stamina because think about it...
	// Also update minimum and maximum stamina.
	data2 = ply:Stamina();
	data1Min = 0;
	data2Max = ply:GetMaxStamina();
	
	// Reset the bar index so not to draw a million bars.
	self.DATA.barIndex = 0;
end

/*
 * FUNCTION:
 *  HUD:DrawPanel()
 * 
 * ARGUMENTS:
 *  (null)
 * 
 * PURPOSE:
 *  - Draw the main panel.
 * 
 * NOTES:
 *  - I'm not going to get into the formulas here,
 *    think about them using common sense.
 */
function HUD:DrawPanel()
	self.VARS.x = self.VARS.margin;
	self.VARS.width = self.VARS.widthPercent * ScrW();
	
	local x = self.VARS.x;
	local width = self.VARS.width;
	local height = ((self.VARS.bar_height * self.VARS.barCount) + (self.VARS.bar_spacing * (self.VARS.barCount - 1))) + (self.VARS.padding * 2);
	
	self.VARS.y = (ScrH() - height) - self.VARS.margin;
	
	local y = self.VARS.y;
	
	draw.RoundedBox(0, x, y, width, height, self.COLORS.panel.border);
	draw.RoundedBox(0, x + self.VARS.panelBorderWidth, y + self.VARS.panelBorderWidth, width - (self.VARS.panelBorderWidth * 2), height - (self.VARS.panelBorderWidth * 2), self.COLORS.panel.background);
end

/*
 * FUNCTION:
 *  HUD:DrawBars()
 * 
 * ARGUMENTS:
 *  (null)
 * 
 * PURPOSE:
 *  - Draws the bars.
 * 
 * NOTES:
 *  - Add all your bars here.
 */
function HUD:DrawBars()
	// Get the player.
	local ply = LocalPlayer();
	
	// Make sure the player is valid and alive.
	if(!IsValid(ply) or !ply:Alive()) then
		return
	end
	
	// DRAW BARS HERE!
	
	HUD:DrawBar(data0, data0Min, data0Max, self.COLORS.health_bar);
	HUD:DrawBar(data1, data1Min, data1Max, self.COLORS.suit_bar);
	HUD:DrawBar(data2, data2Min, data2Max, self.COLORS.stamina_bar);
end

/*
 * FUNCTION:
 *  HUD:Drawbar(value, minValue, maxValue, colors);
 * 
 * ARGUMENTS:
 *  value = Amount to fill the bar.
 *  minValue = Lowest possible amount.
 *  maxValue = Highest possible amount.
 *  colors = Colors of the bar.
 * 
 * NOTES:
 *  - You may notice that minValue isn't ever used.
 *    I never got around to implementing it. Feel free
 *    to implement it yourself.
 *  - I'm not going to get into the formulas here,
 *    think about them using common sense.
 */
function HUD:DrawBar(value, minValue, maxValue, colors)
	local x = self.VARS.x + self.VARS.padding;
	local y = self.VARS.y + ((self.VARS.bar_height * self.DATA.barIndex) + (self.VARS.bar_spacing * self.DATA.barIndex)) + self.VARS.padding;
	local width = self.VARS.width - (self.VARS.padding * 2);
	local height = self.VARS.bar_height;
	
	draw.RoundedBox(0, x, y, width, height, colors.border);
	
	local x = x + self.VARS.barBorderWidth;
	local y = y + self.VARS.barBorderWidth;
	local width = width - (self.VARS.barBorderWidth * 2);
	local height = height - (self.VARS.barBorderWidth * 2);
	
	draw.RoundedBox(0, x, y, width, height, colors.background);
	
	draw.RoundedBox(0, x, y, width * math.Clamp(value / maxValue, 0, 1), height, colors.fill);
	
	self.DATA.barIndex = self.DATA.barIndex + 1;
end

// Initialize once.
HUD:Initialize();
	
/*
 * FORMULA:
 *  d = (1/2)at^2
 * 
 * VARIABLES:
 *  d = distance
 *  a = acceleration
 *  t = time
 * 
 * NOTES:
 *  - This is where the magic happens.
 *  - You may notice that the distance will take a while,
 *    to finally hit zero. There's really no way to
 *    fix this that I can think of in the time I have.
 *    So essentially 64 bit users may experience an almost
 *    unnoticable FPS drop, if your hardware blows.
 *    Most likely not a problem though.
 */
local distance;
local acceleration = 0.05;
local time;
timer.Create("Update", 0.00001, 0, function()
	if(data0 != LocalPlayer():Health()) then
		time = LocalPlayer():Health() - data0;
		distance = (1 / 2) * acceleration * math.pow(time, 2);
		
		if(data0 < LocalPlayer():Health()) then
			data0 = data0 + distance;
		elseif(data0 > LocalPlayer():Health()) then
			data0 = data0 - distance;
		end
	end
	
	if(data1 != LocalPlayer():Armor()) then
		time = LocalPlayer():Armor() - data1;
		distance = (1 / 2) * acceleration * math.pow(time, 2);
		
		if(data1 < LocalPlayer():Armor()) then
			data1 = data1 + distance;
		elseif(data1 > LocalPlayer():Armor()) then
			data1 = data1 - distance;
		end
	end
	
	// Remember there is no update for stamina cause duh...
end);

local function HUDPaint()
	HUD:DrawPanel();
	HUD:DrawBars();
	
	HUD:Update();
end
hook.Add("HUDPaint", "Paint player HUD.", HUDPaint);

Pics?

Added one.

Sorry for the stupid question, but I assume the red bar is health, blue is armour, but what is green?

I’m assuming it’s for stamina. He did say this:


// Remember there is no update for stamina cause duh...

Yeah it’s for stamina, sorry that wasn’t clear. That’s just an example.

Well, there are few mistakes that aren’t ‘lethal’ but shouldn’t be there.
Instead of doing:


local ply = LocalPlayer()

And repeating it in each func that uses player, you could simply run that code on top of the file, outside any of the functions and skip the unnecessary variable creation (as said before, it’s a minor thing).

Also I don’t see a point of calling


local var = val

more than once in the same function.

Other than that - it looks quite average but in a good way, you can’t really expect some super-positive comments on this because it’s really missing features.

Nice hud. It would be better if you would change the stamina bar to hungermod bar.

Nice though the yellow outline doesn’t go well in my opinion. Try a different colour.

This will be great as an example to show LUA beginners thanks to your extensive commenting

Thanks. And yeah I was just messing around, you can obviously change this.

[editline]20th May 2014[/editline]

Yeah wow I’m dumb. That’s a stupid mistake. Thanks for pointing that out man.