How to make a Carousel menu?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Reygreisin

I want to make a carousel menu similar to “Mario and Luigi Inside Bowser” that isn’t too complicated, it doesn’t matter if it’s with code, it’s for a personal project, but I can’t find the answer to this all over the internet, and many except in the godot program, if someone can help me I would be very grateful.

enter image description here

I wouldn’t use a scrollcontainer for this, I would suggest creating your own container node

exuin | 2022-05-23 16:30

It is a very good idea, but I have only used very few nodes, of all those that exist, but the ones I have used have served me for what I want to do, apart from that I do not know how to use all of them, since for me they are very complicated to understand and there is no video explaining node by node. But if you have any ideas how to do it, I’m open to any suggestions.

Reygreisin | 2022-05-23 19:03

Okay, I will make a carousel menu node for you

exuin | 2022-05-23 20:58

By the way, dog on discord says that there is although no video that explains every node, you can look at the documentation to explain every node.

exuin | 2022-05-24 00:29

:bust_in_silhouette: Reply From: exuin
extends Container
class_name CircularCarousel


export var starting_angle := PI / 2
export var darkness := 0.2
export var transition_time := 0.5

var tween: Tween
var queued_turns := []


func _ready() -> void:
	tween = Tween.new()
	add_child(tween)
# warning-ignore:return_value_discarded
	tween.connect("tween_all_completed", self, "on_tween_all_completed")
	for i in get_child_count() - 1:
		var c := get_child(i)
		fit_child_in_rect(c, Rect2(calculate_child_position(i), c.rect_size))
		c.modulate = calculate_child_modulate(i)


func _input(event: InputEvent) -> void:
	if event is InputEventMouseButton:
		if event.button_index in [BUTTON_WHEEL_UP, BUTTON_WHEEL_DOWN] and event.pressed:
			if event.button_index == BUTTON_WHEEL_UP:
				if tween.is_active():
					queued_turns.append("move_up")
				else:
					move_up()
			else:
				if tween.is_active():
					queued_turns.append("move_down")
				else:
					move_down()


func move_up() -> void:
	var arr := []
	for i in get_child_count() - 1:
		arr.append(null)
	for i in get_child_count() - 1:
		if not (get_child_count() - 1) % 2:
			if i == 0:
				arr[1] = get_child(i)
			elif i == get_child_count() - 2:
				arr[i - 1] = get_child(i)
			elif i % 2:
				arr[i + 2] = get_child(i)
			else:
				arr[i - 2] = get_child(i)
		else:
			if i == 0:
				arr[1] = get_child(i)
			elif i == get_child_count() - 3:
				arr[i + 1] = get_child(i)
			elif i % 2:
				arr[i + 2] = get_child(i)
			else:
				arr[i - 2] = get_child(i)
	arr.append(tween)
	for child in get_children():
		remove_child(child)
	for child in arr:
		add_child(child)
	tween_children()


func move_down() -> void:
	var arr := []
	for i in get_child_count() - 1:
		arr.append(null)
	for i in get_child_count() - 1:
		if not (get_child_count() - 1) % 2:
			if i == 1:
				arr[0] = get_child(i)
			elif i == get_child_count() - 3:
				arr[i + 1] = get_child(i)
			elif i % 2:
				arr[i - 2] = get_child(i)
			else:
				arr[i + 2] = get_child(i)
		else:
			if i == 1:
				arr[0] = get_child(i)
			elif i == get_child_count() - 2:
				arr[i - 1] = get_child(i)
			elif i % 2:
				arr[i - 2] = get_child(i)
			else:
				arr[i + 2] = get_child(i)
	arr.append(tween)
	for child in get_children():
		remove_child(child)
	for child in arr:
		add_child(child)
	tween_children()
	
	
func tween_children() -> void:
	for i in get_child_count() - 1:
		var c := get_child(i)
# warning-ignore:return_value_discarded
		tween.interpolate_property(c, "rect_position", c.rect_position, calculate_child_position(i), transition_time)
# warning-ignore:return_value_discarded
		tween.interpolate_property(c, "modulate", c.modulate, calculate_child_modulate(i), transition_time)
# warning-ignore:return_value_discarded
	tween.start()


func calculate_child_position(i: int) -> Vector2:
	var angle_sign := -1 if i % 2 else 1
	var spacing := PI / (get_child_count() - 1) * (get_child_count() - i - 2)
	var offset := PI / (get_child_count() - 1) / 2
	var angle := starting_angle + angle_sign * spacing
	if not get_child_count() == i + 2:
		if not (get_child_count() - 1) % 2:
			angle += offset
		else:
			angle -= offset
	
	if not (get_child_count() - 1) % 2 and i == 0:
		angle += offset
	var center := Vector2(cos(angle), sin(angle)) * rect_size / 2 + rect_size / 2
	var top_left: Vector2 = center - get_child(i).rect_size / 2
	return top_left


func calculate_child_modulate(i: int) -> Color:
# warning-ignore:integer_division
	return Color.white.darkened(darkness * ((get_child_count() - i - 1) / 2))


func on_tween_all_completed() -> void:
	if queued_turns:
		call(queued_turns.pop_front())

Thanks thanks, I know it sounds silly, but what Nodes and children should I use for this code? TvT, I am indebted to you.

Reygreisin | 2022-05-24 04:11

Put the script on a Container. Make sure that the child nodes are Control-extended nodes like TextureRects or Buttons.

exuin | 2022-05-24 05:34

Dude, I’m sorry to bother you so much, because you must be busy with your own personal projects, but isn’t there a way to do it with buttons instead of the mouse?

Reygreisin | 2022-05-26 03:01

Yes, just change the _input function to listen for those buttons instead of the mouse.

exuin | 2022-05-26 14:49

Friend it shows that you have knowledge and your code is good, I do not understand it at all, honestly it did not work for me and I made many mistakes when implementing it in my game, apart from changing the mouse function to key, nothing worked, anyway, thanks . It’s too complicated for my knowledge.

Reygreisin | 2022-08-23 22:47