Target Appearing When Screen Clicked (SOLVED)

Hi, I’m trying to make it so that when the player presses on the screen, a target sprite appears in the location then fades away. I also have a camera that follows the player, which has made this sorta difficult for me to do and I’m honestly lost. There are no errors and the world position of the sprite appears where I want it to, but it doesn’t appear on the screen. Here is the code:

local function complete(self)
	local pos = vmath.vector3(-100, -100, 1)  -- Move the target off-screen
	go.set_position(pos, go.get_id())
end

function init(self)
	msg.post(".", "acquire_input_focus")
end

function on_input(self, action_id, action)
	if action_id == hash("touch") and action.pressed then
		-- Ensure the target is visible
		go.set("#target", "tint.w", 1)

		-- Get the camera's view and projection matrices
		local camera_url = msg.url("/cameraP2#camera")  -
		local view_matrix = go.get(camera_url, "view")
		local projection_matrix = go.get(camera_url, "projection")

		local screen_width = 1280 
		local screen_height = 640  -

		-- Normalize screen coordinates to [-1, 1] range
		local normalized_x = (2.0 * (action.x / screen_width)) - 1.0
		local normalized_y = 1.0 - (2.0 * (action.y / screen_height))  -- Flip Y

		-- Create a ray in normalized device coordinates (NDC)
		local ray_ndc = vmath.vector4(normalized_x, normalized_y, -1, 1)  -- Near plane
		local ray_ndc_far = vmath.vector4(normalized_x, normalized_y, 1, 1)  -- Far plane

		-- Transform the ray to world space
		local inv_view_proj = vmath.inv(projection_matrix * view_matrix)
		local ray_world_near = inv_view_proj * ray_ndc
		local ray_world_far = inv_view_proj * ray_ndc_far

		-- Normalize the ray direction (convert to vmath.vector3)
		local ray_dir = vmath.normalize(vmath.vector3(
		ray_world_far.x - ray_world_near.x,
		ray_world_far.y - ray_world_near.y,
		ray_world_far.z - ray_world_near.z
	))

	-- Define a plane at Z = 400 (match the camera's Z position)
	local plane_normal = vmath.vector3(0, 0, 1)
	local plane_point = vmath.vector3(0, 0, 400)

	-- Calculate the intersection of the ray with the plane
	local denom = vmath.dot(plane_normal, ray_dir)
	if math.abs(denom) > 0.0001 then  -- Avoid division by zero
		local t = vmath.dot(plane_point - vmath.vector3(ray_world_near.x, ray_world_near.y, ray_world_near.z), plane_normal) / denom
		local world_pos = vmath.vector3(
		ray_world_near.x + ray_dir.x * t,
		ray_world_near.y + ray_dir.y * t,
		ray_world_near.z + ray_dir.z * t
	)

	-- Debug: Print the world position
	print("World position:", world_pos.x, world_pos.y, world_pos.z)

	-- Move the target to the world position
	go.set_position(world_pos, go.get_id())
else
	print("Ray is parallel to the plane; no intersection.")
end

-- Animate the sprite's tint with a callback to the complete function
go.animate("#target", "tint.w", go.PLAYBACK_ONCE_FORWARD, 0, go.EASING_INOUTSINE, 0.5, 0, function() complete(self) end)
end
end```

If this is for 2D, use this for screen-to-world instead of LLM-generated, probably buggy code(you dont need to raycast to the world for 2D)

1 Like

Thank you for the help, I’m running into a minor issue where the first two lines of the bee.script

local DISPLAY_WIDTH = sys.get_config_int("display.width")
local DISPLAY_HEIGHT = sys.get_config_int("display.height")

are causing my game to crash. Is there a way around this?

Those lines are correct, nothing wrong with them. What is this error, can you please share the full error?

This is what pops up on the script, with the stack result on the side and the yellow highlighting of the first line.


There is no console error, however the game crashes when I try to load the collection that this is on.

For some reason this has just randomly disappeared. However, the target still does not appear on the screen when I click on the screen.

local DISPLAY_WIDTH = sys.get_config_int("display.width")
local DISPLAY_HEIGHT = sys.get_config_int("display.height")

-- function to convert screen (mouse/touch) coordinates to
-- world coordinates given a camera component
-- this function will use the camera view and projection to
-- translate the screen coordinates into world coordinates
local function screen_to_world(x, y, z, camera_id)
	local projection = camera.get_projection(camera_id)
	local view = camera.get_view(camera_id)
	local w, h = window.get_size()
	-- The window.get_size() function will return the scaled window size,
	-- ie taking into account display scaling (Retina screens on macOS for
	-- instance). We need to adjust for display scaling in our calculation.
	w = w / (w / DISPLAY_WIDTH)
	h = h / (h / DISPLAY_HEIGHT)

	-- https://defold.com/manuals/camera/#converting-mouse-to-world-coordinates
	local inv = vmath.inv(projection * view)
	x = (2 * x / w) - 1
	y = (2 * y / h) - 1
	z = (2 * z) - 1
	local x1 = x * inv.m00 + y * inv.m01 + z * inv.m02 + inv.m03
	local y1 = x * inv.m10 + y * inv.m11 + z * inv.m12 + inv.m13
	local z1 = x * inv.m20 + y * inv.m21 + z * inv.m22 + inv.m23
	return x1, y1, z1
end


local function complete(self)
	local pos = vmath.vector3(-100, -100, 1)  -- Move the target off-screen
	go.set_position(pos, go.get_id())
end

function init(self)
	msg.post(".", "acquire_input_focus")
end

function on_input(self, action_id, action)
	if action_id == hash("touch") and action.pressed then
		-- Ensure the target is visible
		go.set("#target", "tint.w", 1)

		local worldx, worldy = screen_to_world(action.x, action.y, 0, "/cameraP2#camera")
		local world = vmath.vector3(worldx, worldy, 1)
		
	-- Move the target to the world position
		go.set_position(world, go.get_id())
	end

	-- Animate the sprite's tint with a callback to the complete function
	go.animate("#target", "tint.w", go.PLAYBACK_ONCE_FORWARD, 0, go.EASING_INOUTSINE, 0.5, 0, function() complete(self) end)

end```

I’m not using the built-in IDE, so I have no idea why there’s an error. But this is just an IntelliSense error.

If your game is crashing without any error in the console, it means you messed up something badly.
Could you please zip your project without the build folder and share it so we can take a look.

Here’s a google dropbox link for it.
https://www.dropbox.com/scl/fo/y0gp7ghd2ly1o760ufyyd/AFrxcUIAQMRoMy3O9nl5yTw?rlkey=zmgx55nxf0z19nfqimdqfx1gd&st=foimnjen&dl=0

I don’t understand, does your project crash or not?
Please check the Z value. Z values are cumulative, so you might end up with a Z greater than 1

My project does not crash anymore, I didn’t change anything so I’m not sure why it started or stopped crashing.

I have printed out the value in the world variable and these are the results after a few clicks:

DEBUG:SCRIPT: vmath.vector3(5439.7299804688, 2109.9501953125, 1)
DEBUG:SCRIPT: vmath.vector3(5439.9018554688, 2109.8549804688, 1)
DEBUG:SCRIPT: vmath.vector3(5439.6865234375, 2110.0546875, 1)
DEBUG:SCRIPT: vmath.vector3(5439.5424804688, 2110.0983886719, 1)

Your cameras for the girl and boy are not set to Orthographic. If you change them to Orthographic Projection, world results will be expected.

I also recommend moving go.animate inside the if…pressed condition; otherwise, it will be triggered on every input, including mouse movement.

	if action_id == hash("touch") and action.pressed then
		-- Ensure the target is visible
		go.set("#target", "tint.w", 1)

		local worldx, worldy = screen_to_world(action.x, action.y, 0, "/cameraP2#camera")
		local world = vmath.vector3(worldx, worldy, 1)
		pprint(world)
		-- Move the target to the world position
		go.set_position(world, go.get_id())
		go.animate("#target", "tint.w", go.PLAYBACK_ONCE_FORWARD, 0, go.EASING_INOUTSINE, 0.5, 0, function() complete(self) end)
	end

Thank you so much, I followed those steps and everything works as intended now. I really appreciate the help!

1 Like

Good luck on your journey! :slightly_smiling_face:

1 Like

One more thing I forgot to mention: it’s better to keep your GOs within the -1 to 1 Z range. So don’t forget to set the camera’s Near Z to -1 and Far Z to 1. Currently, any GO lower than 0.1 will not be rendered.

Thanks! Would it theoretically still be fine if I kept it as is and never put GOs below 0.1? Another question - I’m struggling to figure out how to change the camera size/view with the orthographic projection, I’d like the camera to be more ‘zoomed in’. Do you know how to manage that?

EDIT: Another issue I’m having is that since in my game you can choose between a female and male character, I have two separate game objects with two separate cameras, but it appears only the male one works. Am I only allowed to use one? If so would it be best to put both players and their respective scripts under one GO with one camera? (Attached screenshot of how it’s currently set out)

AFAIK yes , this is just a convention for normalized depth, not a strict requirement for your orthographic camera. But you might end up with z-fighting issues due to precision issues .

You can set the camera’s orthographic zoom!!?

It is better to use a single container (Game Object or collection) and factories to create the player. Then, you can simply set or change the sprite to a boy or a girl. This way, you’ll have a single container for both.

2 Likes

The default render script is designed to use only one camera view and projection, which means that if you have two active cameras it will be the last camera to send its view and projection to the render script which will be used. Which camera happens to be “the last” is undefined. But if you disable one camera (ie the one you don’t want to use) it should work.

I would recommend this approach though.

1 Like

Thank you both! I’ll take that approach, since it’ll also make controlling them easier in my code. :slight_smile:

1 Like