Hello, I wanna try scripting a natural disaster like game but I don’t know how to really script things such as: Tornadoes, Sandstorm, or etc. I would love it if someone were here to teach me! Please and thank you.
I have a sample of a tornado code btw.
if mdl then
if mdl:IsA('BasePart') then
if mdl.Position.y<maxhight then -- Apply tornado effects regardless of anchored state
local dist=((mdl.Position*Vector3.new(1,0,1))-script.Parent.Position).magnitude
if dist<breakradius then
local h=mdl.Parent:FindFirstChild("Humanoid")
if not h then
mdl:BreakJoints()
end
local pushpull=pull
local hightpercentage=(mdl.Position.y/(maxhight-low))
pushpull=pull+((push-pull)*hightpercentage)
if dist<cycloneradius then
pushpull=push
end
local angle=math.atan2(mdl.Position.x-script.Parent.Position.x,mdl.Position.z-script.Parent.Position.z)
local ncf=(CFrame.new(script.Parent.Position.X, mdl.Position.Y, script.Parent.Position.Z))*CFrame.Angles(0,angle+.1,0)*CFrame.new(0,0,dist+pushpull) -- Corrected ncf calculation!
Not sure how much this will help but its a start so taking code found from Cone’s Video I altered a little and changed some API usage.
local ServerScriptService = game:GetService("ServerScriptService")
local weather = require(ServerScriptService:WaitForChild("WeatherModule"))
task.wait(5)
-- 1. Define where the tornado will start
local spawnLocation = Vector3.new(0, 0, 0)
-- 2. Setup your tornado's configuration
local tornadoConfig = {
Duration = 45, -- How long it lasts in seconds
Height = 25, -- How many segments tall it is
BaseWidth = 10, -- Size of the funnel at the bottom
TopWidth = 70, -- Size of the funnel at the top (creates the V-shape)
Aggression = 20, -- How wildly it swings and wobbles
UseCache = true, -- Set to false if you want the map destruction to be permanent
MoveSpeed = 12 -- How fast it wanders around the map
}
-- 3. Spawn the tornado!
print("Spawning Tornado...")
weather.initTornado(spawnLocation, tornadoConfig)
local RunService = game:GetService("RunService")
local Debris = game:GetService("Debris")
local TweenService = game:GetService("TweenService")
local weather = {}
-- [[ CONFIGURATION TEMPLATES ]] --
local lightday = {
Color3.fromRGB(225, 225, 225), -- cloud color
0.64, -- cover
0.44, -- cloud density
0.3, -- atmos density
Color3.fromRGB(225, 225, 225), -- color correction
4.3 -- brightness
}
local lightingStorm = {
Color3.fromRGB(80, 80, 80),
1,
1,
0.8,
Color3.fromRGB(150, 160, 170),
1
}
local function convertLighting(template)
if workspace.Terrain:FindFirstChild("Clouds") then
TweenService:Create(workspace.Terrain.Clouds, TweenInfo.new(5), {Color = template[1], Cover = template[2], Density = template[3]}):Play()
end
if game.Lighting:FindFirstChild("ColorCorrection") then
TweenService:Create(game.Lighting.ColorCorrection, TweenInfo.new(5), {TintColor = template[5]}):Play()
end
if game.Lighting:FindFirstChild("Atmosphere") then
TweenService:Create(game.Lighting.Atmosphere, TweenInfo.new(5), {Density = template[4]}):Play()
end
TweenService:Create(game.Lighting, TweenInfo.new(5), {Brightness = template[6]}):Play()
end
export type config = {
Duration: number?,
Height: number?,
BaseWidth: number?,
TopWidth: number?,
Aggression: number?,
UseCache: boolean?,
MoveSpeed: number?
}
function weather.initTornado(spawnPosition, config: config)
config = config or {}
local duration = config.Duration or 30
local height = config.Height or 20
local baseWidth = config.BaseWidth or 10
local topWidth = config.TopWidth or 50
local aggression = config.Aggression or 15 -- How violently it wobbles
local useCache = config.UseCache == nil and true or config.UseCache -- Defaults to true
local moveSpeed = config.MoveSpeed or 10
convertLighting(lightingStorm)
-- Setup Data Structures
local segments = {}
local destroyedPartsCache = {}
local isLive = true
-- Create an invisible base part to control the tornado's overall position
local tornadoBase = Instance.new("Part")
tornadoBase.Size = Vector3.new(2, 2, 2)
tornadoBase.Position = spawnPosition
tornadoBase.Transparency = 1
tornadoBase.Anchored = true
tornadoBase.CanCollide = false
tornadoBase.Parent = workspace
-- Build the Tornado
-- Works with a single mesh (if height = 1) or stacking segments
for i = 1, height do
local seg = script.tornadoSeg:Clone()
-- Calculate Funnel Shape (Interpolate between baseWidth and topWidth based on height)
local progress = height > 1 and (i - 1) / (height - 1) or 1
local currentWidth = baseWidth + ((topWidth - baseWidth) * progress)
seg.Size = Vector3.new(currentWidth, seg.Size.Y, currentWidth)
seg.Anchored = true
seg.CanCollide = false
seg.Parent = workspace
if i == 1 and seg:FindFirstChild("Sound") then
seg.Sound.Playing = true
end
table.insert(segments, {
part = seg,
yOffset = (i - 1) * seg.Size.Y,
width = currentWidth
})
end
-- Movement & Wobble System
local timeElapsed = 0
local movementConnection
movementConnection = RunService.Heartbeat:Connect(function(deltaTime)
timeElapsed += deltaTime
-- Move the base around randomly using Perlin Noise
local moveX = math.noise(timeElapsed * 0.2, 0, 0) * moveSpeed
local moveZ = math.noise(0, timeElapsed * 0.2, 0) * moveSpeed
tornadoBase.CFrame *= CFrame.new(moveX * deltaTime, 0, moveZ * deltaTime)
-- Update segments to follow base + apply wobble
for i, segData in ipairs(segments) do
local progress = i / height
-- Wobble calculation
local wobbleX = math.noise(timeElapsed, progress, 0) * aggression * progress
local wobbleZ = math.noise(0, progress, timeElapsed) * aggression * progress
local targetPos = tornadoBase.Position + Vector3.new(wobbleX, segData.yOffset, wobbleZ)
-- Smoothly interpolate segments to their target positions
segData.part.CFrame = segData.part.CFrame:Lerp(CFrame.new(targetPos), deltaTime * 10)
end
end)
-- Destruction & Physics System (Runs every 0.2 seconds to save performance)
local overlapParams = OverlapParams.new()
overlapParams.FilterType = Enum.RaycastFilterType.Exclude
-- Build exclusion list (Tornado parts + Base)
local ignoreList = {tornadoBase}
for _, segData in ipairs(segments) do table.insert(ignoreList, segData.part) end
overlapParams.FilterDescendantsInstances = ignoreList
task.spawn(function()
while isLive do
-- Check radius around the base of the tornado
local partsInRadius = workspace:GetPartBoundsInRadius(tornadoBase.Position, topWidth * 0.8, overlapParams)
for _, part in ipairs(partsInRadius) do
if part:IsA("BasePart") and not part.Anchored then continue end -- Skip already flying parts
local parentModel = part:FindFirstAncestorOfClass("Model")
local isHumanoid = parentModel and parentModel:FindFirstChildOfClass("Humanoid")
if isHumanoid then
-- Fling players instead of instakilling
local root = parentModel:FindFirstChild("HumanoidRootPart")
if root then
root.AssemblyLinearVelocity = Vector3.new(math.random(-50, 50), 100, math.random(-50, 50))
end
elseif part:IsA("BasePart") and part.Name ~= "Terrain" then
-- CACHE SYSTEM: Save the part's original state
if useCache and not destroyedPartsCache[part] then
destroyedPartsCache[part] = {
CFrame = part.CFrame,
Anchored = part.Anchored,
Parent = part.Parent
}
end
-- Destroy/Break it
part:BreakJoints()
part.Anchored = false
-- Fling it upward and outward
local flingDir = (part.Position - tornadoBase.Position).Unit
part:ApplyImpulse((flingDir + Vector3.new(0, 2, 0)) * part.Mass * math.random(30, 60))
end
end
task.wait(0.2)
end
end)
-- Duration & Cleanup
task.delay(duration, function()
isLive = false
movementConnection:Disconnect()
-- Fade out segments and delete
for _, segData in ipairs(segments) do
TweenService:Create(segData.part, TweenInfo.new(3), {Transparency = 1, Size = Vector3.new(0,0,0)}):Play()
Debris:AddItem(segData.part, 3.5)
end
Debris:AddItem(tornadoBase, 3.5)
convertLighting(lightday)
-- Reconstruct cached parts
if useCache then
task.wait(4) -- Wait for tornado to fully disappear before rebuilding
for part, originalData in pairs(destroyedPartsCache) do
if part and part.Parent then
part.AssemblyLinearVelocity = Vector3.zero
part.AssemblyAngularVelocity = Vector3.zero
part.Anchored = true
TweenService:Create(part, TweenInfo.new(1.5, Enum.EasingStyle.Quart), {CFrame = originalData.CFrame}):Play()
end
end
destroyedPartsCache = {} -- Clear memory
end
end)
end
return weather
I modified the script so it can fit the aspects of my game it was really cool but i wish that the destruction is more like that one tornado from natural disaster survival game
I found a script but the thing is sometimes it could be buggy. For example, when the tornado pulls the parts circle around the tornado (which is what I want) and when parts reach on top of the tornado the parts all fall at once instead of one part falling when it reaches it’s maximum height here’s the code: `— START OF FILE Script.txt —
–Create a model or folder called “Structure”. Place anything you want to be affected in it.
–Place this script in any object or tornado part
maxhight=300
low=40
speed=100
push=.1
pull=-.5
lift=.25
breakradius=35
cycloneradius=10
rate = 1/30
unanchorChance = 0.005 – Chance for an object to be unanchored per frame (adjust as needed)
–storage = game.ServerStorage
function unanchorObjects(mdl)
if mdl then
if mdl:IsA(‘BasePart’) then
if mdl.Anchored and mdl.Position.y<maxhight then – Only check anchored parts for unanchoring
local dist=((mdl.Position*Vector3.new(1,0,1))-script.Parent.Position).magnitude
if dist<breakradius then
– Chance to unanchor objects
if math.random() < unanchorChance then
mdl.Anchored = false
end
end
end
end
if mdl then
if mdl.Parent~=nil then
for i3,v3 in ipairs(mdl:GetChildren()) do
unanchorObjects(v3) – Recursively check children for unanchoring
end
end
end
end
end
function updatetornado(mdl)
if mdl then
if mdl:IsA(‘BasePart’) then
if mdl.Position.y<maxhight then – Apply tornado effects regardless of anchored state
local dist=((mdl.Position*Vector3.new(1,0,1))-script.Parent.Position).magnitude
if dist<breakradius then
local h=mdl.Parent:FindFirstChild(“Humanoid”)
if not h then
mdl:BreakJoints()
end
local pushpull=pull
local hightpercentage=(mdl.Position.y/(maxhight-low))
pushpull=pull+((push-pull)*hightpercentage)
if dist<cycloneradius then
pushpull=push
end
local angle=math.atan2(mdl.Position.x-script.Parent.Position.x,mdl.Position.z-script.Parent.Position.z)
local ncf=(CFrame.new(script.Parent.Position.X, mdl.Position.Y, script.Parent.Position.Z))*CFrame.Angles(0,angle+.1,0)*CFrame.new(0,0,dist+pushpull) – Corrected ncf calculation!
if game.Workspace:FindFirstChild("Marker") then
game.Workspace.Marker.CFrame=ncf
end
local vec=(ncf.Position-mdl.Position).unit
-- print(vec.Y) -- Uncomment for debugging vec.Y
local speedpercent=(dist-cycloneradius)/(breakradius-cycloneradius)
if speedpercent<0 then
speedpercent=0
end
speedpercent=1-speedpercent
speedpercent=speedpercent+.1
if speedpercent>1 then
speedpercent=1
end
mdl.Velocity=(vec*speedpercent*speed*(1+(2*hightpercentage)))+Vector3.new(0,(lift*(speedpercent+hightpercentage)*speed),0)
mdl.RotVelocity=mdl.RotVelocity+Vector3.new(math.random(-1,1),math.random(-1,1)+.1,math.random(-1,1))
if not tornadodparts[mdl] then
if h then
if mdl.Name=="HumanoidRootPart" and not h.PlatformStand then
h.PlatformStand=true
delay(5,function()
if h then
h.PlatformStand=false
end
end)
end
if mdl.Name~='HumanoidRootPart' and mdl.Name~='Head' and mdl.Name~='UpperTorso' and mdl.Name~='LowerTorso' then
if math.random()<0.005 then -- randomly rip limbs off. :D
mdl:Destroy()
end
end
else
if math.random(1,2)==1 then
mdl:Destroy()
else
tornadodparts[mdl]=true
end
end
end
end
end
end
if mdl then
if mdl.Parent~=nil then
for i3,v3 in ipairs(mdl:GetChildren()) do
updatetornado(v3)
end
end
end
end
end
local tornadoing=true
tornadodparts={}
–local tp=storage.TornadoPart:clone()
–tp.CFrame=CFrame.new(script.Parent.Position - Vector3.new(0,low,0))
–tp.Parent=game.Workspace.Structure
wait(.2)
–local iwsound=tp:FindFirstChild(“IntenseWind”)
–if iwsound then
– iwsound:Play()
–end
delay(0,function()
local starttime=tick()
local endtime=starttime+90
while tornadoing do
– No tornado movement path anymore, tornado position is always the parent’s position
unanchorObjects(game.Workspace.Structure) – Call the unanchor function
updatetornado(game.Workspace.Structure) – Call the tornado effect function
for i,v in ipairs(game.Players:GetPlayers()) do
if v then
if v.Character~=nil then
unanchorObjects(v.Character) – Apply unanchoring to characters too, if desired
updatetornado(v.Character) – Apply tornado effects to characters
end
end
end
wait(rate)
end
end)
–wait(90)
–tornadodparts={}
–tornadoing=false`
actual code: — START OF FILE Script.txt —
–Create a model or folder called “Structure”. Place anything you want to be affected in it.
–Place this script in any object or tornado part
maxhight=300
low=40
speed=100
push=.1
pull=-.5
lift=.25
breakradius=35
cycloneradius=10
rate = 1/30
unanchorChance = 0.005 – Chance for an object to be unanchored per frame (adjust as needed)
–storage = game.ServerStorage
function unanchorObjects(mdl)
if mdl then
if mdl:IsA(‘BasePart’) then
if mdl.Anchored and mdl.Position.y<maxhight then – Only check anchored parts for unanchoring
local dist=((mdl.Position*Vector3.new(1,0,1))-script.Parent.Position).magnitude
if dist<breakradius then
– Chance to unanchor objects
if math.random() < unanchorChance then
mdl.Anchored = false
end
end
end
end
if mdl then
if mdl.Parent~=nil then
for i3,v3 in ipairs(mdl:GetChildren()) do
unanchorObjects(v3) – Recursively check children for unanchoring
end
end
end
end
end
function updatetornado(mdl)
if mdl then
if mdl:IsA(‘BasePart’) then
if mdl.Position.y<maxhight then – Apply tornado effects regardless of anchored state
local dist=((mdl.Position*Vector3.new(1,0,1))-script.Parent.Position).magnitude
if dist<breakradius then
local h=mdl.Parent:FindFirstChild(“Humanoid”)
if not h then
mdl:BreakJoints()
end
local pushpull=pull
local hightpercentage=(mdl.Position.y/(maxhight-low))
pushpull=pull+((push-pull)*hightpercentage)
if dist<cycloneradius then
pushpull=push
end
local angle=math.atan2(mdl.Position.x-script.Parent.Position.x,mdl.Position.z-script.Parent.Position.z)
local ncf=(CFrame.new(script.Parent.Position.X, mdl.Position.Y, script.Parent.Position.Z))*CFrame.Angles(0,angle+.1,0)*CFrame.new(0,0,dist+pushpull) – Corrected ncf calculation!
if game.Workspace:FindFirstChild("Marker") then
game.Workspace.Marker.CFrame=ncf
end
local vec=(ncf.Position-mdl.Position).unit
-- print(vec.Y) -- Uncomment for debugging vec.Y
local speedpercent=(dist-cycloneradius)/(breakradius-cycloneradius)
if speedpercent<0 then
speedpercent=0
end
speedpercent=1-speedpercent
speedpercent=speedpercent+.1
if speedpercent>1 then
speedpercent=1
end
mdl.Velocity=(vec*speedpercent*speed*(1+(2*hightpercentage)))+Vector3.new(0,(lift*(speedpercent+hightpercentage)*speed),0)
mdl.RotVelocity=mdl.RotVelocity+Vector3.new(math.random(-1,1),math.random(-1,1)+.1,math.random(-1,1))
if not tornadodparts[mdl] then
if h then
if mdl.Name=="HumanoidRootPart" and not h.PlatformStand then
h.PlatformStand=true
delay(5,function()
if h then
h.PlatformStand=false
end
end)
end
if mdl.Name~='HumanoidRootPart' and mdl.Name~='Head' and mdl.Name~='UpperTorso' and mdl.Name~='LowerTorso' then
if math.random()<0.005 then -- randomly rip limbs off. :D
mdl:Destroy()
end
end
else
if math.random(1,2)==1 then
mdl:Destroy()
else
tornadodparts[mdl]=true
end
end
end
end
end
end
if mdl then
if mdl.Parent~=nil then
for i3,v3 in ipairs(mdl:GetChildren()) do
updatetornado(v3)
end
end
end
end
end
local tornadoing=true
tornadodparts={}
–local tp=storage.TornadoPart:clone()
–tp.CFrame=CFrame.new(script.Parent.Position - Vector3.new(0,low,0))
–tp.Parent=game.Workspace.Structure
wait(.2)
–local iwsound=tp:FindFirstChild(“IntenseWind”)
–if iwsound then
– iwsound:Play()
–end
delay(0,function()
local starttime=tick()
local endtime=starttime+90
while tornadoing do
– No tornado movement path anymore, tornado position is always the parent’s position
unanchorObjects(game.Workspace.Structure) – Call the unanchor function
updatetornado(game.Workspace.Structure) – Call the tornado effect function
for i,v in ipairs(game.Players:GetPlayers()) do
if v then
if v.Character~=nil then
unanchorObjects(v.Character) – Apply unanchoring to characters too, if desired
updatetornado(v.Character) – Apply tornado effects to characters
end
end
end
wait(rate)
end
end)
–wait(90)
–tornadodparts={}
–tornadoing=false
Kinda swamped with my own game but I can help you with the overall logic.
It’s usually best to map out how these things go step by step.
General Map Flow
This is a very basic way to do it but this would get the job done for most cases
Read
LEFT → RIGHT
Somethings you’re gonna need: ( for optional )
- AlignPosition
- AlignOrientation
- reaction frame work or an understanding of meta tables for __newindex
- A centralized loop to update part positions
- An understanding of CFrame and Vector3 Position math
- A definite way of destroying something
(this can be un-anchoring single parts as they enter a boundingbox/ ZoneModule)
(This can be un-anchoring full models/folders of parts as one part enters)
I Hope this helps you get a better structure to operate step by step.
