Hello.
I made rocket system for my helicopter.
Rockets pushed by LinearVelocity.
I have touched event for detect when rocket touched something.
But its very unstability and lazy.
Now it turns out that the rocket is flying, hits somewhere and the Touched event does not react in time, which causes the rocket to bounce and explode in another place. At the same time, when a rocket hits something, it becomes Anchored. That is, it bounces off due to the fact that the Touched event did not work on time.
Moreover, I tried to make a separate Part and search for Touched through it, but it doesn’t help. Even if it’s too long and big. Apparently, due to the speed of the rocket (Velocity 300 ), it does not work properly.
dont use .Touched events and instead do constant and small spatial queries infront of the missile and once it finds something other than the missile detonate it (this can be achieved with OverlapParams.new()), but that might be very expensive if youre firing 300 missiles at once
if youre more concerned on performance just fire 1 or more raycasts and if youre firing more raycasts then face it in different directions. this will be easy/hard to do depending on if you want to rotate the vector or just find it with :VectorToWorldSpace().
raycasts
from the missile fire raycasts facing the missiles LookVector and if you want more, again, take the CFrame of the part and use :VectorToWorldSpace() to figure out any inbetweens between the LookVector and wherever else you want to face (this can be found from the CFrame of the missile), and if you want to give it a larger detection radius just multiply the vector of the raycasts
shapecasts via workspace:GetPartBoundsInBox()
figure out some CFrame facing in the direction of the missile but is positioned directly ahead of the missile, then make a bounding box of your preferred size. if anything but the missile is in there then detonate. if youre lazy just use OverlapParams and then have the bounding box
slightly larger than the missile, this will include the missile
shapecasts via workspace:GetPartBoundsInRadius()
literally just get the position of the missile, put a radius and detonate if something else is found. this requires using OverlapParams because its semi obvious that the missile will be apart of the query
rotation matrices are a pain to understand but to think of them intuitively, treat every 3 columns as a vector. suddenly you unlock the secrets to life as you then realize that the rotation matrices, when represented as vectors, basically give you 3 vectors that tell you where the object is facing
remember however that in Roblox coordinate systems, front faces 0, 0, -1 and not 0, 0, 1, which explains why you see in the documentation for CFrames, specifically under the documentation for LookVectors, this:
The forward-direction component of the CFrame object’s orientation, equivalent to the negated ZVector or the negated third column of the rotation matrix.
so keep that in mind, probably a bit too much to cram into anyone’s head but it’s still very important to understand when coming to rotations
then again CFrame.fromOrientation() exists so if you want to use whatever works for you then go ahead
the rocket still manages to bounce off the point of impact (
local function detonateRocket(rocket, hitPart)
if not rocket:GetAttribute("hasHit") then
rocket:SetAttribute("hasHit", true)
print("Ракета " .. rocket.Name .. " попала в " .. (hitPart and hitPart.Name or "неизвестный объект"))
rocket.DR.Engine.LinearVelocity.Enabled = false
rocket.DR.Anchored = true -- Фиксируем на месте
rocket.DR.Transparency = 1 -- Делаем невидимой
rocket.DR.CanCollide = false -- Отключаем столкновения
if rocket.DR.Engine:FindFirstChild("Sound") then
rocket.DR.Engine.Sound:Destroy()
end
rocket.DR.Engine.ParticleEmitter:Destroy()
rocket.DRElements.Transparency = 1 -- Делаем невидимой
rocket.DRElements.Anchored = true -- Фиксируем на месте
rocket.DRElements.CanCollide = false -- Отключаем столкновения
rocket.RayHolder.ExplosionVFX.Sound:Play()
-- Запускаем эффекты взрыва
for _, effect in ipairs(explosionEffects) do
local emitter = rocket.RayHolder.ExplosionVFX:FindFirstChild(effect.name)
if emitter and emitter:IsA("ParticleEmitter") then
print("Найден эмиттер: " .. effect.name .. " для ракеты " .. rocket.Name)
if effect.delay > 0 then
task.delay(effect.delay, function()
emitter:Emit(effect.emitCount)
print("Эффект " .. effect.name .. " запущен с задержкой " .. effect.delay .. " для " .. rocket.Name)
end)
else
emitter:Emit(effect.emitCount)
print("Эффект " .. effect.name .. " запущен сразу для " .. rocket.Name)
end
else
warn("Эффект " .. effect.name .. " не найден или не является ParticleEmitter в ракете " .. rocket.Name)
end
end
-- Удаляем ракету после завершения всех эффектов
task.delay(8, function()
rocket:Destroy()
end)
end
end
-- Функция для настройки параметров Raycast
local function createRaycastParams(rocket)
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
raycastParams.FilterDescendantsInstances = {helicopter, rocket} -- Игнорируем вертолёт и саму ракету
return raycastParams
end
-- Функция для проверки лучей
local function checkRays(rocket, rayHolder, raycastParams)
local rayLength = 30 -- Длина луча (настраивайте по необходимости)
local rayOrigin = rayHolder.Position -- Начальная точка луча
-- Определяем направления лучей
local directions = {
rayHolder.CFrame.LookVector, -- Основной луч (вперёд)
rayHolder.CFrame:VectorToWorldSpace(Vector3.new(0.2, 0, -1)), -- Немного вправо
rayHolder.CFrame:VectorToWorldSpace(Vector3.new(-0.2, 0, -1)), -- Немного влево
rayHolder.CFrame:VectorToWorldSpace(Vector3.new(0, 0.2, -1)), -- Немного вверх
rayHolder.CFrame:VectorToWorldSpace(Vector3.new(0, -0.2, -1)) -- Немного вниз
}
-- Проверяем каждый луч
for i, direction in ipairs(directions) do
local rayDirection = direction * rayLength
local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
if raycastResult then
local hitPart = raycastResult.Instance
print("Ракета " .. rocket.Name .. " (луч " .. i .. ") обнаружила объект: " .. hitPart.Name)
detonateRocket(rocket, hitPart)
return true -- Прерываем проверку, если нашли попадание
end
end
return false
end
-- Функция для запуска проверки лучей для ракеты
local activeRockets = {} -- Таблица для хранения активных подключений Heartbeat для ракет
local function startRayChecking(rocket)
local rayHolder = rocket:WaitForChild("RayHolder")
if not rayHolder then
warn("RayHolder не найден в ракете " .. rocket.Name)
return
end
local raycastParams = createRaycastParams(rocket)
-- Подключаем Heartbeat для проверки лучей
local connection
connection = RunService.Heartbeat:Connect(function()
if rocket and rocket.Parent then -- Проверяем, существует ли ракета
if not rocket:GetAttribute("hasHit") then -- Проверяем, не взорвалась ли ракета
local hit = checkRays(rocket, rayHolder, raycastParams)
if hit then
-- Если попали, отключаем Heartbeat
if connection then
connection:Disconnect()
activeRockets[rocket] = nil
print("Raycast Heartbeat отключен для ракеты " .. rocket.Name)
end
end
else
-- Если ракета взорвалась, отключаем Heartbeat
if connection then
connection:Disconnect()
activeRockets[rocket] = nil
print("Raycast Heartbeat отключен для ракеты " .. rocket.Name)
end
end
else
-- Если ракета удалена, отключаем Heartbeat
if connection then
connection:Disconnect()
activeRockets[rocket] = nil
print("Raycast Heartbeat отключен для ракеты " .. rocket.Name .. " (ракета удалена)")
end
end
end)
-- Сохраняем подключение в таблице
activeRockets[rocket] = connection
end
I increased the RayLenght and now the rocket explodes in the place where it hit, but, true, this length is a little too long, that is, it explodes a little short of the point of contact, but I think this is not critical for the rocket, since an explosion occurs anyway.
But if there is a way to make sure that the explosion site is exactly at the point of contact, then it would be good