Updates:
0.1 - 28.12.2020
- Examine Service was created.
0.2 - 29.12.2020
- Connections is now a normal table. Probably best because it wasn’t a metatable before.
- All CoreGUI’s are set back to enabled after an object has been examined/inspected.
- Depth of Field and the Keys GUI Is now made in the module itself.
Download:
Code:
--[[
DevForum:
> https://devforum.roblox.com/t/examine-service-inspect-models/948771
Documentation:
> This is used to show an object up-close with 3D interaction using the mouse.
Functions:
> ExamineService.new(model, speed, angleX, angleY)
> ExamineService:Render(model)
Example:
> local ExamineService = require(DirectoryToTheModule)
> local AK47 = WorkspaceService:WaitForChild("AK47")
> local Object = ExamineService.new(AK47)
> ExamineService:Render(Object)
--]]
-- CLASS
local ExamineService = {}
ExamineService.__index = ExamineService
-- SERVICES
local Players = game:GetService("Players")
local WorkspaceService = game:GetService("Workspace")
local StarterGui = game:GetService("StarterGui")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local Lighting = game:GetService("Lighting")
-- VARIABLES
local Player = Players.LocalPlayer
local PlayerGui = Player.PlayerGui
local Mouse = Player:GetMouse()
local Camera = WorkspaceService.Camera
local CurrentCamera = WorkspaceService.CurrentCamera
-- TABLES
local Connections = {}
-- METHODS
function ExamineService.new(model, ratio, x, y)
if Connections.IsExamining then
return warn("[Examine Service] - Examining an object already!")
end
if not model then
return warn("[Examine Service] - Model must be specified!")
end
if not (typeof(model) == "Instance") then
return warn("[Examine Service] - Model must be an Instance!")
end
if not model.PrimaryPart then
return warn("[Examine Service] - Model must have a set PrimaryPart!")
end
-- METATABLE:
local Object = {}
setmetatable(ExamineService, Object)
Object.Model = model:Clone()
Object.Ratio = ratio or 0.0325
Object.X = x or math.rad(90)
Object.Y = y or math.rad(0)
Object.DepthOfField = nil
Object.Keys = nil
return Object
end
function ExamineService:NewDepthOfField()
local DepthOfField = Instance.new("DepthOfFieldEffect")
DepthOfField.Name = "Effect"
DepthOfField.FarIntensity = 1
DepthOfField.FocusDistance = 0
DepthOfField.InFocusRadius = 8
DepthOfField.NearIntensity = 1
DepthOfField.Parent = Lighting
DepthOfField.Enabled = true
return DepthOfField
end
function ExamineService:NewKeys()
local ScreenGui = Instance.new("ScreenGui")
ScreenGui.Name = "Keys"
ScreenGui.IgnoreGuiInset = true
ScreenGui.ResetOnSpawn = false
ScreenGui.ZIndexBehavior = Enum.ZIndexBehavior.Global
-- MAIN FRAME:
local Frame = Instance.new("Frame")
Frame.Name = "Main"
Frame.AnchorPoint = Vector2.new(1, 1)
Frame.BackgroundTransparency = 1
Frame.Position = UDim2.new(1, 0, 1, 0)
Frame.Size = UDim2.new(0.035, 0, 0.15, 0)
Frame.ZIndex = 100
-- ASPECT RATIO FOR FRAME:
local UIAspectRatio = Instance.new("UIAspectRatioConstraint")
UIAspectRatio.AspectRatio = 0.5
UIAspectRatio.Parent = Frame
-- MOUSE BUTTON ONE:
local MouseButton1 = Instance.new("ImageButton")
MouseButton1.Name = "MouseButton1"
MouseButton1.AnchorPoint = Vector2.new(0.5, 0.5)
MouseButton1.BackgroundTransparency = 1
MouseButton1.Position = UDim2.new(0.5, 0, 0.275, 0)
MouseButton1.Size = UDim2.new(0.8, 0, 0.8, 0)
MouseButton1.ZIndex = 101
MouseButton1.Image = "http://www.roblox.com/asset/?id=4905957737"
MouseButton1.ScaleType = Enum.ScaleType.Slice
MouseButton1.ImageRectOffset = Vector2.new(200, 600)
MouseButton1.ImageRectSize = Vector2.new(100, 100)
MouseButton1.Parent = Frame
-- UI ASPECT RATIO FOR ICONS:
local UIAspectRatioIcons = Instance.new("UIAspectRatioConstraint")
UIAspectRatioIcons.AspectRatio = 1
UIAspectRatioIcons.Parent = MouseButton1
-- MOUSE BUTTON TWO:
local MouseButton2 = MouseButton1:Clone()
MouseButton2.Name = "MouseButton2"
MouseButton2.Position = UDim2.new(0.5, 0, 0.725, 0)
MouseButton2.ImageRectOffset = Vector2.new(400, 600)
MouseButton2.Parent = Frame
-- TEXT FOR ICONS:
local Title = Instance.new("TextLabel")
Title.Name = "Title"
Title.BackgroundTransparency = 1
Title.AnchorPoint = Vector2.new(1, 0.5)
Title.Position = UDim2.new(0, 0, 0.5, 0)
Title.Size = UDim2.new(1.7, 0, 0.5, 0)
Title.ZIndex = 102
Title.Font = Enum.Font.GothamBold
Title.TextColor3 = Color3.fromRGB(255, 255, 255)
Title.TextXAlignment = Enum.TextXAlignment.Right
Title.TextScaled = true
Title.Text = "Move"
Title.Parent = MouseButton1
local Title2 = Title:Clone()
Title2.Text = "Cancel"
Title2.Parent = MouseButton2
-- SHOW ON CLIENT:
Frame.Parent = ScreenGui
ScreenGui.Parent = PlayerGui
return ScreenGui
end
function ExamineService:Render(model)
if Connections.IsExamining or not model then
return
end
Connections.IsExamining = true
-- HIDE CORE:
StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.All, false)
-- OBJECT:
self.Model = model.Model
ExamineService.SetCollision(self.Model)
-- EXTRA EFFECTS:
self.DepthOfField = self:NewDepthOfField()
self.Keys = self:NewKeys()
-- VALUES:
local Ratio = model.Ratio
local X, Y = model.X, model.Y
local diffX, diffY = 0, 0
-- CAMERA SETUP:
ExamineService.SetIdle(true)
-- CAMERA RENDER:
self.Model.Parent = WorkspaceService
Connections.Render = RunService.RenderStepped:Connect(function()
local Pos = CurrentCamera.CFrame * CFrame.new(0, 0, -5)
self.Model:SetPrimaryPartCFrame(Pos * CFrame.Angles(0, X, Y))
end)
-- MOUSE MOVEMENT:
local lastX, lastY = 0, 0
Connections.Input = UserInputService.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
Connections.MouseMovement = Mouse.Move:Connect(function()
diffX = (Mouse.X - lastX)
diffY = (Mouse.Y - lastY)
X = ExamineService.GetDirectionBasedOnDifference(diffX, X, Ratio)
Y = ExamineService.GetDirectionBasedOnDifference(diffY, Y, Ratio)
lastX = Mouse.X
lastY = Mouse.Y
end)
elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
ExamineService:Disconnect()
end
end)
-- PAUSE MOVEMENT:
Connections.Pause = UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
if Connections.MouseMovement then
if Connections.MouseMovement.Connected then
Connections.MouseMovement:Disconnect()
end
end
end
end)
end
function ExamineService:Disconnect()
ExamineService.SetIdle(false)
-- DISCONNECT EVENTS:
if Connections.Render then
if Connections.Render.Connected then
Connections.Render:Disconnect()
end
end
if Connections.Input then
if Connections.Input.Connected then
Connections.Input:Disconnect()
end
end
if Connections.Pause then
if Connections.Pause.Connected then
Connections.Pause:Disconnect()
end
end
if Connections.MouseMovement then
if Connections.MouseMovement.Connected then
Connections.MouseMovement:Disconnect()
end
end
if self.DepthOfField then
self.DepthOfField:Destroy()
end
if self.Keys then
self.Keys:Destroy()
end
if self.Model then
self.Model:Destroy()
end
-- CLEAR METATABLE:
setmetatable(ExamineService, nil)
-- SHOW CORE:
StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.All, true)
-- ENABLE EXAMINATION:
Connections.IsExamining = false
end
-- FUNCTIONS
function ExamineService.SetCollision(model)
for _, obj in pairs(model:GetDescendants()) do
if obj:IsA("BasePart") then
obj.CanCollide = false
obj.CastShadow = false
end
end
end
function ExamineService.GetDirectionBasedOnDifference(Diff, Axis, Ratio)
if Diff < 0 then
Axis = Axis - 1 * Ratio
elseif Diff > 0 then
Axis = Axis - -1 * Ratio
end
return Axis
end
function ExamineService.SetIdle(state)
if state then
Player.CameraMinZoomDistance = 32
Player.CameraMaxZoomDistance = 32
else
Player.CameraMinZoomDistance = 0.5
Player.CameraMaxZoomDistance = 64
end
end
return ExamineService
Preview:
https://gyazo.com/555333ce0a8196eb9193fb94f8f50fa7
What is this?
This is a module that will allow you to (Examine / Inspect / Look) at an object in front of your screen with control from your mouse.
Why did you make this?
I haven’t seen anything open-sourced like this, and I did this to get a better understanding of OOP and Metatables. This might need some tweaks & updates considering I’ve never used metatables, nor dealt with OOP.
How do I use this?
First of all, the object you want to use must have a set PrimaryPart. There are two primary functions for this.
local Object = ExamineService.new(model, speed, x, y) -- Creates the object with custom settings.
ExamineService:Render(Object) -- Shows it visually.
It’s best to keep the speed (ratio) at around 0.0325 for easy control.
This must only be used on the Client.
When will you update this?
I’ll try to update it in my spare time or if I get a suggestion on what could be improved, considering I have time. It would be interesting to add a feature where you could move an object with acceleration and to utilize TweenService. That will probably be the main focus if I update this.
Do I have to give credit?
Short answer, no. This was made for fun. I wouldn’t say no to be credited, but as said. It is not required.
Poll:
- Yes
- Maybe
- No
0 voters
Notice:
Please let me know if something is broken and a fix should be expected asap.