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: 1x75x1, 1x25x3, 1x15x5, 3x5x5
At its default size (5x0.50x2) 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.
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
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)