Interact With Objects in Godot 4 | Let's Godot
Vložit
- čas přidán 22. 08. 2024
- 👇Dialog Tutorial👇
• Cute and Simple Dialog...
Let's make an interaction system in Godot that allows players to press a button to trigger an event. You can make your NPCs start dialogs, implement save points, toggle lights, and anything else you could want your player to be able to do in your game world with a press of a button. By following this tutorial you'll also learn about Callables in Godot 4, as well as how to use composition in place of inheritance to make your code modular and take your indie gamedev skills to the next level.
If you enjoyed the video leave a like :)
🎵 HOME - Head First
🎵 Less Gravity - Shhhh! Golf Is On - Lofi
🎵 Towerz - while it lasted
🎵 l'Outlander - Soul Searching
Please keep making this, all of your tutorials helped me a lot to polish my game - it's a matter of time you will blow up due to the quality of materials and Godot getting more popularity.
Thank you very much!
Thank you for the kind words!
When adding the interaction area scene to your interactable node, you must add it exactly as they do at 8:23! Don't use "Add child node". Use "Instantiate Child Scene". You can do this a few ways:
- Click the chain link button in the top left of the node list
- Right click -> Instantiate child scene
- Drag the interaction area scene (tscn) file into the node list
If you add it as a child node, you can see that it won't have the little WiFi symbol in the node list (no signals attached). If you add it with "Instantiate Child Scene", it will have the little WiFi symbol, carrying over the signals you defined in the interaction area scene.
I was having trouble getting the _on_body_entered/_on_body_exited signals to fire like some other here but finally figured out what I was doing wrong. Hopefully this helps someone else!
Thank you! That was exactly what I was missing :D
Thanks man, this answer should be pinned!
MVP comment right here! I couldn't figure out why it wasn't working when it looked all correct in the code. Thank you!
thx.i was searching comments for solution to this :D
Thanks for your comment, I triple checked the video but did not notice this.
I can notice the ammount of work you are putting into these videos!!! Looking forward to more of these!
For those that having trouble with the label not showing above the NPC, try replace label.global_position.x = label.size.x / 2 to label.global_position.x = active_areas[0].global_position.x - 50 , it worked for me, the label is centered above the NPC.
Really high quality code here. Thanks for sharing.
Thank you so much! Please keep making such awesome tutorials.
I really like this tutorial and the architecture of this interact system!
One thing I would suggest, though, is trying to used .distance_squared_to() rather than .distance_to() in cases where you are directly comparing the length of two vectors. Obviously in this case, the .distance_to() function wouldn't get called that much, but its still a good thing to do ahead of time since from your perspective the two options are identical. For example, if someone wanted to use this code to implement picking up loot drops in a Diablo-like ARPG, there might be a lot of interactables in the queue, in which case the difference between the two variables would make a difference.
Thanks for the tip. I had no idea those two can be used interchangeably and that the squared one comes with a performance boost. Will keep that in mind
@@DashNothing Probably good to note that they aren't interchangeable in *every* case, like if you really do need the specific value for distance. But if all you need to know is which object is closer, .distance_squared_to() is the way to go! Great video, I definitely learned something new!
No way you're still sub-thousand with such videos! Here before this'll blow up! :D
The amount of work you did to make that video is astounding. Excellent work. Also, great content!
Much appreciated!
I don’t know why the hell I’m watching this when I’m still doing the official tutorial thing but it’s just too damn interesting
This is fantastic! Please keep making these sorts of videos
Excellent tutorial! I subbed and will be watching your other Godot content. Thanks for making it!
This is excellent. New subscriber as I'm building up courage to dive back into Godot after a long break.
Great tutorial, thanks very much. I've been looking for one that worked for a few days and hadn't managed until finding this. Really high quality code too. Liked and subscribed.
Bro literally that's really hard but pretty amazing thanks for the help man!
Awesome! Great tutorial as always.
A legend, an unsung hero, cute animal saver
Thank you sir, keep up the great work
These are honestly so well done, great job man. Keep em coming!
Loved the video, explained all i needed to know, will watch the rest of your videos, thank you for making them
Well played. Now I have to watch the DialogManager-Video aswell. ;-)
Very good Video, I learned a lot!
Concise yet clear! Amazing video
Process is a preset function that is called every frame. Sorting an array every frame has a big toll on the perfomance if the array is getting bigger.
@@mkrojwvl8745 True, but I imagined the player would be around maybe half a dozen interactables at one time at most, which isn't that bad. If performance is an issue sorting could happen in a timer's timeout callback which would be called a few times a second.
Great video! Quick and easy to understand. Thanks!
This is really great tutorial. You are doing amazing job mate! Subscribed, for sure.
Subscribed. really appreciate you are sharing good design patterns here
I've never done programming before and this was so easy to follow thank you for this
I tried to figure this by myself with almost zero knowledge in programming... I thought I was still far from the right result, but not by THAT much :D This seems so complicated for such a simple thing in a game
Came for the Godot information, stayed for the Hot Mulligan lo-fi. Thanks for such a great video!
Amazing video, clear, well edited and produced, and super helpful with good practices and explanations, thanks you! +sub
Composition makes me happy I strive to be like you
Thanks for the great video! It helps a lot.
I'd like to suggest an improvement for `interaction_manager.gd`
```
func register_area(area: InteractionArea):
_active_areas.push_back(area)
_active_areas.sort_custom(_sort_by_distance_to_player)
func unregister_area(area: InteractionArea):
_active_areas.erase(area)
_active_areas.sort_custom(_sort_by_distance_to_player)
```
- `erase` method can simplify the find & remove.
- only sort whenever the area is registered or unregisted; no need to sort the array on every process cycle
interaction area could have a signal that has a callable as the first argument that allows you to tell the interaction area when you're done interacting, instead of needing to create a reference then pass a callable to the area. Mainly because the current setup means only one function can be run to interact with the area, but if it's a signal any number of things could also happen when interacting.
Fair, but even this, in my opinion simpler approach, can easily be expanded with an interact or interaction_finished signal on the interaction area which can be emitted from the manager
I have no idea about programming, like absolutely zero and it's like learning mandarine for me when I try to watch these tutorials as I just started to make games with Godot. But your Video was exactly what I was searching for. Thanks!
this so great. i love this approach. You are awesome! thank you SO MUCH! ♥
Thanks for the video, I'm creating dialogues using Dialogic addon and this method really helped.
Amazing as always
This is much better than my raycast method, Good job 👍
perfect for what i needed, thanks!
Im a unity refugee so this has been a massive help!
nice video as always thankyou,
i also implemented the same in 3d ,so simple and reusable
Muy buenos tus videos, aprovecha el declive de Unity y métele mas a Godot, te va a ayudar mucho a crecer, tus videos son muy interesantes!
Thank you!
This implementation has potentially a lot of bugs for an actual game, but its good enough to get the idea
I'd love to know so I could potentially improve on this solution - what do you think would cause bugs?
hello, i have an small problem running the code, so, everything works fine until I change to another scene, then when it enters another interaction area the game just stops and gives me the error:
Invalid get index 'global_position' (on base: 'previously freed').
Hope you can respond to me.
oh and i forgot to mention that to change scenes I used get_tree().change_scene_to_file("res://niveles/nivel test/world_test.tscn"), dunno if it is the best way to do it but... it works
My bad, the issue is with the way of getting the player node in the InteractionManager script. When a new scene is loaded and the old player is deleted, the script doesn't fetch the new player. Good thing you found this!
Here's the solution with a bit more info:
gist.github.com/DashNothing/dd25d62a264bd581ee2ea2ffdac026a4
@@DashNothing Oh. My. GOD, you have done it, and so fast too, you have saved me (and many others), you are stablising a new generation of programmers, thank you for your very important service
Thank you! I was having this same problem too and had no idea what was going on.@@DashNothing
@@DashNothing I was having the same issue, thanks for this!
Really awesome, your tutorials speed up my dev process by a ton, thanks!
I just wanted to ask something, whenever I reload a scene, the InteractionManager doesn't because it's a singleton, which doesn't update the player variable and thus creating an error in the func _sort_distance_to_player because the variable player's value is null after a reset. What I did was just updating the player variable constantly.
Am I doing it right lmao
wow. needed this
Nice implementation! One thing had me thinking though; what's the use of the "Action name" variable on this system? (The one you input "talk" for the player and "toggle" for the lamp), i've watched the video twice and I still don't think it has much of an use hehe
It's for the interact prompt label ([E] to ..). So you can change the prompt based on the context of the interaction.
I'm super new to this and would love to have seen how you set up the lamp from the beginning in order to implement it into the game. Woud there be a chance that you could show that in the future? Or lead me to a resource that would show me how to set up an object with an animation to interact with the player?
In the case of the lamp I really didn't use any animations. I just changed the frame of the sprite, as you can see in the code. To learn more about the topic check out Godot's documentation for Sprite2D, AnimatedSprite and AnimationPlayer. Hope that helps!
does this work with 3d?
sorry im getting "Invalid set index 'global_position' (on base: 'Label') with value of type Vector 3" everytime my player collides with the area :/
it redirects me to the "label.global_position = active_areas[0].global_position" line in the process function but i'm not seeing any typos so :(
It looks like you used an Area3D node instead of an Area2D for the interaction area. Hope this helps!
@@DashNothing sorry aahah i forgot to mention that my project is in in fact 3D
@@bestyesecretclub Okay, then you can use the Label3D node instead, which is the easiest alternative. Check out your options here: docs.godotengine.org/en/stable/tutorials/3d/3d_text.html
Amazing video, helped me a lot on developing my first 2d game I'm making! But I got 1 question though, how did you switch the sprites of the lamp with no Animation Player or AnimatedSprite2D? I have the open and closed sprites for a chest and im trying to make it change to that open sprite.
I used a texture with both frames next to eachother. Then I set the sprite's hframes property to 2, telling Godot to split the texture into two parts. To change which part is shown I change the frame property.
@@DashNothing Oh thanks a lot!
@@DashNothing Also is there a way to make the interact option appear and work only once?
@@PiglioXDD at the end of the interact function you can do one of the following:
- get interaction area's collision shape and set its disabled property to true
- call interaction_area.queue_free() to delete it permanently
This is off the top of my head so if it doesn't work let me know.
@@DashNothing Used the second option and worked perfectly, again thank you very much! Keep it up with the videos!
Great tutorial, but can I use this in InteractionManager?
func unregister_area(area):
active_areas.erase(area)
instead of this:
func unregister_area(area):
var index = active_areas.find(area)
if index != -1:
active_areas.remove_at(index)
Yes you can. At the time I didn't know about the erase function, but yeah it's cleaner.
Keep it up ! Let’s godot !!!
Hey, great video, much appreciated! I have one issue though, it seems that I'm able to interact w/ an interactable object from any distance, and immediately when the two enter a scene together. It's confusing to me because I assume that you can't register an area as interactable until the player collides w/ the interaction_area's collisionShape. I'm not sure why it isn't acting like that.
If I add two interactable objects to the scene, they do shift between distances, but I can interact with either of them from any distance as well. I don't know if that's relevant, but yeah. Again, thanks for the video!
Never mind, I realized it's because while my InteractionArea's collisionshape was masked to only detect the player, the actual object it was attached to was set on the default layer, which is player. It fired immediately on startup because it thought it was the player lmao.
@@enderalex300 Easy mistake to make. Glad you figured it out.
worked! Thanks
Need help fixing my codes, in your InteractionManager, you made a function called _sort_by_distance_to_player, and had arguments area1, and area2, but it is giving me an error that says, "Invalid get index 'global_position' (on base: 'null instance'). Do you have an idea why so?
It could be that your InteractionManager can't find the player scene (first line where it gets first node in group "player"), in which case make sure player has his group set to "player" and that it's in the scene.
The other problem could be with regjstering areas, so make sure each area calls register and unregister functions when the player enters/exits them. You shouldn't have any null values in InteractionManager's actjve_areas, so you can print() it and check and different times in code.
Hope that helps!
@@DashNothing @therealugly3846 I have the same problem. but it is not happening all the time. When a mob dies and drops a ammo_crate this happens. could it be that when an scene ist instantiates the player does not get registered as area_entered? Did you ever solve your prolem @therealugly3846
I was trying to figure something like this out so this was super helpful. One thing I am encountering is an invalid get index for global position in the sort by distance function. I am trying to figure out if this is something happening as I move just out of range of one item and it starts to trigger on another. I am pretty sure it is trying to unregister as it does this. I am using this for drops from monsters and it works pretty well except for this one thing.
actually seems to be something with the two items being inside the same area....
ah I figured it out... my group for player is Player and following I did player. So excellent tutorial. Much appreciated.
@@prof_nobody Awesome! Glad you got it working
good job
very nice!
Hi DashNothing!
Thank you for the great video! It's super helpful. I have a question to you and to anyone in the chat really. I'm implementing the InteractionManager singleton and InteractionArea class into my current game, however I am getting a runtime error of: Invalid type in function 'register_area' in base 'Node2D (InteractionManager.gd)'. The Object-derived class of argument 1 (CharacterBody2D (Player.gd)) is not a subclass of the expected argument class.
It seems that the instantiated version of InteractionManager within the InteractionArea class is not being recognized properly. Any ideas to why this happens?
This seems like you're passing the player scene to the reguster_area function, when you should be passing in the interaction area scene. Because register_area is called from the interaction area script you have to pass in "self".
when i write
func _ready():
Interaction_area.interact = Callable(self, "_on_interact")
in the npc that i want to add, it says that "invalid set index "interact" with vallue of type Callable"
thats how i wrote the var interact:
var interact: Callable = func():
pass
are you using godot 4?
@@yxgen4 yeah, 4.2 i think
Ok so I'm running into a problem trying to register the interaction area in the manager on body entered and exited (7:59). It's saying that InteractionManager is not declared in the current scope. Do I not need to import that somehow? Not sure how this works in godot, is that a singleton?
Yes, it's a singleton but you need to tell Godot to use it as such. Click on Projecr settings, then autoload tab and add interaction_manager.gd. Now it wiill be accessible from any script.
This is covered in the video as well if you find yourself lost
Hope that helps!
@@DashNothing awesome man. Yes sorry looking back I realized I missed this in the video. Thank you so much for the reply!
I used your tutorials for the dialog manager and interaction manager and they seem to work great. But when I try to incorporate my inventory into the mix, and I'm trying to add a yes/no prompt to pick up items, I cannot at all get it to work. Any tips? Also, is it possible to have my main character's dialogue to appear over his head and whatever object I'm interacting with has its dialogue closer to it? Like a conversation. Thank you for whatever help you can give!
This was working fine until i overlapped objects with this and then it crashed.
@@kiroky95 What's the error you get? It should work 100% fine with overlapped interaction areas, as shown in the video.
@@DashNothing Invalid get index 'global_position' (on base:'null instance") on sort by distance to player area1 in InteractionManager script
It says that there is no label shown
Hello! i have a problem where it says "Invalid get index 'global_position' (on_base: null instance) upon entering to areas in the same time. in Addition labels are not shown whilst entering areas. Have any ideas why?
There's a number of things that could be wrong:
- Your player variable in InteractionManager is null - this could be if the player isn't in the player group
- You added the interaction area scene the wrong way - you have to add it by clicking on the "instantiate child scene" button (link icon) and NOT on the "add child node" button (plus icon)
- The null instance is one of the interaction areas, which means they most likely don't register / unregister themselves properly
Use the debug tool to find out exactly what variable is causing the issue and I'll be happy to help you further!
@@DashNothingI think I fixed the problem, player var was null even tho it’s in group so I made my own function which gets player pos to global vector and its given to area_to_player. But the label “[E] to interact “ is not showing (wasn’t before fix) any idea why?
@@kamolr6539 Do you update the player's position in the proccess function? If not, you should do that to always have the newest position available.
@@DashNothing function which changes global vector is inside player script and is called after move and slide function and play animation function
@@kamolr6539 Can you interact even though the label doesn't show up?
I like this vid
hey! this was an amazing tutorial!
I have a question about the sprite frame changing at 10:00
Instead of using a light i am using a campfire which as a animated sprite. for func _toggle_fire how would i change the animated sprite from a animated unlit campfire to a animated lit campfire?
You can have multiple animations per animated sprite. You can change which animation is playing programatically with sprite.animation = ...
I have a few questions:
Can this not be done with a tile from a tile set?
Do i have to export every object I want the player to interact with as a separate image, then use it as an image file? (or what type of file, anyway?)
I also find it weird that I need a separate window for every item?
Can't I create every interactable object in my main scene from scratch?
pls someone help 😩
If I wanted to make an interaction that teleported the player, how would I go about that?
"make sure you player is in the player group" Can you expand? @3:09 Great videos, btw.
If you click on the root node of your player scene by default you see the inspector on the right side of the editor. Click on the "node" tab next to the inspector tab and you will see the node's signals. Click on the "groups" tab next to the signals tab. There you can add the scene to groups by typing in the name of the group. In this case type in "player" and click add group.
nvm, fixed it by myself, thank u for the video, if I finish my game, you should be in the credits ❤
I keep getting the error "Invalid set index 'text' (on base: null instance) with value type of String." for my label. What do I do?
Make sure you got the @onready var label = $... line at the top of the interaction manager script. Also make sure that the name of the label node in the scene tree is the same as the name you put after the $ symbol.
When my player collides with an object that I attached the interaction_area to, I get Invalid get index 'global_postiion' on base: Array[Node] in the custom sort function. The player group is player and everything else is just like in the video. What could be wrong?
Check if the word "global_position" is spelled correctly in the sorting function.
func _sort_by_distance_to_player(area1, area2):
var area1_to_player = player.global_position.distance_to(area1.global_position)
var area2_to_player = player.global_position.distance_to(area2.global_position)
return area1_to_player < area2_to_player@@DashNothing
@@ivanchistyakovyt hmm, it seems like the function is trying to get the global_position of a nonexistant node. Could it be that an interaction area registered itself on the manager, and then it was deleted from the scene before unregistering? Or something other than an interaction area registered itself?
@@DashNothing Found mistake! Was @onready var player = get_tree().get_nodes_in_group("player") instead of @onready var player = get_tree().get_first_node_in_group("player"). Big thanks for the video and trying to help! Liked and recommended!
hello! i came from your textbox video. thank you so much for this high-quality content!
i was wondering if you could help me - when using the interaction manager and dialogue managers together, how do we "tell" the interaction manager to display the textbox we set up in the dialogue manager? thank you!
Thanks for the kind words!
You don't really tell the interaction manager what to do at all. The beauty of this is that each interaction area has its own oninteract callable that defines what happens.
So when you're overriding the oninteract callable you can just call DialogManager.start_dialog(). I put an example of just that near the end of this video, when making an npc.
@@DashNothing Ahh ok, so to use the dialogue manager, we would go into the "triggerring" object's script and call the dialogue manager? Or would we call the dialogue manager in the interaction script?
Thank you for your support I really appreciate it!
@@bellehuberty9457 You'd do ti from the triggering object's script. Check out how I made an NPC at the end of this video.
Good.
I can tell I've got a long road ahead of learning.. the small oversights and assumed knowledge in this video leave me completely lost. This isn't quite as beginner friendly as I thought, I suppose I'll return in the future.
It's not meant to be for complete beginners, unfortunately. When we're talking about systems such as this there's an implied required understanding of software architecture and coding fundamentals. Of course, starting out you should tackle easier topics, such as the action rpg tutorial series by HeartBeast. I followed that one when I first started and I cannot recommend it enough.
Can i use this method to add a value to a variable in another scene, for example i want to add ammo to my weapon?, i tried to use and a made a global var but i dont know if it is the best way to do.
You can either have a global singleton that holds the ammo value and then add to it from your interact function and read from it in the player script. I assume this is what you did.
Or you can have a reference to the player in the object that adds the ammo and add it directly to the player.
I think the first approach might be better because there's less coupling. But it's fine either way.
@@DashNothing thanks
Wouldn't using a singleton for interactionmanager restrict the game to being singleplayer? Or are multiple players able to use it with no problem?
@@lordbarron3352 I've never made a multiplayer game, so I don't know. What do you suggest?
@@DashNothingFor composition rather than inheritance, id have to think for a while. The way you structured it in the video works pretty similar to inheritance though. You essentially just decoupled the class to be overriden and set it as a singleton.
For the inheritance route, you could have the player check for objects with an interactable component within an area, then call the objects override method.
Currently though, i think I like your method better.
hi that was a very great tutorial ! Everything worked almost perfectly until i tried to see the text bubble, but it didn't appear... The "press E to.." work just fine but for some reason not the text, so if anyone have info on that, i will gladly take advice
Hi, i really love your tutorials but for some reason the interaction isn't triggered, the interactionmanager is in the autoload, the collision of the interaction area and the player are setup correctly, but it just dose nothing. I double checked the code with the video multiple times and didn't find an error.
Thank you!
Does the text "[E] to interact" appear when the player is in range or does that not work either?
@@DashNothing That dosnt work either.
@@doingitsidesways So it's either your active_areas is always empty shich means your interaction areas don't register themselves properly OR your can_interact starts at false. Can you print out those two into the output from InteractionManager's _unhandled_input() function when the player tries to interact?
Hi thanks for the help, i figured it out after breaking my head over it, for some reason connecting the signals trough the editor didnt work, even tho it looked like it did, i connected it trough code:
func _ready() -> void:
body_entered.connect(_on_body_entered)
body_exited.connect(_on_body_exited)
and now it works as expected. Thank you for replying to me, i looking forward to more of your videos.
@@doingitsidesways holy shit thank you. i was about to rip my hair out lmao you're a godsend. amazing tutorial @DashNothing this streamlines so much for me!
Hi again Dash!
Quick question with your Interaction Manager.
Could it be adjusted if we wanted to interact with objects that open UI? (inventory, crafting menus, etc.?)
If so, where? In the "input" section?
Yes, you can do that easily. You would do it the same way you do other interactions - by assigning a function to interaction_area's interact callable. Only instead of let's say starting a dialog there, you could get a reference to a hidden UI element in the scene and set its visible = true.
@@DashNothing I see - what Node would you recommend for a UI? I'm currently using a Control Node and it just doesn't seem to make UI appear, even when the function IS called.
It works if I hardset a key to it like "i", but not with the interaction "e".
@@HammHammVT Any node that inherits from the Control node would work. You would probably use a PanelContainer for a background, and then place the text and buttons inside it.
Then in the interact function you can write something like $PanelContainer.visible = true
@@DashNothing
DashNothing, Godot Sensei LOL
I'll give it a try - wish me luck!
@@DashNothing
Hi Dash - no luck.
Even with "visible = true", the UI doesn't want to appear.
I can make it appear without the interaction object (Input.is_action..) but not with the object.
My print statements tell me that the value has changed tho. Idk how to proceed from there.
Any suggestions?
Hey, All of this works but when i try do interaction_area and the stuff under on_body_entered/exited it says Identifier "InteractionManager" not Declared in the current scope.
amazing! can we please grab the source code for this project?!
I just wanted to know how to make the interaction text return as in the video because when I first interact with the NPC it doesn't appear again
@@matezu It should be working like that with the code in the video. Maybe you didn't set can_interact back to true?
For some reason I always have on_enter triggered once, when I enter actual area, I have 2.
uPD. Seems to be fixed. I did not pay enough attention to collision layer and mask part.
In the interaction manager I'm being told that in lines 13(active_areas.push_back(area)) and 22(if active_areas.size() > 0 && can_interact:) that there is an 'Invalid call. Nonexistent function 'size' and 'push_back' in base 'String'.'
active_areas is supposed to be an array, and it looks like you made it a string. When you define var active_areas at the top of the script make sure you add = [] to initialize it as an empty array.
var active_areas = []
Why did you/he decide to get the player node by using a group instead of doing it with "$" like done with var label?
With $ you can only get children of the node. Player is not a child of InteractionManager
When I add the functions to my interactionArea I get this mistake, can anyone help me??
Parse Error: Identifier "InteractionManager" not declared in the current scope.
@@marcelodavila8569 You didn't autoload the interaction manager. Go to project settings, click the autoload tab, click the folder icon at the top, find InteractionManager.tscn, then click the add button on the right.
@@DashNothing Thank you but now I have another problem where my sprite does not interact with anything even though I have it all set up
Thank you so much for this tutorial! I tried following it, but also converting it to 3d. I followed all of the steps that you made, but I'm getting this error "Invalid set index 'global_position' (on base: 'Label') with value type of 'Vector3'" and it's pointing to a line that states:
label.global_position = active_areas[0].global_position
Any idea what could be happening here? I'm not skilled enough to understand this yet 😅
Label is a control node, meant to work in 2D space. Maybe you could try using the Label3D node which was meant to work in 3D space.
Great vid! I had to modify the "player" var to be set during the "_process()" function in the InteractionManager. For some reason whenever trying to set "player" like you showed in the video, it would try to get the node before it was defined, leading to null reference errors.
My line was identical to yours (@onready var player = get_tree().get_first_node_in_group("player"), but it would be set to null because the player node did not exist when it was trying to set the variable. Not sure why my situation was different, but seems to work okay with my modification.
Maybe some other script creates the player at the start of the game? Unlikely, but I can't think of anything else.
Godot creates nodes bottom up, so autoloaded nodes should be created last, unless I'm mistaken. By the time they're created, all other nodes should be available.
If you didn't already, before setting it in _proccess() I would put an if to check if the player is already not null. Just so you don't set it over and over again.
mines having the same issue can you possibly tell me4 how you called it cus for me var player = get_tree().get_first_node_in_group("Player") doesnt work
@@Lupin_VA, sure! Instead of declaring the player variable on line 4 (3:42 in video), i moved this line inside of the _process(delta) function. Here are the first lines of my _process(delta) function:
func _process(delta):
if activeAreas.size() > 0 && canInteract:
if player == null:
player = get_tree().get_first_node_in_group("player")
...
Notice i also removed the @onready decorator. For me, this change makes sure the player is never null and also does not get called more times than necessary.
@@aflyingcougar8290, thanks so much! i had this same problem
Do I need to connect _body_entered and _exited and write out that bit of code every time I want to make an object interactible, or am I doing something wrong? It doesn't seem to want to inherit that.
No, the signals are connected in the interaction area script. All you have to do is add the interaction area to an object you want to interact with and override it's interact callable. The interaction area + InteractionManager will handle the rest for you!
Hello there! Thanks a lot for the tutorial! I've spent a couple of hours rewatching the videos and fixing some errors, BUT there's still one. Everything works fine, but the text box doesn't disappear after the dialog and keeps hanging... Seeking for help :) Thanks!
I assume you followed my dialog system tjtorial, in which case the problem must be in the dialog amanger script. It the _unhandled_input function theres a line textbox.queue_free() at 10:09 in that video. Does this not destroy your textbox? It should destroy it after every line and finally at the end.
It doesn't! Looked through the script again and can see no mistakes (but mb I'm blind). That part looks like this:
func _unhandled_input(event):
if (
event.is_action_pressed("advance_dialog") &&
is_dialog_active &&
can_advance_line
):
text_box.queue_free()
I GOT IT. I didn't assign the "advance_dialog" input... Now it works perfectly. Thank you! You're doing a great job and helped me a lot.@@DashNothing
@@owps2121 Yeah, that looks right. Can you check if it gets deleted after a line finishes, but before the whole dialog is over? You should never have more than one text box in the scene.
You can check it by playing the game and clicking on the remote tab in the scene tree. There you can find the dialog manager and a single text box should be its child at most.
Hello, I am a bit confused what to do at 9:20 and in ''Cute and Simple dialog box'': It seems both script for NPC is different(10.45 other video)..So I am more confuse which NPC script to take in order to make the interaction and dialog work... Is it possible to share here what to write exactly? (Also, line 20 at 9:20 seems to be cut at the end, no idea what to write) (I am really a newbie in writing code)
My other question is about the label: it doesn't show up above the NPC, I don't know why! I tried a few things as some peeps said below but no luck. I also have this message warning next to Label "Changing the Z index of a control only affects the drawing order, not the input even handling order"
Thanks!
Use the NPc script from this video as a reference. I don't suggest copying the code that wasn't narrated in the video line by line - these sections just show you an example of how you can use the system.
As for the label not showing up, I can't tell you why without seeing your code, but the warning you get about the z_index is not a problem and you can ignore that.
@@DashNothing Okay, I see thank you!
As for the label, here's my script from InteractionManager:
extends Node2D
@onready var player = get_tree().get_first_node_in_group("player")
@onready var label = $Label
const base_text = "(E) to"
var active_areas = []
var can_interact = true
func register_area(area: InteractionArea):
active_areas.push_back(area)
func unregister_area(area: InteractionArea):
var index = active_areas.find(area)
if index != -1:
active_areas.remove_at(index)
func _process(delta):
if active_areas.size() > 0 && can_interact:
active_areas.sort_custom(_sort_by_distance_to_player)
label.text = base_text + active_areas[0].action_name
label.global_position = active_areas[0].global_position
label.global_position.y -= 36
label.global_position.x = label.size.x / 2
label.show()
else:
label.hide()
func _sort_by_distance_to_player(area1, area2):
var area1_to_player = player.global_position.distance_to(area1.global_position)
var area2_to_player = player.global_position.distance_to(area2.global_position)
return area1_to_player < area2_to_player
func _input(event):
if event.is_action_pressed("interact") && can_interact:
if active_areas.size() > 0:
can_interact = false
label.hide()
await active_areas[0].interact.call()
can_interact = true
How would make it so that the label stop showing once input is pressed, but returns when you exit and reenter the interactable area?
In the interaction manager you would make a var show_label which you would set to false when an interaction happens.
In the register area function you would set show_label to true.
In the process function you would check if show_label is true before displaying the label.
This is off the top of my head, but I'm pretty sure it would work.
Great tutorial!
At first I was having trouble with the text appearing/disappearing until I realized that the "active_areas.size() > x" counts any CollisionShape2D node in its range so I needed to change it from "0" to "1" (at least in my code). My player character's CollisionShape and the Interactable Object's CollisionShape.
My only current issue is now my "E to Interact" key press works even OUTSIDE of the Interaction Area. Any advice for that?
Thank you!
The interaction area colission mask should be set to player only (1:02). This way it will not detect any other object.
Of course, with this you should change the 1 back to a 0.
@@DashNothing ah, I'm new to Layers and Masks. Will take another look back.
I'm having trouble, it seems that my interaction area is not detecting my player. I don't know if it has to do something with the play group or the collision layer + mask on the interaction area? I've checked and the Collision layer on my player is the same as mask on the interection thing. Which is set to "1". Then I am not sure about the group thing you mentioned, because am I supposed to have the entire 2D node in it and player node also named player? Or is it only supposed to be the player node by it self and the naming of the player node does not matter?
Your collission layer/mask setup is correct. As for the group, the second option is right.
- ONLY the player node should be in tbe "player" group
- it doesn't matter what the player scene is called, as long as it's in the "player" group it will work
Let me know if you need further help!
Yeah so, turns out that that wasn't the problem, I tried multiple debugging points whilst using print to determine what's being run. It turns out that _process in InteractionManager is always doing the label.hide() function, no matter if I enter a interaction area or not, it just won't add the area to the array. And as a matter of fact, it does not register the player entering it at all. Thank you for your help btw, I am a total newbie and I appriciate the help with your tutorials!@@DashNothing
@@supergames6696 Okay, so it seems the problem is with the interaction area. If collision mask is set to player's collision layer, also check these:
- does the area have monitoring turned on? It should by default and it's needed to detect when objects enter
- does the area have a collision shape child with a big enough area so that the player can enter it?
And it's no problem, I'm happy to help. You are doing very well in debugging this issue, especially for a newbie.
hey ive copied the tutoriail and its working for the most part i just cant get the text to appear becuase i get a warning and a crash saying "Invalid get index 'Y' (on base: 'Vector2')" what have i done wrong and how do i fix it
y should be lower case
@@DashNothing thx
There are a lot of codes and scripts missing in this video, like dialogue script , text box script and other
Yes, those are not relevant to the topic of the video. But if you want to check out how the dialog system is made, you can do so here: czcams.com/video/1DRy5An_6DU/video.htmlsi=UgbdeEQOgOv1UPwv
Great video! Just for some reason, the system doesn't work for me, unless I manually connect the InteractionArea to itself every time I add it to a node. Maybe I did something wrong, but if it's a version issue, hopefully this also helps somebody out.
Maybe you're adding the node with the add node button (plus icon) and not with the instance scene button (link icon).
@@DashNothing Oh wow, that is exactly what I did, I'm really learning a lot from this, thanks a lot! 😄
Hey thanks for sharing this, very useful! I tried implementing this in c# last night and I struggle to get the callable propagation called. I define the callable in InteractionArea like this "public Callable interact;". Then on the object i want to interact with i assign the callable like this "interactionArea.interact = new Callable(this, nameof(OnInteract));". But the OnInteract is not triggered. I think it has to do with how the callable is defined in InteractionArea nut not initialized properly maybe, but not sure how to implement this overridable callable from GDScript to C#. Any Godot c# dev have an idea?