How to draw a dotted line demonstrating the raycast path

For right now, I am using draw_line function to create circle inside circle with stroke only.

Look at debug-draw library. It helps to draw circles by draw_line segments.

debugdraw.circle(cx, cy, radius, [color], [segments], [baseAngle])

Note: this method is actual only for debug, not for release.

2 Likes

Just note that the debug functions aren’t available in release builds.

2 Likes

For release games, instead of using the draw line function I would dynamically regenerate and position a list of GOs with dot sprites to position along the path of raycast path and its reflections.

2 Likes

To reflect a direction vector you need the normal of the plane you wish use.

N = normalized normal (i.e. length 1)
VÂŽ = V - 2 * N * dot(N, V)

In Defold, this would be (untested):

local N = vmath.normalize(n)
local reflected = v - N * (2 * vmath.dot(N, v))

Note that if you’re using the base axes for reflection (e.g. the side walls, or the top bottom walls), it’s easy enough to flip using that axis:

local reflectedInXAxis = vmath.vector3(-v.x, v.y, v.z)

How I can create similar funtionality like this Creating a dotted line in Unity. An article from www.knowledgescoops.com | by Kunal Tandon | Medium in defold. I tried by deleting go and adding once per frame all dots but that reduces perfomance and making go flickering. I am almost finished my game but this is only problem that I left it for so long unfinshed.

Can you post the script that you have? It sounds like it works but it just needs some fixing?
Deleting and adding the objects every frame shouldn’t be a problem, but if it is you can keep a list of object IDs and reuse them. Are you dealing with a lot of lines, or just one?

DottedLine.zip (629.0 KB)

In the end I have to use it for drawing aiming line for raycast so yes I want multiple lines to draw at least two lines.

Defold should have a more handful drawing API.

4 Likes

Yes, this is definitely a good candidate for an extension of some sort where you get a fullscreen drawing API. It could be built like DrawPixels or using some kind of immediate mode drawing API built like Dear ImGUI.

5 Likes

Dear ImGui not support all platform.
I do not know, graphics api.
Is it hard to use some gl code that will run on all platform?

When rive will be done, it will be more easy?

Dynamic vector generation for Rive runtime rendering would be interesting.

Rive has state machines so you can already do many interesting things with it.

1 Like

Almost all and likely easy to also support iOS.

1 Like

@vikash Well, I was halfway done reworking it to not spawn & delete all the objects every frame when I realized the actual problem, the cause of the flickering:

function update(self, dt)
	if table.maxn(self.dots) > 0 then
		destroy_all_dots(self)
	else
		draw_dotted_line(self)
	end
end

If there are already dots that frame, you destroy them
otherwise
you spawn them. You’re alternating between spawning and destroying them every frame, instead of doing both every frame, so it flickers. It’s pretty obvious if you turn the frame cap in your game.project down to 15 or so (seizure warning :P).

So it’s a quick fix:

function update(self, dt)
	if table.maxn(self.dots) > 0 then
		destroy_all_dots(self)
	end
	draw_dotted_line(self)
end
3 Likes

Here’s my own version that keeps objects instead of destroying and re-creating them all. It did get a little bit fiddly trying to prevent every bit of flicker, but it seems to work pretty well.

I renamed some stuff to make it more clear (to me), so double-check your script property settings if you copy-paste this.

dotted line script
go.property('start_point', vmath.vector3())
go.property('end_point', vmath.vector3())
go.property('dist_between_dots', 20)

-- Keep a buffer of old extra dots that are just disabled, not destroyed, to
-- prevent flickering when the line gets shorter.
local extra_dots_to_keep = 15

local function draw_dotted_line(self)
	assert(self.dist_between_dots ~= 0, "dotted_line.script - `dist_between_dots` property can't be zero.")
	local segment = self.end_point - self.start_point
	local unit_vec = vmath.normalize(segment)
	local distance = vmath.length(segment)

	-- Change to math.ceil if you want it to overshoot by 1 dot like before.
	local dot_count = math.floor(distance / self.dist_between_dots)

	for i=1,dot_count do
		local position = self.start_point + (i * self.dist_between_dots * unit_vec)
		if self.dot_ids[i] then -- An object already exists for this dot.
			local dot_id = self.dot_ids[i]
			msg.post(dot_id, "enable")
			go.set_position(position, dot_id)
		else -- We need a new object for this dot.
			local dot_id = factory.create("/line#factory", position)
			self.dot_ids[i] = dot_id
		end
	end

	-- Destroy extra dots.
	if #self.dot_ids > dot_count then
		local last_dot_index_to_keep = dot_count + extra_dots_to_keep
		-- Need to loop backwards because we're removing things from the table as we go.
		for i=#self.dot_ids,dot_count+1,-1 do
			local dot_id = self.dot_ids[i]
			msg.post(dot_id, "disable") -- Seems to be necessary, even if you're destroying them, to prevent old dots rendering for 1 frame.
			if i > last_dot_index_to_keep then
				go.delete(dot_id)
				self.dot_ids[i] = nil
			end
		end
	end
end

function init(self)
	self.dot_ids = {}
	draw_dotted_line(self)
end

function update(self, dt)
	draw_dotted_line(self)
end

I also removed the “changes” message because it was unnecessary. Since ‘end_point’ is a script property, it’s public and you can use go.set() to set it from anywhere (or animate it with go.animate).

-- Instead of sending the "changes" message:
go.set('/line#dotted_line', 'end_point.x', action.x)
go.set('/line#dotted_line', 'end_point.y', action.y)

(Though now that I think about it, using messages might be better from a code-maintenance point of view, depending on how you use it. The points could be private.)

3 Likes

Today I will check them out.

On Android, it works. But it doesn’t stretch like the white dot does.

1 Like

This is expected. The Dear ImGUI rendering is done completely separate from anything else and it is not affected by your render script or any other Defold related settings.

1 Like

It can’t be built for ios.
The error is:
/imgui/src/imgui/stb_image.h
Line 592: expanded from macro ‘STBI_THREAD_LOCAL’
#define STBI_THREAD_LOCAL thread_local
^
Line 931: In file included from upload/imgui/src/extension_imgui.cpp:27:
thread-local storage is not supported for the current target
STBI_THREAD_LOCAL
^
Line 1070: thread-local storage is not supported for the current target
static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set;
^
Line 1070: thread-local storage is not supported for the current target

Unknown source
clang-10: warning: treating ‘c’ input as ‘c++’ when in C++ mode, this behavior is deprecated [-Wdeprecated]

1 Like

It works fine on both Windows & MacOS.

1 Like