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

107 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?

9 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

1 Like

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