Help with first game

Godot Version

v4.2.2.stable.official [15073afe3]

Hello everyone. This is my first post so I am sorry if its too long or too convoluted.
I am making my first game in Godot and I am trying to recreate a mechanic that I saw in a game called “The Very Organized Thief”.

Basically, you have a list of things that you can get, when you pick them up, the item on the list is crossed off.


Image showcasing the list.

I have already started the project, I have set up the list model, the animations for it and the text that is on it

and also set up the interaction, I have followed this tutorial.

But I am having trouble with how to go on about doing the part with the checking if the object is on the list and if it is to make it disappear and get the item crossed off.


Image showcasing an item, that doesn’t always spawn there.


Image showcasing item being crossed off.

How can I randomize the list of items?
How can I make the objects appear in the world in preset locations randomly?
How can I check if the object that is collided with is on the list?
And also if the object is on the list how can I make the item get crossed off?

I have tried to search for how to do it but I have no idea what I am searching for exactly.
I am a beginner in Godot and am always happy to learn new things.
Please help.

  1. i would give every possible stealable item an ID. Put the ID to the corresponding text/label/sprite on the list. This way you can check if the ID of the Item you are looking at is also on the list.
  2. Have a list of possible positions for items on every map, for example in an Array[Vector3]
  3. to do randomization you can use the randomization-methods godot provides: Random number generation — Godot Engine (stable) documentation in English
  4. to cross an item on the list you would need an “animation”. I suggest having every itemname as a texture/sprite with one frame as Idle(not crossed of) and one(or more, if you do an animation) frames for the crossed off texture. Then you can just change the frame and have it crossed off
2 Likes

Hi, sorry but could you please explain how you give the items IDs? I am a bit lost and did not find a clear answer.

Just a static variable or normal variable. Then make a function to get that variable from the item you’re looking at. If it is static, just set up the id to be equal to the static variable.

1 Like

Im assuming you would create a Scene for every item and you have also a Script for them? Put a variable in the script called “id” and assign a value. Every item should have a different id so you can differentiate them

1 Like

How can you make a function get a variable?

You can get/set values inside the class directly to the database.

Example :

class_name Database

static var Key_List = {"Key_One": false, "Key_Two": true}

class_name KeyItem
extends Node

@export var Item_ID : String

var Key_Check : bool :
	get : return Database.Key_List[Item_ID]
	set(value) : Database.Key_List[Item_ID] = value

1 Like

How do you get the Item_ID from the Key_Check variable? I have rewritten the interaction script and have been able to set an ID to the items but I don’t understand how you get the Item_ID for example when the raycast collides. Could you please explain how the variable works? And also why is it a bool?

The way this works would be you make your own database and name your items using a string label. You make the database static and attached this to autoloads, so this persists even when changing scenes.

Then when you create a level and place objects, you assign those objects their own scripts to manage their own data. The @Export allows you to set the data in the inspector, so they’re unique within the entire scene.

The get/set are basically changing how you save/load that variable. So when you use the variable like normal, this gets/sets the data directly to the database. The string ID that you assign is case sensitive so make sure they’re exactly the same when you’re designing your level.

So inside the class, all you have to do :

class_name KeyItem
extends Node

@export var Item_ID : String

var Key_Check : bool :
	get : return Database.Key_List[Item_ID]
	set(value) : Database.Key_List[Item_ID] = value

func Pickup():
	if Key_Check == false:
		Key_Check = true 
		# Do something like an animation or whatever

You use the function whenever the player presses the button within reach to interact with the object. But when you change the value to true, this shows they already interreacted with the object. The IF statement makes sure the function only works when the value is false.

Booleans are great for yes/no answers. Do you have the object or not?


You can also make another variable and make this one an INT and use this to check the randomness of your objects. You place the exact same object in many different places, you assign them the same ID, but a different number.

You generate a random number and assign that number to objects in the database. The ones who don’t have numbers that match exactly, can auto-delete themselves when the level loads.

1 Like

Thank you for explaining it to me. I was able to make it work, I made a database, made a list with three items in it, then made a script for the items like you showed me.

For the items being crossed off, I connected a signal from the interaction script and after checking the Key_Check variable, it itself emits a signal that I connected to the player script, that makes a Label3D with a line visible.

It works but now if I would like to check if just one item is true, how can I do that?
How can I get just one item from the database?

1 Like

Depends on what you mean by check if one is true?

How many items have you collected? This would be a number.

1 Like

If I collect all the items, I would like to display a text or do something else. How can I check if all the items have been collected? As I understand the Key_Check variable gets the ID and then sets the value to true when its interacted with. How can I get the value from an item in the database?

The easiest way would be to treat them like a score. Every time you collect an item you increase the score count by one. When the score reaches your desired result you create an if statement that checks the score number.

class_name Database

static var ScoreCount : int = 0
class_name KeyItem
extends Node

@export var Item_ID : String

var Key_Check : bool :
	get : return Database.Key_List[Item_ID]
	set(value) : Database.Key_List[Item_ID] = value

func Pickup():
	if Key_Check == false:
		Key_Check = true 
		Database.ScoreCount += 1 
1 Like

That did it, thank you for responding and explaining. It was really helpful.

No Problem. Hope everything works out.

1 Like

Hey, sorry to bother again but I got lost trying to make the randomization of the objects, items on the list and the locations.
I was able to randomize the location of an object using an array as @herrspaten suggested in the first reply. But for the IDs and the list, I didn’t know how to do it correctly.

I made a script to randomize the Items on the list, then added it to the autoload and made a seperate script for each item that runs the _ready() function and gets the ID from the randomization script.

But I don’t think this is the best way to randomize the IDs, I have 8 items and I would like to have just one script for all of them, not one for every item.

Also made a script for the physical object that is in the world, to get the same randomized ID as the item on the list, so both are the same, for checking purposes.

Here is how it looks in game:


But this doesn’t really work.

How can I make just one script for all IDs & locations to get randomized?
How can I save that ID and add it to the object as well, so both have the same ID?

Edited - Removed the code, some images and some parts of the text because I didn’t really like how it was structured.

You can randomize location by simple numbers. You place the exact same object with the exact same String ID around the map. But you give them another variable and make this an INT instead of a BOOL.

When the level loads, within the ready function you check whether the number matches. When the number != that number, you delete that object. That means only the object with the exact same number wont delete themselves.

class_name KeyItem extends Node

@export var Item_ID : String
@export var Item_Number : int

var Key_Check : bool :
	get : return Database.Key_List[Item_ID]
	set(value) : Database.Key_List[Item_ID] = value

var Random_Check : int :
	get : return Database.Random_List[Item_ID]
	set(value) : Database.Random_List[Item_ID] = value

func _ready():
	if Random_Check != Item_Number:
		# Delete object

func Pickup():
	if Key_Check == false:
		Key_Check = true 
		Database.ScoreCount += 1 

This makes things simple without a huge mess of convoluted code. Within Godot you’re given a Scene/Level Editor which can make things easier. Take advantage of this.

That means you can place a Kettle around the map in 10 different places and assign them the exact same Item_ID, but assign them different numbers. Then when you start a new game, generate a random number between 1 and 10 and assign that to the database.

When you save/load the game, you can use these lists for the save file. Make sure to take into account the player may exit to start menu and start a new game, where you will reset this data.

1 Like
  1. Try this template:
    `
func randomise_items(items : Array) -> Array:`
    var randomised_items = [ ]
    for i in items.size():
        randomised_items.append(items.pick_random())
    return randomised_items
  1. Specify the list of locations, using Marker3D, store them in array, then inside of the function you duplicate the both arrays of objects to spawn (create one) and positions to spawn, then start for loop, simply pick one random item from each array, then spawn output object to output location, like this:
func spawn_objects_on_random_positions(obj : Array, pos : Array) -> void:
    var objects = obj.duplicate()
    var positions = pos.duplicate()
    for i in objects.size():
        var object = objects.pick_random()
        var position = positions.pick_random()
        var new_obj = object.instantiate()
        new_obj.global_position = position
        add_child(new_obj)
        # there you can implement logic for removing used items from objects and positions
1 Like

Thank you for the solution. I went with @StormWHM’s method, because I thought it was simpler but thank you nonetheless. Its good to know what other ways there are for solving this.

Thank you for explaining this again. I didn’t understand it at first but after some re-reading of your reply and tinkering with the code and editor, I finally understood and I got the locations to get randomized using your method, which is much more simple than what I had with the array and the shuffle bags.

But now, how can I randomize the IDs of the items and objects?
How I did it worked for the randomization but didn’t really work for the scripts because I had a script for each item. I would like to have just one script.

If I am able to have just one script to randomize the IDs, there’s a problem with the objects, If i get a model for them, how can I check if they are on the list?

I’ll try to explain: say there is a toaster on the map, and the list is randomized and there is no toaster listed, in this case I would like for the toaster to not be interactable and the ID to remain false in the database.

And if the toaster is on the list, the labels would be randomized so how can I check what slot/label its on and then cross off the correct label?
Sorry if this is a bit confusing. I don’t really know how to explain it better.