Just want to say, this is a great system, I love the way you made it and I believe it’s useful for many up-and-coming games! However, there are some things to fix or add that could make it better.
For a start, it would be nice to make it so if the Hologram is open and the player switches sides with it open, for the Hologram to also move to the side the player is on. As of now; If the player opens the Hologram and then goes to a different side of the part, the Hologram does not follow the new side.
Also, a bug that can stop the Hologram from showing is: If the player enters the range of which a Hologram is shown, if the player exits the range of the Hologram and then reenters the range before tweening has finished, the new Hologram will disappear, this is due to your FinalTween
variable in OnPromptHidden
.
Now, I’ve taken it upon myself to make a quick fix to your system for the bug that causes a Hologram to not show, however the side switching was not done by me and is still your choice if you wish to add or not add it.
Here are the steps to add the fix:
- In the
InitialiseGlobally
function, update this line so that Hologram.New
isn’t being stored unnecessarily in the Holograms
table, this is mostly for optimization, since the rest of the changes don’t actually use the Holograms table.
Hologram.New(Prompt, Vector3.zero, CheckTag, ShowBeam)
- Inside the
.New
function, add a new table to keep track of cloned prompts. This will make managing multiple prompts easier, cause, well we like easy things.
self.ClonedPrompts = {}
- Replace the
CreatePrompt
function to properly handle cloned prompts, this is a hefty change.
function Hologram:CreatePrompt()
local CloneUUID = os.clock() + math.random()
local ClonedPrompt = PromptTemplate:Clone()
local KeyCodePart = ClonedPrompt.KeyCode
local KeyCodeUI = KeyCodePart.HologramKeyCodeUI
local KeyCodeBackground = KeyCodeUI.Background
local KeyCodeText = KeyCodeBackground.KeyCode
local KeyCodeImage = KeyCodeBackground.KeyCodeImage
local KeyCodeProgress = KeyCodeBackground.Progress
local InstructionPart = ClonedPrompt.Instruction
local InstructionUI = InstructionPart.HologramInstructionUI
local InstructionBackground = InstructionUI.Background
local ActionUIText = InstructionBackground.Action
local ObjectUIText = InstructionBackground.Object
local InvisibleButton = InstructionBackground.InvisiButton
local DesignPart = ClonedPrompt.Design
local DesignUI = DesignPart.HologramDesignUI
local DesignImage = DesignUI.Image
KeyCodeUI.Name = "Hologram" .. self.PromptName .. CloneUUID .. "KeyCodeUI"
InstructionUI.Name = "Hologram" .. self.PromptName .. CloneUUID .. "InstructionUI"
DesignUI.Name = "Hologram" .. self.PromptName .. CloneUUID .. "DesignUI"
local Beam = KeyCodePart.Beam
local BeamAttachment = KeyCodePart.Attachment
local TransparencyValue = Beam:WaitForChild("TransparencyValue")
ClonedPrompt.Name = "Hologram" .. self.PromptName .. CloneUUID
ClonedPrompt.Parent = self.PromptParent
KeyCodeUI.Parent = PlayerUI
InstructionUI.Parent = PlayerUI
DesignUI.Parent = PlayerUI
local BeamConnect = TransparencyValue:GetPropertyChangedSignal("Value"):Connect(function()
Beam.Transparency = NumberSequence.new(TransparencyValue.Value)
end)
local CloneData = {
ClonedPrompt = ClonedPrompt,
KeyCodePart = KeyCodePart,
KeyCodeUI = KeyCodeUI,
KeyCodeBackground = KeyCodeBackground,
KeyCodeText = KeyCodeText,
KeyCodeImage = KeyCodeImage,
KeyCodeProgress = KeyCodeProgress,
InstructionPart = InstructionPart,
InstructionUI = InstructionUI,
InstructionBackground = InstructionBackground,
ActionUIText = ActionUIText,
ObjectUIText = ObjectUIText,
InvisibleButton = InvisibleButton,
DesignPart = DesignPart,
DesignUI = DesignUI,
DesignImage = DesignImage,
Beam = Beam,
BeamAttachment = BeamAttachment,
TransparencyValue = TransparencyValue,
BeamConnect = BeamConnect,
UUID = CloneUUID,
}
table.insert(self.ClonedPrompts, CloneData)
return CloneData
end
- Replace the
DestroyPrompt
function to actually use the CloneData
function Hologram:DestroyPrompt(CloneData)
if not CloneData then return end
for _, PromptUI in pairs(PlayerUI:GetChildren()) do
if string.find(PromptUI.Name, "Hologram" .. self.PromptName .. CloneData.UUID) then
PromptUI:Destroy()
end
end
if CloneData.HoldEndedConnection then
CloneData.HoldEndedConnection:Disconnect()
end
CloneData.BeamConnect:Disconnect()
if CloneData.InvisiButtonConnect then
CloneData.InvisiButtonConnect:Disconnect()
end
if CloneData.BillboardUI then
CloneData.BillboardUI:Disconnect()
end
CloneData.ClonedPrompt:Destroy()
for index, data in ipairs(self.ClonedPrompts) do
if data == CloneData then
table.remove(self.ClonedPrompts, index)
break
end
end
end
- Replace the
OnPromptShown
function to manage multiple clones
function Hologram:OnPromptShown(InputType: Enum.ProximityPromptInputType)
local CloneData = self:CreatePrompt()
if not CloneData.ClonedPrompt then return end
local ClosestFace = GetClosestFace(self.PromptParent)
local FacePosition, FaceNormal
if self.BillboardActive == true then
FacePosition = self.PromptParent.CFrame:PointToWorldSpace(self.StudsOffset)
FaceNormal = (Camera.CFrame.Position - FacePosition).Unit
else
FaceNormal = GetFaceNormals(self.PromptParent)[ClosestFace]
FacePosition = self.PromptParent.Position + (FaceNormal * (self.PromptParent.Size / 2)) + (FaceNormal * 1)
end
CloneData.KeyCodeText.TextTransparency = 1
CloneData.ActionUIText.TextTransparency = 1
CloneData.ObjectUIText.TextTransparency = 1
CloneData.InstructionBackground.Transparency = 1
CloneData.KeyCodeBackground.Transparency = 1
CloneData.DesignImage.ImageTransparency = 1
CloneData.KeyCodeImage.ImageTransparency = 1
CloneData.DesignImage.ImageColor3 = self.Colours.Primary
CloneData.KeyCodeProgress.BackgroundColor3 = self.Colours.Primary
CloneData.InstructionBackground.BackgroundColor3 = self.Colours.Secondary
CloneData.KeyCodeBackground.BackgroundColor3 = self.Colours.Secondary
CloneData.KeyCodeText.TextColor3 = self.Colours.Tertiary
CloneData.KeyCodeImage.ImageColor3 = self.Colours.Tertiary
CloneData.ActionUIText.TextColor3 = self.Colours.Tertiary
CloneData.ObjectUIText.TextColor3 = self.Colours.Tertiary
if self.BillboardActive == true then
CloneData.BillboardUI = RunService.RenderStepped:Connect(function()
if not CloneData.ClonedPrompt or not CloneData.ClonedPrompt.PrimaryPart then return end
local PartCFrame = self.PromptParent.CFrame
local WorldOffset = PartCFrame:PointToWorldSpace(self.StudsOffset)
local CameraPosition = Camera.CFrame.Position
CloneData.ClonedPrompt:PivotTo(CFrame.lookAt(WorldOffset, CameraPosition, Vector3.new(0, 1, 0)))
CloneData.DesignPart.CFrame = CloneData.KeyCodePart.CFrame + (CloneData.KeyCodePart.CFrame.LookVector * 0.05)
CloneData.InstructionPart.CFrame = CloneData.KeyCodePart.CFrame + (CloneData.KeyCodePart.CFrame.LookVector * -0.05) + (CloneData.KeyCodePart.CFrame.RightVector * -2.845)
end)
else
CloneData.ClonedPrompt:PivotTo(CFrame.new(FacePosition, FacePosition + FaceNormal))
CloneData.ClonedPrompt:PivotTo(CloneData.ClonedPrompt.PrimaryPart.CFrame + (CloneData.ClonedPrompt.PrimaryPart.CFrame.LookVector * (self.StudsOffset.Z)) + (CloneData.ClonedPrompt.PrimaryPart.CFrame.RightVector * (self.StudsOffset.X)) + (CloneData.ClonedPrompt.PrimaryPart.CFrame.UpVector * (self.StudsOffset.Y)))
CloneData.DesignPart.CFrame = CloneData.KeyCodePart.CFrame + (CloneData.KeyCodePart.CFrame.LookVector * 1)
CloneData.InstructionPart.CFrame = CloneData.KeyCodePart.CFrame + (CloneData.KeyCodePart.CFrame.LookVector * -1) + (CloneData.KeyCodePart.CFrame.RightVector * -2.845)
end
if self.ShowBeam == true then
BorderHighlight.Adornee = self.PromptParent
CloneData.Beam.Attachment1 = Character:WaitForChild("UpperTorso", 10).BodyFrontAttachment or Character:WaitForChild("Torso", 10).BodyFrontAttachment
CloneData.BeamAttachment.Parent = self.PromptParent
Tween(CloneData.TransparencyValue, "Value", 0)
end
if InputType == Enum.ProximityPromptInputType.Gamepad then
if GamepadButtonImage[self.Prompt.GamepadKeyCode] then
CloneData.KeyCodeImage.Image = GamepadButtonImage[self.Prompt.GamepadKeyCode]
end
elseif InputType == Enum.ProximityPromptInputType.Touch then
CloneData.KeyCodeImage.Image = "rbxasset://textures/ui/Controls/TouchTapIcon.png"
else
local ButtonTextString = UserInputService:GetStringForKeyCode(self.Prompt.KeyboardKeyCode)
local ButtonTextImage = KeyboardButtonImage[self.Prompt.KeyboardKeyCode]
if ButtonTextImage == nil then
ButtonTextImage = KeyboardButtonIconMapping[ButtonTextString]
end
if ButtonTextImage == nil then
local KeyCodeMappedText = KeyCodeToTextMapping[self.Prompt.KeyboardKeyCode]
if KeyCodeMappedText then
ButtonTextString = KeyCodeMappedText
end
end
if ButtonTextImage then
CloneData.KeyCodeImage.Image = ButtonTextImage
elseif ButtonTextString ~= nil and ButtonTextString ~= "" then
CloneData.KeyCodeText.Text = ButtonTextString
else
error(
"ProximityPrompt '"
.. self.Prompt.Name
.. "' has an unsupported keycode for rendering UI: "
.. tostring(self.Prompt.KeyboardKeyCode)
)
end
end
CloneData.ActionUIText.Text = self.Prompt.ActionText
CloneData.ObjectUIText.Text = self.Prompt.ObjectText
CloneData.InvisiButtonConnect = CloneData.InvisibleButton.InputBegan:Connect(function(Input: InputObject)
if Input.UserInputType == Enum.UserInputType.Touch or Input.UserInputType == Enum.UserInputType.MouseButton1 then
self.Prompt:InputHoldBegin()
end
end)
CloneData.ImageHoldEndedConnection = CloneData.InvisibleButton.InputEnded:Connect(function(Input: InputObject)
if Input.UserInputType == Enum.UserInputType.Touch or Input.UserInputType == Enum.UserInputType.MouseButton1 then
self.Prompt:InputHoldEnd()
end
end)
if self.BillboardActive == false then
Tween(CloneData.DesignPart, "CFrame", CloneData.DesignPart.CFrame * CFrame.new(0, 0, 0.95))
Tween(CloneData.InstructionPart, "CFrame", CloneData.InstructionPart.CFrame * CFrame.new(0, 0, -0.95))
end
Tween(CloneData.DesignImage, "ImageTransparency", 0)
Tween(CloneData.KeyCodeImage, "ImageTransparency", 0)
Tween(CloneData.ActionUIText, "TextTransparency", 0)
Tween(CloneData.ObjectUIText, "TextTransparency", 0)
Tween(CloneData.KeyCodeText, "TextTransparency", 0)
Tween(CloneData.InstructionBackground, "Transparency", 0.8)
local FinalTween = Tween(CloneData.KeyCodeBackground, "Transparency", 0)
FinalTween.Completed:Wait()
end
- Replace
OnPromptHidden
to go through all cloned prompts and handle them properly
function Hologram:OnPromptHidden(InputType: Enum.ProximityPromptInputType)
for _, CloneData in ipairs(self.ClonedPrompts) do
if CloneData.ClonedPrompt then
if self.ShowBeam then
BorderHighlight.Adornee = nil
Tween(CloneData.TransparencyValue, "Value", 1)
CloneData.Beam.Attachment1 = nil
CloneData.BeamAttachment.Parent = CloneData.KeyCodePart
end
Tween(CloneData.DesignPart, "CFrame", CloneData.DesignPart.CFrame * CFrame.new(0, 0, -0.95))
Tween(CloneData.InstructionPart, "CFrame", CloneData.InstructionPart.CFrame * CFrame.new(0, 0, 0.95))
Tween(CloneData.DesignImage, "ImageTransparency", 1)
Tween(CloneData.KeyCodeImage, "ImageTransparency", 1)
Tween(CloneData.ActionUIText, "TextTransparency", 1)
Tween(CloneData.ObjectUIText, "TextTransparency", 1)
Tween(CloneData.KeyCodeText, "TextTransparency", 1)
Tween(CloneData.InstructionBackground, "Transparency", 1)
local FinalTween = Tween(CloneData.KeyCodeBackground, "Transparency", 1)
FinalTween.Completed:Wait()
self:DestroyPrompt(CloneData)
end
end
end
- Update
PromptHolding
to maek sure it works with multiple cloned prompts instead of just the original one.
function Hologram:PromptHolding()
for _, CloneData in ipairs(self.ClonedPrompts) do
local Fill = TweenService:Create(CloneData.KeyCodeProgress, TweenInfo.new(self.HoldDuration, Enum.EasingStyle.Sine), {Size = UDim2.fromScale(1, 1)})
Fill:Play()
local Size = TweenService:Create(CloneData.DesignImage, TweenInfo.new(self.HoldDuration, Enum.EasingStyle.Sine), {Size = UDim2.fromScale(0.9, 0.9)})
Size:Play()
local function PromptHoldEnded()
Fill:Cancel()
Size:Cancel()
Tween(CloneData.KeyCodeProgress, "Size", UDim2.fromScale(1, 0))
Tween(CloneData.DesignImage, "Size", UDim2.fromScale(1, 1))
if CloneData.HoldEndedConnection then
CloneData.HoldEndedConnection:Disconnect()
end
end
CloneData.HoldEndedConnection = self.Prompt.PromptButtonHoldEnded:Connect(PromptHoldEnded)
end
end
- Finally, make sure
DestroyPrompt
fully cleans up everything related to cloned prompts, including UI elements and any lingering connections.
function Hologram:DestroyPrompt(CloneData)
if not CloneData then return end
for _, PromptUI in pairs(PlayerUI:GetChildren()) do
if string.find(PromptUI.Name, "Hologram" .. self.PromptName .. CloneData.UUID) then
PromptUI:Destroy()
end
end
if CloneData.HoldEndedConnection then
CloneData.HoldEndedConnection:Disconnect()
end
CloneData.BeamConnect:Disconnect()
if CloneData.InvisiButtonConnect then
CloneData.InvisiButtonConnect:Disconnect()
end
if CloneData.BillboardUI then
CloneData.BillboardUI:Disconnect()
end
CloneData.ClonedPrompt:Destroy()
for index, data in ipairs(self.ClonedPrompts) do
if data == CloneData then
table.remove(self.ClonedPrompts, index)
break
end
end
end
So, if all was follows correctly, you should end up with this new module:
-- Written by Lightning_Game27
--// Services
local ProximityPromptService = game:GetService("ProximityPromptService")
local TweenService = game:GetService("TweenService")
local PlayerService = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
--// Values
local Camera = workspace.CurrentCamera
local BorderHighlight = script:WaitForChild("Highlight")
local PromptTemplate = script:WaitForChild("PromptTemplate")
--// Prompt Images
local GamepadButtonImage = {
[Enum.KeyCode.ButtonX] = "rbxasset://textures/ui/Controls/xboxX.png",
[Enum.KeyCode.ButtonY] = "rbxasset://textures/ui/Controls/xboxY.png",
[Enum.KeyCode.ButtonA] = "rbxasset://textures/ui/Controls/xboxA.png",
[Enum.KeyCode.ButtonB] = "rbxasset://textures/ui/Controls/xboxB.png",
[Enum.KeyCode.DPadLeft] = "rbxasset://textures/ui/Controls/dpadLeft.png",
[Enum.KeyCode.DPadRight] = "rbxasset://textures/ui/Controls/dpadRight.png",
[Enum.KeyCode.DPadUp] = "rbxasset://textures/ui/Controls/dpadUp.png",
[Enum.KeyCode.DPadDown] = "rbxasset://textures/ui/Controls/dpadDown.png",
[Enum.KeyCode.ButtonSelect] = "rbxasset://textures/ui/Controls/xboxView.png",
[Enum.KeyCode.ButtonStart] = "rbxasset://textures/ui/Controls/xboxmenu.png",
[Enum.KeyCode.ButtonL1] = "rbxasset://textures/ui/Controls/xboxLB.png",
[Enum.KeyCode.ButtonR1] = "rbxasset://textures/ui/Controls/xboxRB.png",
[Enum.KeyCode.ButtonL2] = "rbxasset://textures/ui/Controls/xboxLT.png",
[Enum.KeyCode.ButtonR2] = "rbxasset://textures/ui/Controls/xboxRT.png",
[Enum.KeyCode.ButtonL3] = "rbxasset://textures/ui/Controls/xboxLS.png",
[Enum.KeyCode.ButtonR3] = "rbxasset://textures/ui/Controls/xboxRS.png",
[Enum.KeyCode.Thumbstick1] = "rbxasset://textures/ui/Controls/xboxLSDirectional.png",
[Enum.KeyCode.Thumbstick2] = "rbxasset://textures/ui/Controls/xboxRSDirectional.png",
}
local KeyboardButtonImage = {
[Enum.KeyCode.Backspace] = "rbxasset://textures/ui/Controls/backspace.png",
[Enum.KeyCode.Return] = "rbxasset://textures/ui/Controls/return.png",
[Enum.KeyCode.LeftShift] = "rbxasset://textures/ui/Controls/shift.png",
[Enum.KeyCode.RightShift] = "rbxasset://textures/ui/Controls/shift.png",
[Enum.KeyCode.Tab] = "rbxasset://textures/ui/Controls/tab.png",
}
local KeyboardButtonIconMapping = {
["'"] = "rbxasset://textures/ui/Controls/apostrophe.png",
[","] = "rbxasset://textures/ui/Controls/comma.png",
["`"] = "rbxasset://textures/ui/Controls/graveaccent.png",
["."] = "rbxasset://textures/ui/Controls/period.png",
[" "] = "rbxasset://textures/ui/Controls/spacebar.png",
}
local KeyCodeToTextMapping = {
[Enum.KeyCode.LeftControl] = "Ctrl",
[Enum.KeyCode.RightControl] = "Ctrl",
[Enum.KeyCode.LeftAlt] = "Alt",
[Enum.KeyCode.RightAlt] = "Alt",
[Enum.KeyCode.F1] = "F1",
[Enum.KeyCode.F2] = "F2",
[Enum.KeyCode.F3] = "F3",
[Enum.KeyCode.F4] = "F4",
[Enum.KeyCode.F5] = "F5",
[Enum.KeyCode.F6] = "F6",
[Enum.KeyCode.F7] = "F7",
[Enum.KeyCode.F8] = "F8",
[Enum.KeyCode.F9] = "F9",
[Enum.KeyCode.F10] = "F10",
[Enum.KeyCode.F11] = "F11",
[Enum.KeyCode.F12] = "F12",
}
--// Player
local Player = PlayerService.LocalPlayer
local PlayerUI = Player:WaitForChild("PlayerGui")
local Character = Player.Character or Player.CharacterAdded:Wait()
--// Common Functions
local function GetFaceNormals(Part: BasePart)
local PartCF = Part.CFrame
return {
Back = -PartCF.LookVector,
Front = PartCF.LookVector,
Bottom = -PartCF.UpVector,
Top = PartCF.UpVector,
Left = PartCF.RightVector,
Right = -PartCF.RightVector
}
end
local function GetClosestFace(Part: BasePart)
local FaceNormals = GetFaceNormals(Part)
local ClosestFace = nil
local HighestDotProduct = -math.huge
local CameraPos = Camera.CFrame.Position
for Face, Normal in pairs(FaceNormals) do
local DirectionToPart = (CameraPos - Part.Position).Unit
local DotProduct = Normal:Dot(DirectionToPart)
if DotProduct > HighestDotProduct then
HighestDotProduct = DotProduct
ClosestFace = Face
end
end
return ClosestFace
end
local Hologram = {}
Hologram.__index = Hologram
Hologram.GlobalTweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Exponential)
Hologram.Holograms = {}
local function Tween(Object, Property: string, Value)
local NewTween = TweenService:Create(Object, Hologram.GlobalTweenInfo, {[Property] = Value})
NewTween:Play()
return NewTween
end
function Hologram.InitialiseGlobally(CheckTag: boolean?, ShowBeam: boolean?)
CheckTag = CheckTag or false
ShowBeam = ShowBeam or false
for _, Prompt in pairs(workspace:GetDescendants()) do
if Prompt:IsA("ProximityPrompt") then
Hologram.New(Prompt, Vector3.zero, CheckTag, ShowBeam)
end
end
end
function Hologram.New(Prompt: ProximityPrompt, StudsOffset: Vector3?, CheckTag: boolean?, ShowBeam: boolean?)
if CheckTag then
if not Prompt:HasTag("Hologram") then
error("ProximityPrompt does not have CollectionService Tag 'Hologram'.")
end
end
Prompt.Style = Enum.ProximityPromptStyle.Custom
local self = setmetatable({}, Hologram)
self.ActionText = Prompt.ActionText
self.ObjectText = Prompt.ObjectText
self.PromptName = Prompt.Name
self.KeyboardKeyCode = Prompt.KeyboardKeyCode
self.HoldDuration = Prompt.HoldDuration
self.Prompt = Prompt
self.ShowBeam = ShowBeam or false
self.StudsOffset = StudsOffset or Vector3.zero
self.Colours = {
Primary = Color3.fromRGB(0, 144, 255),
Secondary = Color3.fromRGB(0, 0, 0),
Tertiary = Color3.fromRGB(255, 255, 255)
}
self.UUID = os.clock() + math.random()
self.BillboardActive = false
if Prompt.Parent:IsA("BasePart") or Prompt.Parent:IsA("Attachment") then
self.PromptParent = Prompt.Parent
elseif Prompt.Parent:IsA("Model") and Prompt.Parent.PrimaryPart ~= nil then
self.PromptParent = Prompt.Parent.PrimaryPart
else
error("Hologram requires ProximityPrompt to be parented to a BasePart, Attachment or Model with a PrimaryPart.")
end
Prompt.PromptShown:Connect(function(InputType: Enum.ProximityPromptInputType)
self:OnPromptShown(InputType)
end)
Prompt.PromptHidden:Connect(function(InputType: Enum.ProximityPromptInputType)
self:OnPromptHidden(InputType)
end)
Prompt.PromptButtonHoldBegan:Connect(function()
self:PromptHolding()
end)
self.ClonedPrompts = {}
Hologram.Holograms[Prompt] = self
return self
end
function Hologram:CreatePrompt()
local CloneUUID = os.clock() + math.random()
local ClonedPrompt = PromptTemplate:Clone()
local KeyCodePart = ClonedPrompt.KeyCode
local KeyCodeUI = KeyCodePart.HologramKeyCodeUI
local KeyCodeBackground = KeyCodeUI.Background
local KeyCodeText = KeyCodeBackground.KeyCode
local KeyCodeImage = KeyCodeBackground.KeyCodeImage
local KeyCodeProgress = KeyCodeBackground.Progress
local InstructionPart = ClonedPrompt.Instruction
local InstructionUI = InstructionPart.HologramInstructionUI
local InstructionBackground = InstructionUI.Background
local ActionUIText = InstructionBackground.Action
local ObjectUIText = InstructionBackground.Object
local InvisibleButton = InstructionBackground.InvisiButton
local DesignPart = ClonedPrompt.Design
local DesignUI = DesignPart.HologramDesignUI
local DesignImage = DesignUI.Image
KeyCodeUI.Name = "Hologram" .. self.PromptName .. CloneUUID .. "KeyCodeUI"
InstructionUI.Name = "Hologram" .. self.PromptName .. CloneUUID .. "InstructionUI"
DesignUI.Name = "Hologram" .. self.PromptName .. CloneUUID .. "DesignUI"
local Beam = KeyCodePart.Beam
local BeamAttachment = KeyCodePart.Attachment
local TransparencyValue = Beam:WaitForChild("TransparencyValue")
ClonedPrompt.Name = "Hologram" .. self.PromptName .. CloneUUID
ClonedPrompt.Parent = self.PromptParent
KeyCodeUI.Parent = PlayerUI
InstructionUI.Parent = PlayerUI
DesignUI.Parent = PlayerUI
local BeamConnect = TransparencyValue:GetPropertyChangedSignal("Value"):Connect(function()
Beam.Transparency = NumberSequence.new(TransparencyValue.Value)
end)
local CloneData = {
ClonedPrompt = ClonedPrompt,
KeyCodePart = KeyCodePart,
KeyCodeUI = KeyCodeUI,
KeyCodeBackground = KeyCodeBackground,
KeyCodeText = KeyCodeText,
KeyCodeImage = KeyCodeImage,
KeyCodeProgress = KeyCodeProgress,
InstructionPart = InstructionPart,
InstructionUI = InstructionUI,
InstructionBackground = InstructionBackground,
ActionUIText = ActionUIText,
ObjectUIText = ObjectUIText,
InvisibleButton = InvisibleButton,
DesignPart = DesignPart,
DesignUI = DesignUI,
DesignImage = DesignImage,
Beam = Beam,
BeamAttachment = BeamAttachment,
TransparencyValue = TransparencyValue,
BeamConnect = BeamConnect,
UUID = CloneUUID,
}
table.insert(self.ClonedPrompts, CloneData)
return CloneData
end
function Hologram:DestroyPrompt(CloneData)
if not CloneData then return end
for _, PromptUI in pairs(PlayerUI:GetChildren()) do
if string.find(PromptUI.Name, "Hologram" .. self.PromptName .. CloneData.UUID) then
PromptUI:Destroy()
end
end
if CloneData.HoldEndedConnection then
CloneData.HoldEndedConnection:Disconnect()
end
CloneData.BeamConnect:Disconnect()
if CloneData.InvisiButtonConnect then
CloneData.InvisiButtonConnect:Disconnect()
end
if CloneData.BillboardUI then
CloneData.BillboardUI:Disconnect()
end
CloneData.ClonedPrompt:Destroy()
for index, data in ipairs(self.ClonedPrompts) do
if data == CloneData then
table.remove(self.ClonedPrompts, index)
break
end
end
end
function Hologram:OnPromptShown(InputType: Enum.ProximityPromptInputType)
local CloneData = self:CreatePrompt()
if not CloneData.ClonedPrompt then return end
local ClosestFace = GetClosestFace(self.PromptParent)
local FacePosition, FaceNormal
if self.BillboardActive == true then
FacePosition = self.PromptParent.CFrame:PointToWorldSpace(self.StudsOffset)
FaceNormal = (Camera.CFrame.Position - FacePosition).Unit
else
FaceNormal = GetFaceNormals(self.PromptParent)[ClosestFace]
FacePosition = self.PromptParent.Position + (FaceNormal * (self.PromptParent.Size / 2)) + (FaceNormal * 1)
end
CloneData.KeyCodeText.TextTransparency = 1
CloneData.ActionUIText.TextTransparency = 1
CloneData.ObjectUIText.TextTransparency = 1
CloneData.InstructionBackground.Transparency = 1
CloneData.KeyCodeBackground.Transparency = 1
CloneData.DesignImage.ImageTransparency = 1
CloneData.KeyCodeImage.ImageTransparency = 1
CloneData.DesignImage.ImageColor3 = self.Colours.Primary
CloneData.KeyCodeProgress.BackgroundColor3 = self.Colours.Primary
CloneData.InstructionBackground.BackgroundColor3 = self.Colours.Secondary
CloneData.KeyCodeBackground.BackgroundColor3 = self.Colours.Secondary
CloneData.KeyCodeText.TextColor3 = self.Colours.Tertiary
CloneData.KeyCodeImage.ImageColor3 = self.Colours.Tertiary
CloneData.ActionUIText.TextColor3 = self.Colours.Tertiary
CloneData.ObjectUIText.TextColor3 = self.Colours.Tertiary
if self.BillboardActive == true then
CloneData.BillboardUI = RunService.RenderStepped:Connect(function()
if not CloneData.ClonedPrompt or not CloneData.ClonedPrompt.PrimaryPart then return end
local PartCFrame = self.PromptParent.CFrame
local WorldOffset = PartCFrame:PointToWorldSpace(self.StudsOffset)
local CameraPosition = Camera.CFrame.Position
CloneData.ClonedPrompt:PivotTo(CFrame.lookAt(WorldOffset, CameraPosition, Vector3.new(0, 1, 0)))
CloneData.DesignPart.CFrame = CloneData.KeyCodePart.CFrame + (CloneData.KeyCodePart.CFrame.LookVector * 0.05)
CloneData.InstructionPart.CFrame = CloneData.KeyCodePart.CFrame + (CloneData.KeyCodePart.CFrame.LookVector * -0.05) + (CloneData.KeyCodePart.CFrame.RightVector * -2.845)
end)
else
CloneData.ClonedPrompt:PivotTo(CFrame.new(FacePosition, FacePosition + FaceNormal))
CloneData.ClonedPrompt:PivotTo(CloneData.ClonedPrompt.PrimaryPart.CFrame + (CloneData.ClonedPrompt.PrimaryPart.CFrame.LookVector * (self.StudsOffset.Z)) + (CloneData.ClonedPrompt.PrimaryPart.CFrame.RightVector * (self.StudsOffset.X)) + (CloneData.ClonedPrompt.PrimaryPart.CFrame.UpVector * (self.StudsOffset.Y)))
CloneData.DesignPart.CFrame = CloneData.KeyCodePart.CFrame + (CloneData.KeyCodePart.CFrame.LookVector * 1)
CloneData.InstructionPart.CFrame = CloneData.KeyCodePart.CFrame + (CloneData.KeyCodePart.CFrame.LookVector * -1) + (CloneData.KeyCodePart.CFrame.RightVector * -2.845)
end
if self.ShowBeam == true then
BorderHighlight.Adornee = self.PromptParent
CloneData.Beam.Attachment1 = Character:WaitForChild("UpperTorso", 10).BodyFrontAttachment or Character:WaitForChild("Torso", 10).BodyFrontAttachment
CloneData.BeamAttachment.Parent = self.PromptParent
Tween(CloneData.TransparencyValue, "Value", 0)
end
if InputType == Enum.ProximityPromptInputType.Gamepad then
if GamepadButtonImage[self.Prompt.GamepadKeyCode] then
CloneData.KeyCodeImage.Image = GamepadButtonImage[self.Prompt.GamepadKeyCode]
end
elseif InputType == Enum.ProximityPromptInputType.Touch then
CloneData.KeyCodeImage.Image = "rbxasset://textures/ui/Controls/TouchTapIcon.png"
else
local ButtonTextString = UserInputService:GetStringForKeyCode(self.Prompt.KeyboardKeyCode)
local ButtonTextImage = KeyboardButtonImage[self.Prompt.KeyboardKeyCode]
if ButtonTextImage == nil then
ButtonTextImage = KeyboardButtonIconMapping[ButtonTextString]
end
if ButtonTextImage == nil then
local KeyCodeMappedText = KeyCodeToTextMapping[self.Prompt.KeyboardKeyCode]
if KeyCodeMappedText then
ButtonTextString = KeyCodeMappedText
end
end
if ButtonTextImage then
CloneData.KeyCodeImage.Image = ButtonTextImage
elseif ButtonTextString ~= nil and ButtonTextString ~= "" then
CloneData.KeyCodeText.Text = ButtonTextString
else
error(
"ProximityPrompt '"
.. self.Prompt.Name
.. "' has an unsupported keycode for rendering UI: "
.. tostring(self.Prompt.KeyboardKeyCode)
)
end
end
CloneData.ActionUIText.Text = self.Prompt.ActionText
CloneData.ObjectUIText.Text = self.Prompt.ObjectText
CloneData.InvisiButtonConnect = CloneData.InvisibleButton.InputBegan:Connect(function(Input: InputObject)
if Input.UserInputType == Enum.UserInputType.Touch or Input.UserInputType == Enum.UserInputType.MouseButton1 then
self.Prompt:InputHoldBegin()
end
end)
CloneData.ImageHoldEndedConnection = CloneData.InvisibleButton.InputEnded:Connect(function(Input: InputObject)
if Input.UserInputType == Enum.UserInputType.Touch or Input.UserInputType == Enum.UserInputType.MouseButton1 then
self.Prompt:InputHoldEnd()
end
end)
if self.BillboardActive == false then
Tween(CloneData.DesignPart, "CFrame", CloneData.DesignPart.CFrame * CFrame.new(0, 0, 0.95))
Tween(CloneData.InstructionPart, "CFrame", CloneData.InstructionPart.CFrame * CFrame.new(0, 0, -0.95))
end
Tween(CloneData.DesignImage, "ImageTransparency", 0)
Tween(CloneData.KeyCodeImage, "ImageTransparency", 0)
Tween(CloneData.ActionUIText, "TextTransparency", 0)
Tween(CloneData.ObjectUIText, "TextTransparency", 0)
Tween(CloneData.KeyCodeText, "TextTransparency", 0)
Tween(CloneData.InstructionBackground, "Transparency", 0.8)
local FinalTween = Tween(CloneData.KeyCodeBackground, "Transparency", 0)
FinalTween.Completed:Wait()
end
function Hologram:OnPromptHidden(InputType: Enum.ProximityPromptInputType)
for _, CloneData in ipairs(self.ClonedPrompts) do
if CloneData.ClonedPrompt then
if self.ShowBeam then
BorderHighlight.Adornee = nil
Tween(CloneData.TransparencyValue, "Value", 1)
CloneData.Beam.Attachment1 = nil
CloneData.BeamAttachment.Parent = CloneData.KeyCodePart
end
Tween(CloneData.DesignPart, "CFrame", CloneData.DesignPart.CFrame * CFrame.new(0, 0, -0.95))
Tween(CloneData.InstructionPart, "CFrame", CloneData.InstructionPart.CFrame * CFrame.new(0, 0, 0.95))
Tween(CloneData.DesignImage, "ImageTransparency", 1)
Tween(CloneData.KeyCodeImage, "ImageTransparency", 1)
Tween(CloneData.ActionUIText, "TextTransparency", 1)
Tween(CloneData.ObjectUIText, "TextTransparency", 1)
Tween(CloneData.KeyCodeText, "TextTransparency", 1)
Tween(CloneData.InstructionBackground, "Transparency", 1)
local FinalTween = Tween(CloneData.KeyCodeBackground, "Transparency", 1)
FinalTween.Completed:Wait()
self:DestroyPrompt(CloneData)
end
end
end
function Hologram:PromptHolding()
for _, CloneData in ipairs(self.ClonedPrompts) do
local Fill = TweenService:Create(CloneData.KeyCodeProgress, TweenInfo.new(self.HoldDuration, Enum.EasingStyle.Sine), {Size = UDim2.fromScale(1, 1)})
Fill:Play()
local Size = TweenService:Create(CloneData.DesignImage, TweenInfo.new(self.HoldDuration, Enum.EasingStyle.Sine), {Size = UDim2.fromScale(0.9, 0.9)})
Size:Play()
local function PromptHoldEnded()
Fill:Cancel()
Size:Cancel()
Tween(CloneData.KeyCodeProgress, "Size", UDim2.fromScale(1, 0))
Tween(CloneData.DesignImage, "Size", UDim2.fromScale(1, 1))
if CloneData.HoldEndedConnection then
CloneData.HoldEndedConnection:Disconnect()
end
end
CloneData.HoldEndedConnection = self.Prompt.PromptButtonHoldEnded:Connect(PromptHoldEnded)
end
end
--// Settings
function Hologram:SetAlwaysOnTop(isOnTop: boolean)
self.KeyCodeUI.AlwaysOnTop = isOnTop
self.InstructionUI.AlwaysOnTop = isOnTop
self.DesignUI.AlwaysOnTop = isOnTop
end
function Hologram:SetPrimaryColour(Colour: Color3)
self.Colours.Primary = Colour
end
function Hologram:SetSecondaryColour(Colour: Color3)
self.Colours.Secondary = Colour
end
function Hologram:SetTertiaryColour(Colour: Color3)
self.Colours.Tertiary = Colour
end
function Hologram:SetStudsOffset(StudsOffset: Vector3)
self.StudsOffset = StudsOffset
end
function Hologram:SetBeam(ShowBeam: boolean)
self.ShowBeam = ShowBeam
end
function Hologram:SetBillboardActive(isActive: boolean)
self.BillboardActive = isActive
end
return Hologram
Hope this helps with the future of Hologram!