@rbxts/touch-button 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,25 @@
1
+ import { TouchButtonConfig } from '../shared/remotes';
2
+ export declare class TouchButton {
3
+ private touchBtn;
4
+ private name;
5
+ private defaultConfig;
6
+ private configUpdateScheduled;
7
+ static configEditingMode: import("@rbxts/charm").Atom<boolean>;
8
+ static touchButtons: TouchButton[];
9
+ constructor(options: {
10
+ name: string;
11
+ icon: string;
12
+ size: UDim2;
13
+ position: UDim2;
14
+ onPress?: () => void;
15
+ onRelease?: () => void;
16
+ });
17
+ setIcon(icon: string): void;
18
+ setSize(size: UDim2): void;
19
+ setPosition(position: UDim2): void;
20
+ setConfig(config: TouchButtonConfig): void;
21
+ private scheduleConfigUpdate;
22
+ private updateConfig;
23
+ private resetConfigToDefault;
24
+ destroy(): void;
25
+ }
@@ -0,0 +1,292 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local observeProperty = TS.import(script, TS.getModule(script, "@rbxts", "roblox-observers").out["observe-property"]).observeProperty
4
+ local _services = TS.import(script, TS.getModule(script, "@rbxts", "services"))
5
+ local Players = _services.Players
6
+ local UserInputService = _services.UserInputService
7
+ local Workspace = _services.Workspace
8
+ local remotes = TS.import(script, script.Parent.Parent, "shared", "remotes").remotes
9
+ local _charm = TS.import(script, TS.getModule(script, "@rbxts", "charm"))
10
+ local atom = _charm.atom
11
+ local effect = _charm.effect
12
+ local Trove = TS.import(script, TS.getModule(script, "@rbxts", "trove").out).Trove
13
+ local localPlayer = Players.LocalPlayer
14
+ local camera = Workspace.CurrentCamera
15
+ local touchGui
16
+ local jumpBtn
17
+ local EDITING_MODE_BUTTON_COLOR = Color3.fromRGB(15, 143, 255)
18
+ local MIN_RESIZE = 40
19
+ -- This is taken from TouchJump.lua in PlayerScripts.
20
+ local function getJumpButtonLayout()
21
+ local minAxis = math.min(camera.ViewportSize.X, camera.ViewportSize.Y)
22
+ local isSmallScreen = minAxis <= 500
23
+ local jumpButtonSize = if isSmallScreen then 70 else 120
24
+ return {
25
+ size = UDim2.fromOffset(jumpButtonSize, jumpButtonSize),
26
+ position = if isSmallScreen then UDim2.new(1, -(jumpButtonSize * 1.5 - 10), 1, -jumpButtonSize - 20) else UDim2.new(1, -(jumpButtonSize * 1.5 - 10), 1, -jumpButtonSize * 1.75),
27
+ }
28
+ end
29
+ local function getTouchGui()
30
+ if touchGui and jumpBtn then
31
+ return { touchGui, jumpBtn }
32
+ end
33
+ touchGui = Instance.new("ScreenGui")
34
+ touchGui.Name = "CustomTouchGui"
35
+ touchGui.ResetOnSpawn = false
36
+ touchGui.Parent = localPlayer.PlayerGui
37
+ jumpBtn = Instance.new("Frame")
38
+ jumpBtn.Name = "Jump"
39
+ jumpBtn.BackgroundTransparency = 1
40
+ observeProperty(camera, "ViewportSize", function()
41
+ local jumpBtnLayout = getJumpButtonLayout()
42
+ jumpBtn.Position = jumpBtnLayout.position
43
+ jumpBtn.Size = jumpBtnLayout.size
44
+ end)
45
+ jumpBtn.Parent = touchGui
46
+ local characterExists = atom(not not localPlayer.Character)
47
+ local lastInputType = atom(UserInputService:GetLastInputType())
48
+ effect(function()
49
+ local charExists = characterExists()
50
+ local inputType = lastInputType()
51
+ touchGui.Enabled = (inputType == Enum.UserInputType.Touch) and charExists
52
+ end)
53
+ localPlayer.CharacterAdded:Connect(function()
54
+ characterExists(true)
55
+ end)
56
+ localPlayer.CharacterRemoving:Connect(function()
57
+ characterExists(false)
58
+ end)
59
+ UserInputService.LastInputTypeChanged:Connect(lastInputType)
60
+ return { touchGui, jumpBtn }
61
+ end
62
+ local TouchButton
63
+ do
64
+ TouchButton = setmetatable({}, {
65
+ __tostring = function()
66
+ return "TouchButton"
67
+ end,
68
+ })
69
+ TouchButton.__index = TouchButton
70
+ function TouchButton.new(...)
71
+ local self = setmetatable({}, TouchButton)
72
+ return self:constructor(...) or self
73
+ end
74
+ function TouchButton:constructor(options)
75
+ self.configUpdateScheduled = false
76
+ self.name = options.name
77
+ self.defaultConfig = {
78
+ position = options.position,
79
+ size = options.size,
80
+ }
81
+ local configPromise = remotes.getTouchButtonConfig:request(options.name)
82
+ local touchBtn = Instance.new("ImageButton")
83
+ touchBtn.Name = "TouchButton"
84
+ touchBtn.Active = false
85
+ touchBtn.Image = "http://www.roblox.com/asset/?id=15340864550"
86
+ touchBtn.ImageColor3 = Color3.fromRGB(255, 255, 255)
87
+ touchBtn.ImageTransparency = 0.5
88
+ touchBtn.AnchorPoint = Vector2.new(0.5, 0.5)
89
+ touchBtn.BackgroundTransparency = 1
90
+ touchBtn.Size = UDim2.fromScale(1, 1)
91
+ local iconImage = Instance.new("ImageLabel")
92
+ iconImage.Name = "Icon"
93
+ iconImage.AnchorPoint = Vector2.new(0.5, 0.5)
94
+ iconImage.BackgroundTransparency = 1
95
+ iconImage.ImageTransparency = 0.2
96
+ iconImage.Position = UDim2.fromScale(0.5, 0.5)
97
+ iconImage.Size = UDim2.fromScale(0.56, 0.56)
98
+ iconImage.Parent = touchBtn
99
+ touchBtn.InputBegan:Connect(function(input)
100
+ if (input.UserInputType ~= Enum.UserInputType.MouseButton1) and (input.UserInputType ~= Enum.UserInputType.Touch) then
101
+ return nil
102
+ end
103
+ if input.UserInputState ~= Enum.UserInputState.Begin then
104
+ return nil
105
+ end
106
+ if TouchButton.configEditingMode() then
107
+ return nil
108
+ end
109
+ touchBtn.ImageColor3 = Color3.fromRGB(200, 200, 200)
110
+ iconImage.ImageColor3 = Color3.fromRGB(200, 200, 200)
111
+ local _result = options.onPress
112
+ if _result ~= nil then
113
+ _result()
114
+ end
115
+ local connection
116
+ connection = input:GetPropertyChangedSignal("UserInputState"):Connect(function()
117
+ if input.UserInputState ~= Enum.UserInputState.End then
118
+ return nil
119
+ end
120
+ connection:Disconnect()
121
+ touchBtn.ImageColor3 = Color3.fromRGB(255, 255, 255)
122
+ iconImage.ImageColor3 = Color3.fromRGB(255, 255, 255)
123
+ local _result_1 = options.onRelease
124
+ if _result_1 ~= nil then
125
+ _result_1()
126
+ end
127
+ end)
128
+ end)
129
+ local editingTrove = Trove.new()
130
+ effect(function()
131
+ local isEditing = TouchButton.configEditingMode()
132
+ if isEditing then
133
+ touchBtn.Active = true
134
+ touchBtn.ImageColor3 = EDITING_MODE_BUTTON_COLOR
135
+ iconImage.ImageColor3 = EDITING_MODE_BUTTON_COLOR
136
+ do
137
+ local resetBtn = Instance.new("TextButton")
138
+ resetBtn.Name = "Reset"
139
+ resetBtn.AnchorPoint = Vector2.new(0.5, 0.5)
140
+ resetBtn.BackgroundColor3 = Color3.new()
141
+ resetBtn.BackgroundTransparency = 0.25
142
+ resetBtn.FontFace = Font.new("rbxasset://fonts/families/GothamSSm.json", Enum.FontWeight.Medium, Enum.FontStyle.Normal)
143
+ resetBtn.Position = UDim2.fromScale(0.5, 0.5)
144
+ resetBtn.Size = UDim2.fromOffset(120, 32)
145
+ resetBtn.Text = "Reset Buttons"
146
+ resetBtn.TextColor3 = Color3.new(1, 1, 1)
147
+ resetBtn.TextSize = 16
148
+ resetBtn.Parent = touchGui
149
+ editingTrove:add(resetBtn)
150
+ local UICorner = Instance.new("UICorner")
151
+ UICorner.CornerRadius = UDim.new(0, 5)
152
+ UICorner.Parent = resetBtn
153
+ resetBtn.MouseButton1Click:Connect(function()
154
+ for _1, touchBtn in TouchButton.touchButtons do
155
+ touchBtn:resetConfigToDefault()
156
+ end
157
+ end)
158
+ end
159
+ local resizeBtn = Instance.new("ImageButton")
160
+ resizeBtn.AnchorPoint = Vector2.new(0.5, 0.5)
161
+ resizeBtn.BackgroundTransparency = 1
162
+ resizeBtn.Image = "http://www.roblox.com/asset/?id=101680714548913"
163
+ resizeBtn.Position = UDim2.fromScale(0.82, 0.82)
164
+ resizeBtn.Size = UDim2.fromOffset(18, 18)
165
+ resizeBtn.Parent = touchBtn
166
+ editingTrove:add(resizeBtn)
167
+ resizeBtn.InputBegan:Connect(function(input)
168
+ if (input.UserInputType ~= Enum.UserInputType.MouseButton1) and (input.UserInputType ~= Enum.UserInputType.Touch) then
169
+ return nil
170
+ end
171
+ if input.UserInputState ~= Enum.UserInputState.Begin then
172
+ return nil
173
+ end
174
+ local parentSize = (touchBtn.Parent).AbsoluteSize
175
+ local initialAbsSize = touchBtn.AbsoluteSize
176
+ local initialPos = Vector2.new(input.Position.X, input.Position.Y)
177
+ local moveConn = UserInputService.InputChanged:Connect(function(moveInput)
178
+ if (moveInput.UserInputType ~= Enum.UserInputType.MouseMovement) and (moveInput.UserInputType ~= Enum.UserInputType.Touch) then
179
+ return nil
180
+ end
181
+ local delta = Vector2.new(moveInput.Position.X, moveInput.Position.Y) - initialPos
182
+ local maxDelta = math.max(delta.X, delta.Y)
183
+ local newAbsSize = initialAbsSize.X + maxDelta
184
+ local finalAbsSize = math.max(MIN_RESIZE, newAbsSize)
185
+ local finalScale = finalAbsSize / parentSize.X
186
+ self:setSize(UDim2.fromScale(finalScale, finalScale))
187
+ end)
188
+ local endConn
189
+ endConn = UserInputService.InputEnded:Connect(function(endInput)
190
+ if (endInput.UserInputType ~= Enum.UserInputType.MouseButton1) and (endInput.UserInputType ~= Enum.UserInputType.Touch) then
191
+ return nil
192
+ end
193
+ moveConn:Disconnect()
194
+ endConn:Disconnect()
195
+ end)
196
+ end)
197
+ editingTrove:add(touchBtn.InputBegan:Connect(function(input)
198
+ if (input.UserInputType ~= Enum.UserInputType.MouseButton1) and (input.UserInputType ~= Enum.UserInputType.Touch) then
199
+ return nil
200
+ end
201
+ if input.UserInputState ~= Enum.UserInputState.Begin then
202
+ return nil
203
+ end
204
+ local parentSize = (touchBtn.Parent).AbsoluteSize
205
+ local initialInputPos = Vector2.new(input.Position.X, input.Position.Y)
206
+ local initialBtnPos = touchBtn.Position
207
+ local moveConn = UserInputService.InputChanged:Connect(function(moveInput)
208
+ if (moveInput.UserInputType ~= Enum.UserInputType.MouseMovement) and (moveInput.UserInputType ~= Enum.UserInputType.Touch) then
209
+ return nil
210
+ end
211
+ local currentPos = Vector2.new(moveInput.Position.X, moveInput.Position.Y)
212
+ local delta = currentPos - initialInputPos
213
+ local deltaScale = Vector2.new(delta.X / parentSize.X, delta.Y / parentSize.Y)
214
+ self:setPosition(UDim2.fromScale(initialBtnPos.X.Scale + deltaScale.X, initialBtnPos.Y.Scale + deltaScale.Y))
215
+ end)
216
+ local endConn
217
+ endConn = UserInputService.InputEnded:Connect(function(endInput)
218
+ if (endInput.UserInputType ~= Enum.UserInputType.MouseButton1) and (endInput.UserInputType ~= Enum.UserInputType.Touch) then
219
+ return nil
220
+ end
221
+ moveConn:Disconnect()
222
+ endConn:Disconnect()
223
+ end)
224
+ end))
225
+ else
226
+ editingTrove:clean()
227
+ touchBtn.Active = false
228
+ touchBtn.ImageColor3 = Color3.fromRGB(255, 255, 255)
229
+ iconImage.ImageColor3 = Color3.fromRGB(255, 255, 255)
230
+ end
231
+ end)
232
+ self.touchBtn = touchBtn
233
+ self:setIcon(options.icon)
234
+ self:setSize(options.size)
235
+ self:setPosition(options.position)
236
+ configPromise:andThen(function(config)
237
+ if not config then
238
+ return nil
239
+ end
240
+ self:setConfig(config)
241
+ end)
242
+ local _binding = getTouchGui()
243
+ local _ = _binding[1]
244
+ local jumpBtn = _binding[2]
245
+ touchBtn.Parent = jumpBtn
246
+ local _touchButtons = TouchButton.touchButtons
247
+ local _self = self
248
+ table.insert(_touchButtons, _self)
249
+ end
250
+ function TouchButton:setIcon(icon)
251
+ self.touchBtn.Icon.Image = icon
252
+ end
253
+ function TouchButton:setSize(size)
254
+ self.touchBtn.Size = size
255
+ self:scheduleConfigUpdate()
256
+ end
257
+ function TouchButton:setPosition(position)
258
+ self.touchBtn.Position = position
259
+ self:scheduleConfigUpdate()
260
+ end
261
+ function TouchButton:setConfig(config)
262
+ self:setPosition(config.position)
263
+ self:setSize(config.size)
264
+ end
265
+ function TouchButton:scheduleConfigUpdate()
266
+ if self.configUpdateScheduled then
267
+ return nil
268
+ end
269
+ self.configUpdateScheduled = true
270
+ task.defer(function()
271
+ self.configUpdateScheduled = false
272
+ self:updateConfig()
273
+ end)
274
+ end
275
+ function TouchButton:updateConfig()
276
+ remotes.setTouchButtonConfig:fire(self.name, {
277
+ position = self.touchBtn.Position,
278
+ size = self.touchBtn.Size,
279
+ })
280
+ end
281
+ function TouchButton:resetConfigToDefault()
282
+ self:setConfig(self.defaultConfig)
283
+ end
284
+ function TouchButton:destroy()
285
+ self.touchBtn:Destroy()
286
+ end
287
+ TouchButton.configEditingMode = atom(false)
288
+ TouchButton.touchButtons = {}
289
+ end
290
+ return {
291
+ TouchButton = TouchButton,
292
+ }
package/out/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { TouchButton } from './client/touch-button';
2
+ export { TouchButtonServer } from './server';
package/out/init.luau ADDED
@@ -0,0 +1,6 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local exports = {}
4
+ exports.TouchButton = TS.import(script, script, "client", "touch-button").TouchButton
5
+ exports.TouchButtonServer = TS.import(script, script, "server").TouchButtonServer
6
+ return exports
@@ -0,0 +1,3 @@
1
+ export declare namespace TouchButtonServer {
2
+ function init(validTouchButtonNames: Set<string>): void;
3
+ }
@@ -0,0 +1,115 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _services = TS.import(script, TS.getModule(script, "@rbxts", "services"))
4
+ local DataStoreService = _services.DataStoreService
5
+ local RunService = _services.RunService
6
+ local remotes = TS.import(script, script.Parent, "shared", "remotes").remotes
7
+ local observePlayers = TS.import(script, TS.getModule(script, "@rbxts", "roblox-observers").out["observe-players"]).observePlayers
8
+ local Signal = TS.import(script, TS.getModule(script, "@rbxts", "sleitnick-signal"))
9
+ local entriesCache = {}
10
+ local playerDataLoaded = Signal.new()
11
+ local initialized = false
12
+ local function getDataStoreKeyForPlayer(player)
13
+ return `Player{player.UserId}`
14
+ end
15
+ local function waitForEntry(player)
16
+ local _player = player
17
+ local entry = entriesCache[_player]
18
+ if not entry then
19
+ local loadedPlayer
20
+ local loadedEntry
21
+ repeat
22
+ do
23
+ loadedPlayer, loadedEntry = playerDataLoaded:Wait()
24
+ end
25
+ until not (loadedPlayer ~= player)
26
+ entry = loadedEntry
27
+ end
28
+ return entry
29
+ end
30
+ local function serializeEntry(entry)
31
+ local res = {}
32
+ for buttonName, props in pairs(entry) do
33
+ local serializedProps = {}
34
+ for propKey, propValue in pairs(props) do
35
+ local serializedProp
36
+ if typeof(propValue) == "Vector2" then
37
+ serializedProp = { propValue.X, propValue.Y }
38
+ elseif typeof(propValue) == "UDim2" then
39
+ serializedProp = { propValue.X.Scale, propValue.X.Offset, propValue.Y.Scale, propValue.Y.Offset }
40
+ end
41
+ serializedProps[propKey] = serializedProp
42
+ end
43
+ res[buttonName] = serializedProps
44
+ end
45
+ return res
46
+ end
47
+ local function deserializeEntry(entry)
48
+ local res = {}
49
+ for buttonName, serializedProps in pairs(entry) do
50
+ local props = {}
51
+ for propKey, serializedValue in pairs(serializedProps) do
52
+ local deserializedProp
53
+ if #serializedValue == 2 then
54
+ deserializedProp = Vector2.new(serializedValue[1], serializedValue[2])
55
+ elseif #serializedValue == 4 then
56
+ deserializedProp = UDim2.new(serializedValue[1], serializedValue[2], serializedValue[3], serializedValue[4])
57
+ end
58
+ props[propKey] = deserializedProp
59
+ end
60
+ res[buttonName] = props
61
+ end
62
+ return res
63
+ end
64
+ local TouchButtonServer = {}
65
+ do
66
+ local _container = TouchButtonServer
67
+ local function init(validTouchButtonNames)
68
+ local _arg0 = RunService:IsServer()
69
+ assert(_arg0, "TouchButtonServer can only be initialized from the server")
70
+ local _arg0_1 = not initialized
71
+ assert(_arg0_1, "TouchButtonServer already initialized")
72
+ initialized = true
73
+ local buttonsStore = DataStoreService:GetDataStore("TouchButtonConfigs")
74
+ remotes.getTouchButtonConfig:onRequest(function(player, buttonName)
75
+ local entry = waitForEntry(player)
76
+ return entry[buttonName]
77
+ end)
78
+ remotes.setTouchButtonConfig:connect(function(player, buttonName, config)
79
+ local _validTouchButtonNames = validTouchButtonNames
80
+ local _buttonName = buttonName
81
+ local _arg0_2 = _validTouchButtonNames[_buttonName] ~= nil
82
+ local _arg1 = `[TOUCH BUTTONS] {buttonName} is not a valid savable TouchButton name`
83
+ assert(_arg0_2, _arg1)
84
+ local entry = waitForEntry(player)
85
+ entry[buttonName] = config
86
+ end)
87
+ observePlayers(function(player)
88
+ local key = getDataStoreKeyForPlayer(player)
89
+ local suc, entryOrUndefinedOrErr = pcall(function()
90
+ return buttonsStore:GetAsync(key)
91
+ end)
92
+ if not suc then
93
+ warn(`[TOUCH BUTTONS] Failed to load DataStore entry:`, entryOrUndefinedOrErr)
94
+ return nil
95
+ end
96
+ local entry = if entryOrUndefinedOrErr ~= 0 and entryOrUndefinedOrErr == entryOrUndefinedOrErr and entryOrUndefinedOrErr ~= "" and entryOrUndefinedOrErr then deserializeEntry(entryOrUndefinedOrErr) else {}
97
+ local _player = player
98
+ local _entry = entry
99
+ entriesCache[_player] = _entry
100
+ playerDataLoaded:Fire(player, entry)
101
+ return function()
102
+ local _player_1 = player
103
+ local entry = entriesCache[_player_1]
104
+ if not entry then
105
+ return nil
106
+ end
107
+ buttonsStore:SetAsync(key, serializeEntry(entry), { player.UserId })
108
+ end
109
+ end)
110
+ end
111
+ _container.init = init
112
+ end
113
+ return {
114
+ TouchButtonServer = TouchButtonServer,
115
+ }
@@ -0,0 +1,9 @@
1
+ import { Server } from '@rbxts/remo';
2
+ export interface TouchButtonConfig {
3
+ position: UDim2;
4
+ size: UDim2;
5
+ }
6
+ export declare const remotes: import("@rbxts/remo").Remotes<{
7
+ getTouchButtonConfig: import("@rbxts/remo").RemoteBuilder<(buttonName: string) => TouchButtonConfig | undefined, Server>;
8
+ setTouchButtonConfig: import("@rbxts/remo").RemoteBuilder<(buttonName: string, config: TouchButtonConfig) => void, Server>;
9
+ }>;
@@ -0,0 +1,16 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _remo = TS.import(script, TS.getModule(script, "@rbxts", "remo").src)
4
+ local createRemotes = _remo.createRemotes
5
+ local remote = _remo.remote
6
+ local t = TS.import(script, TS.getModule(script, "@rbxts", "t").lib.ts).t
7
+ local remotes = createRemotes({
8
+ getTouchButtonConfig = remote(t.string).returns(),
9
+ setTouchButtonConfig = remote(t.string, t.strictInterface({
10
+ position = t.UDim2,
11
+ size = t.UDim2,
12
+ })),
13
+ })
14
+ return {
15
+ remotes = remotes,
16
+ }
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@rbxts/touch-button",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "out/init.lua",
6
+ "scripts": {
7
+ "build": "rbxtsc",
8
+ "watch": "rbxtsc -w",
9
+ "prepublishOnly": "npm run build"
10
+ },
11
+ "keywords": [],
12
+ "author": "CriShoux",
13
+ "license": "ISC",
14
+ "type": "commonjs",
15
+ "types": "out/index.d.ts",
16
+ "files": [
17
+ "out",
18
+ "!**/*.tsbuildinfo"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "devDependencies": {
24
+ "@rbxts/compiler-types": "^3.0.0-types.0",
25
+ "@rbxts/types": "^1.0.848",
26
+ "roblox-ts": "^3.0.0",
27
+ "typescript": "^5.8.3"
28
+ },
29
+ "dependencies": {
30
+ "@rbxts/charm": "^0.10.0",
31
+ "@rbxts/remo": "^1.5.1",
32
+ "@rbxts/roblox-observers": "^1.0.7",
33
+ "@rbxts/services": "^1.5.5",
34
+ "@rbxts/sleitnick-signal": "^1.0.8",
35
+ "@rbxts/t": "^3.2.1",
36
+ "@rbxts/trove": "^1.3.0"
37
+ }
38
+ }