feat: 50 игр на Lua + импорт Roblox для всех + поддержка Lua в плеере #39

Merged
min merged 215 commits from feat/lua-50-games-bundle into main 2026-06-09 21:59:25 +00:00
Showing only changes of commit 07fb192623 - Show all commits

View File

@ -3835,15 +3835,13 @@ game.onTick(() => {
},
{
id: 'recipes-touch',
title: 'S2. Касание объекта (onTouch)',
title: 'S2. Касание объекта',
body: (
<>
<p>
Самое частое событие <b>игрок коснулся объекта</b>. Вешаем
скрипт на объект и подписываемся через <code>game.self.onTouch</code>.
</p>
<p>Самое частое событие <b>игрок коснулся объекта</b>.</p>
<ScriptKind kind="object" on="любой объект (плита, зона, предмет)" />
<Code>{`// Игрок наступил на объект — показать надпись и звук
<LangTabs
js={<Code>{`// Игрок наступил на объект — показать надпись и звук
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);
});`}</Code>
<p>
Можно подписаться и на <b>чужой</b> объект из глобального
скрипта найди его по имени:
</p>
});`}</Code>}
lua={<Code>{`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)`}</Code>}
/>
<p>Подписаться на <b>чужой</b> объект из глобального скрипта:</p>
<ScriptKind kind="global" />
<Code>{`const trap = game.scene.findOne('Ловушка');
trap.onTouch(() => game.player.damage(20));`}</Code>
<LangTabs
js={<Code>{`const trap = game.scene.findOne('Ловушка');
trap.onTouch(() => game.player.damage(20));`}</Code>}
lua={<Code>{`local trap = workspace:WaitForChild("Ловушка")
trap.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then humanoid:TakeDamage(20) end
end)`}</Code>}
/>
</>
),
},
@ -3870,30 +3890,65 @@ trap.onTouch(() => game.player.damage(20));`}</Code>
<>
<p>
<b>Килблок</b> объект, который наносит урон или мгновенно
убивает, когда игрок его коснулся (лава, шипы, кислота).
убивает при касании (лава, шипы, кислота).
</p>
<ScriptKind kind="object" on="блок-ловушку (лава, шипы)" />
<Code>{`// Мгновенная смерть при касании
<LangTabs
js={<Code>{`// Мгновенная смерть при касании
game.self.onTouch(() => {
game.player.kill();
game.ui.showText('💀 Ты сгорел в лаве!', 2);
});`}</Code>
game.ui.showText('Ты сгорел в лаве!', 2);
});`}</Code>}
lua={<Code>{`local part = script.Parent
part.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then
humanoid.Health = 0 -- мгновенная смерть
end
end)`}</Code>}
/>
<p>Если хочешь не убивать сразу, а наносить урон:</p>
<Code>{`// Урон 25 при касании (учитывает кадры неуязвимости)
game.self.onTouch(() => {
<LangTabs
js={<Code>{`game.self.onTouch(() => {
game.player.damage(25);
game.camera.shake(0.2, 0.3); // лёгкая тряска
});`}</Code>
<p>
<b>Постоянный урон</b>, пока игрок стоит в зоне (например,
ядовитое облако) урон каждые 0.5 сек, пока касается:
</p>
<Code>{`let inside = false;
game.camera.shake(0.2, 0.3);
});`}</Code>}
lua={<Code>{`local part = script.Parent
part.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then humanoid:TakeDamage(25) end
end)`}</Code>}
/>
<p><b>Постоянный урон</b>, пока игрок стоит в зоне:</p>
<LangTabs
js={<Code>{`let inside = false;
game.self.onTouch(() => { inside = true; });
game.self.onUntouch(() => { inside = false; });
game.every(0.5, () => {
if (inside) game.player.damage(5);
});`}</Code>
});`}</Code>}
lua={<Code>{`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`}</Code>}
/>
<Try>
Сделай красный неоновый куб, повесь на него скрипт смерти
получится лава. Поставь его в проёме как преграду.
@ -3907,38 +3962,73 @@ game.every(0.5, () => {
body: (
<>
<p>
Предмет <b>исчезает</b>, когда игрок его коснулся основа
сбора монеток, ключей, бонусов.
Предмет <b>исчезает</b> при касании основа сбора монет.
</p>
<ScriptKind kind="object" on="монетку / собираемый предмет" />
<Code>{`// Простое исчезновение + звук
game.self.onTouch(() => {
<LangTabs
js={<Code>{`game.self.onTouch(() => {
game.sound.play('coin');
game.self.delete();
});`}</Code>
<p>
Со <b>счётчиком</b>: предмет сообщает глобальному скрипту,
тот считает. На монетке:
</p>
<Code>{`game.self.onTouch(() => {
game.broadcast('coin'); // сообщить всем скриптам
});`}</Code>}
lua={<Code>{`local part = script.Parent
part.Touched:Connect(function(hit)
local h = hit.Parent:FindFirstChild("Humanoid")
if h then
part:Destroy()
end
end)`}</Code>}
/>
<p>Со <b>счётчиком</b>: монетка увеличивает leaderstats игрока:</p>
<LangTabs
js={<Code>{`game.self.onTouch(() => {
game.broadcast('coin');
game.self.delete();
});`}</Code>
<p>В глобальном скрипте приём и счёт:</p>
});`}</Code>}
lua={<Code>{`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)`}</Code>}
/>
<p>JS: глобальный скрипт принимает broadcast и считает:</p>
<ScriptKind kind="global" />
<Code>{`let score = 0;
<LangTabs
js={<Code>{`let score = 0;
game.ui.score = 0;
game.onMessage('coin', () => {
score = score + 1;
game.ui.score = score; // обновить счётчик в углу
if (score >= 10) game.ui.showText('🏆 Собрал все!', 3);
});`}</Code>
<Note>
<b>Не</b> ставь счётчик на саму монетку каждая монетка это
свой скрипт, они не видят переменные друг друга. Считай в
одном глобальном скрипте, монетки только шлют
<code> game.broadcast</code>.
</Note>
game.ui.score = score;
if (score >= 10) game.ui.showText('Собрал все!', 3);
});`}</Code>}
lua={<Code>{`-- В 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)`}</Code>}
/>
</>
),
},
@ -3949,34 +4039,62 @@ game.onMessage('coin', () => {
<>
<p>
При касании <b>переместить игрока</b> (портал) или
<b> сдвинуть сам объект</b> (движущаяся платформа).
<b> сдвинуть сам объект</b>.
</p>
<p><b>Портал</b> телепорт игрока в точку:</p>
<p><b>Портал</b> телепорт игрока:</p>
<ScriptKind kind="object" on="портал" />
<Code>{`game.self.onTouch(() => {
game.player.teleport(0, 20, 50); // x, y, z назначения
<LangTabs
js={<Code>{`game.self.onTouch(() => {
game.player.teleport(0, 20, 50);
game.sound.play('win');
game.camera.shake(0.15, 0.2);
});`}</Code>
<p>
<b>Сдвинуть сам объект</b> при касании (например, опустить
мост). <code>game.self.move</code> ставит новую позицию:
</p>
<Code>{`let opened = false;
});`}</Code>}
lua={<Code>{`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)`}</Code>}
/>
<p><b>Сдвинуть сам объект</b> при касании (опустить мост):</p>
<LangTabs
js={<Code>{`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 м
});`}</Code>
<p>
<b>Плавно</b> сдвинуть через <code>game.tween</code> (анимация):
</p>
<Code>{`// дверь уезжает вбок за 1 секунду
const p = game.self.position;
game.self.move(p.x, p.y - 3, p.z);
});`}</Code>}
lua={<Code>{`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)`}</Code>}
/>
<p><b>Плавно</b> сдвинуть через TweenService:</p>
<LangTabs
js={<Code>{`const p = game.self.position;
game.self.onTouch(() => {
game.tween(game.self.ref, { x: p.x + 4 }, { duration: 1, easing: 'ease' });
});`}</Code>
game.tween(game.self.ref, { x: p.x + 4 },
{ duration: 1, easing: 'ease' });
});`}</Code>}
lua={<Code>{`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)`}</Code>}
/>
</>
),
},
@ -3986,88 +4104,169 @@ game.self.onTouch(() => {
body: (
<>
<p>
Любой примитив можно <b>создать</b> и <b>менять</b> из
скрипта. Вот все свойства и как их задать.
Любой примитив можно <b>создать</b> и <b>менять</b> из скрипта.
</p>
<ScriptKind kind="global" />
<p><b>Создать примитив</b> со всеми свойствами:</p>
<Code>{`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
<p><b>Создать примитив</b>:</p>
<LangTabs
js={<Code>{`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)
});`}</Code>
<p>Типы примитивов для <code>spawn</code>:</p>
<Code>{`'cube' 'sphere' 'cylinder' 'cone' 'pyramid' 'torus' 'wedge' 'cornerwedge' 'plane'`}</Code>
<p><b>Менять свойства</b> уже существующего объекта:</p>
<Code>{`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 });`}</Code>
<p>
Удобнее через <b>объект-прокси</b> (присваивание свойств):
</p>
<Code>{`const obj = game.scene.findOne('МойКуб');
mass: 5,
});`}</Code>}
lua={<Code>{`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`}</Code>}
/>
<p>Типы примитивов:</p>
<LangTabs
js={<Code>{`'cube' 'sphere' 'cylinder' 'cone' 'pyramid' 'torus' 'wedge' 'cornerwedge' 'plane'`}</Code>}
lua={<Code>{`Enum.PartType.Block / Ball / Cylinder / Wedge / CornerWedge
-- Для cone/pyramid/torus используются MeshPart или SpecialMesh:
local sphere = Instance.new("Part")
sphere.Shape = Enum.PartType.Ball -- сфера`}</Code>}
/>
<p><b>Менять свойства</b> существующего объекта:</p>
<LangTabs
js={<Code>{`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(); // удалить`}</Code>
<Note>
<b>Радианы:</b> поворот задаётся в радианах, не градусах.
90° = <code>Math.PI/2</code> 1.57, 180° = <code>Math.PI</code> 3.14.
</Note>
obj.destroy();`}</Code>}
lua={<Code>{`-- Прямое присваивание свойств 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() -- удалить`}</Code>}
/>
<LangTabs
js={<Note>
<b>Радианы:</b> поворот в радианах. 90° = <code>Math.PI/2</code> 1.57.
</Note>}
lua={<Note>
<b>Градусы:</b> Orientation в градусах (не радианах).
Для CFrame.Angles в радианах: <code>math.rad(90)</code>.
</Note>}
/>
</>
),
},
{
id: 'recipes-anim',
title: 'S7. Движение, вращение, мигание (onTick и tween)',
title: 'S7. Движение, вращение, мигание',
body: (
<>
<p>
<b>Вращающийся объект</b> (монета, портал) крутим каждый
кадр через <code>game.onTick</code> (dt = время кадра):
</p>
<p><b>Вращающийся объект</b> (монета, портал):</p>
<ScriptKind kind="object" on="вращающийся объект" />
<Code>{`let angle = 0;
<LangTabs
js={<Code>{`let angle = 0;
game.onTick((dt) => {
angle = angle + dt * 2; // скорость вращения
angle = angle + dt * 2;
game.self.rotateY(angle);
});`}</Code>
<p><b>Парение вверх-вниз</b> (плавно качается):</p>
<Code>{`const start = game.self.position;
});`}</Code>}
lua={<Code>{`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)`}</Code>}
/>
<p><b>Парение вверх-вниз</b>:</p>
<LangTabs
js={<Code>{`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);
});`}</Code>
<p><b>Пульсация размера</b> через tween (бесконечно туда-обратно):</p>
<Code>{`game.tween(game.self.ref, { sy: 1.4 }, {
});`}</Code>}
lua={<Code>{`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)`}</Code>}
/>
<p><b>Пульсация размера</b>:</p>
<LangTabs
js={<Code>{`game.tween(game.self.ref, { sy: 1.4 }, {
duration: 0.6, easing: 'ease', yoyo: true, repeat: -1
});`}</Code>
<p><b>Мигание цветом</b> каждые полсекунды:</p>
<Code>{`let on = false;
});`}</Code>}
lua={<Code>{`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()`}</Code>}
/>
<p><b>Мигание цветом</b>:</p>
<LangTabs
js={<Code>{`let on = false;
game.every(0.5, () => {
on = !on;
game.self.setColor(on ? '#ff0000' : '#330000');
});`}</Code>
});`}</Code>}
lua={<Code>{`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`}</Code>}
/>
</>
),
},
@ -4076,47 +4275,92 @@ game.every(0.5, () => {
title: 'S8. Кнопка по E и дверь',
body: (
<>
<p>
<b>Взаимодействие по клавише E</b> (как в Roblox ProximityPrompt)
через <code>game.self.onInteract</code>. Появляется подсказка
«[E] » когда игрок рядом.
</p>
<p><b>Взаимодействие по клавише E</b>:</p>
<ScriptKind kind="object" on="кнопку / рычаг / сундук" />
<Code>{`game.self.onInteract(() => {
<LangTabs
js={<Code>{`game.self.onInteract(() => {
game.ui.showText('Открыто!', 2);
game.broadcast('open-door');
}, { text: 'Открыть', key: 'e', distance: 4 });`}</Code>
<p>На двери глобальный/объектный скрипт, который её открывает:</p>
}, { text: 'Открыть', key: 'e', distance: 4 });`}</Code>}
lua={<Code>{`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)`}</Code>}
/>
<p>На двери:</p>
<ScriptKind kind="object" on="дверь" />
<Code>{`const closed = game.self.position;
<LangTabs
js={<Code>{`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); // через неё можно пройти
});`}</Code>
game.tween(game.self.ref, { y: closed.y + 4 },
{ duration: 1, easing: 'ease' });
game.self.setCollide(false);
});`}</Code>}
lua={<Code>{`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)`}</Code>}
/>
<Note>
<code>holdDuration: 1</code> в опциях onInteract держать E
1 секунду (для важных действий). <code>distance</code>
с какого расстояния появляется подсказка.
<code>holdDuration: 1</code> в onInteract / <code>prompt.HoldDuration = 1</code>
в Roblox держать E одну секунду.
</Note>
</>
),
},
{
id: 'recipes-gui-timer',
title: 'S9. Надписи на экране, таймер, кнопки GUI',
title: 'S9. HUD надписи, таймер, кнопки',
body: (
<>
<p><b>HUD-надписи</b> в углу и по центру:</p>
<p><b>HUD-надписи</b>:</p>
<ScriptKind kind="global" />
<Code>{`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'); // убрать метку`}</Code>
<p><b>Обратный отсчёт</b> и проигрыш по времени:</p>
<Code>{`let time = 30;
<LangTabs
js={<Code>{`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');`}</Code>}
lua={<Code>{`-- В 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)`}</Code>}
/>
<p><b>Обратный отсчёт</b>:</p>
<LangTabs
js={<Code>{`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();
}
});`}</Code>
<p><b>Кнопка на экране</b> (GUI) и обработка клика:</p>
<Code>{`const btn = game.gui.create('button', {
});`}</Code>}
lua={<Code>{`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`}</Code>}
/>
<p><b>Кнопка GUI</b>:</p>
<LangTabs
js={<Code>{`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);
});`}</Code>
});`}</Code>}
lua={<Code>{`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)`}</Code>}
/>
</>
),
},
@ -4144,34 +4420,75 @@ game.gui.onClick(btn, () => {
title: 'S10. Спавн, падение, проверка падения вниз',
body: (
<>
<p><b>Спавнить объекты с неба</b> каждую секунду (ловилка):</p>
<p><b>Спавнить объекты с неба</b> каждую секунду:</p>
<ScriptKind kind="global" />
<Code>{`game.every(1, () => {
<LangTabs
js={<Code>{`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
});
});`}</Code>
<p>
<b>Игрок упал вниз</b> (за карту) вернуть на спавн. Проверяем
высоту каждый кадр:
</p>
<Code>{`game.onTick(() => {
});`}</Code>}
lua={<Code>{`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`}</Code>}
/>
<p><b>Игрок упал вниз</b>:</p>
<LangTabs
js={<Code>{`game.onTick(() => {
if (game.player.position.y < -10) {
game.player.respawn();
game.ui.showText('Упал! Назад на старт.', 2);
game.ui.showText('Упал!', 2);
}
});`}</Code>
<p><b>Финиш</b> дошёл до зоны, победа:</p>
});`}</Code>}
lua={<Code>{`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)`}</Code>}
/>
<p><b>Финиш</b>:</p>
<ScriptKind kind="object" on="финишную плиту" />
<Code>{`game.self.onTouch(() => {
game.ui.showText('🏁 ПОБЕДА!', 4);
<LangTabs
js={<Code>{`game.self.onTouch(() => {
game.ui.showText('ПОБЕДА!', 4);
game.sound.play('win');
game.player.setInputBlocked(true); // заморозить управление
});`}</Code>
game.player.setInputBlocked(true);
});`}</Code>}
lua={<Code>{`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)`}</Code>}
/>
</>
),
},
@ -4180,30 +4497,68 @@ game.gui.onClick(btn, () => {
title: 'S11. Враг, который идёт за игроком',
body: (
<>
<p>
<b>NPC/враг</b>, который преследует игрока и наносит урон.
</p>
<p><b>NPC/враг</b>, преследующий игрока:</p>
<ScriptKind kind="global" />
<Code>{`const enemy = game.scene.spawnNpc('zombie', {
<LangTabs
js={<Code>{`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 });
});`}</Code>
<p>Урон игроку, когда враг близко:</p>
<Code>{`game.every(0.5, () => {
});
// Урон когда близко
game.every(0.5, () => {
const d = game.distance(enemy.position, game.player.position);
if (d < 2) game.player.damage(10);
});`}</Code>
<Note>
Облачка урона над всеми мобами одной строкой:
<code> game.fx.autoMobFloaters(true)</code>.
</Note>
});`}</Code>}
lua={<Code>{`-- Враг должен быть 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)`}</Code>}
/>
</>
),
},
@ -4212,35 +4567,74 @@ enemy.onDeath(() => {
title: 'S12. Сохранение прогресса и лидерборд',
body: (
<>
<p>
<b>Лидерборд</b> (таблица очков справа) объяви стат и
прибавляй:
</p>
<p><b>Лидерборд</b>:</p>
<ScriptKind kind="global" />
<Code>{`game.leaderstats.define('Монеты', { initial: 0 });
// прибавить текущему игроку:
game.leaderstats.me.add('Монеты', 1);`}</Code>
<p>
<b>Сохранение между сессиями</b> (прогресс не теряется после
выхода):
</p>
<Code>{`// записать
game.save.merge('progress', {
patch: { level: 3 }, // обычные поля
increment: { coins: 10 }, // атомарно прибавить
max: { bestScore: 5000 } // запишется только если больше старого
<LangTabs
js={<Code>{`game.leaderstats.define('Монеты', { initial: 0 });
game.leaderstats.me.add('Монеты', 1);`}</Code>}
lua={<Code>{`-- 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`}</Code>}
/>
<p><b>Сохранение между сессиями</b>:</p>
<LangTabs
js={<Code>{`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);
}
});`}</Code>
});`}</Code>}
lua={<Code>{`-- В 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)`}</Code>}
/>
<Try>
Собери всё вместе: монетки шлют broadcast глобальный скрипт
считает в leaderstats раз в N монет сохраняет через
game.save. Получится игра с прогрессом как в настоящем Roblox.
Собери всё вместе: монетки добавляются в leaderstats
глобальный скрипт раз в N монет вызывает save.merge
или DataStore:SetAsync. Получится игра с прогрессом.
</Try>
</>
),