Wind Effect - Customizable - Module

What is this?

This is a module that creates visible wind around you, similarly to Wind Waker and other games. It may add a better depth to cartoony or low poly games.


Additional Information:

I made this because I was inspired by some recent tweets from Boatbomber, and EgoMoose where they make the same sort of effect, so I decided it would be fun to create my own customizable version, as a learning experience.

Greatly customizable with settings like Amplitude, Range, Lifetime, Randomizer, &, etc.

Let me know if there is something that can be improved or fixed and please explain, or show how.


Preview:

https://gyazo.com/61b3b377819acd9c5be891043674bc41 - Normal
https://gyazo.com/f7045e6de6e4d82100280d384f1341e9 - Randomized


How to use?

Start by requiring the module with a variable. Then create a new variable and call the .new(Settings) function with a table inside the parameters, which are the settings.

After that is done, call the method Variable:Start() which will start creating the wind objects based on your settings.

When you don’t need the wind anymore, just call Variable:Stop().

Having settings in the parameter is not necessary to Variable:Start() or Variable:Stop() because it will then use the default settings from the module.


Download:

Model
Place


Code:

-- CLASS
local WindService = {}
WindService.__index = WindService

-- SERVICES
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

-- VARIABLES
local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local RNG = Random.new()

-- FUNCTIONS
function WindService.new(setting)
	local Object = {}
	setmetatable(Object, WindService)
	
	Object.Location = setting.Location or game.Workspace.Camera
	Object.Randomized = setting.Randomized or false
	Object.Velocity = setting.Velocity or Vector3.new(0.45, 0, 0)
	Object.Amount = setting.Amount or 10
	Object.Current = 0
	Object.Frequency = setting.Frequency or 0.5
	Object.Lifetime = setting.Lifetime or 2
	Object.Amplitude = setting.Amplitude or 0.35
	Object.Range = setting.Range or 50
	Object.Part = script:WaitForChild("Wind")
	Object.Time = setting.Time or nil
	
	return Object
end

-- METHODS
function WindService:Start()
	if self.Time then
		local Timer = tick()
		self.While = RunService.Heartbeat:Connect(function()
			if (tick() - Timer) >= self.Time then
				Timer += self.Time
				if self.Current < self.Amount then
					self:CreateWind()
				end
			end
		end)
	else
		self.While = RunService.Heartbeat:Connect(function()
			while self.Current < self.Amount do
				self:CreateWind()
			end
		end)
	end
end

function WindService:Stop()
	if self.Connection then
		if self.Connection.Connected then
			self.Connection:Disconnect()
		end
	end
	if self.While then
		if self.While.Connected then
			self.While:Disconnect()
		end
	end
end

function WindService:GetRandomPosition()
	if not Character.PrimaryPart then
		return
	end
	return Vector3.new(
		Character.PrimaryPart.Position.X + RNG:NextInteger(-self.Range, self.Range), 
		Character.PrimaryPart.Position.Y + RNG:NextInteger(1, self.Range), -- Replace to: "1, self.Range / 1.5" or see more wind nearby.
		Character.PrimaryPart.Position.Z + RNG:NextInteger(-self.Range, self.Range)
	)
end

function WindService:CalculateSineWave(amp, x, freq, phase)
	return amp * math.sin((x / freq) + phase)
end

function WindService:FadeWind(part)
	for i = 0, 1, 0.01 do wait(0.01)
		part.Trail.Transparency = NumberSequence.new{
			NumberSequenceKeypoint.new(0, 1),
			NumberSequenceKeypoint.new(0.3, i),
			NumberSequenceKeypoint.new(0.6, i),
			NumberSequenceKeypoint.new(1, 1),
		}
	end
	wait(0.1)
end

function WindService:CreateWind()
	local Part = self.Part:Clone()
	Part.Parent = self.Location
	Part.Position = self:GetRandomPosition()
	self.Current += 1
	
	-- CHECK RANDOM STATE:
	if self.Randomized then
		-- GET RANDOM VALUES:
		local Towards = Vector3.new(RNG:NextNumber(-self.Velocity.X, self.Velocity.X), RNG:NextNumber(-self.Velocity.Y, self.Velocity.Y), RNG:NextNumber(-self.Velocity.Z, -self.Velocity.Z))
		local Latency = RNG:NextNumber(self.Lifetime, self.Lifetime + RNG:NextNumber(0.5, 1.5))
		
		-- MOVEMENT:
		local Render = RunService.RenderStepped:Connect(function()
			local Formula = WindService:CalculateSineWave(self.Amplitude, tick(), self.Frequency, 0)
			Part.CFrame = Part.CFrame * CFrame.new(0, 0, Formula) + Towards
		end)
		
		-- CLEANUP:
		delay(Latency, function()
			WindService:FadeWind(Part)
			Render:Disconnect()
			Part:Destroy()
			self.Current -= 1
		end)
	else
		-- MOVEMENT:
		local Render = RunService.RenderStepped:Connect(function()
			local Formula = WindService:CalculateSineWave(self.Amplitude, tick(), self.Frequency, 0)
			Part.CFrame = Part.CFrame * CFrame.new(0, 0, Formula) + self.Velocity
		end)
		
		-- START ENDING:
		delay(self.Lifetime, function()
			WindService:FadeWind(Part)
			Render:Disconnect()
			Part:Destroy()
			self.Current -= 1
		end)
	end
end

return WindService

Recent Updates:

V0.1
  • Wind effect doesn’t look like a shooting star anymore.
  • Fades in and out.

Poll:

Is this useful for you?
  • Yes
  • Maybe
  • No

0 voters

101 Likes

It’s a creative use of a sine wave but the effect is somewhat ruined when the trail disappears instantly. Maybe have the transparency fade out?

5 Likes

Ngl looks more like shooting stars to meh

4 Likes

That sounds good! I’ll make sure to add a setting which allows it fade out at the end and keep it on by deafult. Maybe even a windy sound effect setting as well.

2 Likes

This guy’s wind seems a little more realistic
Maybe you should make it do a loop instead of sine?

8 Likes

That is way better, I agree. I’ll definitely try to add that.

As a dev that has no knowledge to scripting, how do you add it to your game?
Is there a more detailed tutorial for this?

1 Like

u are awesome! i was looking for this one

hey, if you’re still keeping up with this program can u tell me how to make it go in all directions

Sorry for my late response.

@gb_handsome I’m planning to rewrite this module and I will definitely add a detailed tutorial that users with no knowledge of scripting can understand.

@LittleGuyForHire You must set the: Randomized, setting to true.

3 Likes

Is it possible for you to create speed setting? it seems to always be at one speed no matter how I change up the settings

local WindService = require(game.ReplicatedStorage.WindService)
local Settings = {}

local newWind = WindService.new(Settings)
newWind:Start()
1 Like
local Settings = {
      ["Velocity"] = Vector3.new(1,0,0)
}

I know this might be kinda late but would there be a way to attach the creation of the wind to a part rather than following the player character? E.g. so the wind can surround one area’s environment rather than trail the player

Sorry I’m late, but just wondering, is this module good for performance in a large game?