Hungrit Docs

Doors and switches#

Doors that open on a click, close by themselves, open as you walk up, or answer to a switch across the room. Each one is complete: paste it onto a box.

A door that opens when you click it#

Paste this on the door
--!strictlocal self = require("self") local open = self.store:get("open") == "yes" local function swing()    self:tween({        rot = { x = 0, y = open and 90 or 0, z = 0 },        seconds = 0.6,        ease = "in_out",    })end -- put it back where it was, without animating, after a restartself:rotate_to(0, open and 90 or 0, 0) self:on_touch(function()    open = not open    self.store:set("open", open and "yes" or "no")    swing()end)

Click the door and it swings a quarter turn open. Click again and it closes. Whichever way you left it, that is how you find it after a restart.

A door that closes itself#

Paste this on the door
--!strictlocal self = require("self") local WAIT = 5local open = falselocal timer = nil local function set(to_open: boolean)    open = to_open    self:tween({        rot = { x = 0, y = open and 90 or 0, z = 0 },        seconds = 0.6,        ease = "in_out",    })end self:on_touch(function()    if timer then        timer:cancel()        timer = nil    end     set(not open)     if open then        timer = self:after(WAIT, function()            timer = nil            if open then set(false) end        end)    endend)

Two habits worth keeping:

  • Cancel the old timer before arming a new one. Without it, clicking twice leaves two timers and the door slams while somebody is walking through.
  • Check the state inside the timer. Five seconds is long enough for somebody to have closed it by hand.

A door that opens by itself as you approach#

No clicking at all:

Paste this on the door
--!strictlocal self = require("self") local inside = 0 local function set(to_open: boolean)    self:tween({        rot = { x = 0, y = to_open and 90 or 0, z = 0 },        seconds = 0.5,        ease = "in_out",    })end self:on_region({    radius = 4,     near = function()        inside += 1        if inside == 1 then set(true) end    end,     far = function()        inside -= 1        if inside <= 0 then            inside = 0            set(false)        end    end,})

Walk within four metres and it opens ahead of you. It stays open until the last person has walked away.

Count people, do not track a boolean. Two people inside and one leaving must not close the door. The counter is clamped at zero because a player who disconnects inside gets their far, so every near gets its pair, and you would rather be safe than negative.

A switch across the room#

The switch and the door are different objects. They talk with events.

Paste this on the switch
--!strictlocal self = require("self")local events = require("events") self:on_touch(function(player)    events:emit("gate", { by = player.id })end)
Paste this on the door
--!strictlocal self = require("self")local events = require("events") local open = false events:listen("gate", function()    open = not open    self:tween({        rot = { x = 0, y = open and 90 or 0, z = 0 },        seconds = 0.6,        ease = "in_out",    })end)

Touch the switch and the door swings, across the room, out of sight.

Three switches and five doors work with no extra code: everything that emits "gate" opens everything that listens for it.

emit is private to your scripts. Another creator's build cannot hear it, even if they guess the name.

A door only the owner may open#

Luau
self:on_touch(function(player)    if player.id ~= self:owner() then        return    end    -- ...end)

A door that opens on a worn badge#

Luau
--!strictlocal self = require("self")local players = require("players") local BADGE = "…the asset id of the badge…" self:on_touch(function(player)    if not players:is_wearing(player.id, BADGE) then        print(player.name .. " has no badge")        return    end    -- ...end)

is_wearing counts visible items only, and answers nil rather than false, if the person is not in the region.

Opening without moving#

A door does not have to swing. Turning off collision lets people walk through a solid-looking doorway, which is what a force field or a shimmering portal wants:

Luau
self:set({ collide = false, transparency = 0.6 })

Making the click feel instant#

A click travels to the server and back, which is a visible pause. Describe the movement as well, and the player's own machine plays it immediately:

Luau
self:on_touch({    predict = {        { when = "open",            set = { open = false },          tween = { rot = { x = 0, y = 0,  z = 0 }, seconds = 0.6 } },        { when = { "not", "open" }, set = { open = true },          tween = { rot = { x = 0, y = 90, z = 0 }, seconds = 0.6 } },    },    server = function(player)        print(player.name .. " used the door")    end,})

Careful

Only predict what always succeeds. A locked door, a purchase or anything that can be refused would be seen to happen and then snap back.

See also#

Updated 17 October 2026