Attaching viewmodel to camera with :BindToRenderStep() not working

I need to fix the issue with attaching viewmodel to camera. So, i use module to do that, but i think it’s kinda… bad? Because idk why it isn’t working.

I also found that printing self.Camera gives nil.

MODULE:

local module = {}
module.__index = module

local replicatedStorage = game:GetService("ReplicatedStorage")
local runService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")

local Framework = replicatedStorage.Framework
local frameworkWeapons = Framework.Weapons

local frameworkInventory = require(frameworkWeapons.Inventory)

function module.InitPlayer(Player:Player, Camera:Camera)
	local self = setmetatable({}, module)
	self.Camera = Camera
	self.Player = Player
	return self
end

function module:CleanWeapon(viewmodelContainer:any, deleteFromInventory:boolean, nextSlot:number)
	for _, Weapon:Model in pairs(viewmodelContainer:GetChildren()) do
		if Weapon and Weapon:IsA("Model") then
			Weapon:Destroy()
		end
		
		if deleteFromInventory == true then
			if table.find(frameworkInventory.Inventory, Weapon) then
				table.remove(frameworkInventory.Inventory, Weapon)
			end
		end
		
		if nextSlot then
			frameworkInventory.currentSlot = nextSlot
		else
			frameworkInventory.currentSlot = nil
		end
	end
end

function module:EquipWeapon(Viewmodel:Model, viewmodelContainer:any, weaponData:ModuleScript, Slot:number)
	module:CleanWeapon(viewmodelContainer, false, 1)
        print(self.Camera) -- nil
	if not table.find(frameworkInventory.Inventory, Viewmodel.Name) then
		table.insert(frameworkInventory.Inventory, Viewmodel.Name)
		frameworkInventory.currentSlot = Slot
	end
	
	local renderedViewmodel = Viewmodel:Clone()
	renderedViewmodel.Parent = viewmodelContainer
	
	local function Update()
		renderedViewmodel.PrimaryPart.CFrame = self.Camera.CFrame
	end
	
	if self.Camera then
		runService:BindToRenderStep("RenderingViewmodel", Enum.RenderPriority.Last.Value, Update)
	end
end

return module

SCRIPT:

--[SERVICES]--
local runService = game:GetService("RunService")
local tweenService = game:GetService("TweenService")
local userInputService = game:GetService("UserInputService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")

--[FRAMEWORK FOLDERS]--
local frameworkFolder = replicatedStorage.Framework
local frameworkModules = frameworkFolder.Modules
local frameworkWeapons = frameworkFolder.Weapons

--[MODULES]--
local initEffects = require(frameworkModules.InitEffects)
local weaponManipulator = require(frameworkModules.WeaponManipulations)
local Inventory = require(frameworkWeapons.Inventory)
local frameworkSettings = require(frameworkFolder.FrameworkSettings)

--[PLAYER]--
local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()

local Camera = Workspace.CurrentCamera
local HUD = Player.PlayerGui:WaitForChild("HUD")

--[SETUP]--
weaponManipulator.InitPlayer(Player, Camera)
initEffects.Init(HUD, HUD.Crosshair, HUD.Hitmarker)

--[MAIN]--

weaponManipulator:EquipWeapon(frameworkWeapons.Viewmodels.USPCompact, Camera, frameworkWeapons.WeaponData.Data, 1) --here!
3 Likes

Your script flies way over my head (being an amateur myself), but when I tried to make a gun engine with a proper viewmodel (I usually just allow arms to render for the client and use worldmodels), I used constraints to align the VM to the character and used LocalTransparencyModifier properties to make it visible only on the client.

My engine was terrible, but I think I did at least that much right - here’s a video of that engine that shows the M6Ds constraining the VM with the character (I missed the part with another M6D parented to the character’s head that constrained ‘CentralP’ to it)

Probably not what you’re looking for, but it’s been 17 hours since you posted it and I feel like a bad reply is better than no reply. Best of luck!

1 Like

Oh, that’s very cool, but my Framework system not using tools. Also, the module that attaches viewmodel to player is called from local script, so the viewmodel should be local i think

1 Like

I did some quick reading and while I’m definitely not an expert, I’m pretty sure self is only usable in methods called with :, which might explain why self.Camera is returning nil. I see variables defined using self while defining method module.InitPlayer. I don’t know if this will work, but try replacing the . with a :.

(Possibly) Fixed ModuleScript:

local module = {}
module.__index = module

local replicatedStorage = game:GetService("ReplicatedStorage")
local runService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")

local Framework = replicatedStorage.Framework
local frameworkWeapons = Framework.Weapons

local frameworkInventory = require(frameworkWeapons.Inventory)

function module:InitPlayer(Player:Player, Camera:Camera)
	local self = setmetatable({}, module)
	self.Camera = Camera
	self.Player = Player
	return self
end

function module:CleanWeapon(viewmodelContainer:any, deleteFromInventory:boolean, nextSlot:number)
	for _, Weapon:Model in pairs(viewmodelContainer:GetChildren()) do
		if Weapon and Weapon:IsA("Model") then
			Weapon:Destroy()
		end
		
		if deleteFromInventory == true then
			if table.find(frameworkInventory.Inventory, Weapon) then
				table.remove(frameworkInventory.Inventory, Weapon)
			end
		end
		
		if nextSlot then
			frameworkInventory.currentSlot = nextSlot
		else
			frameworkInventory.currentSlot = nil
		end
	end
end

function module:EquipWeapon(Viewmodel:Model, viewmodelContainer:any, weaponData:ModuleScript, Slot:number)
	module:CleanWeapon(viewmodelContainer, false, 1)
        print(self.Camera) -- nil
	if not table.find(frameworkInventory.Inventory, Viewmodel.Name) then
		table.insert(frameworkInventory.Inventory, Viewmodel.Name)
		frameworkInventory.currentSlot = Slot
	end
	
	local renderedViewmodel = Viewmodel:Clone()
	renderedViewmodel.Parent = viewmodelContainer
	
	local function Update()
		renderedViewmodel.PrimaryPart.CFrame = self.Camera.CFrame
	end
	
	if self.Camera then
		runService:BindToRenderStep("RenderingViewmodel", Enum.RenderPriority.Last.Value, Update)
	end
end

return module

Hope this helps!

Thanks for helping me, but initializing self variable can be CALLED only from method, not from constructor, in constructor you initilize the self (sorry for bad english)

1 Like