Screen Scaling

Hi. So I’ve wanted to do VGUI for a long time, and the first major thing I made was my CS:GO scoreboard. I realized it didn’t scale when I gave it to friends, and attempted to remedy that as you can see in my code below.

I have absolutely no idea what I’m doing, other than what the wiki said to do. The functions I’m using for scaling are for convenience, and were only meant to help me see if that scaling equation worked.

Any help would be appreciated.

[lua]local gradient = surface.GetTextureID(“gui/gradient”)local logo = Material(“vgui/scoreboard/header.png”)
local surface = surface
local maintextcolor = Color(83, 122, 146)

local function scaleheight( size, factor )
return (factor or ScrH())*(size / (factor or ScrH()))
end

local function scalewidth( size, factor )
return (factor or ScrW())*(size / (factor or ScrW()))
end

surface.CreateFont( “Stratum”, {
font = “StratumNo2-Medium”,
size = 25,
weight = 100,
antialias = false
} )

surface.CreateFont( “zheader”, {
font = “StratumNo2-Regular”,
size = 23,
weight = 100,
antialias = true
} )

local sizex, sizey = ScrW() * 0.6, ScrH() * 0.7
local PANEL = {}
function PANEL:Init()
self:SetSize(sizex, sizey)
self:Center()
self:MakePopup()
self:SetPaintBackground(false)

	self.list = self:Add("DScrollPanel")
	self.list:Dock(FILL)
	self.list:DockMargin(8, 140, 8, 8)
	self.list:DockPadding(4, 4, 4, 4)
	self.list.Paint = function(panel, w, h)
		surface.SetDrawColor(10, 10, 10, 0)
		surface.DrawRect(0, 0, w, h)
	end


	self.mapgm = self:Add("DLabel")
	self.mapgm:SetPos(scalewidth(10, sizex), scaleheight(65, sizey))
	self.mapgm:SetFont("Stratum")
	self.mapgm:SetText(game.GetMap().." - "..gmod.GetGamemode().Name)
	self.mapgm:SetExpensiveShadow(1, color_black)
	self.mapgm:SetContentAlignment(4)
	self.mapgm:SizeToContents()
	self.mapgm:SetTextColor(Color(123, 140, 151, 170))


	-- HEADERS


	self.p = self:Add("DLabel")
	self.p:SetPos(scalewidth(18, sizex), scalewidth(115, sizey))
	self.p:SetFont("zheader")
	self.p:SetText("PING")
	self.p:SetTextColor(Color(200,200,200,70))
	self.p:SetContentAlignment(4)
	self.p:SizeToContents()


	self.ply = self:Add("DLabel")
	self.ply:SetPos(scalewidth(97, sizex), scaleheight(115, sizey))
	self.ply:SetFont("zheader")
	self.ply:SetText("PLAYER")
	self.ply:SetTextColor(Color(200,200,200,70))
	self.ply:SetContentAlignment(4)
	self.ply:SizeToContents()


	self.kills = self:Add("DLabel")
	self.kills:SetPos(scalewidth(1068, sizex), scaleheight(115, sizey))
	self.kills:SetFont("zheader")
	self.kills:SetText("K")
	self.kills:SetTextColor(Color(200,200,200,70))
	self.kills:SetContentAlignment(6)
	self.kills:SizeToContents()


	self.kills = self:Add("DLabel")
	self.kills:SetPos(scalewidth(1120, sizex), scaleheight(115, sizey))
	self.kills:SetFont("zheader")
	self.kills:SetText("D")
	self.kills:SetTextColor(Color(200,200,200,70))
	self.kills:SetContentAlignment(6)
	self.kills:SizeToContents()


	self:PopulateList()


	self.lastUpdate = 0
	self.lastCount = #player.GetAll()
end


function PANEL:PopulateList()


	local players = player.GetAll()


	table.sort(players, function(a, b)
		return a:Frags() > b:Frags()
	end)


	for k, v in ipairs(players) do
		if (IsValid(self)) then
			local panel2 = self.list:Add("playerscore")
			panel2:DockMargin(6, 0, 6, 3)
			panel2:Dock(TOP)
			panel2:SetPlayer(v)
		end
	end
end


function PANEL:Think()
	if (self.lastUpdate < CurTime()) then
		self.lastUpdate = CurTime() + 1


		if (self.lastCount != #player.GetAll()) then
			self.list:Clear(true)
			self:PopulateList()
		end


		self.lastCount = #player.GetAll()
	end


	if (input.IsMouseDown(MOUSE_LEFT) or input.IsMouseDown(MOUSE_RIGHT)) and (self.nextclick or 0) < CurTime() and not self.mdown then
		if IsValid(infomenu) and not infomenu.onoption then
			infomenu:Remove()
		end
		self.nextclick = CurTime() + 0.2
		self.mdown = true
	elseif not (input.IsMouseDown(MOUSE_LEFT) or input.IsMouseDown(MOUSE_RIGHT)) and self.mdown then
		self:DoClick()
		self.mdown = false
	end
end


function PANEL:OnCursorEntered()
	self.hovered = true
end


function PANEL:OnCursorExited()
	self.hovered = false
end


function PANEL:DoClick()
	if IsValid(infomenu) and (infomenu.timer or 0) < CurTime() and not infomenu.onoption then
		infomenu:Remove()
	end
end


function PANEL:Paint(w, h)
	surface.SetDrawColor(0, 0, 0, 250)
	surface.DrawRect(0, 0, w, h)




	render.SetScissorRect( 0, 0, ScrW(), scaleheight(ScrH()/4.19, sizex), true )
		surface.SetDrawColor(0, 0, 0, 255)
		surface.DrawRect(0, 0, w, h/8)


		surface.SetDrawColor(255, 255, 255, 15)
		surface.SetMaterial(logo)
		surface.DrawTexturedRect(scalewidth(ScrW()/7, sizex), scaleheight(-100, sizey), scalewidth(512, sizex), scaleheight(512, sizey))


		surface.SetDrawColor(83, 122, 146, 40)
		surface.SetTexture(gradient)
		surface.DrawTexturedRectRotated(scalewidth(570, sizex), scaleheight(78, sizey), h/7, w*1.1, 90)
	render.SetScissorRect( 0, 0, 0, 0, false )
end

vgui.Register(“csgoboard”, PANEL, “DPanel”)

PANEL = {}

function PANEL:Init()


	self:SetTall(27)
	local color = maintextcolor


	self.name = self:Add("DLabel")
	self.name:SetPos(scalewidth(85, sizex), scaleheight(-1, sizey))
	self.name:SetText("PLAYER")
	self.name:SetFont("Stratum")
	self.name:SetTextColor(color_white)
	self.name:SetExpensiveShadow(1, color_black)
	self.name:SetContentAlignment(4)




	self.ping = self:Add("DLabel")
	self.ping:SetPos(scalewidth(-25, sizex), scaleheight(1, sizey))
	self.ping:SetText("000")
	self.ping:SetTextColor(color_white)
	self.ping:SetExpensiveShadow(1, color_black)
	self.ping:SetFont("Stratum")
	self.ping:SetContentAlignment(6)




	self.kills = self:Add("DLabel")
	self.kills:SetPos(scalewidth(1023, sizex), scaleheight(-1, sizey))
	self.kills:SetFont("Stratum")
	self.kills:SetText("000")
	self.kills:SetContentAlignment(6)
	self.kills:SizeToContents()


	self.deaths = self:Add("DLabel")
	self.deaths:SetPos(scalewidth(1073, sizex), scaleheight(-1, sizey))
	self.deaths:SetFont("Stratum")
	self.deaths:SetText("000")
	self.deaths:SetTextColor(Color(200,200,200,15))
	self.deaths:SetContentAlignment(6)
	self.deaths:SizeToContents()




	self.ico = self:Add("AvatarImage")
	self.ico:SetSize(scalewidth(28, sizex),scaleheight(28, sizey))
	self.ico:SetPos(scalewidth(ScrW()/40.5, sizex), scaleheight(-1, sizey))
	self.ico:SetContentAlignment(4)
end


function PANEL:Think()
	if (IsValid(self.player)) then
		local color = maintextcolor
		local r, g, b = color.r, color.g, color.b
		--team.GetColor(self.player:Team())
		self.ping:SetText(self.player:Ping())
		self.kills:SetText(self.player:Frags())
		self.deaths:SetText(self.player:Deaths())


		if not self.hovered then
			self.kills:SetTextColor(self.player:Alive() and Color(r + 45, g + 45, b + 45, 250) or ColorAlpha(color, 100))
			self.deaths:SetTextColor(self.player:Alive() and Color(r + 45, g + 45, b + 45, 250) or ColorAlpha(color, 100))
			self.name:SetTextColor(self.player:Alive() and Color(r + 45, g + 45, b + 45, 250) or ColorAlpha(color, 100))
			self.ping:SetTextColor(self.player:Alive() and Color(r + 45, g + 45, b + 45, 250) or ColorAlpha(color, 100))
			self.ico:SetAlpha(self.player:Alive() and 255 or 50)
		elseif self.hovered then
			self.kills:SetTextColor(self.player:Alive() and Color(r + 70, g + 70, b + 70, 250) or Color(r + 10, g + 10, b + 10, 220))
			self.deaths:SetTextColor(self.player:Alive() and Color(r + 70, g + 70, b + 70, 250) or Color(r + 10, g + 10, b + 10, 220))
			self.name:SetTextColor(self.player:Alive() and Color(r + 70, g + 70, b + 70, 250) or Color(r + 10, g + 10, b + 10, 220))
			self.ping:SetTextColor(self.player:Alive() and Color(r + 70, g + 70, b + 70, 250) or Color(r + 10, g + 10, b + 10, 220))
			self.ico:SetAlpha(self.player:Alive() and 255 or 100)
		end
	end


	if (input.IsMouseDown(MOUSE_LEFT) or input.IsMouseDown(MOUSE_RIGHT)) and not self.hasclicked and self.hovered then
		self.hasclicked = true
	elseif not (input.IsMouseDown(MOUSE_LEFT) or input.IsMouseDown(MOUSE_RIGHT)) and self.hasclicked then
		self.hasclicked = false
		self:DoClick()
	end
end


function PANEL:SetPlayer(client)
	self.player = client
	local name = client:Name()




	self.name:SetText(name)
	self.name:SizeToContents()


	self.ico:SetPlayer(self.player, 64)
end


function PANEL:Paint(w, h)


	if (IsValid(self.player)) then
		local color = maintextcolor --team.GetColor(self.player:Team())


		local x,y = self:GetPos()


		if self.hovered then
			surface.SetDrawColor(color.r, color.g, color.b, self.player:Alive() and 100 or 15)
		elseif not self.hovered then
			surface.SetDrawColor(color.r, color.g, color.b, self.player:Alive() and 60 or 5)
		end


		local refbar = math.floor(ScrW()/50)
		surface.DrawRect(scalewidth(5, sizex), 0, scalewidth(ScrW()/50, sizex), h)
		surface.DrawRect(scalewidth(ScrW()/24, sizex), 0, w / 1.2, h)
		surface.DrawRect(scalewidth(1019, sizex), 0, scalewidth(48, sizex), h)
		surface.DrawRect(scalewidth(1070, sizex), 0, scalewidth(48, sizex), h)


	end
end


function PANEL:OnCursorEntered()
	self.hovered = true
end


function PANEL:OnCursorExited()
	self.hovered = false
end


function PANEL:DoClick()
	if IsValid(infomenu) and not infomenu.onoption then
		infomenu:Remove()
	end
	infomenu = vgui.Create("playerinfo")
	infomenu:SetPos(gui.MouseX(), gui.MouseY())
	infomenu:SetPlayer(self.player)
	infomenu.timer = CurTime()+0.5
end


function PANEL:OnRemove()
	if IsValid(infomenu) then
		infomenu:Remove()
	end
end


vgui.Register("playerscore", PANEL, "DPanel")

function draw.OutlinedBox(x, y, w, h, thickness, clr)
surface.SetDrawColor(clr)

for i = 0, thickness - 1 do
	surface.DrawOutlinedRect(x + i, y + i, w - i * 2, h - i * 2)
end

end

PANEL = {}

function PANEL:Init()
self:MakePopup()
self:MoveToFront()
self:SetSize(scalewidth(210),scaleheight(75))

self.viewprofile = self:Add("DButton")
self.viewprofile:SetText("View Steam Profile")
self.viewprofile:SetFont("Stratum")
self.viewprofile:SizeToContents()
self.viewprofile:SetPos(scalewidth(10),scaleheight(10))
self.viewprofile:SetWidth(self:GetWide())
self.viewprofile:SetContentAlignment(4)
self.viewprofile:SetCursor("arrow")
self.viewprofile.DoClick = function(pnl)
	surface.PlaySound("ui/buttonclick_1.wav")
	csgoboard:Remove()
	self.player:ShowProfile()
end


self.viewprofile.Paint = function(pnl, w, h)
end


self.viewprofile.OnCursorEntered = function(pnl)
	surface.PlaySound("ui/buttonrollover_1.wav")
	self.onoption = true
	pnl.hovered = true
end


self.viewprofile.OnCursorExited = function(pnl)
	self.onoption = false
	pnl.hovered = false
end


self.copysteamid = self:Add("DButton")
self.copysteamid:SetText("Copy Steam ID")
self.copysteamid:SetFont("Stratum")
self.copysteamid:SizeToContents()
self.copysteamid:SetPos(scalewidth(10),scaleheight(40))
self.copysteamid:SetWidth(self:GetWide())
self.copysteamid:SetContentAlignment(4)
self.copysteamid:SetCursor("arrow")
self.copysteamid.DoClick = function(pnl)
	surface.PlaySound("ui/buttonclick_1.wav")
	SetClipboardText(self.player:SteamID())
	chat.AddText(color_white, "Successfully copied the Steam ID of " .. self.player:Name() .. " to clipboard.")
	self:Remove()
end


self.copysteamid.Paint = function(pnl, w, h)
end


self.copysteamid.OnCursorEntered = function(pnl)
	surface.PlaySound("ui/buttonrollover_1.wav")
	self.onoption = true
	pnl.hovered = true
end


self.copysteamid.OnCursorExited = function(pnl)
	self.onoption = false
	pnl.hovered = false
end

end

function PANEL:Think()
local r,g,b = maintextcolor.r,maintextcolor.g,maintextcolor.b
self.viewprofile:SetTextColor(self.viewprofile.hovered and Color(r+100, g+100, b+100) or Color(r+40, g+40, b+40))
self.copysteamid:SetTextColor(self.copysteamid.hovered and Color(r+100, g+100, b+100) or Color(r+40, g+40, b+40))
end

function PANEL:SetPlayer(client)
self.player = client
end

function PANEL:Paint(w, h)
surface.SetDrawColor(Color(maintextcolor.r-70, maintextcolor.g-70, maintextcolor.b-70))
surface.DrawRect(0, 0, w-2, h-2)
surface.SetTexture(gradient)
surface.SetDrawColor(Color(maintextcolor.r+30, maintextcolor.g+30, maintextcolor.b+30, 80))
surface.DrawTexturedRectRotated(w/2, h-4, w/1.5, w, 90)

draw.OutlinedBox(0, 0, w, h, 2, Color(maintextcolor.r+20, maintextcolor.g+20, maintextcolor.b+20))

end

// function PANEL:OnCursorExited()
// if not (self.viewprofile.hovered) then
// self:Remove()
// end
// end

vgui.Register(“playerinfo”, PANEL, “DPanel”)

hook.Add(“ScoreboardShow”, “showsc”, function()
if (not IsValid(csgoboard)) then
csgoboard = vgui.Create(“csgoboard”)
end

return true

end)

hook.Add(“ScoreboardHide”, “hidesc”, function()
if (IsValid(csgoboard)) then
csgoboard:Remove()
end
end)
[/lua]

What are you trying to achieve, and what is happening instead? I’m sorry, I’m just confused as to what you want.

Help with how to properly scale my scoreboard to the resolution of the client’s monitor.

A really botched way of fixing this would be to just multiply by the screen resolution and divide by the resolution you used when you made it

So for example if I was at 1920x1200 when I made my scoreboard, I’d multiply all width values by ( ScrW()/1920 ) and all height values by ( ScrH() / 1200 )

It’s extra processing and not the cleanest way to do it but it gets the job done for anything, even fonts

[editline]2nd November 2015[/editline]

The next time you make a VGUI make every value in reference to the ScrW() and ScrH(). It’ll keep EVERYTHING in proportion.

I’d recommend starting all your VGUI files with


 
local w,h = ScrW(), ScrH()


and just working off those

Well that was one of my earlier versions of the file, and while it looked great on 1920x1080, that still had large inconsistencies between resolutions. Namely positions


local myScreenW = 1920
local myScreenH = 1080
local function myScreenScaleW(w)
	return ScrW() * ((w or 0) / myScreenW)
end
local function myScreenScaleH(h)
	return ScrH() * ((h or 0) / myScreenH)
end

Change myScreenW and myScreenH to your resolution.

You shouldn’t be using ANY numbers whatsoever if you use this method properly

The worst you might get is between aspect ratios ( I’m looking at you 4:3 you little fucker ) but it won’t be that large a discrepancy. If you want to make it perfect you can get the ratio difference between a 4:3 monitor and adjust the values according to that

-snip- jake already mentioned all my points

Could you also check for 4:3 resolutions and adjust based on that?