Misc Scripts & Modules - My smaller projects

I’ve made many large projects that each got their own threads. However, I’ve also made smaller things that I just dropped into the What Are You Working On thread. They get lost in the thousands of replies, so I’ll be putting them all here for easier access, and any new ones will go here instead.


Markov Chain

December 2019

Pretty useless besides for having a laugh, but I implemented the markov chain algorithm into Rbx Lua.

API for the module is extremely simple:

MarkovChain.new()
-- Returns a Chain object
	
Chain:Study(string Text, bool IncludePunctuation)
-- Parses the words in Text and has the chain "learn" it
-- IncludePunctuation is optional and defaults to false
		
Chain:Generate(number MaxSize)
-- Returns a string generated by the module
-- MaxSize is an optional variable and defaults to 10000

I made it study 160+ Lua Learning tutorials, and this is what it wrote:

A Debounce script prevents a script into ServerScriptService. Name it whatever you’d like.Insert a LocalScript into StarterCharacterScripts.* With a LocalScript, players cannot see other players’ trails.* With a normal script and hit CTRL+C and you will need to be red, dull, and probably crash. That’s what ‘wait’ commands are for. If we wanted to make variables accessible in any script however that will increase the distance. It the delta is lesser that 0, then it will be matched 1+ times.‘-’ - The plus quantifier makes the stringall lower case.>i like chocolate, do you??I don’t really think I need them? Can’t I do not see the game. They can use one of the model and the size of the pattern matching.Looks for the script, we would need. So basically purchasePassID is the scale to offset. This is the service named MarketplaceService for you. You can do this manually, but no one wants to reload. Now for the gun.These variables should be named"killbrick," or something like this:This is especially useful for mobile add this into our ModuleScript:Now we’re going to directly set the CFrame in this instance, a table) Table: A container that stores values In the previous event we created. So we retrieve those numbers and gets the sum out of the code would be:> 5 seconds wouldn’t have passed, then… How are we putting the list ofvowels inside [] characters would be just like the other methods and events when the player We are done! You can also type "Hey want to open notepad on your left and right click and find out?After testing it through, it probably did select random text, and made it variablename to demonstrate that variablescan be named about whatever you want. You can imagine a bowling ball vs. a boulder. Although they are both rocks, the boulder has a HumanoidRootPart, and you should hit “Play” and verify the same mistake, yet being made with starting up your computer. You press the Stop button. It will always be 0.But if you make a variable called "IAMCOOL"Now, I want to dynamically create text on the spawn. Now that our ModuleScript returns, we will change color back to the language. After all, lua is a stringpattern onto itself. well, all three of the part. If you want it. I’m gonna put a string With that said, here are some pages about getting euler angles from a keyboard, we can start off by creating a thread by doingand run it by using the lua users wiki would be outputted. Fixing this is fixed in newer lua versions. Returns a function you need to have the connection to the nearby panels.In this extremely large amounts of applications for this tutorial. As you can use a more complicated rng then you need to check if we can’t get them by their name in ProcessReceipt. Now that we will need either a dot or *colon.*It’s good we have to select a random digit in the string matching %a+ is stored at capture index 1, the part to be first (descending order). So given our easy-to-remember strategy, return a < b mean? I’m not going to be before it can be read back by the Server only. You also need to check if the RemoteEvent and wait for to happen. More specifically, the Sound.Ended event. This event will fire the Event in the Explorer window and click on Move Tool. Now you will first need to escape magic characters. Any non-alphanumeric character (including all punctuation characters%s - represents all letters%c - represents all printable characters except space characters%l - represents all punctuation characters, even the non-magical) can be used to write that line of code should looks something like ‘1’ for 1 script instead of game.Workspace.Part when we are going to check if the ID in the empty space of the characters between (and including) from and to.Output:string.sub() can be fired. Here is the most useful service. We’re gonna start off with some currency, change it to fall off then go to the next section. Global Variables are a beginner at scripting and that no character is 2 and Cross(Up[#Up-1], Up[#Up], Nodes[I]) <= 0 do Low[#Low] = nil Now organization for _, I in next, Nodes do while #Low >= 2 and 3, and only I can act on them."Now, when calling the function didn’t fail, but it’ll return false if it is, more visible it becomes. (Remember that 0 is the equivalent of: We can make your variables. This will decrease the amount of typing nessacary, and if you are take the left number and divide it by the end of the Camera, just the beginning of subject string. Using both will anchor the pattern (a?(%a+).(%s+)), the part it’s parented to.BodyForce has only one property. Force. This is the end.I hope this makes your character will be in a half-second. Why? **Making too many datastore calls can cause an error, then the script is very important!**I’ll be saving the coins in the terrain we generate, wewould use numeric for loops works, you need a function that will never get disconnected:Never use methods like this afterwards.Now, we’re gonna put mine in StarterGui. Get the LocalPlayer in a ModuleScript in ReplicatedStorage, and call the :MoveTo() method and wait for it to workspace, but it’s the shortest axis, which in this example, we use string.sub(msg, 0,1), we only look at the Part we created simple custom player commands using string.sub.First, insert a Remote Event for now.) Why are we getting some strange output? This is a table of nodes: local Nodes = workspace.Nodes:GetChildren() table.sort(Nodes, function(A, B) return A.Position.X < B.Position.X or B.Position.X == B.Position.X and A.Position.Z < B.Position.Z end We need a looping function that checks if the purchase granted. To do this, if the condition is not the plugin).You may be wondering why these two new sub-properties have one number associated with them!Although accessory handles are, by default, but there’s nothing wrong with it. Then you’re completely wrong.Connections take up quite a lot of text it’s going to be first (ascending order). *> means greater than. This would mean if the program failed.

Source Code
--[=[
	boatbomber
	
	Really simple Markov Chain module
	
	MarkovChain.new()
		Returns a Chain object
	
	Chain:Study(string Text, bool IncludePunctuation)
		Parses the words in Text and has the chain "learn" it
		IncludePunctuation is optional and defaults to false
		
	Chain:Generate(number MaxSize)
		Returns a string generated by the chain
		MaxSize is optional and defaults to 10000
	
--]=]

local NOWORD = "\n"

math.randomseed(tick())

local function GetWords(Text, IncludePunctuation)
	
	local lines = string.split(Text, "\n")
	local lineNum = 1
	
	local line = lines[lineNum]    -- current line
	local pos = 1             -- current position in the line
	return function ()        -- iterator function
		while line do           -- repeat while there are lines
			local s, e = string.find(line, IncludePunctuation and "%S+" or "%w+", pos)
			if s then      -- found a word?
				pos = e + 1  -- update next position
				return string.sub(line, s, e)   -- return the word
			else
				lineNum = lineNum +1
				line = lines[lineNum]    -- word not found; try next line
				pos = 1             -- restart from first position
			end
		end
		return nil            -- no more lines: end of traversal
	end
end

local MarkovChain = {}

function MarkovChain.new()
	
	local Chain = {
		StateTable = {};
	}
	
	function Chain:Study(Text, IncludePunctuation)
		local w1, w2 = NOWORD, NOWORD
		for w in GetWords(Text, IncludePunctuation) do
			local i,v = w1.." "..w2, w
			if not self.StateTable[i] then
				self.StateTable[i] = {n=0}
			end
			self.StateTable[i][#self.StateTable[i]+1] = v
			
			w1 = w2; w2 = w;
		end
		
		local i,v = w1.." "..w2, NOWORD
		if not self.StateTable[i] then
			self.StateTable[i] = {n=0}
		end
		self.StateTable[i][#self.StateTable[i]+1] = v
	end
	
	function Chain:Generate(MaxSize)
		MaxSize = type(MaxSize) == "number" and MaxSize or 10000
		
		local TextOutput = ""
		
		local w1,w2 = NOWORD,NOWORD
		for i=1,MaxSize do
			local list = self.StateTable[(w1.." "..w2)]
			
			-- choose a random item from list
			local r = math.random(#list)
			local nextword = list[r]
			
			if nextword == NOWORD then break end
			
			TextOutput = TextOutput..nextword.." "
			w1 = w2; w2 = nextword
		end
		
		return TextOutput
	end
	
	return Chain
end

return MarkovChain


ClickDetectors

November 2019

The ClickDetector Instances have very shoddy behavior, and I found them to be unusable.
A MouseHoverEnter event doesn’t always fire a MouseHoverLeave event afterwards. Additionally, sometimes Enter fired on the next detector before the previous one fired Leave.

Differences between my custom Lua ClickDetector vs the Instance version:

  • Mine has stable and reliable behavior. Every Enter is sure to have a Leave fired after (unless you never leave :stuck_out_tongue:) and Leave will always fire before the Enter fires on the next one.

  • Mine has a read-only .Hovering property. Instead of having to hook Enter and Leave and track it in a variable, you can just check this property when needed. I found this useful, hope you agree.

  • Mine has a .Enabled property, so you can just set that to false for enter events to stop firing. For some reason, the Instance versions don’t have a way to be disabled.

  • Mine does not put Instances inside the part, which I find nice for avoiding clutter.

  • The Instance ClickDetector APIs are named differently than everything else. They’re verbose and old. I hate that. Mine follows the API of a GuiButton. Instead of MouseHoverEnter, it’s MouseEnter.

  • Mine doesn’t replicate to the server. It’s entirely clientside. Worth noting, however, that the Instance is super insecure and people have to write modules to be able to use it on the server reasonably.

It’s not entirely complete. Here’s what missing (will be added later):

  • MouseButton1Click (You can use MouseButton1Down and MouseButton1Up for now)
  • MouseButton2Click (You can use MouseButton2Down and MouseButton2Up for now)
Source code:
--[=[
	
	Example usage:
	
local ClickDetector = require(script.ClickDetector)

local Part = ClickDetector.new(workspace:WaitForChild("Part"))
	Part.MaxActivationDistance = 15
	Part.CursorIcon = "http://www.roblox.com/asset/?id=4317746479"

	Part.MouseEnter:Connect(function()
		print("Entered part")
	end)
	Part.MouseLeave:Connect(function()
		print("Left part")
	end)
	
	Part.MouseButton1Down:Connect(function()
		print("Clicked part")
	end)
	
	
--]=]


local ClickDetector = {}


local UIS = game:GetService("UserInputService")

local BINDABLE = Instance.new("BindableEvent")

local Cam = workspace.CurrentCamera
local Plr = game.Players.LocalPlayer
local Mouse = Plr:GetMouse()
local Char = Plr.Character or Plr.CharacterAdded:Wait()
local HRP = Char:WaitForChild("HumanoidRootPart")



local ActiveDetectors,CurrentHover,last_t,last_i = {},nil,nil,Mouse.Icon

Cam:GetPropertyChangedSignal("CFrame"):Connect(function()
	
	local h = ActiveDetectors[last_t]
	
	if h then
		if (HRP.Position - h._Internal.Adornee.Position).Magnitude <=h.MaxActivationDistance and h.Enabled then
			--Within distance
			if not h.Hovering then
				-- not yet set to hover, do it
				rawset(h, "Hovering", true)
				h._Internal.EnterEvent:Fire()
				
				Mouse.Icon = h.CursorIcon or ""
				
				CurrentHover = h
			end
		else
			-- Far away
			if h.Hovering then
				-- set to hover, undo
				rawset(h, "Hovering", false)
				h._Internal.LeaveEvent:Fire()
				
				Mouse.Icon = ""
				
				if CurrentHover == h then
					CurrentHover = nil
				end
			end
		end
	end
	
end)

UIS.InputChanged:Connect(function(Input)
	if Input.UserInputType == Enum.UserInputType.MouseMovement then
		
		-- Handle hover detection
		local t = Mouse.Target
		
		if t ~= last_t then last_t = t
			local h = ActiveDetectors[t]
			
			-- Leave old
			if CurrentHover and CurrentHover._Internal.Adornee ~= t then
				rawset(CurrentHover, "Hovering", false)
				CurrentHover._Internal.LeaveEvent:Fire()
				
				Mouse.Icon = ""
			end
			
			-- Enter new
			if h and (HRP.Position - h._Internal.Adornee.Position).Magnitude <=h.MaxActivationDistance and h.Enabled then
				rawset(h, "Hovering", true)
				h._Internal.EnterEvent:Fire()
				
				Mouse.Icon = h.CursorIcon or ""
				
				CurrentHover = h
			else
				CurrentHover =  nil
			end
		end
		
	end
end)
UIS.InputBegan:Connect(function(Input,GP)
	
	if not CurrentHover then return end
	
	if Input.UserInputType == Enum.UserInputType.MouseButton1 then
		
		CurrentHover._Internal.Button1DownEvent:Fire()
		
	elseif Input.UserInputType == Enum.UserInputType.MouseButton2 then
		
		CurrentHover._Internal.Button2DownEvent:Fire()
	
	end
end)
UIS.InputEnded:Connect(function(Input,GP)
	
	if not CurrentHover then return end
	
	if Input.UserInputType == Enum.UserInputType.MouseButton1 then
		CurrentHover._Internal.Button1UpEvent:Fire()
	elseif Input.UserInputType == Enum.UserInputType.MouseButton2 then
		CurrentHover._Internal.Button2UpEvent:Fire()
	end
end)


function ClickDetector.new(BasePart)
	
	--Type check
	if typeof(BasePart)~="Instance" or not BasePart:IsA("BasePart") then
		error("Invalid BasePart for click detection "..BasePart,2)
	end
	
	local EnterEvent = BINDABLE:Clone()
	local LeaveEvent = BINDABLE:Clone()
	
	local Button1DownEvent = BINDABLE:Clone()
	local Button2DownEvent = BINDABLE:Clone()
	
	local Button1UpEvent = BINDABLE:Clone()
	local Button2UpEvent = BINDABLE:Clone()
	
	local Detector = {
		
		Enabled		= true;
		MaxActivationDistance = 32;
		CursorIcon	= "";
		
		Hovering	= false;
		
		
		MouseEnter	= EnterEvent.Event;
		MouseLeave	= LeaveEvent.Event;
		
		MouseButton1Down	= Button1DownEvent.Event;
		MouseButton2Down	= Button2DownEvent.Event;
		
		MouseButton1Up	= Button1UpEvent.Event;
		MouseButton2Up	= Button2UpEvent.Event;
		
		
		
		_Internal	= {
			EnterEvent = EnterEvent;
			LeaveEvent = LeaveEvent;
			
			Button1DownEvent	= Button1DownEvent;
			Button2DownEvent	= Button2DownEvent;
			
			Button1UpEvent	= Button1UpEvent;
			Button2UpEvent	= Button2UpEvent;
			
			Adornee		= BasePart;
		};
		
	}
	
	function Detector:Destroy()
		
		ActiveDetectors[self._Internal.Adornee] = nil
		
		EnterEvent:Destroy()
		LeaveEvent:Destroy()
		
		Button1UpEvent:Destroy()
		Button2UpEvent:Destroy()
		
		if self.Hovering then
			Mouse.Icon = ""
		end
		
		self._Internal.Adornee = nil
	end
	
	-- Catch inputs
	ActiveDetectors[BasePart] = Detector
	
	return Detector
end

return ClickDetector


Film Grain

October 2019

Wrote a short and simple script to add a film grain effect to your game, with easy customization.

Really doesn’t show up in a compressed video. :frowning:

Source code
--[=[
	
:::::::::: ::::::::::: :::        ::::    ::::    ::::::::  :::::::::      :::     ::::::::::: ::::    ::: 
:+:            :+:     :+:        +:+:+: :+:+:+  :+:    :+: :+:    :+:   :+: :+:       :+:     :+:+:   :+: 
+:+            +:+     +:+        +:+ +:+:+ +:+  +:+        +:+    +:+  +:+   +:+      +:+     :+:+:+  +:+ 
:#::+::#       +#+     +#+        +#+  +:+  +#+  :#:        +#++:++#:  +#++:++#++:     +#+     +#+ +:+ +#+ 
+#+            +#+     +#+        +#+       +#+  +#+   +#+# +#+    +#+ +#+     +#+     +#+     +#+  +#+#+# 
#+#            #+#     #+#        #+#       #+#  #+#    #+# #+#    #+# #+#     #+#     #+#     #+#   #+#+# 
###        ########### ########## ###       ###   ########  ###    ### ###     ### ########### ###    #### 


by boatbomber, 2019

--]=]
-- Put this as a LocalScript and tweak the settings to your liking.


-- Settings

local GRAIN_SIZE		= 50	-- A number 1 to 100
local GRAIN_SPEED		= 50	-- A number 1 to 100
local GRAIN_VISIBILITY	= 15	-- A number 1 to 100

---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------

local udim2	= UDim2.new
local rand	= math.random
local plr	= game.Players.LocalPlayer
local plrGui= plr:WaitForChild("PlayerGui")

local baseSize	= 300*(GRAIN_SIZE*0.02)
local speed		= 1/(40*(GRAIN_SPEED*0.02))
local vis		= 1.05-(GRAIN_VISIBILITY*0.01)

local GrainGui = Instance.new("ScreenGui")
	GrainGui.Name = "FilmGrain"
	GrainGui.IgnoreGuiInset = true
	GrainGui.DisplayOrder = 99
	GrainGui.Parent = plrGui

local GrainImage = Instance.new("ImageLabel")
	GrainImage.Size = udim2(1,0,1,0)
	GrainImage.BackgroundTransparency = 1
	GrainImage.ImageTransparency = vis
	GrainImage.ScaleType = Enum.ScaleType.Tile
	GrainImage.Image = "http://www.roblox.com/asset/?id=28756351"
	GrainImage.Parent = GrainGui


local last = 0
game:GetService("RunService").Heartbeat:Connect(function()
	if tick()-last < speed then return end
	
	last = tick()	
	GrainImage.TileSize = udim2(rand(baseSize*0.89,baseSize*1.11)/1000,0,rand(baseSize*0.89,baseSize*1.11)/1000,0)
end)

Filters

June 2019

It quickly and easily applies filters to your world, on-the-fly.
A while after I made this, someone made a wonderful plugin called LightPlus+ that serves the same purpose but only in studio. Mine is more for a “photo mode” in game that allows filters. It comes with a few filters, and you can easily add your own.

Demo World:

Source code
local LightingService	= game:GetService("Lighting")
local Applying			= false
local Objects			= {}

local module = {
	
	-- To create your own filter, just add another table like these
	-- and give it a unique key so it can be called in module.ApplyFilter
	Filters = {
		
		['None']	= {
			["BloomEffect"]				= {Intensity = 0, Size = 0, Threshold = 5};
			["BlurEffect"]				= {Size = 0};
			["ColorCorrectionEffect"]	= {Brightness = 0, Contrast = 0, Saturation = 0, TintColor = Color3.fromRGB(255,255,255)};
			["SunRaysEffect"]			= {Intensity = 0, Spread = 0};
		};
		
		['Vintage']	= {
			["BloomEffect"]				= {Intensity = 2, Size = 50, Threshold = 1};
			["BlurEffect"]				= {Size = 3};
			["ColorCorrectionEffect"]	= {Brightness = 0.04, Contrast = -0.1, Saturation = -0.05, TintColor = Color3.fromRGB(252,255,233)};
			["SunRaysEffect"]			= {Intensity = 0.1, Spread = 0.1};
		};
		
		['Warm']	= {
			["BloomEffect"]				= {Intensity = 2, Size = 50, Threshold = 1};
			["BlurEffect"]				= {Size = 2};
			["ColorCorrectionEffect"]	= {Brightness = 0.01, Contrast = 0.01, Saturation = 0.1, TintColor = Color3.fromRGB(255,227,180)};
			["SunRaysEffect"]			= {Intensity = 0.1, Spread = 0.1};
		};
		
		['Cool']	= {
			["BloomEffect"]				= {Intensity = 5, Size = 40, Threshold = 1};
			["BlurEffect"]				= {Size = 2};
			["ColorCorrectionEffect"]	= {Brightness = -0.03, Contrast = -0.1, Saturation = -0.1, TintColor = Color3.fromRGB(212,240,255)};
			["SunRaysEffect"]			= {Intensity = 0.05, Spread = 0.03};
		};
		
		['Negative']	= {
			["BloomEffect"]				= {Intensity = 10, Size = 50, Threshold = 1};
			["BlurEffect"]				= {Size = 0};
			["ColorCorrectionEffect"]	= {Brightness = 0.1, Contrast = -1.8, Saturation = -1, TintColor = Color3.fromRGB(235,246,255)};
			["SunRaysEffect"]			= {Intensity = 0.2, Spread = 0.1};
		};
		
		['Realistic']	= {
			["BloomEffect"]				= {Intensity = 1, Size = 38, Threshold = 0.8};
			["BlurEffect"]				= {Size = 2};
			["ColorCorrectionEffect"]	= {Brightness = -0.01, Contrast = 0.06, Saturation = -0.06, TintColor = Color3.fromRGB(255,253,252)};
			["SunRaysEffect"]			= {Intensity = 0.2, Spread = 0.1};
		};
		
		['Western']		= {
			["BloomEffect"]				= {Intensity = 2, Size = 40, Threshold = 1};
			["BlurEffect"]				= {Size = 1};
			["ColorCorrectionEffect"]	= {Brightness = 0.02, Contrast = -0.03, Saturation = -0.4, TintColor = Color3.fromRGB(255,205,123)};
			["SunRaysEffect"]			= {Intensity = 0.1, Spread = 0.7};
		};
		
		['Old School']	= {
			["BloomEffect"]				= {Intensity = 3, Size = 50, Threshold = 1};
			["BlurEffect"]				= {Size = 2};
			["ColorCorrectionEffect"]	= {Brightness = 0.05, Contrast = 0.06, Saturation = -0.95, TintColor = Color3.fromRGB(249,255,255)};
			["SunRaysEffect"]			= {Intensity = 0.5, Spread = 0.02};
		};
		
		['Meme']		= {
			["BloomEffect"]				= {Intensity = 10, Size = 80, Threshold = 1};
			["BlurEffect"]				= {Size = 1};
			["ColorCorrectionEffect"]	= {Brightness = 0.2, Contrast = 1, Saturation = 30, TintColor = Color3.fromRGB(255,255,255)};
			["SunRaysEffect"]			= {Intensity = 0.01, Spread = 0.12};
		};
	
	};
}

function module:ApplyFilter(FilterType)
	
	--Avoid doing two applications at once and making a mess
	if Applying then repeat wait(0.1) until not Applying end Applying = true
	
	--Remove old filter
	for i=1,#Objects do
		Objects[i]:Destroy()
	end
	
	--Find chosen filter
	local Filter = self.Filters[FilterType] or self.Filters.Realistic
	
	--Apply filter
	for obj, props in pairs(Filter) do
		local effect = Instance.new(obj)
		
		for p,v in pairs(props) do
			effect[p] = v
		end
		
		Objects[#Objects+1] = effect
		
		effect.Parent = LightingService
		
	end
	
	--Allow new application
	Applying = false
	
end

return module



Enjoying my work? I love to create and share with the community, for free.

If you’d like to help fund my work, consider sponsoring me on GitHub or donating on BuyMeACoffee!

172 Likes

Does adding a film grain effect take a toll on client/server performance? It looks amazing but I’m not sure how practical it is.

1 Like

It shouldn’t cause any performance issues. All the module does is apply color correction effects to Lightning to create the desired filters.

@mircostaff He asked about the grain, not the filters.

@FragmentFour I don’t remember the numbers, and cant check rn. You can run it yourself and just check the Script Performance numbers. I can’t imagine it being that bad though.

I can definitely optimize more if needed.

1 Like

That one is definitely on me. I had the source for the filter code open while reading their question. :confused:

2 Likes

No worries. Easy mistake to make, as they are both visual effects.

Boat “Gui Guy” Bomber

In terms of the ClickDetector API, how does it stack up in terms of performance to the standard ClickDetector?

2 Likes

Definitely worse performance than the Instance. The Instance gets handled by the engine whereas mine runs on Lua in-engine. I can’t win that competition.
However, mine is useful in cases where the stable behavior is necessary. It’s trading a bit of performance for more functionality and use.

1 Like

I figured that’d be the case, but it does function as the click detector would for standard purposes, just with added features?

I updated the OP to have a clear dif list after this reply. He didn’t ask before reading, so don’t think his question was redundant. :slight_smile:

I used it in place of a ClickDetector in a secret project of mine, if that’s what you’re asking.

The end behavior is just a stable ClickDetector, even if the internals are different.

You can refer to the dif list in the OP.

Other than those things, they serve the same purpose. They give hover and click detection for BaseParts, with a MaxActivationDistance. Mine is more reliable, while the Instance is more performant.

1 Like

That Subway station doesn’t look like roblox…Anyways thanks for Film grain

2 Likes

I love your lighting module! Any chance you could add in the Warm, Cool, Negative, and Vintage settings as well?

2 Likes

Oh gosh, I didn’t realize I was missing those! I’ll definitely add them in when I have a chance.

EDIT: Added them.

2 Likes

Thanks for sharing these modules. I edited your Filter script to tween between filters instead of deleting old filter and I think you should do that as an option
edited version :

function module:ApplyFilter(FilterType)
	
	if Applying then repeat wait(0.1) until not Applying end Applying = true
	
	local Filter = self.Filters[FilterType] or self.Filters.Realistic
	

local TS = game:GetService("TweenService")
local TI = TweenInfo.new(.5)
	for i,v in pairs(game.Lighting:GetChildren()) do
		if v:IsA("PostEffect") then
			for o,p in pairs(Filter) do
				if v.ClassName == o then
					local goal = {}
						for l,vv in pairs(p) do
						goal[l] = vv
						end
					local a =  TS:Create(v,TI,goal)
					a:Play()
				end
			end
		end 
	end
	
	Applying = false
	
end
1 Like

This is a great idea, but this implementation would accidentally mess with your other in game effects!

You’d need to track the filter module’s Instance effects, and loop through those to only apply to them.

Maybe you can give special name for your filter effect I made like this because I dont have pp-effect other than these and I change it with region3

:slight_smile:

2 Likes

Nice work on that light module! I have a question and it is that how did you make that effect with the light in that subway station. Did you make it using a Billboardgui and an imagelabel? I would appreciate if you answer my question, thank you very much for your attention.

Yeah, those flares are BillboardGuis with an ImageLabel that has ImageTransparency set based on the angle to the camera.

2 Likes

That pretty landscape demo looks amazing!
How’d you do that grass? It looks way higher-quality than default terrain grass. Are they meshes, or did you just change the color?

My favorite thing out of the four of these has to be the film grain effect.
Very cool stuff as always!

Made this before terrain grass was a thing iirc, it’s mesh grass placed by hand

3 Likes