How to check if a Game Object has a specific Component?

Hey guys, I’ve been looking around on Google and on here for a while and I haven’t been able to find the solution to my problem.

Essentially the question is in the title, but to elaborate upon my question with an example; in my game I have game objects which have a script called ‘resource’ attached to them. Whenever I collide with the object I want to be able to check if it has that script component and if it does, then I want to be able to call a function from that script or something similar.

The main part I am stuck on is just finding out whether or not that object has a specific component. Is there a better way to go about solving my problem and if not, is there an answer to my original question?

Thank you,
-Alexander

1 Like

There is no way to detect if a component exists or not. But Defold is not that dynamic in its nature in the sense that you attach and detach component at runtime. Don’t you know which game objects that has that script and which doesn’t?

1 Like

Well, I do know which objects should have the script, but since I’m planning on having a lot of resources, I was hoping I could just add that script to the game object and that would be it for the most part. Is there a way I can check what type of game object I’m colliding with?

You get both the collision group of the game object as well as the actual game object id in the collision message.

2 Likes

Maybe you could post a message to the object collided with, and only handle that message in your resource script, while other objects/scripts ignore it?

2 Likes

Actually, if a component is a script, you can do that. Some preparation required.

-- some_module.lua

local get_shared_data
local remove_shared_data
local shared_store = {}

function get_shared_data(gameobject)
	local data = shared_store[gameobject]
	if not data then
		data = {}
		shared_store[gameobject] = data
	end
	return data
end

function remove_shared_data(gameobject)
	shared_store[gameobject] = nil
end

return {
	get_shared_data = get_shared_data,
	remove_shared_data = remove_shared_data,
}

-- some_script and another_script may be attached to the same game object.

-- some_script.script
local some_module = require("some_module")
local some_function

function init(self)
	self.gameobject = go.get_id()
	self.some_function = some_function
	some_module.get_shared_data(self.gameobject).some_script = self
end

function final(self)
	some_module.remove_shared_data(self.gameobject)
end

function some_function(self)
	-- body
end

-- another_script.script
local some_module = require("some_module")
local another_function

function init(self)
	self.gameobject = go.get_id()
	self.another_function = another_function
	some_module.get_shared_data(self.gameobject).another_script = self
end

function final(self)
	some_module.remove_shared_data(self.gameobject)
end

function another_function(self)
	-- body
end

-- in any other script, when you get game_object_id somehow, do:
local some_module = require("some_module")
local context = some_module.get_shared_data(game_object_id).some_script
if context and context.some_function then
	context:some_function()
end
local other_context = some_module.get_shared_data(game_object_id).non_attached_script
if other_context then
	-- do something
end

-- optionaly, you may want to switch context before calling context (script instance) function, for example:
function execute_in_context(context, fn, ...)
	local current = __dm_script_instance__
	__dm_script_instance__ = context
	fn(context, ...)
	__dm_script_instance__ = current
end

-- and then, instead of context:some_function() call:
execute_in_context(context, context.some_function)

-- or you may switch contexts manually, without helper function.
2 Likes

You can’t easily do this exactly the way you describe, but the way Defold works, you can do something very similar with no trouble. Just send a message to the object. The object will pass on the message to all of its components, so if it has a script listening for that message, the script will do what it needs to do, otherwise nothing will happen.

If you need to get some sort of response to the message if there is a script, just have the receiving script send a message back to ‘sender’.

2 Likes

Hey, I thought about this way, but isn’t it quite inefficient to be sending a message to every single object that I collide with? Especially if I might have lots of different instances where I want to do an action upon one object colliding with another. I feel like I’m missing something here.

Message passing is more costly than function calls but it’s still not that expensive. Besides, your not passing hundreds of message every frame right?

I can’t think of an instance where I ever would be. I thought there would be a better way as opposed to just sending a message to every object that it collides with, but if there isn’t I’m okay with using this method as my solution.

I don’t know if it’s a planned feature, or if it’s something that can’t be implemented effectively, but it would be really cool for the engine to have a way of detecting if a game object has a component. Even if it’s just checking to see if a game object has a component by the specified name. That way I could just create lots of different game objects like trees, piles of rocks, e.t.c. and then I could just add the ‘resource’ script to each game object.

Thanks for all the help everybody.

Perhaps I got this backwards (please correct me if I did)

You have a game object, with a collision object component, and a script component (name resource). (right?)

GO
|-- collisionobject
|-- resource (script)

This script will get collision messages automatically to the on_message function. There is no need to pass extra messages to it. I believe this is what you wanted?

That’s the setup I have, but in the collision message, I don’t have a way of seeing what kind of object I collided with. I can get the object’s ID, but that doesn’t really tell me anything about that object, especially if it was spawned from a factory.

I should’ve clarified all this at the start, but I didn’t think it would be helping:

I have two game objects:

Action
|-- Collision Object
|-- Action (Script)

Tree
|-- Collision Object
|-- Resource (Script)

Essentially whenever I click the mouse, it spawns an instance of the Action game object. All it does is detect a collision and on the next frame it deletes itself.

So when I click on a Tree, it should create an Action game object, that Action game object should collide with the Tree. When it does I just want to be able to harvest the Tree. However there are loads of other objects that the Action game object could collide with and depending on the object I would just want it to be ignored or for there to be a different interaction. That’s why I was looking for a way to detect if a certain script is attached to a game object.

Are there more actions than you have collision groups? If not, then maybe you can use the message.other_group value to decide the action?

Are the things you can interact with spawned from a factory or pre-created in the editor? If spawned then you could keep a look-up table keyed on id with object type as value and use that to decide the action.

Note that the max number of different physics groups is 16 though.

@britzl The objects are both placed and spawned, I want to avoid using collision groups because there’s a limit (which I’m sure you already know about) and I feel like it would only be more of a temporary solution.

The solution I used (which I don’t know the practicality of) is to index the id or path/address of the object in a table (which is inside a lua module) and then I just check the other_id against the table to see if it’s a resource or not. Inside my resource script, I index the resource in the init method so that I can just add the script onto whichever game object I want to be considered as a resource. I still feel like there’s a much better way of doing it really, but I’m happy with this method.

One possible solution that is quite general, and so might be suitable if you wish to include more complex behavior (hovering over a tree would yield a text box indicating that it can be chopped down, etc.) could be the following.

Create a game object with a collision box and have it follow the cursor around. If you click an object, that object (say, Tree) can be found by the collision messages sent by the object following the cursor. You can then check if the game object the cursor collided with (say, Tree) has whatever desired game object property you want, by for example, as @britzl noted, having a look-up table to check the game object ID in.

This works for factory spawned objects, too.

2 Likes

You can check if there is a component indirectly by any property.

For example:

local collisionobject_minsk_url = msg.url(nil, go.get_id(), "collisionobject_minsk")
local collisionobject_minsk_exists = pcall(go.get, collisionobject_minsk_url , "mass") 
print(collisionobject_minsk_exists) --true or false
3 Likes