great to see people expand on my video lol. i do wonder if its possible to make an entire game that does calculations from just viewports
Well do I have something for you…,
( it’s not finished)
(It’s not even working)
But I’m using this to do almost that!
Render distance almost works, it looks a little off considering what it does, so Im going to create something to fix the problem
(I have created a module that allows me to loop through all properties in a basePart. Ik pretty cool, This is also works for MeshParts and BaseValues. When I’m done with this module I’ll probably release it)
Small problem…
Too much calculations…
Ima abandoning this but I might come back to it later
I recently wrote a new Renderer becuase the method I mentioned earlier using the Zone module was redundant because I’m using occlusion culling calculations. In short this renderer has caused an increase of about 5-13 FPS in my use case. A good way to reduce calculations is to create a part object in place that is the result of the object not being in view, then just check the part objects if they are in view and unpack them when necessary. That’s how mine works and it saves calculating the distance of objects. Also, multi-threading your renderer to like 4-8 may increase its performance. I handle all caching on the server with 3 different actors and like 8 object categories with different rendering distance. They never exceed about .15-.25% CPU usage on the server and this section can be done very slow because rendering out is lower priority than rendering in which is handled on the client in my case. Using this code snippet which may provide some hints for your project.
-- This creates a zone for every ambient group, then listens for when the local player enters and exits
task.wait(.6)
--local Zone = require(game:GetService("ReplicatedStorage").Zone)
local localPlayer = game.Players.LocalPlayer
local playerGui = localPlayer:WaitForChild("PlayerGui")
local Remote=game.ReplicatedStorage.GlobalSpells.ZoneRemote
local zonearray={}
local id=0
local payloadarray={}
local DistanceArray = require(game:GetService("ReplicatedStorage").Zone.RenderDistance)
local camera=workspace.CurrentCamera
local objects=workspace.TouchBox
local function payloadcache(container)
table.insert(payloadarray,container)
end
-- Calculate adjusted render distance based on FPS
-- Example usage:
local fps = playerGui.CharacterSelect.FPS.Value
--local adjustedRenderDist = calculateAdjustedRenderDist(fps)
local function IsInView(object,cameraViewportSize)
--if math.abs(object.Position.Y-localPlayer.Character.HumanoidRootPart.Position.Y)>50 then return false end
--print(object.Position)
if object.Name=="FurnitureRender" or object.Name=="PlantRender" then
return true
end
local objectPosition = camera:WorldToViewportPoint(object.Position)
-- Check if the object is within the camera's viewport
--print(objectPosition)
if objectPosition.X <= cameraViewportSize.X and
objectPosition.Y <= cameraViewportSize.Y and
objectPosition.Z > 0 then -- Z > 0 means the object is in front of the camera
return true
else
return false
end
end
local minRenderDist = 40 -- Minimum render distance
local fpsThreshold = 30 -- FPS threshold below which render distance decreases
-- Calculate adjusted render distance based on FPS
local function calculateAdjustedRenderDist(maxRenderDist,fps)
return math.max(minRenderDist,maxRenderDist - (maxRenderDist/2) * (math.max(fps, fpsThreshold) - fpsThreshold) / (60 - fpsThreshold))
end
local Tick=game.ReplicatedStorage.GlobalSpells.Tick
local writetime=Tick.Value
local arrays={}
local refresh=6
local function getobjects()
if Tick.Value>writetime+refresh then
arrays=objects:GetChildren()
writetime=Tick.Value
end
end
--lower fps shorter render distance
local RefreshRate=.6
local function checkobjects(pos)--check if object is in view and
local payload={}
local cameraViewportSize = camera.ViewportSize
local fps=playerGui.CharacterSelect.FPS.Value
local fpstiming=playerGui.CharacterSelect.FPSTiming.Value
local adjustedarray={}
for i,v in DistanceArray do
adjustedarray[i]=calculateAdjustedRenderDist(v,fps)
end
getobjects()
--local arrays=objects:GetChildren()
local amnt=math.max(1,#arrays)
local throttle= RefreshRate/amnt
local increment=math.max(1,fpstiming/throttle)
local state=function(reference) if reference.Position.Y<-16 then return adjustedarray[reference.Name] or nil else return nil end end
if pos.Y>-16 then
state=function(reference) if reference.Position.Y>-16 then return adjustedarray[reference.Name] or nil else return nil end end
end
local current=0
task.wait(throttle)
for i,reference in arrays do
current+=1
if current>increment then
task.wait(throttle)
current=0
end
if reference:IsA("BasePart") then
local renderdist=state(reference)--adjustedarray[reference.Name] or nil
if renderdist then
local lengthvector=(reference.Position-pos).Magnitude
if lengthvector<renderdist and IsInView(reference,cameraViewportSize) then
table.insert(payload,reference)
--reference.CanQuery=true
table.remove(arrays,i)
elseif lengthvector>math.min(300,renderdist*2.5) then
table.remove(arrays,i)
--reference.CanQuery=false
end
else
table.remove(arrays,i)
end
else table.remove(arrays,i)
end
end
--arrays=nil
state=nil
return payload,amnt
end
local function RegisterLoop()
spawn(function()
--one payload per second
local prevpos=localPlayer.Character.HumanoidRootPart.Position
while true do
if camera.CFrame~=prevpos then
prevpos=camera.CFrame
local payload=checkobjects(localPlayer.Character.HumanoidRootPart.Position)
Remote:FireServer(payload)
else
task.wait(RefreshRate)
end
end
end)
end
RegisterLoop()
I did some optimizations with the array handler for the objects and conditions to remove objects from the array as well as increasing the Arrays refresh rate which tripled the performance of the renderer. When running this script uses .05%-.15% CPU usage on the client when using the array handler and .3-1.1% without it
Wow! This is very VERY nice!, thank you truly.
I also want to share this easy to use Parallel luau solution which demonstrates how to invoke a bindable function and create a thread handler that evenly spreads the load to all the actors.
I apologize for certain aspects of the code such as FPSTiming not being defined.
This is the code that I use to calculate Frames per second and the time it takes to render each frame (FPSTiming)
local RunService=game:GetService("RunService")
local TimeFunction = RunService:IsRunning() and time or os.clock
local LastIteration, Start
local FrameUpdateTable = {}
local GlobalFPS=1/30
local function HeartbeatUpdate()
LastIteration = TimeFunction()
for Index = #FrameUpdateTable, 1, -1 do
FrameUpdateTable[Index + 1] = FrameUpdateTable[Index] >= LastIteration - 1 and FrameUpdateTable[Index] or nil
end
FrameUpdateTable[1] = LastIteration
local FrameRate=tostring(math.floor(TimeFunction() - Start >= 1 and #FrameUpdateTable or #FrameUpdateTable / (TimeFunction() - Start)))
--Bars.Parent.CharacterSelect.FPS.Value=FrameRate
--Bars.Radar.FrameRate.Text="FPS:"..Bars.Parent.CharacterSelect.FPS.Value
GlobalFPS=1/FrameRate
--Bars.Parent.CharacterSelect.FPSTiming.Value=GlobalFPS
--FPS
end
Start = TimeFunction()
RunService.Heartbeat:Connect(HeartbeatUpdate)
i am not going to lie this is getting to advanced for me
I am abandoning my idea (sadly) to work on my game for now…
Thank you for taking interest however!
It’s fine! although the two things I recently shared are generally very important. Calculating the time it takes to render each frame as a variable that is used for a variety of tasks. Also, I’ve been using the parallel luau solution for like everything lately to make easy to use and efficient tools for my game. But yeah a custom renderer may not be neccessary I only did it because I could compress all the objects not in view to a reference of their source object because I am doing procedural generation. Which reduced memory usage significantly and allows the worlds to be much larger.
i might start using that threader then!
If you do not mind sharing what is the game you are working on?
I have high hopes for it since you are developing it!
Currently, I am working on Prelude:Prelude - Roblox
Game might have bugs but Its just my first 2 months of scripting so bear with me lol!
This is a link to the testing server for my game Epic RPG with AI +Optimized Performance - Roblox
The main menu currently doesn’t have all the options enabled. so please select the custom character option to enter the game.
this is a link to a previous version of the game which features the story mode.
Lumina & Darkness: V 0.9 - Roblox
My old account got hacked so my game doesn’t really get traffic Yet. Gogeta - Roblox
I still consider it incomplete but I plan on a full release soon.
I’ll check out your game too! Some good hints for early scripts is to try to be as organized and efficient as possible and make tools that you want to work with and build up later on.
Also your game is very cool but I’m getting very low FPS sometimes the camera glitches out. But it’s very atmospheric and cool! I couldn’t figure out how to use the sword but I was able to use an electric spell. Definitely keep up the good work and try to create some organized and efficient systems that will allow you to make unlockables or an inventory use the same code for your weapon and use variables to change their behavior
Yea the low fps is the raytracing I put in, I’m trying to figure out how to make it togglable.
Camera Is fine on my side but ill do a double check.
I really dont know what direction to take this game currently lol
Your game reminds of eve online for some reason.
Would love to play it when its done however… systems sound very intricate
features of my game are those RPG elements of power progression, equipment collecting monster fighting, resource collecting, socializing with players and AI and doing procedurally generated quests in an open world in short. Currently looking at the lua heap and doing some nice optimizations trying to get the ping stable.
I noticed the camera it glitched out when geting close to an npc possibly due to camera trying to move around a type of wall.
MY advice for your game would be to make a bunch of spells and sword skills and start out with player versus player while also designing the abilities to be used by NPCs. I had this great thing happen in my development where I was able to convert most of my player systems to be used by NPCs and it saved a lot of work and ends up being very neat in coding and gameplay.
I never played Eve Online! But secretly this game is like a mix of Kingdom Hearts, Runescape and Breath of Fire Dragon Quarter and a little bit of Yugioh inspiration.
I just enjoy making my dream game
WOW! I love your game’s inspirations. I was a big fan of yugioh and kingdom hearts when I
was a kid.
Currently Its Becoming a more daunting task to work on my game as I know what the next step is, However.
(I have no idea how to get there)
This is what I wish to achieve:
This is what I have:
Nothing cant figure out how to use WCS and RefX unfortunately
Besides that I already have the VFX and SFX ready for the abilities but I’ve been stuck on figuring out how to use this framework for almost a week… I know using this one will save me tons of time from bartokens personal experience he shared with me…
Might Hire a scripter honestly
I have a useful function I use for VFX.
This is a multiply number sequence function that can change the size of Particle Emitters in code.
local function multnumseq(sequence,Scale)
local numberKeypoints2 = {}
for i = 1, #sequence.Keypoints do
local currKeypoint = sequence.Keypoints[i]
table.insert(numberKeypoints2, NumberSequenceKeypoint.new(currKeypoint.Time,currKeypoint.Value*Scale))
end
return NumberSequence.new(numberKeypoints2)
end
In my game one thing I did was create like 30 different ball style attacks.
Then wrote one function that handles all of them.
function dfg.Energyball(Subject,Target,Type,Damage,Scale)
local charging
local Subject=Subject
local Target=Target
local Type=Type
local Damage=Damage
local Scale=Scale
local Human=nil
if Scale==nil then Scale=1 end
local Player,Player2=nil
if Target~=nil and Subject~=nil then
Human=Subject.Parent:FindFirstChildOfClass("Humanoid")
if Human==nil then
Human=Subject.Parent.Parent:FindFirstChildOfClass("Humanoid")
end
local Angular
Player=game.Players:GetPlayerFromCharacter(Human.Parent)
local Rotate=false
if Player==nil and Target then
if Human.Parent:FindFirstChild("HumanoidRootPart"):FindFirstChildOfClass("BodyGyro")==nil then
Angular=Instance.new("BodyGyro")
Angular.CFrame=CFrame.new(Human.Parent:FindFirstChild("HumanoidRootPart").Position,Target.Position)
Angular.Parent=Human.Parent:FindFirstChild("HumanoidRootPart")
Debris:AddItem(Angular,.6)
if Human.AutoRotate==true then
Rotate=true
Human.AutoRotate=false
spawn(function() task.wait(.6) Human.AutoRotate=true end)
else Human.AutoRotate=false end
end
end
local Comp=false
local Core=Type.Parent.Core:Clone()
local EFX=Type:Clone()
--local simmodel=Instance.new("Model")
--Core.Parent=simmodel
-- Core.Parent
local w
local Speed=Type.MagicID:GetAttribute("Speed")
local DamageScript=repspell.Damage.DmgRoll:Clone()
DamageScript.Value=Damage
DamageScript.Parent=Core
local Obj=Instance.new("ObjectValue")
Obj.Value=Subject
Obj.Name="Owner"
Obj.Parent=Core
EFX.Parent=Core
Core.Size=Type.MagicID:GetAttribute("Size")
Core.CFrame=Subject.CFrame:ToWorldSpace(CFrame.new(0, 0,(-1*(Core.Size.Z/2+Subject.Size.Z/2))))
Core.Massless=true
Core.Anchored=false
Core.Parent=Subject.Parent
--simmodel:Destroy()
if Human~=nil then
if Human.Parent:FindFirstChild("UpperTorso") then
local AR=mathrandom(1,7)
local Anim=Human:LoadAnimation(script.Animations:FindFirstChild("Energyball"..AR))
Anim:Play()
task.delay(Anim.Length,function() Anim:Stop() end)
end
end
if Player then
Core:SetNetworkOwner(Player)
elseif Target then
Player2=game.Players:GetPlayerFromCharacter(Target.Parent)
if Player2 then
Core:SetNetworkOwner(Player2)
end
end
--task.delay(1.2,function() dfg.Deflect(Core) end)
---local Core2=Core:Clone()
local DamageCharacter=Core
--Core2.CanCollide=false
--Core2.CanQuery=false
--Core2.CanTouch=false
--.Anchored=true
local hittable={}
local function checktable(hit)
for i,v in pairs(hittable) do
if v==hit.Parent then
return true
end
end
return false
end
local Hits = 0
local tcon=nil tcon=Core.Touched:Connect(function(hit)
dfg.ExplosionConditions(hit,Core,Type.MagicID)
if hit.Parent~=Subject and tostring(hit.Parent)~=tostring(Subject) and hit.Parent~=Human.Parent and hit.Parent~=Core.Parent then Core.CanTouch=false
local arrow = Core
local Owner
local Character=nil
local damage
if arrow:FindFirstChild("Owner")==nil then
Owner=Instance.new("ObjectValue")
Owner.Value=arrow.Parent
end
Owner = arrow.Owner
local DMG =repspell.DMG:clone()
Debris:AddItem(DMG,7)
local humanoid = hit.Parent:findFirstChild("Humanoid")
local Protect=hit.Parent:findFirstChild("Protect")
if (humanoid~=nil and Protect~=nil) and humanoid.Parent~=Subject then
if humanoid.Parent ~= Obj.Value then
if checktable(hit)==false then
Comp=true
arrow.CanTouch=false
if game.Players:FindFirstChild(Owner.Value.Name)~=nil then
Character = game.Players:FindFirstChild(Owner.Value.Name).Character
Core.DmgRoll.Value= game.Players:FindFirstChild(Owner.Value.Name).PlayerGui.Bonus.MAG.Value
else
pcall(function() Core.DmgRoll.Value = Damage end)
end
if hit.Parent.Protect.Value ~= 2 then
if Core.DmgRoll.Value>=80 then
damage = (mathrandom(0,Core.DmgRoll.Value/1.1))
Core.DmgRoll.Value= damage
elseif Core.DmgRoll.Value<=80 then
damage = (mathrandom(0,Core.DmgRoll.Value))
Core.DmgRoll.Value= damage
end
elseif hit.Parent.Protect.Value == 2 then
if Core.DmgRoll.Value>=80 then
damage = (mathrandom(0,Core.DmgRoll.Value/3))
elseif Core.DmgRoll.Value<=80 then
damage = (mathrandom(0,Core.DmgRoll.Value/2))
end
end
if Character~=nil then
if Character:FindFirstChild("Staff") ~= nil or Character:FindFirstChild("Scythe") ~= nil or Character:FindFirstChild("ScytheX") ~= nil then
Core.DmgRoll.Value= damage+ damage*.15
damage=Core.DmgRoll.Value
end
end
damage =math.floor(damage)
dfg.DealDamage(humanoid,damage,2,hit,Type.MagicID:GetAttribute("Element"),Subject)
table.insert(hittable,hit.Parent)
--humanoid.Health = humanoid.Health - damage
--Hits = Hits + 1
--DMG.HitSplat.Hit.Text = ""..(damage)..""
-- if humanoid.Parent:FindFirstChild("Head")~=nil then
--DMG.Parent = humanoid.Parent.Head
-- else humanoid.Parent:FindFirstChild("HumanoidRootPart")
-- end
-- DMG.HitSplat.Hit.Server.Disabled = false
else
arrow.CanTouch=false
end
-- explosion = game.Lighting.Bewm:clone()
-- explosion.Position = hit.Position
-- explosion.Mesh.Scale = Vector3.new(0.25, 0.25, 0.25)
-- explosion.Parent = workspace
task.wait()
end
end
end
if Hits<1 then
Core.CanTouch=true
else tcon:Disconnect()
tcon=nil
end
end)
if (Subject) then
w = Instance.new("Weld")
w.Part0,w.Part1 = Subject,Core
w.C0 = Core.CFrame:toObjectSpace(Subject.CFrame):inverse()
w.Parent = Subject
end
for _,v in pairs(EFX:GetDescendants()) do
local v=v
if v:IsA("Sound") then
TweenSound(v)
if v.Looped==true then
spawn(function()
v.RollOffMaxDistance*=Scale
repeat task.wait(.05) until Comp==true
local goal = {}
goal.Volume = 0
local tweenInfo = TweenInfo.new(.5)
local tween = TweenService:Create(v, tweenInfo, goal)
tween:Play()
end)
end
end
if v:IsA("PointLight") then
spawn(function()
local v=v
local goal = {}
goal.Brightness = v.Brightness
goal.Range= v.Range*Scale
local tweenInfo = TweenInfo.new(2)
local tween = TweenService:Create(v, tweenInfo, goal)
v.Brightness=0
v.Range=0
tween:Play()
end)
end
if v:IsA("ParticleEmitter") then
spawn(function()
local Bubbleb=v
local PrevTrans=Bubbleb.Transparency
local Prevsize=dfg.multnumseq(Bubbleb.Size,Scale)
local Steps=15
local CSteps=0
local fram=.0333
local deftimes=Bubbleb.TimeScale
Bubbleb.TimeScale=Bubbleb.TimeScale/3
repeat
v.TimeScale=deftimes*(CSteps/5)
CSteps=CSteps+1
--local Ratio=CSteps/Steps
--local numberKeypoints2 = {
-- NumberSequenceKeypoint.new(0, 1-Ratio), -- At t=0, size of 0
-- NumberSequenceKeypoint.new(1, 1), -- At t=1, size of 10
--}
Bubbleb.Size = dfg.multnumseq(Prevsize,CSteps/15)
Bubbleb.TimeScale=deftimes*(CSteps/15)
Bubbleb.Transparency = dfg.multnumseq(PrevTrans,17/CSteps+2)
task.wait(fram)
until CSteps+2>=Steps
Bubbleb.TimeScale=deftimes
Bubbleb.Transparency=PrevTrans
Bubbleb.Size=Prevsize
local Steps=60
local CSteps=0
local fram=3/30
--local c
repeat
CSteps=CSteps+1
if Comp==true and CSteps<30 then
CSteps=30
else CSteps=CSteps+1
end
if CSteps>=45 or Comp==true then
if CSteps<45 then
CSteps=45
end
task.wait(fram)
local Ratio=CSteps/Steps
local numberKeypoints2 = {
NumberSequenceKeypoint.new(0, Ratio), -- At t=0, size of 0
NumberSequenceKeypoint.new(1, 1), -- At t=1, size of 10
}
Bubbleb.Transparency = dfg.multnumseq(PrevTrans,(CSteps-44)*1)
end
task.wait(fram)
until CSteps>=Steps
Bubbleb.Enabled=false
end)
end
end
if Target~=nil then
local goal = {}
local tweenInfo
local Tick
local tween
task.wait(.75)
local Vectir=nil
if Target then
Vectir=(Target.Position-Subject.Position).Magnitude
else
Vectir=80
end
local B=Instance.new("BodyPosition")
B.Position=Core.Position
w:Destroy()
Core.Anchored=true
if Vectir<40 and Vectir>12.5 then
tweenInfo = TweenInfo.new(Vectir/10,Enum.EasingStyle.Linear)
Tick=Vectir/10
elseif Vectir>=40 then
tweenInfo = TweenInfo.new(4,Enum.EasingStyle.Linear)
Tick=4
elseif Vectir<=12.5 then
Tick=1.25
tweenInfo = TweenInfo.new(1,Enum.EasingStyle.Linear)
end
if Vectir>8 and Target then -- follow the target
goal.Position = Target.Position
tween = TweenService:Create(B, tweenInfo, goal)
tween:Play()
spawn(function()
local Cero=0
repeat
Cero=Cero+.25
if (Target.Position-Core.Position).Magnitude<2 then
Comp=true
end
local tweenInfo
if Tick-Cero>0 then
tweenInfo=TweenInfo.new(Tick-Cero,Enum.EasingStyle.Linear)
else
tweenInfo=TweenInfo.new(.4,Enum.EasingStyle.Linear)
end
goal.Position = Target.Position
local tween2 = TweenService:Create(Core, tweenInfo, goal)
tween:Pause()
tween=tween2
tween:Play()
task.wait(.25/Speed)
until Cero>=Tick or Comp==true
Comp=true
task.wait(.6)
Core.CanTouch=false
task.wait(1.5)
if Player and Core.Parent~=nil then
Core.Anchored=false
Core:SetNetworkOwner(nil)
end
Core:Destroy()
tcon:Disconnect()
end)
else
tweenInfo = TweenInfo.new(1/Speed,Enum.EasingStyle.Linear)
goal.Position = Target.Position
local tween = TweenService:Create(B, tweenInfo, goal)
tween:Play()
tween.Completed:Wait()
Comp=true
task.wait(.6)
Core.CanTouch=false
task.wait(1.5)
if Player then
Core:SetNetworkOwner(nil)
end
Core:Destroy()
tcon:Disconnect()
--if Core2~=nil then
-- Core2:Destroy()
--end
end
end
end
end
This is definitely the way to go about creating simple and effective systems. So don’t worry! Less is more!
They also utilize this neat object that determines their behavior.
Thank you for providing this but I’m probably gonna use refx because it’s documented and doesn’t need presintalled assets
Hey! I just started UI Design!
Wondering if this is a good Fps Reader or if it can be optimized lol
local RunService = game:GetService("RunService")
local FpsLabel = script.Parent
local TimeFunction = RunService:IsRunning() and time or os.clock
local LastIteration, Start
local FrameUpdateTable = {}
local function HeartbeatUpdate()
LastIteration = TimeFunction()
for Index = #FrameUpdateTable, 1, -1 do
FrameUpdateTable[Index + 1] = FrameUpdateTable[Index] >= LastIteration - 1 and FrameUpdateTable[Index] or nil
end
FrameUpdateTable[1] = LastIteration
FpsLabel.Text = "FPS: " .. tostring(math.floor(TimeFunction() - Start >= 1 and #FrameUpdateTable or #FrameUpdateTable / (TimeFunction() - Start)))
end
Start = TimeFunction()
RunService.Heartbeat:Connect(HeartbeatUpdate)
Yes it’s optimized if you run it only once and use the reading of the FPS for all your FPS needs. I use the FPS value FPS timing value for things that require being done once per frame and it is completely seamless, no issues with the calculations on my end.
At some point i had to go through and make sure all my scripts were not calculating 1/FPS in exchange for the FPS timing object which is much more important.
FPSTiming.Value=1/FPS.Value--equivalent to .0333 at 30 FPS and .01666 at 60fps
The code was more of a demonstration on how to go about it of course you can use whatever you’d like. It’s really not that hard and can be a fun project to write them yourself. Generally VFX requires that you have a library of configured assets in my example it goes through the children of an attachment and does tweening of sound, particle emitter size, light and tweening of the CFrame. That’s pretty much all it takes for VFX. Another popular library for VFX is [UPDATE 0.1.0] Lumina - A custom particle system - Resources / Community Resources - Developer Forum | Roblox
Yea was thinking about using lumina but wcs isn’t that compatible with it unfortunately