@rbxts/gravity-controller 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.
- package/GravityController.rbxmx +3208 -0
- package/README.md +65 -0
- package/out/index.d.ts +22 -0
- package/out/init.lua +174 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# @rbxts/gravity-controller
|
|
2
|
+
|
|
3
|
+
Typescript bindings for [EgoMoose's Rbx-Gravity-Controller](https://github.com/EgoMoose/Rbx-Gravity-Controller) with
|
|
4
|
+
ground normal finding by [EmilyBendsSpace](https://x.com/EmilyBendsSpace)
|
|
5
|
+
|
|
6
|
+
## Flamework setup
|
|
7
|
+
|
|
8
|
+
1. Add `installGravityControllerClass` to the `onInit` of some `@Service`
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
import { OnInit, Service } from '@flamework/core'
|
|
12
|
+
import { installGravityControllerClass } from '@rbxts/gravity-controller'
|
|
13
|
+
|
|
14
|
+
@Service()
|
|
15
|
+
export class GravityService implements OnInit {
|
|
16
|
+
onInit() {
|
|
17
|
+
// Install EgoMoose's Rbx-Gravity-Controller https://github.com/EgoMoose/Rbx-Gravity-Controller
|
|
18
|
+
installGravityControllerClass()
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
2. Setup a `GravityController` in some `@Controller`
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { Controller, OnStart } from '@flamework/core'
|
|
27
|
+
import {
|
|
28
|
+
getGravityControllerUp,
|
|
29
|
+
GravityController,
|
|
30
|
+
GravityControllerClass,
|
|
31
|
+
installGravityControllerClass,
|
|
32
|
+
} from '@rbxts/gravity-controller'
|
|
33
|
+
import { Players } from '@rbxts/services'
|
|
34
|
+
|
|
35
|
+
@Controller({})
|
|
36
|
+
export class PlayerGravityController implements OnStart {
|
|
37
|
+
gravityControllerClass: GravityControllerClass | undefined
|
|
38
|
+
gravityController: GravityController | undefined
|
|
39
|
+
|
|
40
|
+
onStart() {
|
|
41
|
+
this.gravityControllerClass = installGravityControllerClass()
|
|
42
|
+
Players.LocalPlayer.CharacterAdded.Connect((_character) => {
|
|
43
|
+
this.disableGravityController()
|
|
44
|
+
this.enableGravityController()
|
|
45
|
+
})
|
|
46
|
+
if (Players.LocalPlayer.Character) this.enableGravityController()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
disableGravityController() {
|
|
50
|
+
this.gravityController?.Destroy()
|
|
51
|
+
this.gravityController = undefined
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
enableGravityController() {
|
|
55
|
+
if (this.gravityController || !this.gravityControllerClass) return
|
|
56
|
+
const gravityController = new this.gravityControllerClass(Players.LocalPlayer)
|
|
57
|
+
|
|
58
|
+
// Use EmilyBendsSpace's getGroundNormal() to walk up walls
|
|
59
|
+
gravityController.GetGravityUp = getGravityControllerUp
|
|
60
|
+
|
|
61
|
+
this.gravityController = gravityController
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
```
|
package/out/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/// <reference types="@rbxts/types" />
|
|
2
|
+
/// <reference types="@rbxts/types" />
|
|
3
|
+
/// <reference types="@rbxts/types" />
|
|
4
|
+
export interface GravityController extends Instance {
|
|
5
|
+
Player: Player;
|
|
6
|
+
Character: Model;
|
|
7
|
+
Humanoid: Humanoid;
|
|
8
|
+
HRP: BasePart;
|
|
9
|
+
Maid: {
|
|
10
|
+
Mark: (connection: RBXScriptConnection) => void;
|
|
11
|
+
};
|
|
12
|
+
GetFallHeight(): number;
|
|
13
|
+
GetGravityUp(self: GravityController, oldGravityUp: Vector3): Vector3;
|
|
14
|
+
ResetGravity(gravityDirection: Vector3): void;
|
|
15
|
+
}
|
|
16
|
+
export interface GravityControllerClass {
|
|
17
|
+
new (player: Player): GravityController;
|
|
18
|
+
}
|
|
19
|
+
export declare let gravityControllerClass: GravityControllerClass;
|
|
20
|
+
export declare function installGravityControllerClass(): GravityControllerClass;
|
|
21
|
+
export declare function getGroundNormal(cframe: CFrame, originOffset: Vector3, oldGravityUp: Vector3): Vector3;
|
|
22
|
+
export declare function getGravityControllerUp(gravityController: GravityController, oldGravityUp: Vector3): Vector3;
|
package/out/init.lua
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v2.3.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local exports = {}
|
|
4
|
+
local _services = TS.import(script, TS.getModule(script, "@rbxts", "services"))
|
|
5
|
+
local Players = _services.Players
|
|
6
|
+
local ReplicatedStorage = _services.ReplicatedStorage
|
|
7
|
+
local RunService = _services.RunService
|
|
8
|
+
local StarterPlayer = _services.StarterPlayer
|
|
9
|
+
local function installGravityControllerClass()
|
|
10
|
+
if exports.gravityControllerClass then
|
|
11
|
+
return exports.gravityControllerClass
|
|
12
|
+
end
|
|
13
|
+
if RunService:IsServer() then
|
|
14
|
+
local starterPlayerScripts = StarterPlayer:WaitForChild("StarterPlayerScripts")
|
|
15
|
+
local starterCharacterScripts = StarterPlayer:WaitForChild("StarterCharacterScripts")
|
|
16
|
+
local _parent = script.Parent
|
|
17
|
+
if _parent ~= nil then
|
|
18
|
+
_parent = _parent:WaitForChild("GravityController")
|
|
19
|
+
end
|
|
20
|
+
local parent = _parent
|
|
21
|
+
if not parent then
|
|
22
|
+
error("GravityController module not found")
|
|
23
|
+
end
|
|
24
|
+
local client = parent:WaitForChild("Client")
|
|
25
|
+
local replace = function(child, parent)
|
|
26
|
+
local found = parent:FindFirstChild(child.Name)
|
|
27
|
+
local _result = found
|
|
28
|
+
if _result ~= nil then
|
|
29
|
+
_result:Destroy()
|
|
30
|
+
end
|
|
31
|
+
child.Parent = parent
|
|
32
|
+
end
|
|
33
|
+
replace(client:WaitForChild("PlayerScriptsLoader"), starterPlayerScripts)
|
|
34
|
+
replace(client:WaitForChild("RbxCharacterSounds"), starterPlayerScripts)
|
|
35
|
+
replace(client:WaitForChild("Animate"), starterCharacterScripts)
|
|
36
|
+
parent:WaitForChild("GravityController").Parent = ReplicatedStorage
|
|
37
|
+
end
|
|
38
|
+
exports.gravityControllerClass = require(ReplicatedStorage:WaitForChild("GravityController"))
|
|
39
|
+
return exports.gravityControllerClass
|
|
40
|
+
end
|
|
41
|
+
local PI2 = math.pi * 2
|
|
42
|
+
local ZERO = Vector3.new(0, 0, 0)
|
|
43
|
+
local LOWER_RADIUS_OFFSET = 3
|
|
44
|
+
local NUM_DOWN_RAYS = 24
|
|
45
|
+
local ODD_DOWN_RAY_START_RADIUS = 3
|
|
46
|
+
local EVEN_DOWN_RAY_START_RADIUS = 2
|
|
47
|
+
local ODD_DOWN_RAY_END_RADIUS = 1.66666
|
|
48
|
+
local EVEN_DOWN_RAY_END_RADIUS = 1
|
|
49
|
+
local NUM_FEELER_RAYS = 9
|
|
50
|
+
local FEELER_LENGTH = 2
|
|
51
|
+
local FEELER_START_OFFSET = 2
|
|
52
|
+
local FEELER_RADIUS = 3.5
|
|
53
|
+
local FEELER_APEX_OFFSET = 1
|
|
54
|
+
local FEELER_WEIGHTING = 8
|
|
55
|
+
-- Thanks to EmilyBendsSpace for the new get normal function!
|
|
56
|
+
-- https://devforum.roblox.com/t/example-source-smooth-wall-walking-gravity-controller-from-club-raven/440229?u=egomoose
|
|
57
|
+
local function getGroundNormal(cframe, originOffset, oldGravityUp)
|
|
58
|
+
local ignoreList = {}
|
|
59
|
+
for _, player in Players:GetPlayers() do
|
|
60
|
+
if player.Character then
|
|
61
|
+
local _character = player.Character
|
|
62
|
+
table.insert(ignoreList, _character)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
local _position = cframe.Position
|
|
66
|
+
local _originOffset = originOffset
|
|
67
|
+
local origin = _position + _originOffset
|
|
68
|
+
local radialVector = if math.abs(cframe.LookVector:Dot(oldGravityUp)) < 0.999 then cframe.LookVector:Cross(oldGravityUp) else cframe.RightVector:Cross(oldGravityUp)
|
|
69
|
+
local centerRayLength = 25
|
|
70
|
+
local _oldGravityUp = oldGravityUp
|
|
71
|
+
local _arg0 = -centerRayLength
|
|
72
|
+
local centerRay = Ray.new(origin, _oldGravityUp * _arg0)
|
|
73
|
+
local centerHit, _centerHitPoint, centerHitNormal = game.Workspace:FindPartOnRayWithIgnoreList(centerRay, ignoreList)
|
|
74
|
+
local mainDownNormal = if centerHit then centerHitNormal else ZERO
|
|
75
|
+
local centerRayHitCount = 0
|
|
76
|
+
local evenRayHitCount = 0
|
|
77
|
+
local oddRayHitCount = 0
|
|
78
|
+
local downHitCount = 0
|
|
79
|
+
local downRaySum = ZERO
|
|
80
|
+
do
|
|
81
|
+
local i = 0
|
|
82
|
+
local _shouldIncrement = false
|
|
83
|
+
while true do
|
|
84
|
+
if _shouldIncrement then
|
|
85
|
+
i += 1
|
|
86
|
+
else
|
|
87
|
+
_shouldIncrement = true
|
|
88
|
+
end
|
|
89
|
+
if not (i < NUM_DOWN_RAYS) then
|
|
90
|
+
break
|
|
91
|
+
end
|
|
92
|
+
local dtheta = PI2 * ((i - 1) / NUM_DOWN_RAYS)
|
|
93
|
+
local angleWeight = 0.25 + 0.75 * math.abs(math.cos(dtheta))
|
|
94
|
+
local isEvenRay = i % 2 == 0
|
|
95
|
+
local startRadius = if isEvenRay then EVEN_DOWN_RAY_START_RADIUS else ODD_DOWN_RAY_START_RADIUS
|
|
96
|
+
local endRadius = if isEvenRay then EVEN_DOWN_RAY_END_RADIUS else ODD_DOWN_RAY_END_RADIUS
|
|
97
|
+
local downRayLength = centerRayLength
|
|
98
|
+
local offset = CFrame.fromAxisAngle(oldGravityUp, dtheta) * radialVector
|
|
99
|
+
local _oldGravityUp_1 = oldGravityUp
|
|
100
|
+
local _arg0_1 = -LOWER_RADIUS_OFFSET
|
|
101
|
+
local _arg0_2 = endRadius - startRadius
|
|
102
|
+
local _arg0_3 = offset * _arg0_2
|
|
103
|
+
local dir = _oldGravityUp_1 * _arg0_1 + _arg0_3
|
|
104
|
+
local _arg0_4 = offset * startRadius
|
|
105
|
+
local ray = Ray.new(origin + _arg0_4, dir.Unit * downRayLength)
|
|
106
|
+
local hit, _hitPoint, hitNormal = game.Workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
|
|
107
|
+
if hit then
|
|
108
|
+
local _downRaySum = downRaySum
|
|
109
|
+
local _arg0_5 = hitNormal * angleWeight
|
|
110
|
+
downRaySum = _downRaySum + _arg0_5
|
|
111
|
+
downHitCount = downHitCount + 1
|
|
112
|
+
if isEvenRay then
|
|
113
|
+
evenRayHitCount = evenRayHitCount + 1
|
|
114
|
+
else
|
|
115
|
+
oddRayHitCount = oddRayHitCount + 1
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
local feelerHitCount = 0
|
|
121
|
+
local feelerNormalSum = ZERO
|
|
122
|
+
do
|
|
123
|
+
local i = 0
|
|
124
|
+
local _shouldIncrement = false
|
|
125
|
+
while true do
|
|
126
|
+
if _shouldIncrement then
|
|
127
|
+
i += 1
|
|
128
|
+
else
|
|
129
|
+
_shouldIncrement = true
|
|
130
|
+
end
|
|
131
|
+
if not (i < NUM_FEELER_RAYS) then
|
|
132
|
+
break
|
|
133
|
+
end
|
|
134
|
+
local dtheta = 2 * math.pi * ((i - 1) / NUM_FEELER_RAYS)
|
|
135
|
+
local angleWeight = 0.25 + 0.75 * math.abs(math.cos(dtheta))
|
|
136
|
+
local offset = CFrame.fromAxisAngle(oldGravityUp, dtheta) * radialVector
|
|
137
|
+
local _exp = offset * FEELER_RADIUS
|
|
138
|
+
local _oldGravityUp_1 = oldGravityUp
|
|
139
|
+
local _arg0_1 = -LOWER_RADIUS_OFFSET
|
|
140
|
+
local dir = (_exp + (_oldGravityUp_1 * _arg0_1)).Unit
|
|
141
|
+
local _oldGravityUp_2 = oldGravityUp
|
|
142
|
+
local _arg0_2 = -FEELER_APEX_OFFSET
|
|
143
|
+
local _exp_1 = origin - (_oldGravityUp_2 * _arg0_2)
|
|
144
|
+
local _arg0_3 = dir * FEELER_START_OFFSET
|
|
145
|
+
local feelerOrigin = _exp_1 + _arg0_3
|
|
146
|
+
local ray = Ray.new(feelerOrigin, dir * FEELER_LENGTH)
|
|
147
|
+
local hit, _hitPoint, hitNormal = game.Workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
|
|
148
|
+
if hit then
|
|
149
|
+
local _feelerNormalSum = feelerNormalSum
|
|
150
|
+
local _arg0_4 = FEELER_WEIGHTING * angleWeight
|
|
151
|
+
local _arg0_5 = hitNormal * _arg0_4
|
|
152
|
+
feelerNormalSum = _feelerNormalSum + _arg0_5
|
|
153
|
+
feelerHitCount = feelerHitCount + 1
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
if centerRayHitCount + downHitCount + feelerHitCount > 0 then
|
|
158
|
+
local _downRaySum = downRaySum
|
|
159
|
+
local _exp = mainDownNormal + _downRaySum
|
|
160
|
+
local _feelerNormalSum = feelerNormalSum
|
|
161
|
+
local normalSum = _exp + _feelerNormalSum
|
|
162
|
+
if normalSum ~= ZERO then
|
|
163
|
+
return normalSum.Unit
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
return oldGravityUp
|
|
167
|
+
end
|
|
168
|
+
local function getGravityControllerUp(gravityController, oldGravityUp)
|
|
169
|
+
return getGroundNormal(gravityController.HRP.CFrame, if gravityController.Humanoid.RigType == Enum.HumanoidRigType.R15 then ZERO else oldGravityUp * 0.35, oldGravityUp)
|
|
170
|
+
end
|
|
171
|
+
exports.installGravityControllerClass = installGravityControllerClass
|
|
172
|
+
exports.getGroundNormal = getGroundNormal
|
|
173
|
+
exports.getGravityControllerUp = getGravityControllerUp
|
|
174
|
+
return exports
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rbxts/gravity-controller",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "out/init.lua",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"gravity",
|
|
7
|
+
"rbxts"
|
|
8
|
+
],
|
|
9
|
+
"author": "angleopera",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/AngleOpera/gravity-controller.git"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/AngleOpera/gravity-controller/issues"
|
|
17
|
+
},
|
|
18
|
+
"types": "out/index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"out",
|
|
21
|
+
"GravityController.rbxmx",
|
|
22
|
+
"!**/*.tsbuildinfo"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "rbxtsc",
|
|
29
|
+
"prepublishOnly": "rbxtsc"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@rbxts/compiler-types": "^2.3.0-types.0",
|
|
33
|
+
"@rbxts/types": "^1.0.760",
|
|
34
|
+
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
|
35
|
+
"@typescript-eslint/parser": "^7.1.1",
|
|
36
|
+
"copyfiles": "^2.4.1",
|
|
37
|
+
"eslint": "^8.57.0",
|
|
38
|
+
"eslint-config-prettier": "^9.1.0",
|
|
39
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
40
|
+
"eslint-plugin-roblox-ts": "^0.0.36",
|
|
41
|
+
"eslint-plugin-simple-import-sort": "^12.0.0",
|
|
42
|
+
"eslint-plugin-unused-imports": "^3.1.0",
|
|
43
|
+
"prettier": "^3.2.5",
|
|
44
|
+
"roblox-ts": "^2.3.0-dev-3a89c93",
|
|
45
|
+
"typescript": "5.4.2"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@rbxts/services": "^1.5.4"
|
|
49
|
+
}
|
|
50
|
+
}
|