Click on game object

97% of game developers don’t want to mess with a render script, they just want to be able to work on a game. (Actual number made up, but it wouldn’t surprise me if it’s that high or higher.) Not being able to easily know when a sprite/go is tapped/clicked is a horrible misstep, I think.

Defold is reminding me of Moai – very powerful, but more useful for programmers and not run-of-the-mill game developers. I’m not saying you should have made another GameSalad, but there’s a “foundation” that people expect with a game engine and Defold only has scaffolding in some places.

Jay

PS - Moai is, for the most part, dead. Defold does have more muscle behind it than Moai did, but I’d hate to see Defold being just a niche player.

4 Likes

IMO this example is a very good foundation for general 2D render script setups. It can maintain aspect ratio at any size, constrain the game area within the window, draw outside of the game window, and be able to translate screen input to game input. Examples like this just need more awareness so they can be adopted and devs can get on with the game making.

4 Likes

; TL/DR My suggestion of general input control is found in the bottom.

Two of the key aspects of Defold are flexibility and usability. The former requires an architecture which assumes very little of what you intend to do. The latter is often the opposite, too much genericness (a word?) confuses things and you see no clear path forward. In other words, we aim to provide you with something that helps you along fast, but does not impose a limit on what you can make. Our approach to solving this is to provide the flexibility through engine architecture, and usability through editor UX in combination with bootstrap data, which consists of the special case “builtin” content found in every project, as well as the template projects you can use to start things of. The grander vision is that when the library system has been more publically packaged, the users will contribute to this bootstrap data as well by providing sharable content. @britzl and others has already kicked this off by using github and other services, and of course this forum.
You can compare this to a general purpose programming language, where the syntax/grammar/compilers corresponds to “engine architecture”, and system libraries, user libraries, and online stackoverflow snippets corresponds to the “bootstrap data”. (You could even argue that the compilers belong to the bootstrap data, but let’s not go there)
The priority is always engine architecture first and bootstrap data second, as the architecture dictates what can follow. When it comes to bootstrap data, utility functions and the like, there is no “physical” boundary on what we can provide. But it suffers under the noise-factor, too much would also be confusing and annoying.
We are now very well aware of that a lot of users want something that helps with the problem of input and screen/world space conversions. We have a few ideas around how to solve this in a good way, but it’s not yet clear which one outperforms the others, or if there is an even better way. What I’m trying to say is that we do intend to solve the “conveniences” for people too, but not until we have an idea that provides us with a cozy gut feeling, to minimise the noise-factor. And since this is something that can be achieved right now without too much work, it will naturally have lower priority than things that can’t be done at all. There is a bandwidth problem to deal with too. In reply to @JAWhye, it is not yet a horrible misstep, it just hasn’t been fixed yet.

This is my suggestion how to do it now, a sufficiently general solution to touch input you can use until Defold supports it out of the box:

  • Everything you want to touch has a collision object with a specific mask, e.g “touch” or “interact”
  • One script deals with the input.
  • Using the view/projection matrices of the camera (which can be queried by this script every frame from the @render: message socket, which corresponds to the render script) it transforms the screen space coordinates into world space. I bet a lot of people have already done the screen/world conversion, if someone could provide a snippet for it it would be much appreciated. Until then, there is plenty of info online
  • It then spawns a new game object with a collision object at this point (e.g. a sphere, which will account for touch radius).
  • The new touch-gameobject will collect collisions and dispatch them back to the input-handling script.
  • If it’s important to distinguish type on the object you touch, you could by convention also add a script property that defines this for them. Another approach could be to send messages to them about the interaction and they would, by convention, have means to deal with these messages.
11 Likes

I feel bad that I made you take so much time to generate that reply, but I do thank you for it – it helps calm my soul. :wink: And I hope you didn’t take my previous post as a flame – it would be awesome for Defold to become a leading game engine and while digging into it I kept coming up with (what I saw as) dead-ends.

Thanks for that suggestion on how to handle the problem for now. That looks like an interesting solution.

Jay

1 Like

Absolutely fine, I just wanted to clarify where we are heading and why. :slight_smile: Keep posting the dead-ends in the forum and we can all pitch in with explanations, or suggestions for workarounds if the features are missing.

2 Likes

I did this for the Link and Slide example I posted a while back and it works really great. It’s a very nice use of collision objects.

3 Likes

Hey there.
I am new to lua and defold (not new in programming ;))

I got some trouble handling couple of gameobjects while clicking on it.
I have x gameobjects instanciated with a script attached that handles the ON_INPUT

function on_input(self, action_id, action)
   if action.released then
      if action_id == hash("clicked") then -- clicked is defined in input.bindings for the left-mousebutton
         ACTIVEOBJECT = go.get_id()
         print(ACTIVEOBJECT)
      end
   end
end

If I do so, ALL gameobjects are printing their own ID. WTF?

I also read something about the return-value of ON.INPUT. Tried to return FALSE or TRUE. If I use TRUE it just prints the FIRST object in hierachy.

the question is: how to solve that only the really clicked object is the ACTIVEOBJECT.

Sure, I could write some code who checks the clicked coordinates comparing to the coordinates of the GO…but isnt there a easier / more professional way? collision?

oh by the way… all the prints are also in the log if I just click somewhere (not an any GO)

Yes, that is by design. Any script that listens to input will receive all input - unless a script before it in the input stack consumes it (by returning true).

If you need to detect a click on a certain object you need to (as you suggest) check clicked coordinates against object location. Alternatively you can use physics collisions. What’s best depends on your case.

I have an example of this where I take into account game object scale, sprite scale and so on:

Try it: http://britzl.github.io/publicexamples/click_game_object/index.html
Code: https://github.com/britzl/publicexamples/tree/master/examples/click_game_object

And I have an example of this approach as well (click and drag):

Try it: http://britzl.github.io/publicexamples/click_and_drag/index.html
Code: https://github.com/britzl/publicexamples/tree/examples/click_and_drag

4 Likes

Thanks for your answers.
I thought it would be a little more handy than checking against coords o.o :wink:

maybe the collision is more useful in my first project. Have to check that.

Hey @britzl,
first, thanks for all of your examples! I am studyiing them. But, lot of them I cant download at once (one zip file). Why? Some projects are downloadable in one simple zip file, others dont have that nice CLONE AND DOWNLOAD button.

(maybe I am too stupid for?)

1 Like

The master repo of my examples project contains multiple projects in a single repository. Each project is in a separate folder in examples, with a main collection etc. This approach with multiple small examples in one project will be changed as soon as the new editor is released and supports bundling.

@britzl dont understand that git-things :smiley:

I am already on that problem. I’ve tried different sollutions. Collisions etc. but I got stuck on all them.
Now I am on the “coordinate-comparing”. Seems easy and logic. Seems…but maybe you can explain me result of the following code :slight_smile:


go.property("maxhitpoints", 100)
go.property("hitpoints", 100)
go.property("maxenergy", 100)
go.property("energy", 100)
go.property("energyreload", 1)
go.property("energyreloadspeed", 1)
go.property("moveenergy", 10)
go.property("movespeed", 5)
go.property("ammo", 0)


local COLLISION_RESPONSE = hash("collision_response")
local RIGHTCLICKED = hash("rightclicked")
local LEFTCLICKED = hash("clicked")
local INPUT = hash("input")
local targetpos = nil
local pos = nil 
local active = false

function init(self)
	-- Add initialization code here
	-- Remove this function if not needed
	msg.post(".", "acquire_input_focus")
	pos = go.get_position()
	active = false
end


function update(self, dt)
	-- Add update code here
	-- Remove this function if not needed
	--[[
	if self.move and active then
		local movetime = math.abs((targetpos.x - pos.x) + (targetpos.y - pos.y))  / self.movespeed			
		go.animate(go.get_id(),"position", go.PLAYBACK_ONCE_FORWARD, targetpos, go.EASING_LINEAR, movetime)
		--go.set_position(targetpos)
		self.move = false
		self.active = false
	end	
	]]
	
	if active then
		print(go.get_id())
		go.set("#sprite", "tint.w", 1.0)
	else
		go.set("#sprite", "tint.w", 0.5)
	end	
end

function on_message(self, message_id, message, sender)
	if message_id == hash("activated") then
		print("CROSSHAIR CLICKED ON ME " .. go.get_id())
		--self.active = true
	end
end

function on_input(self, action_id, action)
	pos = go.get_position()
	--local quat = vmath.quat_rotation_z(angle)
	
	if action.released then		
		targetpos = vmath.vector3(action.x , action.y , 0)	

			if active then
				print("POS " .. pos)
				print("TARGET " .. targetpos)
				self.move = true
			end

			print("---" .. go.get_id())
			print(" math x: " .. math.abs(targetpos.x - pos.x))
			print(" math y: " .. math.abs(targetpos.y - pos.y))
		
		if math.abs(targetpos.x - pos.x) < 32 and math.abs(targetpos.y - pos.y) < 32 then

			active = true
			print("ACTIVATED CLICKED IN MY BOX " .. go.get_id())
		else
			active = false
		end	
		

	end	
end

I’ve attached this script it to all the units (gameobjects with sprites, collision etc named COLLECTOR, TANK, ROCKETLAUNCHER for example). 3 I’ve added to the scene for testing.

Goal should be: clicking on a unit it will activating THIS UNIT for doing something like moving or popping up a menu.
To check that, I’ve easily added the code in the UPDATE function witch tints ITSELF 100% and 50% if not active.

Now the magic!
if you click on ONE “special” unit (always the same (in my project its the COLLECTOR) 1st in the list of the OUTLINE - maybe thats the reason??) and tadaaa… ALL 3 units are 100% shown. the other 2 units are never tint 100%.
But a look in the debug.log shows the correct output which one is active.

I hope my problem is clearly enough to understand (and my germany-english good enough :smiley:)

oh by the way: ignore the ON_MESSAGE thing… that message is also shown sended from the mouse/crosshair. but it has no function in that case.

And if you uncomment the move-stuff in UPDATE it will get more confuzius :slight_smile:

update:
If used self.active in the whole script instead of just active. the activation (highlighting) is working now.
Question to LOCAL definition: If i define a variable like LOCAL active at the beginning (not in any class or method), the variable will be used for the whole project (like public / global)??? Maybe thats the reason for?

if not…
if there is a way to have a global variable for all scripts and gameobjects?

There is a section in the lua manual on variable scopes: http://www.defold.com/manuals/lua/#_locals_globals_and_lexical_scoping

Local vars are shared between every instance of the script. Use self properties if you want a variable only for that specific instance.

3 Likes

Thanks @ross.grams. Thats exactly what I needed to know :slight_smile: and explains where the problems in my scripts are located. I thought local is really local (to the script) like in other languages.

So thats the way I can define a “global to all instances of this script”-variable :slight_smile: kind of global :slight_smile:
This is - in my case - an easy way to define a global variable for the only-one-active-object.

but, if I’ll define a var in a function with LOCAL it will be really local to that function, right?

Yes, the “local” keyword means local to the scope you define the variable in. Inside a function, for-loop, anything between “do/then” and “end”.

I would not recommend that you acquire_input_focus for all your units. At least not if you have many of them. There is a limit (16 I think) on the number of scripts that can have focus at a time. I’d recommend that you have one master script that takes care of input and holds a list of all units and possibly forwards the input data or in some other way processes it before sending an action to the affected unit(s).

Thanks for that hint, @britzl!

Meanwhile I took a deeper look in GAME.PROJECT and I saw all the limitations. Now I am a little scared :wink:
A limit of 128 sprites? Really? Whats the “real” limit. I am working with 32px tiles, also for the random map. By a grid of 100x100 tiles? that could be too much maybe :wink:

If the input-focus is limited my way of solution surely will be a problem! Thanks for the hint. I’ll manage this with the mousepointer and send a msg.post to the unit. means recoding :slight_smile:

Those are default limits. You set those because the engine allocates memory for them up front. If you need 2000 sprites, it is perfectly fine to increase that value. What will happen is that more memory will be allocated for the sprites when the game starts.

Also, note that tiles in a tilemap are not sprites.