Загрузка данных


-- RbxMotion Studio v0.1
-- Полностью заменяет предыдущий LocalScript

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")

local gui = script.Parent

local Main = gui:WaitForChild("Main")
local TopBar = gui:WaitForChild("TopBar")
local ViewportPanel = gui:WaitForChild("Viewport")
local Explorer = gui:WaitForChild("Explorer")
local Inspector = gui:WaitForChild("Inspector")
local Timeline = gui:WaitForChild("Timeline")

--==================================================
-- НАСТРОЙКИ
--==================================================

local COLORS = {
	bg = Color3.fromRGB(14, 15, 18),
	panel = Color3.fromRGB(21, 23, 28),
	panel2 = Color3.fromRGB(27, 30, 36),
	panel3 = Color3.fromRGB(34, 37, 44),
	line = Color3.fromRGB(52, 56, 65),
	text = Color3.fromRGB(230, 233, 240),
	muted = Color3.fromRGB(145, 150, 162),
	accent = Color3.fromRGB(78, 132, 255),
	accentDark = Color3.fromRGB(48, 87, 170),
	selected = Color3.fromRGB(48, 73, 120),
	red = Color3.fromRGB(220, 80, 80),
}

local FPS = 30
local currentFrame = 0
local totalFrames = 180
local playing = false
local selectedPart = nil
local keyframes = {}

--==================================================
-- БАЗОВЫЕ НАСТРОЙКИ GUI
--==================================================

Main.Size = UDim2.fromScale(1, 1)
Main.Position = UDim2.fromScale(0, 0)
Main.BackgroundColor3 = COLORS.bg
Main.BorderSizePixel = 0

TopBar.Size = UDim2.new(1, 0, 0, 48)
TopBar.Position = UDim2.new(0, 0, 0, 0)
TopBar.BackgroundColor3 = COLORS.panel
TopBar.BorderSizePixel = 0

ViewportPanel.Position = UDim2.new(0, 230, 0, 48)
ViewportPanel.Size = UDim2.new(1, -510, 1, -185)
ViewportPanel.BackgroundColor3 = Color3.fromRGB(9, 10, 13)
ViewportPanel.BorderSizePixel = 0

Explorer.Position = UDim2.new(0, 0, 0, 48)
Explorer.Size = UDim2.new(0, 230, 1, -185)
Explorer.BackgroundColor3 = COLORS.panel
Explorer.BorderSizePixel = 0

Inspector.Position = UDim2.new(1, -280, 0, 48)
Inspector.Size = UDim2.new(0, 280, 1, -185)
Inspector.BackgroundColor3 = COLORS.panel
Inspector.BorderSizePixel = 0

Timeline.Position = UDim2.new(0, 0, 1, -137)
Timeline.Size = UDim2.new(1, 0, 0, 137)
Timeline.BackgroundColor3 = COLORS.panel
Timeline.BorderSizePixel = 0

--==================================================
-- УТИЛИТЫ GUI
--==================================================

local function clear(container)
	for _, child in ipairs(container:GetChildren()) do
		if not child:IsA("UIListLayout") then
			child:Destroy()
		end
	end
end

local function corner(object, radius)
	local c = Instance.new("UICorner")
	c.CornerRadius = UDim.new(0, radius or 5)
	c.Parent = object
	return c
end

local function stroke(object, color, thickness)
	local s = Instance.new("UIStroke")
	s.Color = color or COLORS.line
	s.Thickness = thickness or 1
	s.Parent = object
	return s
end

local function text(parent, value, position, size, fontSize, color)
	local label = Instance.new("TextLabel")
	label.Parent = parent
	label.Text = value
	label.Position = position
	label.Size = size
	label.BackgroundTransparency = 1
	label.TextColor3 = color or COLORS.text
	label.Font = Enum.Font.Gotham
	label.TextSize = fontSize or 13
	label.TextXAlignment = Enum.TextXAlignment.Left
	label.TextYAlignment = Enum.TextYAlignment.Center
	return label
end

local function button(parent, value, position, size)
	local b = Instance.new("TextButton")
	b.Parent = parent
	b.Text = value
	b.Position = position
	b.Size = size
	b.BackgroundColor3 = COLORS.panel2
	b.TextColor3 = COLORS.text
	b.Font = Enum.Font.Gotham
	b.TextSize = 12
	b.AutoButtonColor = false
	b.BorderSizePixel = 0

	corner(b, 5)

	b.MouseEnter:Connect(function()
		b.BackgroundColor3 = COLORS.panel3
	end)

	b.MouseLeave:Connect(function()
		b.BackgroundColor3 = COLORS.panel2
	end)

	return b
end

local function divider(parent, position, size)
	local d = Instance.new("Frame")
	d.Parent = parent
	d.Position = position
	d.Size = size
	d.BackgroundColor3 = COLORS.line
	d.BorderSizePixel = 0
	return d
end

--==================================================
-- TOP BAR
--==================================================

clear(TopBar)

text(
	TopBar,
	"RBXMOTION",
	UDim2.new(0, 18, 0, 0),
	UDim2.new(0, 130, 1, 0),
	17
)

local menuX = 155

for _, item in ipairs({
	"FILE",
	"EDIT",
	"VIEW",
	"ANIMATION",
	"LIGHTING",
	"CAMERA"
}) do
	local b = button(
		TopBar,
		item,
		UDim2.new(0, menuX, 0, 8),
		UDim2.new(0, 82, 0, 32)
	)

	menuX += 84

	if item == "LIGHTING" then
		b.MouseButton1Click:Connect(function()
			print("Lighting panel")
		end)
	end
end

local settings = button(
	TopBar,
	"⚙",
	UDim2.new(1, -48, 0, 8),
	UDim2.new(0, 34, 0, 32)
)

--==================================================
-- VIEWPORT
--==================================================

clear(ViewportPanel)

local Preview = Instance.new("ViewportFrame")
Preview.Name = "Preview"
Preview.Parent = ViewportPanel
Preview.Size = UDim2.fromScale(1, 1)
Preview.BackgroundColor3 = Color3.fromRGB(8, 9, 12)
Preview.BorderSizePixel = 0
Preview.Ambient = Color3.fromRGB(150, 150, 150)
Preview.LightColor = Color3.fromRGB(255, 255, 255)
Preview.LightDirection = Vector3.new(-1, -1, -1)

local world = Instance.new("WorldModel")
world.Parent = Preview

local camera = Instance.new("Camera")
camera.Parent = Preview
Preview.CurrentCamera = camera

local cameraDistance = 13
local cameraYaw = 0
local cameraPitch = 8

local function updateCamera()
	local target = Vector3.new(0, 3.2, 0)

	local radiansYaw = math.rad(cameraYaw)
	local radiansPitch = math.rad(cameraPitch)

	local offset = Vector3.new(
		math.sin(radiansYaw) * math.cos(radiansPitch),
		math.sin(radiansPitch),
		math.cos(radiansYaw) * math.cos(radiansPitch)
	) * cameraDistance

	camera.CFrame = CFrame.lookAt(target + offset, target)
end

updateCamera()

--==================================================
-- ТЕСТОВЫЙ ПЕРСОНАЖ
--==================================================

local character = Instance.new("Model")
character.Name = "Character"
character.Parent = world

local parts = {}

local function createPart(name, size, position)
	local p = Instance.new("Part")
	p.Name = name
	p.Size = size
	p.Position = position
	p.Anchored = true
	p.Material = Enum.Material.SmoothPlastic
	p.Color = Color3.fromRGB(150, 155, 165)
	p.Parent = character

	parts[name] = p

	return p
end

createPart("Torso", Vector3.new(2.2, 3, 1.2), Vector3.new(0, 3.2, 0))
createPart("Head", Vector3.new(2, 2, 2), Vector3.new(0, 5.7, 0))

createPart("Arm.L", Vector3.new(0.9, 3, 0.9), Vector3.new(-1.55, 3.2, 0))
createPart("Arm.R", Vector3.new(0.9, 3, 0.9), Vector3.new(1.55, 3.2, 0))

createPart("Leg.L", Vector3.new(1, 3, 1), Vector3.new(-0.55, 0.25, 0))
createPart("Leg.R", Vector3.new(1, 3, 1), Vector3.new(0.55, 0.25, 0))

for _, p in pairs(parts) do
	local selection = Instance.new("SelectionBox")
	selection.Adornee = p
	selection.LineThickness = 0.035
	selection.Color3 = COLORS.accent
	selection.Transparency = 1
	selection.Parent = p

	p:SetAttribute("OriginalColor", p.Color)

	p.MouseEnter:Connect(function()
		if selectedPart ~= p then
			p.Color = Color3.fromRGB(180, 185, 200)
		end
	end)

	p.MouseLeave:Connect(function()
		if selectedPart ~= p then
			p.Color = p:GetAttribute("OriginalColor")
		end
	end)
end

--==================================================
-- EXPLORER
--==================================================

clear(Explorer)

text(
	Explorer,
	"SCENE",
	UDim2.new(0, 15, 0, 10),
	UDim2.new(1, -30, 0, 28),
	12,
	COLORS.muted
)

local explorerItems = {
	{"▾  Scene", 42, nil},
	{"   ◇ Camera", 70, nil},
	{"   ◇ Lighting", 98, nil},
	{"   ▾ Character", 126, nil},
	{"       Head", 154, "Head"},
	{"       Torso", 182, "Torso"},
	{"       Arm.L", 210, "Arm.L"},
	{"       Arm.R", 238, "Arm.R"},
	{"       Leg.L", 266, "Leg.L"},
	{"       Leg.R", 294, "Leg.R"},
}

for _, item in ipairs(explorerItems) do
	local name = item[1]
	local y = item[2]
	local partName = item[3]

	local b = button(
		Explorer,
		name,
		UDim2.new(0, 8, 0, y),
		UDim2.new(1, -16, 0, 25)
	)

	b.TextXAlignment = Enum.TextXAlignment.Left

	if partName then
		b.MouseButton1Click:Connect(function()
			local p = parts[partName]
			if p then
				if selectedPart then
					selectedPart.Color =
						selectedPart:GetAttribute("OriginalColor")
					local oldSelection = selectedPart:FindFirstChildOfClass("SelectionBox")
					if oldSelection then
						oldSelection.Transparency = 1
					end
				end

				selectedPart = p
				selectedPart.Color = COLORS.accent

				local selection = p:FindFirstChildOfClass("SelectionBox")
				if selection then
					selection.Transparency = 0
				end

				print("Selected:", partName)
			end
		end)
	end
end

--==================================================
-- INSPECTOR
--==================================================

clear(Inspector)

text(
	Inspector,
	"INSPECTOR",
	UDim2.new(0, 15, 0, 10),
	UDim2.new(1, -30, 0, 28),
	12,
	COLORS.muted
)

text(
	Inspector,
	"Transform",
	UDim2.new(0, 15, 0, 48),
	UDim2.new(1, -30, 0, 28),
	15
)

local selectedName = text(
	Inspector,
	"Nothing selected",
	UDim2.new(0, 15, 0, 80),
	UDim2.new(1, -30, 0, 25),
	12,
	COLORS.muted
)

local function propertyRow(name, y)
	local label = text(
		Inspector,
		name,
		UDim2.new(0, 15, 0, y),
		UDim2.new(0, 90, 0, 26),
		12,
		COLORS.muted
	)

	local value = Instance.new("TextBox")
	value.Parent = Inspector
	value.Position = UDim2.new(0, 105, 0, y)
	value.Size = UDim2.new(1, -120, 0, 26)
	value.BackgroundColor3 = COLORS.panel2
	value.TextColor3 = COLORS.text
	value.Font = Enum.Font.Gotham
	value.TextSize = 11
	value.Text = "0"
	value.ClearTextOnFocus = false
	value.BorderSizePixel = 0

	corner(value, 4)

	return value
end

local posX = propertyRow("Position X", 120)
local posY = propertyRow("Position Y", 153)
local posZ = propertyRow("Position Z", 186)

local rotX = propertyRow("Rotation X", 230)
local rotY = propertyRow("Rotation Y", 263)
local rotZ = propertyRow("Rotation Z", 296)

local keyButton = button(
	Inspector,
	"◆  CREATE KEYFRAME",
	UDim2.new(0, 15, 0, 340),
	UDim2.new(1, -30, 0, 35)
)

keyButton.BackgroundColor3 = COLORS.accent

local function updateInspector()
	if not selectedPart then
		selectedName.Text = "Nothing selected"
		return
	end

	selectedName.Text = selectedPart.Name

	local p = selectedPart.Position
	local r = selectedPart.Orientation

	posX.Text = string.format("%.2f", p.X)
	posY.Text = string.format("%.2f", p.Y)
	posZ.Text = string.format("%.2f", p.Z)

	rotX.Text = string.format("%.1f", r.X)
	rotY.Text = string.format("%.1f", r.Y)
	rotZ.Text = string.format("%.1f", r.Z)
end

local function applyInspector()
	if not selectedPart then
		return
	end

	local x = tonumber(posX.Text) or selectedPart.Position.X
	local y = tonumber(posY.Text) or selectedPart.Position.Y
	local z = tonumber(posZ.Text) or selectedPart.Position.Z

	local rx = tonumber(rotX.Text) or selectedPart.Orientation.X
	local ry = tonumber(rotY.Text) or selectedPart.Orientation.Y
	local rz = tonumber(rotZ.Text) or selectedPart.Orientation.Z

	selectedPart.CFrame =
		CFrame.new(x, y, z) *
		CFrame.Angles(
			math.rad(rx),
			math.rad(ry),
			math.rad(rz)
		)
end

for _, box in ipairs({
	posX, posY, posZ,
	rotX, rotY, rotZ
}) do
	box.FocusLost:Connect(applyInspector)
end

--==================================================
-- TIMELINE
--==================================================

clear(Timeline)

text(
	Timeline,
	"TIMELINE",
	UDim2.new(0, 15, 0, 8),
	UDim2.new(0, 100, 0, 24),
	12,
	COLORS.muted
)

local playButton = button(
	Timeline,
	"▶",
	UDim2.new(0, 120, 0, 5),
	UDim2.new(0, 34, 0, 30)
)

local stopButton = button(
	Timeline,
	"■",
	UDim2.new(0, 158, 0, 5),
	UDim2.new(0, 34, 0, 30)
)

local frameText = text(
	Timeline,
	"FRAME 0",
	UDim2.new(0, 205, 0, 5),
	UDim2.new(0, 100, 0, 30),
	12
)

local fpsText = text(
	Timeline,
	"30 FPS",
	UDim2.new(1, -80, 0, 5),
	UDim2.new(0, 65, 0, 30),
	12,
	COLORS.muted
)

local timelineArea = Instance.new("Frame")
timelineArea.Parent = Timeline
timelineArea.Position = UDim2.new(0, 90, 0, 40)
timelineArea.Size = UDim2.new(1, -105, 1, -45)
timelineArea.BackgroundColor3 = COLORS.bg
timelineArea.BorderSizePixel = 0

-- TRACK NAMES

local trackNames = {
	"HEAD",
	"BODY",
	"ARM.L",
	"ARM.R",
	"LEG.L",
	"LEG.R"
}

for i, name in ipairs(trackNames) do
	local y = (i - 1) * 18

	text(
		Timeline,
		name,
		UDim2.new(0, 15, 0, 43 + y),
		UDim2.new(0, 70, 0, 18),
		10,
		COLORS.muted
	)

	divider(
		Timeline,
		UDim2.new(0, 90, 0, 51 + y),
		UDim2.new(1, -105, 0, 1)
	)
end

-- FRAME MARKERS

local function createKeyVisual(frame, track)
	local x = frame / totalFrames

	local diamond = Instance.new("TextButton")
	diamond.Parent = Timeline
	diamond.Text = "◆"
	diamond.TextSize = 11
	diamond.TextColor3 = COLORS.accent
	diamond.BackgroundTransparency = 1
	diamond.Position = UDim2.new(
		0.06 + x * 0.91,
		0,
		0,
		43 + track * 18
	)
	diamond.Size = UDim2.new(0, 18, 0, 18)

	return diamond
end

local playhead = Instance.new("Frame")
playhead.Parent = Timeline
playhead.Size = UDim2.new(0, 2, 1, -40)
playhead.Position = UDim2.new(0.06, 0, 0, 40)
playhead.BackgroundColor3 = COLORS.accent
playhead.BorderSizePixel = 0
playhead.ZIndex = 10

local function updatePlayhead()
	local alpha = currentFrame / totalFrames

	playhead.Position = UDim2.new(
		0.06 + alpha * 0.91,
		0,
		0,
		40
	)

	frameText.Text = "FRAME " .. currentFrame
end

--==================================================
-- KEYFRAME SYSTEM
--==================================================

local function saveKeyframe()
	if not selectedPart then
		return
	end

	keyframes[selectedPart.Name] = keyframes[selectedPart.Name] or {}

	keyframes[selectedPart.Name][currentFrame] = {
		cframe = selectedPart.CFrame
	}

	local track = 0

	for i, name in ipairs(trackNames) do
		if name == selectedPart.Name then
			track = i - 1
			break
		end
	end

	createKeyVisual(currentFrame, track)

	print(
		"Keyframe:",
		selectedPart.Name,
		"Frame:",
		currentFrame
	)
end

keyButton.MouseButton1Click:Connect(saveKeyframe)

--==================================================
-- PLAYBACK
--==================================================

playButton.MouseButton1Click:Connect(function()
	playing = not playing

	if playing then
		playButton.Text = "Ⅱ"
	else
		playButton.Text = "▶"
	end
end)

stopButton.MouseButton1Click:Connect(function()
	playing = false
	currentFrame = 0
	playButton.Text = "▶"
	updatePlayhead()
end)

RunService.RenderStepped:Connect(function(dt)
	if playing then
		currentFrame += math.floor(dt * FPS)

		if currentFrame >= totalFrames then
			currentFrame = 0
		end

		updatePlayhead()
	end
end)

--==================================================
-- DRAGGING TIMELINE
--==================================================

timelineArea.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		local x = input.Position.X - timelineArea.AbsolutePosition.X
		local width = timelineArea.AbsoluteSize.X

		local alpha = math.clamp(x / width, 0, 1)

		currentFrame = math.floor(alpha * totalFrames)

		updatePlayhead()
	end
end)

--==================================================
-- CAMERA CONTROL
--==================================================

local draggingCamera = false
local lastMouse

Preview.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton2 then
		draggingCamera = true
		lastMouse = input.Position
	end
end)

Preview.InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton2 then
		draggingCamera = false
	end
end)

UserInputService.InputChanged:Connect(function(input)
	if not draggingCamera then
		return
	end

	if input.UserInputType == Enum.UserInputType.MouseMovement then
		local delta = input.Position - lastMouse

		cameraYaw -= delta.X * 0.35
		cameraPitch = math.clamp(
			cameraPitch + delta.Y * 0.25,
			-70,
			70
		)

		lastMouse = input.Position

		updateCamera()
	end
end)

Preview.InputChanged:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseWheel then
		cameraDistance = math.clamp(
			cameraDistance - input.Position.Z,
			5,
			30
		)

		updateCamera()
	end
end)

--==================================================
-- ВЫБОР ЧАСТЕЙ В VIEWPORT
--==================================================

for name, part in pairs(parts) do
	local click = Instance.new("ClickDetector")
	click.MaxActivationDistance = 100
	click.Parent = part

	click.MouseClick:Connect(function()
		if selectedPart then
			selectedPart.Color =
				selectedPart:GetAttribute("OriginalColor")

			local old = selectedPart:FindFirstChildOfClass("SelectionBox")
			if old then
				old.Transparency = 1
			end
		end

		selectedPart = part
		selectedPart.Color = COLORS.accent

		local selection = part:FindFirstChildOfClass("SelectionBox")

		if selection then
			selection.Transparency = 0
		end

		updateInspector()
	end)
end

--==================================================
-- INITIALIZE
--==================================================

updateInspector()
updatePlayhead()

print("RbxMotion Studio v0.1 loaded.")