Motion Blur effect

I have a question
I would like to add a sprite blur effect, when the object is moving. The higher the speed, the bigger the blur. Is it possible to achieve this programmatically ?

1 Like

Youd have to search and learn about render targets. Although im not the best person to give aid about, for i have tried in the past making something similar but didnt quite achieve it. But i think that for something like that there might be an already existing extension.

Hi @FredZX !

What you want to achieve is most probably a motion blur. I once adapted an implementation of motion blur from shadertoy (Shader - Shadertoy BETA) as a fragment program for a sprite to Defold:

You probably want it rather to be aplied to everything on your screen, so you might render your scene to a render target (a separate, internal buffer/texture) and then perform this motion blur as a postprocessing when drawing this render target to a quad (simplest plane, a geometry to which you assign a texture) and this quad will fill the screen. This is the most common post-processing pipeline in Defold, let us know if this is understandable to you or you need more explanations :wink:

You might get to know how to draw to an offset buffer / render target and to quad filling the screen after watchin my video:

or reading official Defold example:

After understading how to make post-processing with this technique - let us know how are you familiar with GLSL and if you can adapt the motion blur example to your needs :wink:

4 Likes

I’m not familiar with this technique, but I’m going to try it :wink:

1 Like

I will be experimenting with this, maybe I will come with some solution, but please don’t rely on me, it could be that I will get it done quickly or after ages, as I rarely have time for such experiments, sorry! :sweat_smile:

But if you will try and get stuck on anything, let us know and we will try to help :blush:

3 Likes

Did you find a solution? I made a trail.

--[[
Estela Simple con go.animate() y Delay por Segmento - v21 (Cabeza Animada)

- CORRECCIÓN: La cabeza AHORA TAMBIÉN se mueve con go.animate().
- Al hacer clic:
- La 'cabeza' (este GO) inicia una animación go.animate() hacia el punto de clic (delay 0).
- TODOS los segmentos inician una animación go.animate() hacia el MISMO punto de clic.
- Cada segmento inicia su animación con un DELAY diferente y configurable.
- Usa go.EASING_OUTELASTIC para TODO.
- SIN seguimiento continuo, SIN Lerp manual para segmentos.

Asegúrate de que 'anim_duration' sea suficientemente largo para apreciar el rebote elástico.
]]

-- Propiedades Necesarias --
go.property("numero_segmentos", 10)
go.property("url_fabrica_segmento", msg.url("#trail_factory")) -- Fábrica para los segmentos
go.property("anim_duration", 0.8) -- Duración de la animación de CADA objeto (cabeza y segmentos)
-- ¡Prueba valores ALTOS (0.6+) para ver el rebote!
go.property("segment_delay_step", 0.05) -- Delay ADICIONAL por cada segmento (segundos)

-- INIT: Crear los segmentos
function init(self)
	msg.post(".", "acquire_input_focus")

	-- Validar propiedades
	self.numero_segmentos = math.max(0, self.numero_segmentos)
	self.anim_duration = math.max(0.01, self.anim_duration)
	self.segment_delay_step = math.max(0.0, self.segment_delay_step)

	self.segments_id = {} -- Tabla para guardar los IDs

	-- Crear los segmentos iniciales
	local initial_pos = go.get_position()
	for i = 1, self.numero_segmentos do
		local id = factory.create(self.url_fabrica_segmento, initial_pos)
		if id then
			table.insert(self.segments_id, id)
		else
			print("ERROR: No se pudo crear segmento", i, "desde", self.url_fabrica_segmento)
		end
	end
	print("Estela Simple v21 lista (Cabeza Animada). Segmentos:", self.numero_segmentos)
	print("Anim Duration:", self.anim_duration, "s | Delay Step:", self.segment_delay_step, "s")
	print("Haz clic para mover la estela con Easing OUTELASTIC y delay.")
end

-- ON_INPUT: Detectar clic y lanzar TODAS las animaciones (incluida la cabeza)
function on_input(self, action_id, action)
	if (action_id == hash("touch") or action_id == hash("click")) and action.pressed then

		-- 1. Determinar la posición objetivo final (el clic)
		local target_pos = vmath.vector3(action.x, action.y, 0)
		local current_head_pos = go.get_position() -- Posición actual antes de animar
		target_pos.z = current_head_pos.z

		print("Clic -> Animando cabeza y segmentos hacia", target_pos)

		-- 2. *** CAMBIO CLAVE: Animar la CABEZA en lugar de moverla instantáneamente ***
		-- Cancelar animación previa de la cabeza
		go.cancel_animations(".", "position")
		-- Animar la cabeza hacia el target con delay 0
		go.animate(
		".",                        -- Este GO (la cabeza)
		"position",
		go.PLAYBACK_ONCE_FORWARD,
		target_pos,                 -- El destino del clic
		go.EASING_OUTELASTIC,
		self.anim_duration,         -- Misma duración que los segmentos
		0                           -- Sin delay para la cabeza
	)
	-- Ya NO usamos: go.set_position(target_pos, ".")

	-- 3. Iterar sobre TODOS los segmentos y lanzar su animación con su delay
	for i, segment_id in ipairs(self.segments_id) do
		local segment_exists = pcall(function() return go.exists(segment_id) end)
		if segment_exists then
			-- Calcular el delay para ESTE segmento
			-- (El delay ahora es relativo al inicio de la animación de la cabeza)
			local delay = i * self.segment_delay_step -- Ajuste: el primero (i=1) tiene step*1 de delay
			-- Si quieres que el primero siga pegado a la cabeza, usa (i-1) * step

			-- Cancelar animación de posición anterior para este segmento
			go.cancel_animations(segment_id, "position")

			-- Iniciar la animación para ESTE segmento hacia el MISMO target_pos
			go.animate(
			segment_id,
			"position",
			go.PLAYBACK_ONCE_FORWARD,
			target_pos,                 -- Mismo destino que la cabeza
			go.EASING_OUTELASTIC,
			self.anim_duration,
			delay                       -- El delay calculado
		)
	end
end
end
end

-- FINAL: Limpiar los segmentos creados
function final(self)
msg.post(".", "release_input_focus")
for _, segment_id in ipairs(self.segments_id) do
pcall(function()
	if go.exists(segment_id) then
		go.cancel_animations(segment_id)
		go.delete(segment_id)
	end
end)
end
self.segments_id = {}
end

-- UPDATE: No necesario
-- function update(self, dt)
-- end
3 Likes

If you’re looking for a trail fx, there’s a great one:

3 Likes