-- Coded by @scarrletsky | for https://www.roblox.com/games/129018804998742/anemoia-the-last-warm-feelings
-- E / Q For zoom
-- W A S D For camera rotation
-- Please, credit me or my game. It will be perfect. Wish you a goodluck in your creations! <3
-- =====================================
-- CONFIGURATION (English Docs)
-- =====================================
-- This config table allows full customization. Edit values below as needed.
-- All changes are documented briefly. Reload script after edits.
local Config = {
-- CONTROLS
-- Keybinds for actions. Use Enum.KeyCode values (e.g., Enum.KeyCode.E).
zoomInKey = Enum.KeyCode.Q, -- Key to zoom in (decrease FOV).
zoomOutKey = Enum.KeyCode.E, -- Key to zoom out (increase FOV).
pitchUpKey = Enum.KeyCode.W, -- Key to pitch up (look up).
pitchDownKey = Enum.KeyCode.S, -- Key to pitch down (look down).
yawLeftKey = Enum.KeyCode.A, -- Key to yaw left (turn left).
yawRightKey = Enum.KeyCode.D, -- Key to yaw right (turn right).
exitKey = Enum.KeyCode.Space, -- Key to exit telescope view.
-- CAMERA & FOV
-- Field of view settings.
originalFOV = 70, -- Default camera FOV outside telescope.
initialFOV = 20, -- Starting FOV when entering telescope.
minFOV = 1, -- Minimum zoom level (fully zoomed in).
maxFOV = 60, -- Maximum zoom level (zoomed out).
zoomSpeed = 30, -- FOV change speed per second (higher = faster zoom).
-- MOVEMENT
-- Pitch/Yaw rotation settings.
pitchYawAcceleration = 3, -- Acceleration for pitch/yaw velocity (higher = quicker response).
pitchYawMaxVelocity = 15, -- Max velocity for smooth stopping (higher = faster max speed).
pitchYawDamping = 5, -- Damping factor for inertia (higher = quicker stop).
pitchClamp = {-80, 20}, -- Pitch angle limits {min, max} in degrees (positive = up).
yawClamp = {-50, 50}, -- Yaw angle limits {min, max} in degrees.
-- PLANET GENERATION
-- Procedural planet visuals.
imagePool = { -- Array of planet image asset IDs (rbxassetid://).
"rbxassetid://96187902144866",
"rbxassetid://122921439218483",
"rbxassetid://81833840139401",
"rbxassetid://131303639162154",
"rbxassetid://123164483372427",
"rbxassetid://77500182783459",
"rbxassetid://123081392222008",
"rbxassetid://99030718560060",
"rbxassetid://122419204894645"
},
maxPlanets = 120, -- Maximum simultaneous planets visible.
genInterval = 5, -- Seconds between new planet spawns.
initialPlanets = 5, -- Number of planets to spawn on enter.
planetLifetime = 180, -- Seconds each planet lives (3 min = 180).
driftSpeed = 0.02, -- Sky drift speed (higher = faster background movement).
planetScaleMin = 0.2, -- Min random scale multiplier for planets (0.9-1.1 range).
planetScaleMax = 0.8, -- Max random scale multiplier for planets.
planetTransparencyMin = 0.4, -- Min transparency for planets (0=opaque, 1=invisible).
planetTransparencyMax = 0.8, -- Max transparency for planets.
planetPitchMin = -30, -- Min relative pitch for generation (lower magnitude = sky-only spawn).
planetPitchMax = -80, -- Max relative pitch.
-- PROMPT
-- Proximity prompt settings.
holdDuration = 0.2, -- Seconds to hold prompt before triggering.
actionText = "Look Through", -- Text shown on prompt (e.g., "Use Telescope").
objectText = "Telescope", -- Object name on prompt.
-- SOUNDS
-- Audio effects.
zoomSoundId = "rbxassetid://13211461501", -- Asset ID for zoom sound.
zoomVolume = 0.4, -- Volume level (0-1).
-- GUI & EFFECTS
-- Visual overlays and transitions.
overlayImageId = "rbxassetid://94548852777206", -- Asset ID for telescope overlay image.
overlaySize = {2.5, 0, 2.5, 0}, -- UDim2 size for overlay {XScale, XOffset, YScale, YOffset}.
overlayAnchor = {0.32, 0.265}, -- Vector2 anchor point for overlay.
overlayFadeInTime = 0.5, -- Seconds for overlay fade-in.
overlayFadeOutTime = 2, -- Seconds for overlay fade-out.
blurMaxSize = 20, -- Max blur effect intensity during zoom.
blurChangeSpeed = 100, -- Blur adjustment speed per second.
infoEnabled = true, -- Show debug info text (true/false).
infoTextSize = 14, -- Font size for info text.
infoFont = Enum.Font.Code, -- Font for info text.
-- TWEENS
-- Animation timings (TweenInfo.new(time, style, direction)).
enterTweenTime = 4, -- Seconds for camera/FOV tween on enter.
enterEasingStyle = Enum.EasingStyle.Quad, -- Easing style for enter tween.
enterEasingDirection = Enum.EasingDirection.InOut, -- Easing direction for enter tween.
exitBlurTweenTime = 4, -- Seconds for blur fade-out on exit.
planetFadeInTime = 1, -- Seconds for new planet fade-in.
-- PLAYER IMMOBILIZATION
-- Restrict player movement while in telescope.
immobilizeEnabled = true, -- Enable player immobilization (true/false).
walkSpeedZero = 0, -- Walk speed while immobilized (usually 0).
jumpPowerZero = 0, -- Jump power while immobilized (usually 0).
}
-- =====================================
-- END CONFIGURATION
-- =====================================
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local SoundService = game:GetService("SoundService")
local player = Players.LocalPlayer
local camera = Workspace.CurrentCamera
local playerGui = player:WaitForChild("PlayerGui")
local telescope = Workspace:WaitForChild("Telescope")
local camPart = telescope:WaitForChild("Cam")
local prompt = Instance.new("ProximityPrompt")
prompt.Parent = telescope
prompt.HoldDuration = Config.holdDuration
prompt.ActionText = Config.actionText
prompt.ObjectText = Config.objectText
local inTelescope = false
local originalFOV = Config.originalFOV
local currentFOV = Config.initialFOV
local pitchOffset = 0
local yawOffset = 0
local character = nil
local originalWalkSpeed = 16
local originalJumpPower = 50
local heldKeys = {}
local rsConnection = nil
local pitchVel = 0
local yawVel = 0
local imagePool = Config.imagePool
local telescopeGui = Instance.new("ScreenGui")
telescopeGui.Parent = playerGui
telescopeGui.IgnoreGuiInset = true
telescopeGui.DisplayOrder = 1000
local telescopeImage = Instance.new("ImageLabel")
telescopeImage.Size = UDim2.new(unpack(Config.overlaySize))
telescopeImage.AnchorPoint = Vector2.new(unpack(Config.overlayAnchor))
telescopeImage.Position = UDim2.new(0, 0, 0, 0)
telescopeImage.BackgroundTransparency = 1
telescopeImage.Image = Config.overlayImageId
telescopeImage.ImageTransparency = 1
telescopeImage.ScaleType = Enum.ScaleType.Fit
telescopeImage.ZIndex = 2
telescopeImage.Parent = telescopeGui
local infoGui = Instance.new("ScreenGui")
infoGui.Parent = playerGui
infoGui.IgnoreGuiInset = true
infoGui.DisplayOrder = 1001
local infoText = Instance.new("TextLabel")
infoText.Size = UDim2.new(1, 0, 0, 30)
infoText.Position = UDim2.new(0, 0, 0, 0)
infoText.BackgroundTransparency = 1
infoText.Text = ""
infoText.TextColor3 = Color3.new(1, 1, 1)
infoText.TextSize = Config.infoTextSize
infoText.Font = Config.infoFont
infoText.Parent = infoGui
infoGui.Enabled = Config.infoEnabled
local blurEffect = Instance.new("BlurEffect")
blurEffect.Size = 0
blurEffect.Parent = camera
local planets = {}
local usedCounts = {}
local sky_drift = 0
local last_gen_time = 0
local drift_speed = Config.driftSpeed
local gen_interval = Config.genInterval
local max_planets = Config.maxPlanets
local imageFadeIn = TweenService:Create(telescopeImage, TweenInfo.new(Config.overlayFadeInTime, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut), {ImageTransparency = 0})
local imageFadeOut = TweenService:Create(telescopeImage, TweenInfo.new(Config.overlayFadeOutTime, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut), {ImageTransparency = 1})
local tweenInfoLong = TweenInfo.new(Config.enterTweenTime, Config.enterEasingStyle, Config.enterEasingDirection)
local zoomSoundId = Config.zoomSoundId
local zoomVolume = Config.zoomVolume
player.CharacterRemoving:Connect(function()
if inTelescope then
inTelescope = false
if rsConnection then
rsConnection:Disconnect()
rsConnection = nil
end
if character and Config.immobilizeEnabled then
local humanoid = character:FindFirstChild("Humanoid")
if humanoid then
humanoid.PlatformStand = false
humanoid.WalkSpeed = originalWalkSpeed
humanoid.JumpPower = originalJumpPower
end
end
for _, p in ipairs(planets) do
p.gui:Destroy()
end
planets = {}
imageFadeOut:Play()
camera.CameraType = Enum.CameraType.Custom
camera.FieldOfView = originalFOV
TweenService:Create(blurEffect, TweenInfo.new(Config.exitBlurTweenTime, Enum.EasingStyle.Quad), {Size = 0}):Play()
infoText.Text = ""
heldKeys = {}
pitchOffset = 0
yawOffset = 0
pitchVel = 0
yawVel = 0
end
end)
local function updateCameraCFrame()
local baseCFrame = camPart.CFrame
local yawRot = CFrame.Angles(0, math.rad(-yawOffset), 0)
local pitchRot = CFrame.Angles(math.rad(-pitchOffset), 0, 0)
local newCFrame = baseCFrame * yawRot * pitchRot
camera.CFrame = newCFrame
end
local function createZoomSound()
local newSound = Instance.new("Sound")
newSound.SoundId = zoomSoundId
newSound.Volume = zoomVolume
newSound.Parent = SoundService
newSound:Play()
newSound.Ended:Connect(function()
newSound:Destroy()
end)
end
local function generatePlanet()
if #planets >= max_planets then return end
local unused = {}
local all = {}
for id, _ in pairs(usedCounts) do
table.insert(all, id)
if usedCounts[id] == 0 then
table.insert(unused, id)
end
end
local picked
if #unused > 0 and math.random() < 0.9 then
picked = unused[math.random(1, #unused)]
else
picked = all[math.random(1, #all)]
end
usedCounts[picked] = (usedCounts[picked] or 0) + 1
local gui = Instance.new("ImageLabel")
gui.Name = "Planet"
gui.Parent = telescopeGui
gui.ZIndex = 1
gui.BackgroundTransparency = 1
gui.Image = picked
gui.ImageTransparency = 1
gui.ScaleType = Enum.ScaleType.Fit
local rand_scale = Config.planetScaleMin + math.random() * (Config.planetScaleMax - Config.planetScaleMin)
local scale = 20 / currentFOV
gui.Size = UDim2.new(0, 33 * scale * rand_scale, 0, 33 * scale * rand_scale)
local trans = Config.planetTransparencyMin + math.random() * (Config.planetTransparencyMax - Config.planetTransparencyMin)
TweenService:Create(gui, TweenInfo.new(Config.planetFadeInTime, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut), {ImageTransparency = trans}):Play()
local aspect_ratio = camera.ViewportSize.X / camera.ViewportSize.Y
local h_fov = 2 * math.deg(math.atan(math.tan(math.rad(currentFOV) / 2) * aspect_ratio))
local rel_yaw = math.random(-h_fov * 0.5, h_fov * 1.5)
local low_pitch = math.min(Config.planetPitchMin, Config.planetPitchMax)
local high_pitch = math.max(Config.planetPitchMin, Config.planetPitchMax)
local rel_pitch = math.random(low_pitch, high_pitch)
local sky_yaw = yawOffset + rel_yaw
local sky_pitch = pitchOffset + rel_pitch
table.insert(planets, {gui = gui, sky_yaw = sky_yaw, sky_pitch = sky_pitch, rand_scale = rand_scale, spawn_time = tick()})
end
prompt.Triggered:Connect(function()
if inTelescope then return end
inTelescope = true
originalFOV = camera.FieldOfView
currentFOV = Config.initialFOV
pitchOffset = 0
yawOffset = 0
pitchVel = 0
yawVel = 0
character = player.Character
if not character then
character = player.CharacterAdded:Wait()
end
local humanoid = character:WaitForChild("Humanoid")
if Config.immobilizeEnabled then
originalWalkSpeed = humanoid.WalkSpeed
originalJumpPower = humanoid.JumpPower
humanoid.PlatformStand = true
humanoid.WalkSpeed = Config.walkSpeedZero
humanoid.JumpPower = Config.jumpPowerZero
end
for _, id in ipairs(imagePool) do
usedCounts[id] = 0
end
planets = {}
sky_drift = 0
last_gen_time = tick() - gen_interval
for i = 1, Config.initialPlanets do
generatePlanet()
end
camera.CameraType = Enum.CameraType.Scriptable
local tweenCFrame = TweenService:Create(camera, tweenInfoLong, {CFrame = camPart.CFrame})
local tweenFOV = TweenService:Create(camera, tweenInfoLong, {FieldOfView = currentFOV})
tweenCFrame:Play()
tweenFOV:Play()
imageFadeIn:Play()
rsConnection = RunService.Heartbeat:Connect(function(dt)
if not inTelescope then return end
local prevFOV_frame = currentFOV
local prevFloor = math.floor(prevFOV_frame)
local zoomed = false
local pitchInput = 0
if heldKeys[Config.pitchUpKey] then pitchInput = 1 end
if heldKeys[Config.pitchDownKey] then pitchInput = -1 end
pitchVel = pitchVel + pitchInput * Config.pitchYawAcceleration * dt
if pitchInput == 0 then
pitchVel = pitchVel * math.exp(-Config.pitchYawDamping * dt)
else
pitchVel = math.clamp(pitchVel, -Config.pitchYawMaxVelocity, Config.pitchYawMaxVelocity)
end
pitchOffset = math.clamp(pitchOffset + pitchVel * dt, Config.pitchClamp[1], Config.pitchClamp[2])
local yawInput = 0
if heldKeys[Config.yawLeftKey] then yawInput = -1 end
if heldKeys[Config.yawRightKey] then yawInput = 1 end
yawVel = yawVel + yawInput * Config.pitchYawAcceleration * dt
if yawInput == 0 then
yawVel = yawVel * math.exp(-Config.pitchYawDamping * dt)
else
yawVel = math.clamp(yawVel, -Config.pitchYawMaxVelocity, Config.pitchYawMaxVelocity)
end
yawOffset = math.clamp(yawOffset + yawVel * dt, Config.yawClamp[1], Config.yawClamp[2])
if heldKeys[Config.zoomInKey] then
currentFOV = math.max(Config.minFOV, currentFOV - Config.zoomSpeed * dt)
zoomed = true
end
if heldKeys[Config.zoomOutKey] then
currentFOV = math.min(Config.maxFOV, currentFOV + Config.zoomSpeed * dt)
zoomed = true
end
local deltaFOV = math.abs(currentFOV - prevFOV_frame)
camera.FieldOfView = currentFOV
local changing = zoomed and deltaFOV > 0.01
if changing then
blurEffect.Size = math.min(blurEffect.Size + Config.blurChangeSpeed * dt, Config.blurMaxSize)
else
blurEffect.Size = math.max(blurEffect.Size - Config.blurChangeSpeed * dt, 0)
end
local newFloor = math.floor(currentFOV)
if newFloor ~= prevFloor then
createZoomSound()
end
sky_drift = sky_drift - drift_speed * dt
local aspect_ratio = camera.ViewportSize.X / camera.ViewportSize.Y
local h_fov = 2 * math.deg(math.atan(math.tan(math.rad(currentFOV) / 2) * aspect_ratio))
for i = #planets, 1, -1 do
local p = planets[i]
if tick() - p.spawn_time > Config.planetLifetime then
p.gui:Destroy()
table.remove(planets, i)
else
local rel_yaw = p.sky_yaw - yawOffset + sky_drift
local rel_pitch = p.sky_pitch - pitchOffset
local screenX = 0.5 + rel_yaw / h_fov
local screenY = 0.5 + rel_pitch / currentFOV
if screenX < -0.2 then
p.sky_yaw = p.sky_yaw + h_fov * 2.4
end
p.gui.Position = UDim2.new(screenX, 0, screenY, 0)
local scale = 20 / currentFOV
p.gui.Size = UDim2.new(0, 33 * scale * p.rand_scale, 0, 33 * scale * p.rand_scale)
end
end
if tick() - last_gen_time > gen_interval and #planets < max_planets then
generatePlanet()
last_gen_time = tick()
end
if Config.infoEnabled then
infoText.Text = string.format("Pitch: %.2f°, Yaw: %.2f°, FOV: %.2f, Planets: %d, Drift: %.2f", pitchOffset, yawOffset, currentFOV, #planets, sky_drift)
end
updateCameraCFrame()
end)
end)
UserInputService.InputBegan:Connect(function(input, gameProcessed)
if gameProcessed then return end
if not inTelescope then return end
heldKeys[input.KeyCode] = true
if input.KeyCode == Config.exitKey then
inTelescope = false
if rsConnection then
rsConnection:Disconnect()
rsConnection = nil
end
if character and Config.immobilizeEnabled then
local humanoid = character:FindFirstChild("Humanoid")
if humanoid then
humanoid.PlatformStand = false
humanoid.WalkSpeed = originalWalkSpeed
humanoid.JumpPower = originalJumpPower
end
end
pitchOffset = 0
yawOffset = 0
pitchVel = 0
yawVel = 0
heldKeys = {}
for _, p in ipairs(planets) do
p.gui:Destroy()
end
planets = {}
imageFadeOut:Play()
imageFadeOut.Completed:Connect(function()
camera.CameraType = Enum.CameraType.Custom
camera.FieldOfView = originalFOV
TweenService:Create(blurEffect, tweenInfoLong, {Size = 0}):Play()
infoText.Text = ""
end)
end
end)
UserInputService.InputEnded:Connect(function(input, gameProcessed)
if gameProcessed then return end
heldKeys[input.KeyCode] = nil
end)