Devvit Community Weapons System
Part 1: Viewmodel.lua
This module is part of an upcoming weapons framework that will be free for everybody to use.
What this does
This handles viewmodels, their animations, and frame update. Essentially, it:
- Pre-loads the viewmodels and animation events before any viewmodel is made
- Handles viewmodels as a class, not just as a model
- Contains methods to Equip, Unequip, Update, Play/Stop animation
- Culls models, not spawns and destroys them
Why you should use it
There are some reasons this module should be considered. These are pointed out below.
Viewmodel culling
This does not call :Destroy()
on viewmodels to remove them, nor does it call :Clone()
. When a viewmodel is not being used, it instead teleports to the position 0, 10e5, 0
, and when it is used, it stays on the screen. This is more optimized as you are not constructing and deconstructing instances.
Flexibility
This is an object-oriented module.
As a class, you can preload the viewmodels and events through Module.Load()
, as well as contruct a new viewmodel with Module.New()
.
You can use this however you would like, and modify each method to your liking as well.
Optimization
This is an extremely lightweight module. Using this will be superior to what most developers use for viewmodel handling.
How to use it
You need to do the following in order:
- Require the module in the appropriate script
- Call the
Load
class method, inputting the Viewmodel folder and the events binded to the animations - For every viewmodel, call
module.New()
ONE TIME AND ONE TIME ONLY
Everything else is up to you.
Source code
local Viewmodel = {}
Viewmodel.__index = Viewmodel
local ViewmodelFolder, EventBinds
local CullCFrame = CFrame.new(0,10e5,0)
local Camera = workspace.CurrentCamera
local SearchBinds = {
Animation = {"Animations", function(self, Child)
return self.Animator:LoadAnimation(Child)
end};
Sound = {"Sounds", function(self, Child)
return Child
end};
}
function Viewmodel.New(ViewmodelName)
local self = {}
self.Name = ViewmodelName
self.Equipped = false
self.Model = ViewmodelFolder:FindFirstChild(ViewmodelName)
self.Animator = self.Model:FindFirstChildOfClass("AnimationController"):WaitForChild("Animator")
for _, Child in pairs(self.Model:GetChildren()) do
local MappedDataSet = SearchBinds[Child.ClassName]
if MappedDataSet then
if not self[MappedDataSet[1]] then
self[MappedDataSet[1]] = {}
end
self[MappedDataSet[1]][Child.Name] = MappedDataSet[2](self, Child)
end
end
for _, Animation in pairs(self.Animations) do
for BindName, BindFunction in pairs(EventBinds) do
Animation:GetMarkerReachedSignal(BindName):Connect(BindFunction)
end
end
return setmetatable(self, Viewmodel)
end
function Viewmodel.Load(_ViewmodelFolder, _EventBinds)
ViewmodelFolder = _ViewmodelFolder
EventBinds = _EventBinds or {}
ViewmodelFolder.Parent = workspace
for _, Model in pairs(ViewmodelFolder:GetChildren()) do
Model:SetPrimaryPartCFrame(CullCFrame)
end
end
----------------------------------------------------------------------
----------------------------------------------------------------------
----------------------------------------------------------------------
function Viewmodel:Equip()
if not self.Equipped then
self.Equipped = true
self.Animations.Idle:Play()
self.Animations.Equip:Play()
self:Update()
end
end
function Viewmodel:Unequip()
if self.Equipped then
self.Equipped = false
for _, Animation in pairs(self.Animations) do
Animation:Stop()
end
self.Model:SetPrimaryPartCFrame(CullCFrame)
end
end
function Viewmodel:Update() -- Change this to your liking!
if self.Equipped then
local CameraCF = Camera.CFrame
self.Model:SetPrimaryPartCFrame(CameraCF)
end
end
function Viewmodel:PlayAnimation(Name)
self.Animations[Name]:Play()
end
function Viewmodel:StopAnimation(Name)
self.Animations[Name]:Stop()
end
return Viewmodel
Footer
This is likely to update due to feedback and internal needs. This is not a final module.
Any feedback is listened to and considered.
Thanks so much for reading!