Inventory Flickering

Godot Version

godot-4

Question

I’m new to Godot and coding in general, but i’m trying to make a test inventory appear and disappear when the player press “E”. I got it to work but it keeps flickering to appear/disappear and sometimes won’t obey the command.

Here’s the code:

func _physics_process(delta):

if Input.is_action_pressed("Inventory") && self.visible == false:
	self.show()
	Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
else:
	if Input.is_action_pressed("Inventory") && self.visible == true:
			self.visible = !visible
			Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

If anyone has a better way to do this and can demonstrate how it would look like in the code. Thanks anyway!

func _physics_process(delta):
if Input.is_action_pressed("Inventory") && self.visible == false:
	self.show()
	Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
else:
	if Input.is_action_pressed("Inventory") && self.visible == true:
			self.visible = !visible
			Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

So, first problem is your are handling this in _physics_process not _input(_event).

Second problem is related, you are using input.is_action_pressed() in the physics_process which runs 60 times per second. Everytime you hit the E if you don’t release it in less than 1/60th of a second your code will run it multiple times.

So, I would change your code to this:

func _input(_event):
	if Input.is_action_just_pressed("Inventory"):
			visibility()

func visibility():
#do all your changing visibility, 
just check if is_visible() do stuff, 
else do other stuff.

I would write it all out if I was on my computer, but formatting code on my phone is a pain. If you dont understand this, once I’m on my computer I’ll change it.

1 Like

Right, i understand what the function would do, but i don’t know how to use it. Would really appreciate if you could show what the full code would look like, when you can.
I tried to do it like this, with no success:

func _input(event):
	
	if Input.is_action_just_pressed("Inventory"):
			visibility()

func visibility():
	if is_visible():
		self.visible = true
	else:
		self.visible = false
1 Like

Okay, when I have a moment to mess around with it i’ll see what I can do.

However, if you create a UI controller that all you ui elements are children of this would solve that priblem.

You could leave the controller visible since it wouldn’t have any visuals. You would handle all initialization of ui elements through this ui controller.

Ui controller → makes visible and invisible
Child inventory ui

Edit:
Okay, so I created a minimal recreation with just a color rect and the code. It works with that, so, since it isn’t working with yours there is something else happening. Place print statements like I have in my example below, and tell me what statements print.

func _input(event: InputEvent) -> void:
	if Input.is_action_just_pressed("test"):
		print("test")
		visibility()

func visibility() -> void:
	if is_visible():
		set_visible(false)
		print("false")
	else:
		set_visible(true)
		print("true")
2 Likes

Dude, thank you so much! Followed your advice and created a UI controller scene with the inventory as child and used the code, and now it appears and disappears with zero flickering.
In the code i just altered the “test” on the is_action_just_pressed to “Inventory” and added the code so the mouse would appear with the inventory. I left the ui controller invisible so the inventory starts invisible.
My only doubt is, if i leave the ui controller invisible, it would be a problem to other UIs in the game ? Like if i put a life bar in the same controller, it would start invisible too right ? Theres a way in the code to make the specific ui start as invisible ? Or just create a second ui controller for the ui elements that already start visible from the get go ? Sorry if i’m bad at explaining these doubts, english is not my first language.

1 Like

No worries man! I am one that is living in a land that speaks not my first language, so, I have a lot of patience for those who don’t speak English as a first language, since, I require it from my gracious hosts.

So, to answer your question. You should be able to in the editor when you have your UI controler scene loaded (not instantiated into your gameworld) set which UI you want to be invisible/visible. You can click the little eye icon next to each child you want to be seen or not. This way you’ll leave your controller visible, and only change the visibility of its children.

I hope this answers your question, in general if there is a way to do it, it is best not to duplicate node functions by creating another UI controller.

So, in summary, no, don’t create another UI Controller. Open up your UI Controller scene set it to visible with the eye icon on the left side of the editor (don’t change it in code). Now, all of your children you want to start invisible you will click the eye icons to invisible, and leave the ones you want visible.

This can be accomplished in code as well, by giving each UI child in their _ready function a set_visible(false/true) call. This will only set their initial state.

So, you can do it via the editor, or via code.

1 Like

How can i load the controller scene without it being in the gameworld ?
I tried this in the world script, but didn’t work:
var ui_controller = preload("res://ui_controller.tscn").instantiate()

1 Like

So, first off do you actually want to instantiate it by code? Or were you thinking that is what I was suggesting?

So, for instantiating by code you would do this.

func _ready() -> void:
var ui_controller_load = load("res://ui_controller.tscn")
var ui_controller = ui_controller_load.instantiate()
add_child(ui_controller)

However, it would be easier to instantiate it in the editor. On the left side of the editor just above all your nodes there is the + and next to it is a link image, if you click the link you can search for the ui_controller scene and instantiate it in the editor. Also, you can just find it in your files below the node hierarchy and drag and drop the tscn file.

The second one I would recommend, however, I gave you the first as an example because it is very useful while making a game, in how to instantiate and load in scenes dynamically.

1 Like

No, i was a little confused on what instantiate and load were. If i just put the controller scene in the world scene, it only works if i make the controller invisible and inventory child visible.

If it’s visible(controller), with the inventory child invisible, the inventory will still be “on” when the game starts, but not appearing, so the moviment of the player will be hampered and only go back to normal if i press “E”. I would like a way to load the controller scene in my world scene, without the controller being instantiated in the world scene.

That way it would work without needing to make the controller invisible, just the child i want, like you said. So the UI element i want would start invisible and become visible only by some command. But like, how would i do that ? It needs to be in the world script ? If so, how would the code look like ?

So, I am not sure what is happening exactly, but it sounds like your UI controller is eating your inputs for your character movement.

Do you control the movement of the player by mouse or keyboard?

Is UI Controller a Control node?

If you can give me the type of node your UI control is, and your scene structure I’ll see if I can make it work, also how are you moving your character as well?

Also, I think you have some confusion of terms, loading and instantiating. Loading a node doesn’t allow you to use it, but prepares it for instantiating.

Info on Load/Instance

So, you load with a reference, that tells the engine where to find the scene and creates a resource of the scene, after, you instantiate that making it useable, to establish it in the scene tree under a specific node you add it as a child of another node.

The UI controller is a control node. The inventory node is a control too.
The movement is on keyboard(wsad) and mouse to move the player view.

As for scene structure, you mean the UI control scene or the gameworld scene ? The scene structure for my UI control scene is:

UI Control(control node)

  • Inventory(control node)

The game world scene is like this:

World(Node3d)

  • UI controller(control node)
  • World Environment
  • CSGBox3D
  • Player(CharacterBody3D)
  • GridMap

Also, thanks for the info on load/instantiated, wasn’t understanding it.

Okay, this evening (for me) I’ll muck around and see what I can come up with.

One last question, what part of your characters movement is impeded? All, or just the mouse interaction?

All movement gets impeded. I want it impeded when the inventory UI appears, problem is, the scene starts with it open, making the inventory invisible does just that, but it behaves as if is open and impedes the moviment.
When i make the ui controller invisible, it works properly, the inventory starts invisible and not impeding movement, and appears on command.

So the way i want it to work would be:

  • UI controller visible with child being visible/invisible depending on the need.
  • Inventory starting closed in the scene, not just invisible, as it would still be open and impeding the moviment.
  • When inventory opens, movement impeded.

Thanks for taking the time to help me out, gamedev is not easy at all lol.

1 Like

Okay, I’ll try to mock something up when I get a free moment tomorrow, I’m guessing your UI controller is eating inputs, not your inventory.

1 Like

So, you mention impeding movement, but that is intentional for when inventory is open. Do you have code in the inventory that impedes movement? Because otherwise I am not seeing why your movement is being impeded, In a 3d world with your node hierarchy I am still able to receive inputs into my characterbody node.

You said your scene starts with your inventory open, is this by design or is this unintentional? I guess is there other code you have concerning your inventory that you didn’t post here that could be causing the problem?

The other thing is how is your code set up exactly, I realized I didn’t confirm how you set up your new code. If you are controlling inventory from UI controller it should look like this (with your added things for changing mouse):

@onready var inv := $Inventory

func _input(event: InputEvent) -> void:
	if Input.is_action_just_pressed("test"):
		print("test")
		visibility()

func visibility() -> void:
	if inv.is_visible():
		inv.set_visible(false)
		print("false")
	else:
		inv.set_visible(true)
		print("true")

Ah, now the movement isn’t impeded. The inventory starting open is not intentional. Also i would want the game to freeze when the inventory is open, it’s better them just the player movement being impeded i think. My code looked like this:

extends Control
		
func _input(event: InputEvent) -> void:
	if Input.is_action_just_pressed("Inventory"):
		visibility()

func visibility() -> void:
	if is_visible():
		set_visible(false)
		Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
	else:
		set_visible(true)
		Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)

Now i declared the inv var like you showed and the movement is working fine. And if i mark the inventory in the ui controller as invisible it wont show when the scene starts and will not stop player movement, thanks!
Code now:

extends Control

@onready var inv := $Inventory
		
func _input(event: InputEvent) -> void:
	if Input.is_action_just_pressed("Inventory"):
		visibility()

func visibility() -> void:
	if inv.is_visible():
		inv.set_visible(false)
		Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
	else:
		inv.set_visible(true)
		Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)

Last thing is, how would i make it freeze the game when it opens ?

So you’d add a pause call.

When making the inventory visible add get_tree().paused = true

Change it back to false when closing inventory.

Then in the property inspector on the right side there should be a Node category that has a process tab, change mode of ui controller to always, and inventory to whenpaused.

1 Like

Perfect! This solved it, i noticed if i try to exit from the game with the inventory open, it won’t obey because everything is paused. There’s a way to add a exception for a especific command on the pause ?

Exit how? Via code?

If it is via code and it is by button press you could just unpause the game when you try and exit.