Cómo hacer que un enemigo suelte objetos al morir

Claim Your Loot

·

Diagrama de las dos tiradas: si suelta algo y qué suelta según la tabla de pesos

Ya tienes un enemigo al que se puede pegar. Ahora quieres que al morir suelte algo: monedas, un arma, de vez en cuando algo bueno. Es el bucle que engancha en cualquier juego de progresión, y montarlo bien tiene más miga de la que parece.

En la demo puedes darle al enemigo hasta matarlo y ver qué suelta, con las probabilidades a la vista.

Respuesta rápida

Escucha Humanoid.Died, tira la tabla de botín, y crea el objeto en el mundo con un poco de dispersión para que no salgan todos apilados. Y una bandera para que no suelte dos veces, porque Died puede dispararse más de una vez.

Lo que necesitas antes de empezar

Paso 1: detectar la muerte

-- Script dentro del Model del enemigo
local enemigo = script.Parent
local humanoide = enemigo:WaitForChild("Humanoid")

local yaSolto = false

humanoide.Died:Connect(function()
	if yaSolto then return end
	yaSolto = true

	print("el enemigo ha muerto")
end)

Esa bandera no es paranoia: si el enemigo recibe dos golpes en el mismo fotograma, o si algo toca su vida después de morir, Died puede llegar dos veces y te encuentras con el doble de botín.

Paso 2: la tabla de botín

Igual que en las rarezas, con pesos relativos. La diferencia es que aquí conviene separar si suelta algo de qué suelta:

local PROBABILIDAD_DE_SOLTAR = 0.7   -- 70 % de las veces suelta algo

local BOTIN = {
	{nombre = "Moneda",        peso = 600, color = Color3.fromRGB(240, 200, 80)},
	{nombre = "Poción",        peso = 250, color = Color3.fromRGB(90, 200, 130)},
	{nombre = "Gema azul",     peso = 100, color = Color3.fromRGB(80, 150, 220)},
	{nombre = "Anillo raro",   peso = 40,  color = Color3.fromRGB(160, 100, 230)},
	{nombre = "Espada legendaria", peso = 10, color = Color3.fromRGB(240, 170, 60)},
}

local function elegirBotin()
	local total = 0
	for _, o in ipairs(BOTIN) do total = total + o.peso end

	local tirada = math.random() * total
	local acumulado = 0
	for _, o in ipairs(BOTIN) do
		acumulado = acumulado + o.peso
		if tirada <= acumulado then return o end
	end
	return BOTIN[1]
end

Separar las dos tiradas te da mucho control. Un enemigo débil puede tener un 30 % de soltar algo con la misma tabla, y un jefe un 100 % y tres objetos. No duplicas tablas: cambias un número.

Las dos tiradas: si suelta algo y qué suelta según la tabla de pesos

Paso 3: crear el objeto en el mundo

local function soltar(objeto, posicion)
	local drop = Instance.new("Part")
	drop.Name = objeto.nombre
	drop.Size = Vector3.new(1.5, 1.5, 1.5)
	drop.Color = objeto.color
	drop.Material = Enum.Material.Neon
	drop.CanCollide = false        -- que el jugador no tropiece con él
	drop.Anchored = false

	-- Un poco de dispersión para que no salgan apilados
	local desvio = Vector3.new(
		math.random(-30, 30) / 10,
		2,
		math.random(-30, 30) / 10
	)
	drop.Position = posicion + desvio
	drop.Parent = workspace

	-- Un empujoncito hacia arriba, queda mejor
	drop.AssemblyLinearVelocity = Vector3.new(0, 15, 0)

	-- Se borra solo a los 30 segundos para no llenar el mapa
	game:GetService("Debris"):AddItem(drop, 30)

	return drop
end

Tres detalles que marcan la diferencia:

  • CanCollide = false: si los drops chocan, el jugador se queda enganchado en un montón de monedas.
  • La dispersión aleatoria: sin ella, si un enemigo suelta tres objetos salen los tres exactamente en el mismo punto y parece uno solo.
  • Debris:AddItem: los drops que nadie recoge se borran solos. Sin esto, en una hora de partida tienes mil Parts en el mapa y el juego va a tirones.
Los cuatro detalles del objeto que aparece: CanCollide, dispersión, impulso y Debris

Paso 4: que se pueda recoger

local function hacerRecogible(drop, objeto)
	local prompt = Instance.new("ProximityPrompt")
	prompt.ActionText = "Recoger"
	prompt.ObjectText = objeto.nombre
	prompt.MaxActivationDistance = 8
	prompt.RequiresLineOfSight = false
	prompt.Parent = drop

	local recogido = false
	prompt.Triggered:Connect(function(jugador)
		if recogido then return end
		recogido = true

		-- Aquí lo metes en el inventario del jugador
		print(jugador.Name .. " ha recogido " .. objeto.nombre)
		drop:Destroy()
	end)
end

Otra vez una bandera: sin recogido, dos jugadores que pulsen a la vez se llevan los dos el mismo objeto.

Paso 5: todo junto

humanoide.Died:Connect(function()
	if yaSolto then return end
	yaSolto = true

	local raiz = enemigo:FindFirstChild("HumanoidRootPart")
	local posicion = raiz and raiz.Position or Vector3.new(0, 5, 0)

	-- ¿Suelta algo esta vez?
	if math.random() > PROBABILIDAD_DE_SOLTAR then
		return
	end

	local objeto = elegirBotin()
	local drop = soltar(objeto, posicion)
	hacerRecogible(drop, objeto)

	-- El cadáver se borra a los 3 segundos
	game:GetService("Debris"):AddItem(enemigo, 3)
end)

Guardar la posición antes de que desaparezca el cuerpo es importante: si esperas, HumanoidRootPart puede haberse ido ya y el botín aparece en el origen del mapa.

Paso 6: soltar varios objetos

Para un jefe, o para que suelte monedas siempre y además algo con suerte:

local CUANTOS = 3

for i = 1, CUANTOS do
	local objeto = elegirBotin()
	local drop = soltar(objeto, posicion)
	hacerRecogible(drop, objeto)
	task.wait(0.1)   -- pequeño escalonado, queda mucho mejor
end

Ese task.wait(0.1) entre objetos hace que salgan disparados de uno en uno en lugar de todos en el mismo fotograma. Es un detalle visual que se nota.

Pruébalo aquí

Mátalo y mira qué suelta

Las dos tiradas del artículo, funcionando: primero si suelta algo, luego qué suelta.

ObjetoPesoProbabilidadTe ha salido
Enemigos matados: 0 · Objetos soltados: 0

Errores habituales

Suelta el doble de objetos

Falta la bandera yaSolto. Died puede dispararse más de una vez.

El botín aparece en el centro del mapa

Estás leyendo la posición después de que el cuerpo desaparezca. Guárdala en una variable nada más entrar en Died.

Los drops se acumulan y el juego va lento

No estás usando Debris:AddItem. Sin eso, cada objeto que nadie recoge se queda ahí para siempre.

Dos jugadores recogen el mismo objeto

Falta la bandera recogido dentro de Triggered.

El jugador se queda atascado en los drops

CanCollide está activado. Desactívalo en el Part del botín.

attempt to index nil with 'Position'

El enemigo no tiene HumanoidRootPart, normalmente porque es un modelo hecho a mano sin la estructura de un personaje. Usa el PrimaryPart del Model, o cualquier Part suyo.

Preguntas frecuentes

¿Cómo hago que solo pueda recogerlo quien lo mató?

Died no te dice quién fue: tienes que llevar tú la cuenta del daño. Guarda en una tabla quién le pegó y cuánto, y en Triggered compruebas que el jugador sea ese.

¿Cómo evito que un jugador con suerte reviente la economía?

Ahí es donde entra ajustar los pesos y poner topes: máximo de objetos raros por hora, o subir el precio de las cosas conforme el jugador acumula.

¿Puedo soltar herramientas de verdad y no cubos de colores?

Sí: en vez de crear un Part, clona una Tool de ServerStorage y ponla en el Backpack del jugador al recoger.

¿Esto se puede trampear?

Si el script está en el servidor, no. Si la tirada la hiciera el cliente, cualquiera se daría legendarios. Como siempre: las probabilidades se resuelven en el servidor.


Última revisión: 1 de septiembre de 2026

Claim Your Loot

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *