Issue with implementing a slider in GUI (SOLVED)

Greetings,

I am having a little issue with implementing a slider UI element in my game.

The code is as follows. self.vendor_quantity_slider refers to the line between two triangular ends and self.vendor_quantity_slider_head refers to the movable element.

local slider_size = gui.get_size(self.vendor_quantity_slider).x
local slider_position = gui.get_position(self.vendor_quantity_slider).x + gui.get_position(self.vendor).x
local computed_position = map(action.x, slider_position - slider_size / 2, slider_position + slider_size / 2)
gui.animate(self.vendor_quantity_slider_head, "position.x", computed_position - slider_position, gui.EASING_LINEAR, 0.01)

I would expect the movement of the head to correspond with the movement of the mouse cursor (the pivot is set flush with the NW corner of its sprite), however in order to reach either end of the bar, I have to move a considerable amount over the respective side. Furthermore, this amount seems to be change whenever I resize the window, even though I am using action.x instead of action.screen_x (also double checked the size and scale property of the bar just in case).

Link to video
Any ideas on what might be the issue?

Are you sure the mouse cursor is following actual hardware cursor position? Try enabling hardware cursor so you can verify. Are cursor and other GUI in same scene?

I know DefOS has working example of GUI 1:1 setup even with screen resized check the cursor GUI https://github.com/subsoap/defos

2 Likes

Yup, they are on top of one another and part of the same GUI template.
26

Thanks for linking the repo, however I cannot use it as I am using Linux as my development platform of choice. Besides, I like to stay away from using native extensions unless deemed necessary.

1 Like

The useful part for you is the GUI scene setup. There is probably a difference in the GUI setup of the slider and the cursor if they both want to reflect different mouse positions?

Edit: Thinking about this more I think I had this issue but never solved it. Should make a stand alone example.

Hmm. I would expect this if you were using action.screen_x, but not using action.x. I would try just setting the x position of your slider head to action.x (no animation or other calculations), just to make sure the issue is not with your other code.

3 Likes

Guys, I am dumb. Found the cuprit - it was the scale of the container (self.vendor in my case).

First, I tried to set the scale to (1,1,1) to confirm my hypothesis. It worked.
Then I factored the scale (which was 0.8) into my calculations.
The final step was to change the order of operations a bit.

For completeness sake, the map function works as follows:

function map(var, min, max)
	if var < min then return min
	elseif var > max then return max
	else return var end
end

Thanks for helping out. :slight_smile:

5 Likes

Whoops, jumped the gun! Still having some issues when changing resolution on runtime. Is there any GUI property that handles this? Neither size nor scale seems to change.

OK, first, your code is overly convoluted. You’re converting to and from local and global coordinates in multiple places.

Lets refer to your “self.vendor” node as the “parent”, your “map” function as “clamp”, and assume the size of the “parent” node is the limit of your slider range. All you need to do is:

-- get offset/local position of mouse relative to parent node...and divide by parent node scale
local new_x = (action.x - parent_pos.x) * (1/parent_scale.x)
-- then clamp that pos to within the slider range
new_x = clamp(new_x, -parent_size.x/2, parent_size.x/2)

That gives you the local x coordinate you need to use on your slider node, either with gui.animate or gui.set_position.


Your current trouble, when you resize the window, is because of your nodes’ adjust modes. If you set your nodes to “Stretch” mode, it should work fine, but presumably you don’t want your GUI to stretch. With the other adjust modes, you will need to apply both a scale and an offset to your mouse position before you use it.

I added this to Rendercam so I would never have to deal with it again, so I’ll just refer you to my code for that.

Here’s the function I call whenever the window is changed to calculate the scale and offset for each adjust mode:

Click to show code
local function calculate_gui_adjust_data(winX, winY, configX, configY)
	-- winX, winY = the new window size
	-- configX, configY = the window size settings in your game.project file

	-- the X and Y scale of the current window size relative to the original one:
		-- these are used to calculate basically everything else
	local sx, sy = winX / configX, winY / configY

	-- Fit
	-- M.guiAdjust is a table that holds the stuff for each adjust mode
	local adj = M.guiAdjust[M.GUI_ADJUST_FIT]
	local scale = math.min(sx, sy) -- scale for FIT mode is the -smaller- of sx and sy
	adj.sx = scale;  adj.sy = scale
	adj.ox = (winX - configX * adj.sx) * 0.5 / adj.sx -- x offset
	adj.oy = (winY - configY * adj.sy) * 0.5 / adj.sy -- y offset

	-- Zoom
	adj = M.guiAdjust[M.GUI_ADJUST_ZOOM]
	scale = math.max(sx, sy) -- scale for ZOOM mode is the -larger- of sx and sy
	adj.sx = scale;  adj.sy = scale
	adj.ox = (winX - configX * adj.sx) * 0.5 / adj.sx -- x offset
	adj.oy = (winY - configY * adj.sy) * 0.5 / adj.sy -- y offset

	-- Stretch
	adj = M.guiAdjust[M.GUI_ADJUST_STRETCH]
	adj.sx = sx;  adj.sy = sy -- scale for STRETCH mode is...stretched - the scale is non-uniform
	-- distorts to fit window, offsets always zero
end

And here’s the function I use to convert from screen coordinates to “gui” coordinates

Click to show code
function M.screen_to_gui(x, y, adjust, isSize)
	-- note, this converts from action.screen_x/screen_y, not action.x/y
	if not isSize then
		x = x / M.guiAdjust[adjust].sx - M.guiAdjust[adjust].ox
		y = y / M.guiAdjust[adjust].sy - M.guiAdjust[adjust].oy
	else -- if you're converting to a GUI -size- rather than a position, you don't want the offset
		x = x / M.guiAdjust[adjust].sx
		y = y / M.guiAdjust[adjust].sy
	end
	return x, y
end

Here’s a little demo project: Slider Test.zip (3.7 KB) (it uses Rendercam, but you only need to add the two functions above to your own setup.)

10 Likes

Thank you very much for a thorough reply and taking the time to set up a demo project. What can I say, it works :slight_smile:

4 Likes

Closed one, opened another :smiley:

45

9 Likes