Keyboard

Similar to a button, except is programmable to take more than one key input. Can be attached to a vehicle seat or clicked by a player. It is a craftable and spawnable non-flammable solid.
Here is a list of possible sizes that reach the maximum malleability (75) that have integer components: 1x1x75, 1x3x25, 1x5x15, 3x5x5
At its default size (5x0.5x2) it has a durability of 1, at its maximum size it has a durability of 3.
By default, its colour is #dcdcdd.
It requires 10 Button
, 4 Quartz
, and 5 Silicon
to be crafted.
Methods
SimulateKeyPress(key, player)
Simulates a key press just like you would press a key on the keyboard.
The parameters for SimulateKeyPress
are as follows:
- The key parameter is a
string
. It can also benil
. - The player parameter is a
string
.
SimulateTextInput(input, player)
Simulates text input just like you would type in the keyboard and enter. This fires the TextInputted
event.
The parameters for SimulateTextInput
are as follows:
- The input parameter is a
string
. It can also benil
. - The player parameter is a
string
.
SimulateUserInput()
Events
KeyPressed(key, keyName, userId)
Fires when the user presses a key.
The parameters for KeyPressed
are as follows:
- The key parameter is the
Enum.KeyCode
of the key that was pressed. It is anEnum.KeyCode
. - The keyName parameter is the letter of the key that was pressed. If the key is non-printable (i.e. shift or backspace)
keyString
will be an empty string. IfShift
is held, it will be capitalised. It is astring
. - The userId parameter is the
UserId
of the player who pressed the key. It is anumber
.
-- Get all of the necessary parts and throw a useful error if something isn't found.
local anchor = assert(GetPart("Anchor"), "no anchor connected")
local thrusterSwitch = assert(GetPart("Switch"), "no switch connected")
local keyboard = assert(GetPart("Keyboard"), "no keyboard connected")
-- Define the `UserId`s of people who are allowed.
local WHITELIST = {
[1178125707] = true,
}
-- Create a dictionary of what key to press to what code to run
local BINDS = {
-- Toggle the anchor state when `R` is pressed.
[Enum.KeyCode.R] = function()
anchor.Anchor = not anchor.Anchor
end,
-- Toggle the switch state when `X` is pressed.
[Enum.KeyCode.X] = function()
thrusterSwitch.SwitchValue = not thrusterSwitch.SwitchValue
end,
}
-- Connect to the `keyboard.KeyPressed` event, we don't need the `keyName` variable,
-- so we'll call it `_` as to say "we're not going to use this" to anyone reading the code.
keyboard.KeyPressed:Connect(function(key, _, userId)
-- If the user *isn't* in the whitelist, *cancel* this function using `return`.
if not WHITELIST[userId] then
return
end
-- Try and find the piece of code to run for the key that was pressed, if we don't find
-- it, cancel, like it's done when the user isn't in the whitelist.
local callback = BINDS[key]
if not callback then
return
end
-- Run the piece of code!
callback()
end)
TextInputted(text, player)
Fires when a player finishes typing into the keyboard. Note that a newline will be present wherever the cursor of the player was when they pressed enter (generally the end, but if they moved their cursor whilst typing, it may be in the middle of the text).
The parameters for TextInputted
are as follows:
- The text parameter is the text inputted into the keyboard. It is a
string
. - The player parameter is the username of the player who inputted the text. It is a
string
.
UserInput(inputObject, userId)
Will fire when a user presses a key, it is already filtered to gameProcessedEvent
being false.
The parameters for UserInput
are as follows:
- The inputObject parameter is the
UserInputObject
produced by the player, is not whitelisted to anyEnum.UserInputState
. It is aUserInputObject
. - The userId parameter is the
UserId
of the player who pressed the input. It is anumber
.
local UPDATES_PER_TICK = 16 -- How many times to rotate per tick, higher is smoother.
local ANGLE_INCREMENT = 4 -- Every 1/UPDATES_PER_TICK seconds increment the servo angle by this.
local SHIFT_HELD_MULTIPLIER = 0.25 -- When shift is held, multiply the angle increment by this.
-- Who can rotate the servos.
local WHITELIST = {
[1178125707] = true,
}
-- Get references to all the hardware, everything is *required* except for the
-- seat, which is optional.
local yawServo = assert(GetPartFromPort(1, "Servo"), "no yaw servo connected")
local pitchServo = assert(GetPartFromPort(2, "Servo"), "no pitch servo connected")
local keyboard = assert(GetPart("Keyboard"), "no keyboard connected")
local seat = GetPart("Seat")
-- Make sure the servos are wired properly!
assert(yawServo ~= pitchServo, "there is only one servo connected")
local servoAngles = {}
local heldKeys = {}
local function incrementAngle(servo, direction)
-- Make the `ANGLE_INCREMENT` negative if the direction is specified as `-1`
-- with a bit of multiplication.
local baseIncrement = ANGLE_INCREMENT * direction
-- If we need to initialise the target angle of the servo:
if not servoAngles[servo] then
-- Rotate it back to 0 degrees and initialise the current state.
servo:SetAngle(0)
servoAngles[servo] = 0
end
-- Here we return a new function, that when called, applies the angle increment
-- operation to the servo, this is a trick called a "closure".
return function()
-- Calculate the new angle, check if we're holding shift, if we are multiply
-- the increment by the `SHIFT_HELD_MULTIPLIER`
local multiplier = if heldKeys[Enum.KeyCode.LeftShift] then SHIFT_HELD_MULTIPLIER else 1
local currentAngle = baseIncrement * multiplier
-- Update both the real and internal servo angle.
servo:SetAngle(currentAngle)
servoAngles[servo] = currentAngle
end
end
-- Register all of the angle incrementing functions that should keep running
-- whilst the user keeps holding the bind.
local HOLD_BINDS = {
[Enum.KeyCode.W] = incrementAngle(pitchServo, 1),
[Enum.KeyCode.A] = incrementAngle(yawServo, 1),
[Enum.KeyCode.S] = incrementAngle(pitchServo, -1),
[Enum.KeyCode.D] = incrementAngle(yawServo, -1),
}
-- Register all of the general binds that only run on key down.
local BINDS = {
[Enum.KeyCode.F] = function()
TriggerPort(3) -- Assume port 3 has something like guns!
end,
}
-- If we have a seat, we can detect when the user gets up and clear all the
-- held keys as if they stopped inputting.
if seat then
seat.OccupantChanged:Connect(function(occupant)
if occupant then
return
end -- If someone just sat down, cancel
table.clear(heldKeys) -- Whereas if someone got up, clear the held keys
end)
else
-- Provide a warning about issues that might occur when no seat is connected.
warn([[There is no seat connected to the microcontroller!
If a player jumps whilst holding an input, the input will get stuck!]])
end
keyboard.UserInput:Connect(function(input, userId)
-- Check if the user is whitelisted, if they're not, cancel.
if not WHITELIST[userId] then
return
end
-- If the bind pressed has a function to run on key down, run it.
if BINDS[input.KeyCode] then
local callback = BINDS[input.KeyCode]
callback()
end
-- If the user *started* pressing the key, register it as held, otherwise
-- deregister it.
if input.UserInputState == Enum.UserInputState.Begin then
heldKeys[input.KeyCode] = true
elseif input.UserInputState == Enum.UserInputState.End then
heldKeys[input.KeyCode] = nil
end
end)
-- For each tick we want to call all the functions that should run for held keys.
-- You could alternatively use a simple `while true do` loop, but this keeps it
-- tick aligned, and tick alignment may provide benefits in some cases.
Microcontroller.Loop:Connect(function(tickDuration)
for index = 1, UPDATES_PER_TICK do
for key, callback in HOLD_BINDS do
-- If we're not holding this bind, look at the next bind.
if not heldKeys[key] then
continue
end
callback()
end
-- Wait a little for for the next time we should update.
task.wait(tickDuration / UPDATES_PER_TICK)
end
end)