diff --git a/src/community/docsData.jsx b/src/community/docsData.jsx index c32689f..497c4a4 100644 --- a/src/community/docsData.jsx +++ b/src/community/docsData.jsx @@ -3835,15 +3835,13 @@ game.onTick(() => { }, { id: 'recipes-touch', - title: 'S2. Касание объекта (onTouch)', + title: 'S2. Касание объекта', body: ( <> -

- Самое частое событие — игрок коснулся объекта. Вешаем - скрипт на объект и подписываемся через game.self.onTouch. -

+

Самое частое событие — игрок коснулся объекта.

- {`// Игрок наступил на объект — показать надпись и звук + {`// Игрок наступил на объект — показать надпись и звук game.self.onTouch(() => { game.ui.showText('Ты коснулся плиты!', 2); game.sound.play('click'); @@ -3852,14 +3850,36 @@ game.self.onTouch(() => { // Когда игрок ушёл с объекта game.self.onUntouch(() => { game.ui.showText('Отошёл', 1); -});`} -

- Можно подписаться и на чужой объект из глобального - скрипта — найди его по имени: -

+});`}} + lua={{`local part = script.Parent + +-- Игрок наступил +part.Touched:Connect(function(hit) + local player = game:GetService("Players"):GetPlayerFromCharacter(hit.Parent) + if player then + print("Ты коснулся плиты!") + end +end) + +-- Игрок ушёл +part.TouchEnded:Connect(function(hit) + local player = game:GetService("Players"):GetPlayerFromCharacter(hit.Parent) + if player then + print("Отошёл") + end +end)`}} + /> +

Подписаться на чужой объект из глобального скрипта:

- {`const trap = game.scene.findOne('Ловушка'); -trap.onTouch(() => game.player.damage(20));`} + {`const trap = game.scene.findOne('Ловушка'); +trap.onTouch(() => game.player.damage(20));`}} + lua={{`local trap = workspace:WaitForChild("Ловушка") +trap.Touched:Connect(function(hit) + local humanoid = hit.Parent:FindFirstChild("Humanoid") + if humanoid then humanoid:TakeDamage(20) end +end)`}} + /> ), }, @@ -3870,30 +3890,65 @@ trap.onTouch(() => game.player.damage(20));`} <>

Килблок — объект, который наносит урон или мгновенно - убивает, когда игрок его коснулся (лава, шипы, кислота). + убивает при касании (лава, шипы, кислота).

- {`// Мгновенная смерть при касании + {`// Мгновенная смерть при касании game.self.onTouch(() => { game.player.kill(); - game.ui.showText('💀 Ты сгорел в лаве!', 2); -});`} + game.ui.showText('Ты сгорел в лаве!', 2); +});`}} + lua={{`local part = script.Parent + +part.Touched:Connect(function(hit) + local humanoid = hit.Parent:FindFirstChild("Humanoid") + if humanoid then + humanoid.Health = 0 -- мгновенная смерть + end +end)`}} + />

Если хочешь не убивать сразу, а наносить урон:

- {`// Урон 25 при касании (учитывает кадры неуязвимости) -game.self.onTouch(() => { + {`game.self.onTouch(() => { game.player.damage(25); - game.camera.shake(0.2, 0.3); // лёгкая тряска -});`} -

- Постоянный урон, пока игрок стоит в зоне (например, - ядовитое облако) — урон каждые 0.5 сек, пока касается: -

- {`let inside = false; + game.camera.shake(0.2, 0.3); +});`}} + lua={{`local part = script.Parent + +part.Touched:Connect(function(hit) + local humanoid = hit.Parent:FindFirstChild("Humanoid") + if humanoid then humanoid:TakeDamage(25) end +end)`}} + /> +

Постоянный урон, пока игрок стоит в зоне:

+ {`let inside = false; game.self.onTouch(() => { inside = true; }); game.self.onUntouch(() => { inside = false; }); game.every(0.5, () => { if (inside) game.player.damage(5); -});`} +});`}} + lua={{`local part = script.Parent +local inside = {} -- humanoid → true + +part.Touched:Connect(function(hit) + local h = hit.Parent:FindFirstChild("Humanoid") + if h then inside[h] = true end +end) +part.TouchEnded:Connect(function(hit) + local h = hit.Parent:FindFirstChild("Humanoid") + if h then inside[h] = nil end +end) + +-- Урон каждые 0.5 сек пока стоит +while true do + task.wait(0.5) + for h in pairs(inside) do + if h.Parent then h:TakeDamage(5) end + end +end`}} + /> Сделай красный неоновый куб, повесь на него скрипт смерти — получится лава. Поставь его в проёме как преграду. @@ -3907,38 +3962,73 @@ game.every(0.5, () => { body: ( <>

- Предмет исчезает, когда игрок его коснулся — основа - сбора монеток, ключей, бонусов. + Предмет исчезает при касании — основа сбора монет.

- {`// Простое исчезновение + звук -game.self.onTouch(() => { + {`game.self.onTouch(() => { game.sound.play('coin'); game.self.delete(); -});`} -

- Со счётчиком: предмет сообщает глобальному скрипту, - тот считает. На монетке: -

- {`game.self.onTouch(() => { - game.broadcast('coin'); // сообщить всем скриптам +});`}} + lua={{`local part = script.Parent + +part.Touched:Connect(function(hit) + local h = hit.Parent:FindFirstChild("Humanoid") + if h then + part:Destroy() + end +end)`}} + /> +

Со счётчиком: монетка увеличивает leaderstats игрока:

+ {`game.self.onTouch(() => { + game.broadcast('coin'); game.self.delete(); -});`} -

В глобальном скрипте — приём и счёт:

+});`}} + lua={{`local part = script.Parent + +part.Touched:Connect(function(hit) + local player = game:GetService("Players"):GetPlayerFromCharacter(hit.Parent) + if not player then return end + + -- Прибавить монетку в leaderstats + local stats = player:FindFirstChild("leaderstats") + if stats and stats:FindFirstChild("Монеты") then + stats.Монеты.Value = stats.Монеты.Value + 1 + end + + part:Destroy() +end)`}} + /> +

JS: глобальный скрипт принимает broadcast и считает:

- {`let score = 0; + {`let score = 0; game.ui.score = 0; game.onMessage('coin', () => { score = score + 1; - game.ui.score = score; // обновить счётчик в углу - if (score >= 10) game.ui.showText('🏆 Собрал все!', 3); -});`} - - Не ставь счётчик на саму монетку — каждая монетка это - свой скрипт, они не видят переменные друг друга. Считай в - одном глобальном скрипте, монетки только шлют - game.broadcast. - + game.ui.score = score; + if (score >= 10) game.ui.showText('Собрал все!', 3); +});`}} + lua={{`-- В Lua счёт уже в leaderstats игрока (см. код на монетке выше). +-- Проверим достижение цели в глобальном скрипте: +local Players = game:GetService("Players") + +Players.PlayerAdded:Connect(function(player) + -- Создаём leaderstats папку при заходе + local stats = Instance.new("Folder", player) + stats.Name = "leaderstats" + local coins = Instance.new("IntValue", stats) + coins.Name = "Монеты" + coins.Value = 0 + + coins.Changed:Connect(function(newVal) + if newVal >= 10 then + print("Собрал все!") + end + end) +end)`}} + /> ), }, @@ -3949,34 +4039,62 @@ game.onMessage('coin', () => { <>

При касании переместить игрока (портал) или - сдвинуть сам объект (движущаяся платформа). + сдвинуть сам объект.

-

Портал — телепорт игрока в точку:

+

Портал — телепорт игрока:

- {`game.self.onTouch(() => { - game.player.teleport(0, 20, 50); // x, y, z назначения + {`game.self.onTouch(() => { + game.player.teleport(0, 20, 50); game.sound.play('win'); game.camera.shake(0.15, 0.2); -});`} -

- Сдвинуть сам объект при касании (например, опустить - мост). game.self.move ставит новую позицию: -

- {`let opened = false; +});`}} + lua={{`local part = script.Parent + +part.Touched:Connect(function(hit) + local h = hit.Parent:FindFirstChild("Humanoid") + if not h then return end + local hrp = hit.Parent:FindFirstChild("HumanoidRootPart") + if hrp then + hrp.CFrame = CFrame.new(0, 20, 50) + end +end)`}} + /> +

Сдвинуть сам объект при касании (опустить мост):

+ {`let opened = false; game.self.onTouch(() => { if (opened) return; opened = true; const p = game.self.position; - game.self.move(p.x, p.y - 3, p.z); // уехал вниз на 3 м -});`} -

- Плавно сдвинуть — через game.tween (анимация): -

- {`// дверь уезжает вбок за 1 секунду -const p = game.self.position; + game.self.move(p.x, p.y - 3, p.z); +});`}} + lua={{`local part = script.Parent +local opened = false + +part.Touched:Connect(function(hit) + if opened then return end + if not hit.Parent:FindFirstChild("Humanoid") then return end + opened = true + part.Position = part.Position - Vector3.new(0, 3, 0) +end)`}} + /> +

Плавно сдвинуть — через TweenService:

+ {`const p = game.self.position; game.self.onTouch(() => { - game.tween(game.self.ref, { x: p.x + 4 }, { duration: 1, easing: 'ease' }); -});`} + game.tween(game.self.ref, { x: p.x + 4 }, + { duration: 1, easing: 'ease' }); +});`}} + lua={{`local TweenService = game:GetService("TweenService") +local part = script.Parent + +part.Touched:Connect(function(hit) + if not hit.Parent:FindFirstChild("Humanoid") then return end + local goal = { Position = part.Position + Vector3.new(4, 0, 0) } + TweenService:Create(part, TweenInfo.new(1), goal):Play() +end)`}} + /> ), }, @@ -3986,88 +4104,169 @@ game.self.onTouch(() => { body: ( <>

- Любой примитив можно создать и менять из - скрипта. Вот все свойства и как их задать. + Любой примитив можно создать и менять из скрипта.

-

Создать примитив со всеми свойствами:

- {`const box = game.scene.spawn('cube', { - x: 0, y: 2, z: 0, // позиция - sx: 2, sy: 1, sz: 3, // размер по осям (ширина/высота/глубина) - rotationX: 0, rotationY: 0.8, rotationZ: 0, // поворот в радианах - color: '#ff5533', // цвет (hex) - material: 'neon', // matte | neon | metal | glass | studs +

Создать примитив:

+ {`const box = game.scene.spawn('cube', { + x: 0, y: 2, z: 0, + sx: 2, sy: 1, sz: 3, + rotationX: 0, rotationY: 0.8, rotationZ: 0, + color: '#ff5533', + material: 'neon', name: 'МойКуб', - anchored: true, // true = висит на месте; false = падает (физика) - canCollide: true, // false = игрок проходит насквозь + anchored: true, + canCollide: true, visible: true, - mass: 5, // масса (если anchored:false) -});`}
-

Типы примитивов для spawn:

- {`'cube' 'sphere' 'cylinder' 'cone' 'pyramid' 'torus' 'wedge' 'cornerwedge' 'plane'`} -

Менять свойства уже существующего объекта:

- {`game.scene.setColor(box, '#00ff88'); // цвет -game.scene.setMaterial(box, 'glass'); // материал -game.scene.setVisible(box, false); // спрятать -game.scene.setCollide(box, false); // сделать проходимым -game.scene.setOpacity(box, 0.4); // полупрозрачность (1=видно, 0=невидимо) -game.scene.setScale(box, 3, 1, 1); // новый размер -game.scene.move(box, 5, 2, 0); // переместить -game.scene.setRotation(box, 0, 1.57, 0); // повернуть (радианы) -game.scene.setLabel(box, 'Привет!', { color:'#fff', height: 2.5 });`} -

- Удобнее — через объект-прокси (присваивание свойств): -

- {`const obj = game.scene.findOne('МойКуб'); + mass: 5, +});`}} + lua={{`local box = Instance.new("Part") +box.Name = "МойКуб" +box.Shape = Enum.PartType.Block +box.Size = Vector3.new(2, 1, 3) +box.Position = Vector3.new(0, 2, 0) +box.Orientation = Vector3.new(0, math.deg(0.8), 0) -- градусы +box.Color = Color3.fromRGB(255, 85, 51) +box.Material = Enum.Material.Neon +box.Anchored = true +box.CanCollide = true +box.Transparency = 0 +-- Если Anchored=false: box.Mass читается, не задаётся. +-- Управляется через PhysicalProperties и Density. +box.Parent = workspace`}} + /> +

Типы примитивов:

+ {`'cube' 'sphere' 'cylinder' 'cone' 'pyramid' 'torus' 'wedge' 'cornerwedge' 'plane'`}} + lua={{`Enum.PartType.Block / Ball / Cylinder / Wedge / CornerWedge +-- Для cone/pyramid/torus используются MeshPart или SpecialMesh: +local sphere = Instance.new("Part") +sphere.Shape = Enum.PartType.Ball -- сфера`}} + /> +

Менять свойства существующего объекта:

+ {`game.scene.setColor(box, '#00ff88'); +game.scene.setMaterial(box, 'glass'); +game.scene.setVisible(box, false); +game.scene.setCollide(box, false); +game.scene.setOpacity(box, 0.4); +game.scene.setScale(box, 3, 1, 1); +game.scene.move(box, 5, 2, 0); +game.scene.setRotation(box, 0, 1.57, 0); +game.scene.setLabel(box, 'Привет!', { color:'#fff', height: 2.5 }); + +// Или через прокси: +const obj = game.scene.findOne('МойКуб'); obj.color = '#ffd700'; obj.material = 'metal'; -obj.scale = 2; // равномерный масштаб +obj.scale = 2; obj.opacity = 0.5; obj.visible = false; obj.canCollide = false; obj.position = { x: 0, y: 10, z: 0 }; obj.rotateY(1.57); -obj.destroy(); // удалить`} - - Радианы: поворот задаётся в радианах, не градусах. - 90° = Math.PI/2 ≈ 1.57, 180° = Math.PI ≈ 3.14. - +obj.destroy();`}} + lua={{`-- Прямое присваивание свойств Part +box.Color = Color3.fromRGB(0, 255, 136) +box.Material = Enum.Material.Glass +box.Transparency = 0.6 -- 0=видно, 1=невидимо +box.CanCollide = false +box.Size = Vector3.new(3, 1, 1) +box.Position = Vector3.new(5, 2, 0) +box.Orientation = Vector3.new(0, 90, 0) +-- Скрыть: Transparency = 1 (или Parent = nil) + +box:Destroy() -- удалить`}} + /> + + Радианы: поворот в радианах. 90° = Math.PI/2 ≈ 1.57. + } + lua={ + Градусы: Orientation в градусах (не радианах). + Для CFrame.Angles — в радианах: math.rad(90). + } + /> ), }, { id: 'recipes-anim', - title: 'S7. Движение, вращение, мигание (onTick и tween)', + title: 'S7. Движение, вращение, мигание', body: ( <> -

- Вращающийся объект (монета, портал) — крутим каждый - кадр через game.onTick (dt = время кадра): -

+

Вращающийся объект (монета, портал):

- {`let angle = 0; + {`let angle = 0; game.onTick((dt) => { - angle = angle + dt * 2; // скорость вращения + angle = angle + dt * 2; game.self.rotateY(angle); -});`} -

Парение вверх-вниз (плавно качается):

- {`const start = game.self.position; +});`}} + lua={{`local RunService = game:GetService("RunService") +local part = script.Parent +local angle = 0 + +RunService.Heartbeat:Connect(function(dt) + angle = angle + dt * 2 + part.CFrame = CFrame.new(part.Position) * CFrame.Angles(0, angle, 0) +end)`}} + /> +

Парение вверх-вниз:

+ {`const start = game.self.position; let t = 0; game.onTick((dt) => { t = t + dt; - const dy = Math.sin(t * 2) * 0.4; // амплитуда 0.4 м + const dy = Math.sin(t * 2) * 0.4; game.self.move(start.x, start.y + dy, start.z); -});`} -

Пульсация размера через tween (бесконечно туда-обратно):

- {`game.tween(game.self.ref, { sy: 1.4 }, { +});`}} + lua={{`local RunService = game:GetService("RunService") +local part = script.Parent +local startPos = part.Position +local t = 0 + +RunService.Heartbeat:Connect(function(dt) + t = t + dt + local dy = math.sin(t * 2) * 0.4 + part.Position = Vector3.new(startPos.X, startPos.Y + dy, startPos.Z) +end)`}} + /> +

Пульсация размера:

+ {`game.tween(game.self.ref, { sy: 1.4 }, { duration: 0.6, easing: 'ease', yoyo: true, repeat: -1 -});`} -

Мигание цветом каждые полсекунды:

- {`let on = false; +});`}} + lua={{`local TweenService = game:GetService("TweenService") +local part = script.Parent +local origSize = part.Size + +local info = TweenInfo.new( + 0.6, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut, + -1, -- бесконечно + true -- yoyo (туда-обратно) +) +local goal = { Size = Vector3.new(origSize.X, origSize.Y * 1.4, origSize.Z) } +TweenService:Create(part, info, goal):Play()`}} + /> +

Мигание цветом:

+ {`let on = false; game.every(0.5, () => { on = !on; game.self.setColor(on ? '#ff0000' : '#330000'); -});`} +});`}} + lua={{`local part = script.Parent +local on = false + +while true do + task.wait(0.5) + on = not on + part.Color = on and Color3.fromRGB(255, 0, 0) + or Color3.fromRGB(51, 0, 0) +end`}} + /> ), }, @@ -4076,47 +4275,92 @@ game.every(0.5, () => { title: 'S8. Кнопка по E и дверь', body: ( <> -

- Взаимодействие по клавише E (как в Roblox ProximityPrompt) - — через game.self.onInteract. Появляется подсказка - «[E] …» когда игрок рядом. -

+

Взаимодействие по клавише E:

- {`game.self.onInteract(() => { + {`game.self.onInteract(() => { game.ui.showText('Открыто!', 2); game.broadcast('open-door'); -}, { text: 'Открыть', key: 'e', distance: 4 });`} -

На двери — глобальный/объектный скрипт, который её открывает:

+}, { text: 'Открыть', key: 'e', distance: 4 });`}} + lua={{`local part = script.Parent + +local prompt = Instance.new("ProximityPrompt") +prompt.ActionText = "Открыть" +prompt.MaxActivationDistance = 4 +prompt.KeyboardKeyCode = Enum.KeyCode.E +prompt.Parent = part + +-- BindableEvent для оповещения "open-door" +local doorEvent = workspace:FindFirstChild("DoorOpenEvent") + or Instance.new("BindableEvent", workspace) +doorEvent.Name = "DoorOpenEvent" + +prompt.Triggered:Connect(function(player) + print("Открыто!") + doorEvent:Fire() +end)`}} + /> +

На двери:

- {`const closed = game.self.position; + {`const closed = game.self.position; game.onMessage('open-door', () => { - // плавно уехать вверх (открыться) - game.tween(game.self.ref, { y: closed.y + 4 }, { duration: 1, easing: 'ease' }); - game.self.setCollide(false); // через неё можно пройти -});`} + game.tween(game.self.ref, { y: closed.y + 4 }, + { duration: 1, easing: 'ease' }); + game.self.setCollide(false); +});`}} + lua={{`local TweenService = game:GetService("TweenService") +local door = script.Parent +local closedPos = door.Position + +local doorEvent = workspace:WaitForChild("DoorOpenEvent") + +doorEvent.Event:Connect(function() + local goal = { Position = closedPos + Vector3.new(0, 4, 0) } + TweenService:Create(door, TweenInfo.new(1), goal):Play() + door.CanCollide = false +end)`}} + /> - holdDuration: 1 в опциях onInteract — держать E - 1 секунду (для важных действий). distance — - с какого расстояния появляется подсказка. + holdDuration: 1 в onInteract / prompt.HoldDuration = 1 + в Roblox — держать E одну секунду. ), }, { id: 'recipes-gui-timer', - title: 'S9. Надписи на экране, таймер, кнопки GUI', + title: 'S9. HUD надписи, таймер, кнопки', body: ( <> -

HUD-надписи в углу и по центру:

+

HUD-надписи:

- {`game.ui.score = 0; // счётчик «Очки: 0» в углу -game.ui.score = 50; // обновить -game.ui.timer = 60; // таймер mm:ss в углу -game.ui.showText('Старт!', 2); // крупно по центру на 2 сек -game.ui.set('hp', 'Жизни: 3', { x: 50, y: 90, color: '#fff' }); // своя метка -game.ui.remove('hp'); // убрать метку`} -

Обратный отсчёт и проигрыш по времени:

- {`let time = 30; + {`game.ui.score = 0; // счётчик в углу +game.ui.score = 50; +game.ui.timer = 60; // таймер +game.ui.showText('Старт!', 2); +game.ui.set('hp', 'Жизни: 3', { x: 50, y: 90, color: '#fff' }); +game.ui.remove('hp');`}} + lua={{`-- В Roblox HUD = leaderstats папка (см. G7) или свой ScreenGui +local Players = game:GetService("Players") +local player = Players.LocalPlayer +local gui = player:WaitForChild("PlayerGui") + +-- Своя метка по центру +local screen = Instance.new("ScreenGui", gui) +local label = Instance.new("TextLabel", screen) +label.Size = UDim2.new(0.4, 0, 0.1, 0) +label.Position = UDim2.new(0.3, 0, 0.4, 0) +label.Text = "Старт!" +label.TextScaled = true +label.BackgroundTransparency = 0.5 + +task.delay(2, function() screen:Destroy() end)`}} + /> +

Обратный отсчёт:

+ {`let time = 30; game.ui.timer = time; const id = game.every(1, () => { time = time - 1; @@ -4126,16 +4370,48 @@ const id = game.every(1, () => { game.ui.showText('Время вышло!', 3); game.player.kill(); } -});`} -

Кнопка на экране (GUI) и обработка клика:

- {`const btn = game.gui.create('button', { +});`}} + lua={{`local time = 30 + +while time > 0 do + task.wait(1) + time = time - 1 + print("Осталось: " .. time) +end + +print("Время вышло!") +-- Убить локального игрока +local player = game:GetService("Players").LocalPlayer +if player.Character and player.Character:FindFirstChild("Humanoid") then + player.Character.Humanoid.Health = 0 +end`}} + /> +

Кнопка GUI:

+ {`const btn = game.gui.create('button', { name: 'start', text: 'НАЧАТЬ', x: 50, y: 80, - w: 20, h: 8, bg: '#3a6ee0', color: '#fff', fontSize: 18, borderRadius: 12 + w: 20, h: 8, bg: '#3a6ee0', color: '#fff' }); game.gui.onClick(btn, () => { game.ui.showText('Поехали!', 2); game.gui.hide(btn); -});`} +});`}} + lua={{`local player = game:GetService("Players").LocalPlayer +local gui = player:WaitForChild("PlayerGui") +local screen = Instance.new("ScreenGui", gui) + +local btn = Instance.new("TextButton", screen) +btn.Size = UDim2.new(0.2, 0, 0.08, 0) +btn.Position = UDim2.new(0.4, 0, 0.8, 0) +btn.Text = "НАЧАТЬ" +btn.BackgroundColor3 = Color3.fromRGB(58, 110, 224) +btn.TextColor3 = Color3.new(1, 1, 1) + +btn.MouseButton1Click:Connect(function() + print("Поехали!") + btn.Visible = false +end)`}} + /> ), }, @@ -4144,34 +4420,75 @@ game.gui.onClick(btn, () => { title: 'S10. Спавн, падение, проверка падения вниз', body: ( <> -

Спавнить объекты с неба каждую секунду (ловилка):

+

Спавнить объекты с неба каждую секунду:

- {`game.every(1, () => { + {`game.every(1, () => { const x = game.random(-10, 10); game.scene.spawn('sphere', { x: x, y: 20, z: 0, color: '#ffd700', material: 'neon', - anchored: false, // будет падать (физика) - lifetime: 8 // само исчезнет через 8 сек + anchored: false, + lifetime: 8 }); -});`} -

- Игрок упал вниз (за карту) — вернуть на спавн. Проверяем - высоту каждый кадр: -

- {`game.onTick(() => { +});`}} + lua={{`local Debris = game:GetService("Debris") + +while true do + task.wait(1) + local x = math.random(-10, 10) + + local ball = Instance.new("Part") + ball.Shape = Enum.PartType.Ball + ball.Size = Vector3.new(1, 1, 1) + ball.Position = Vector3.new(x, 20, 0) + ball.Color = Color3.fromRGB(255, 215, 0) + ball.Material = Enum.Material.Neon + ball.Anchored = false -- падает + ball.Parent = workspace + + Debris:AddItem(ball, 8) -- удалить через 8 сек +end`}} + /> +

Игрок упал вниз:

+ {`game.onTick(() => { if (game.player.position.y < -10) { game.player.respawn(); - game.ui.showText('Упал! Назад на старт.', 2); + game.ui.showText('Упал!', 2); } -});`} -

Финиш — дошёл до зоны, победа:

+});`}} + lua={{`local Players = game:GetService("Players") +local RunService = game:GetService("RunService") + +RunService.Heartbeat:Connect(function() + local player = Players.LocalPlayer + if not player.Character then return end + local hrp = player.Character:FindFirstChild("HumanoidRootPart") + if hrp and hrp.Position.Y < -10 then + player:LoadCharacter() -- респавн + print("Упал!") + end +end)`}} + /> +

Финиш:

- {`game.self.onTouch(() => { - game.ui.showText('🏁 ПОБЕДА!', 4); + {`game.self.onTouch(() => { + game.ui.showText('ПОБЕДА!', 4); game.sound.play('win'); - game.player.setInputBlocked(true); // заморозить управление -});`} + game.player.setInputBlocked(true); +});`}} + lua={{`local part = script.Parent + +part.Touched:Connect(function(hit) + local h = hit.Parent:FindFirstChild("Humanoid") + if not h then return end + print("ПОБЕДА!") + h.WalkSpeed = 0 -- заморозить + h.JumpPower = 0 +end)`}} + /> ), }, @@ -4180,30 +4497,68 @@ game.gui.onClick(btn, () => { title: 'S11. Враг, который идёт за игроком', body: ( <> -

- NPC/враг, который преследует игрока и наносит урон. -

+

NPC/враг, преследующий игрока:

- {`const enemy = game.scene.spawnNpc('zombie', { + {`const enemy = game.scene.spawnNpc('zombie', { x: 10, y: 0, z: 10, hp: 100, name: 'Зомби', speed: 3 }); -enemy.follow('player'); // идти за игроком +enemy.follow('player'); enemy.say('Хочу тебя поймать!', 3); enemy.onDeath(() => { game.ui.showText('Враг повержен!', 2); - game.fx.damageFloater(enemy.position, 0, { isHeal: true }); -});`} -

Урон игроку, когда враг близко:

- {`game.every(0.5, () => { +}); + +// Урон когда близко +game.every(0.5, () => { const d = game.distance(enemy.position, game.player.position); if (d < 2) game.player.damage(10); -});`} - - Облачка урона над всеми мобами одной строкой: - game.fx.autoMobFloaters(true). - +});`}} + lua={{`-- Враг должен быть Model с Humanoid и HumanoidRootPart в workspace. +-- Например workspace.Зомби. +local Players = game:GetService("Players") +local enemy = workspace:WaitForChild("Зомби") +local humanoid = enemy:WaitForChild("Humanoid") +local hrp = enemy:WaitForChild("HumanoidRootPart") + +-- Преследование игрока +task.spawn(function() + while enemy.Parent and humanoid.Health > 0 do + local player = Players:GetPlayers()[1] + if player and player.Character then + local target = player.Character:FindFirstChild("HumanoidRootPart") + if target then + humanoid:MoveTo(target.Position) + end + end + task.wait(0.5) + end +end) + +humanoid.Died:Connect(function() + print("Враг повержен!") +end) + +-- Урон когда близко +task.spawn(function() + while enemy.Parent and humanoid.Health > 0 do + task.wait(0.5) + local player = Players:GetPlayers()[1] + if player and player.Character then + local target = player.Character:FindFirstChild("HumanoidRootPart") + local playerHum = player.Character:FindFirstChild("Humanoid") + if target and playerHum then + local dist = (target.Position - hrp.Position).Magnitude + if dist < 2 then + playerHum:TakeDamage(10) + end + end + end + end +end)`}} + /> ), }, @@ -4212,35 +4567,74 @@ enemy.onDeath(() => { title: 'S12. Сохранение прогресса и лидерборд', body: ( <> -

- Лидерборд (таблица очков справа) — объяви стат и - прибавляй: -

+

Лидерборд:

- {`game.leaderstats.define('Монеты', { initial: 0 }); -// прибавить текущему игроку: -game.leaderstats.me.add('Монеты', 1);`} -

- Сохранение между сессиями (прогресс не теряется после - выхода): -

- {`// записать -game.save.merge('progress', { - patch: { level: 3 }, // обычные поля - increment: { coins: 10 }, // атомарно прибавить - max: { bestScore: 5000 } // запишется только если больше старого + {`game.leaderstats.define('Монеты', { initial: 0 }); +game.leaderstats.me.add('Монеты', 1);`}} + lua={{`-- leaderstats: см. G7 +local Players = game:GetService("Players") +Players.PlayerAdded:Connect(function(player) + local stats = Instance.new("Folder", player) + stats.Name = "leaderstats" + local coins = Instance.new("IntValue", stats) + coins.Name = "Монеты" + coins.Value = 0 +end) + +-- Прибавить монетку (например, при сборе) +local function addCoin(player, amount) + local stats = player:FindFirstChild("leaderstats") + if stats then + stats.Монеты.Value = stats.Монеты.Value + amount + end +end`}} + /> +

Сохранение между сессиями:

+ {`game.save.merge('progress', { + patch: { level: 3 }, + increment: { coins: 10 }, + max: { bestScore: 5000 } }); -// прочитать при старте game.save.get('progress', (data) => { if (data) { game.ui.showText('С возвращением! Уровень ' + data.level, 3); } -});`} +});`}} + lua={{`-- В Roblox сохранение через DataStoreService (требует онлайн-игру) +local DataStoreService = game:GetService("DataStoreService") +local progress = DataStoreService:GetDataStore("Progress") +local Players = game:GetService("Players") + +Players.PlayerAdded:Connect(function(player) + -- Прочитать при входе + local success, data = pcall(function() + return progress:GetAsync(player.UserId) + end) + if success and data then + print("С возвращением! Уровень " .. (data.level or 1)) + -- Применить прогресс: leaderstats.Монеты.Value = data.coins и т.п. + end +end) + +Players.PlayerRemoving:Connect(function(player) + -- Сохранить при выходе + local data = { + level = 3, + coins = 10, + bestScore = 5000, + } + pcall(function() + progress:SetAsync(player.UserId, data) + end) +end)`}} + /> - Собери всё вместе: монетки шлют broadcast → глобальный скрипт - считает в leaderstats → раз в N монет сохраняет через - game.save. Получится игра с прогрессом как в настоящем Roblox. + Собери всё вместе: монетки добавляются в leaderstats → + глобальный скрипт раз в N монет вызывает save.merge + или DataStore:SetAsync. Получится игра с прогрессом. ),