@quenty/ragdoll 15.6.1-canary.478.88914d9.0 → 15.6.1-canary.523.ca7cac7.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/CHANGELOG.md +2 -8
- package/package.json +27 -27
- package/src/Client/Classes/RagdollClient.lua +106 -0
- package/src/Client/Classes/RagdollableClient.lua +1 -1
- package/src/Client/RagdollServiceClient.lua +6 -5
- package/src/Server/Classes/Ragdollable.lua +1 -1
- package/src/Server/RagdollService.lua +0 -1
- package/src/Shared/Rigging/RagdollMotorUtils.lua +20 -18
- package/src/Shared/Rigging/RxRagdollUtils.lua +3 -65
- package/src/Client/Classes/RagdollCameraShakeClient.lua +0 -149
- package/src/Server/Classes/RagdollCameraShake.lua +0 -33
package/CHANGELOG.md
CHANGED
|
@@ -3,18 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
## [15.6.1-canary.
|
|
6
|
+
## [15.6.1-canary.523.ca7cac7.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/ragdoll@15.6.0...@quenty/ragdoll@15.6.1-canary.523.ca7cac7.0) (2024-12-10)
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
### Bug Fixes
|
|
10
10
|
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
### Features
|
|
15
|
-
|
|
16
|
-
* Declare tie interface we're implementing ([e7150c1](https://github.com/Quenty/NevermoreEngine/commit/e7150c13a685ce88db9c1280a14841c81191a40a))
|
|
17
|
-
* Split out ragdoll camera code ([f596a06](https://github.com/Quenty/NevermoreEngine/commit/f596a066e224d3c46aed4d1ba0065793327f4a88))
|
|
11
|
+
* ragdoll lag fix ([ca7cac7](https://github.com/Quenty/NevermoreEngine/commit/ca7cac7f2b5b255bf548c7373890432f3fc23221))
|
|
18
12
|
|
|
19
13
|
|
|
20
14
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/ragdoll",
|
|
3
|
-
"version": "15.6.1-canary.
|
|
3
|
+
"version": "15.6.1-canary.523.ca7cac7.0",
|
|
4
4
|
"description": "Quenty's Ragdoll system for Roblox - Floppy fun ragdolls",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -25,39 +25,39 @@
|
|
|
25
25
|
"Quenty"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@quenty/adorneedata": "7.4.
|
|
29
|
-
"@quenty/attributeutils": "14.3.
|
|
30
|
-
"@quenty/baseobject": "10.3.
|
|
31
|
-
"@quenty/binder": "14.4.
|
|
32
|
-
"@quenty/brio": "14.3.
|
|
33
|
-
"@quenty/camera": "14.5.
|
|
28
|
+
"@quenty/adorneedata": "7.4.0",
|
|
29
|
+
"@quenty/attributeutils": "14.3.0",
|
|
30
|
+
"@quenty/baseobject": "10.3.0",
|
|
31
|
+
"@quenty/binder": "14.4.0",
|
|
32
|
+
"@quenty/brio": "14.3.0",
|
|
33
|
+
"@quenty/camera": "14.5.0",
|
|
34
34
|
"@quenty/cancellabledelay": "3.5.0",
|
|
35
|
-
"@quenty/characterutils": "12.3.
|
|
36
|
-
"@quenty/draw": "7.3.
|
|
35
|
+
"@quenty/characterutils": "12.3.0",
|
|
36
|
+
"@quenty/draw": "7.3.0",
|
|
37
37
|
"@quenty/enumutils": "3.2.0",
|
|
38
38
|
"@quenty/hapticfeedbackutils": "3.2.0",
|
|
39
|
-
"@quenty/instanceutils": "13.3.
|
|
40
|
-
"@quenty/loader": "10.3.
|
|
41
|
-
"@quenty/maid": "3.2.
|
|
42
|
-
"@quenty/motor6d": "7.5.
|
|
43
|
-
"@quenty/physicsutils": "8.3.
|
|
44
|
-
"@quenty/playerhumanoidbinder": "14.4.
|
|
45
|
-
"@quenty/promise": "10.3.
|
|
46
|
-
"@quenty/qframe": "10.3.
|
|
47
|
-
"@quenty/r15utils": "13.3.
|
|
48
|
-
"@quenty/remoting": "12.4.
|
|
49
|
-
"@quenty/rx": "13.3.
|
|
50
|
-
"@quenty/rxbinderutils": "14.4.
|
|
51
|
-
"@quenty/rxsignal": "7.3.
|
|
52
|
-
"@quenty/spring": "10.3.
|
|
39
|
+
"@quenty/instanceutils": "13.3.0",
|
|
40
|
+
"@quenty/loader": "10.3.0",
|
|
41
|
+
"@quenty/maid": "3.2.0",
|
|
42
|
+
"@quenty/motor6d": "7.5.0",
|
|
43
|
+
"@quenty/physicsutils": "8.3.0",
|
|
44
|
+
"@quenty/playerhumanoidbinder": "14.4.0",
|
|
45
|
+
"@quenty/promise": "10.3.0",
|
|
46
|
+
"@quenty/qframe": "10.3.0",
|
|
47
|
+
"@quenty/r15utils": "13.3.0",
|
|
48
|
+
"@quenty/remoting": "12.4.0",
|
|
49
|
+
"@quenty/rx": "13.3.0",
|
|
50
|
+
"@quenty/rxbinderutils": "14.4.0",
|
|
51
|
+
"@quenty/rxsignal": "7.3.0",
|
|
52
|
+
"@quenty/spring": "10.3.0",
|
|
53
53
|
"@quenty/steputils": "3.4.0",
|
|
54
54
|
"@quenty/table": "3.5.0",
|
|
55
|
-
"@quenty/tie": "10.5.
|
|
56
|
-
"@quenty/valuebaseutils": "13.3.
|
|
57
|
-
"@quenty/valueobject": "13.3.
|
|
55
|
+
"@quenty/tie": "10.5.0",
|
|
56
|
+
"@quenty/valuebaseutils": "13.3.0",
|
|
57
|
+
"@quenty/valueobject": "13.3.0"
|
|
58
58
|
},
|
|
59
59
|
"publishConfig": {
|
|
60
60
|
"access": "public"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "ca7cac7f2b5b255bf548c7373890432f3fc23221"
|
|
63
63
|
}
|
|
@@ -16,9 +16,21 @@
|
|
|
16
16
|
|
|
17
17
|
local require = require(script.Parent.loader).load(script)
|
|
18
18
|
|
|
19
|
+
local Workspace = game:GetService("Workspace")
|
|
20
|
+
local Players = game:GetService("Players")
|
|
21
|
+
local UserInputService = game:GetService("UserInputService")
|
|
22
|
+
local RunService = game:GetService("RunService")
|
|
19
23
|
|
|
20
24
|
local BaseObject = require("BaseObject")
|
|
21
25
|
local Binder = require("Binder")
|
|
26
|
+
local CameraStackService = require("CameraStackService")
|
|
27
|
+
local CharacterUtils = require("CharacterUtils")
|
|
28
|
+
local HapticFeedbackUtils = require("HapticFeedbackUtils")
|
|
29
|
+
local RagdollMotorUtils = require("RagdollMotorUtils")
|
|
30
|
+
local RagdollServiceClient = require("RagdollServiceClient")
|
|
31
|
+
local RxBrioUtils = require("RxBrioUtils")
|
|
32
|
+
local RxInstanceUtils = require("RxInstanceUtils")
|
|
33
|
+
local RxR15Utils = require("RxR15Utils")
|
|
22
34
|
|
|
23
35
|
local RagdollClient = setmetatable({}, BaseObject)
|
|
24
36
|
RagdollClient.ClassName = "RagdollClient"
|
|
@@ -34,8 +46,102 @@ function RagdollClient.new(humanoid, serviceBag)
|
|
|
34
46
|
local self = setmetatable(BaseObject.new(humanoid), RagdollClient)
|
|
35
47
|
|
|
36
48
|
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
49
|
+
self._ragdollServiceClient = self._serviceBag:GetService(RagdollServiceClient)
|
|
50
|
+
self._cameraStackService = self._serviceBag:GetService(CameraStackService)
|
|
51
|
+
|
|
52
|
+
local player = CharacterUtils.getPlayerFromCharacter(self._obj)
|
|
53
|
+
if player == Players.LocalPlayer then
|
|
54
|
+
self._maid:GiveTask(task.spawn(function()
|
|
55
|
+
-- Yield in the same way just to ensure no weird shakes.
|
|
56
|
+
RagdollMotorUtils.yieldUntilStepped()
|
|
57
|
+
|
|
58
|
+
self:_setupHapticFeedback()
|
|
59
|
+
self:_setupCameraShake(self._cameraStackService:GetImpulseCamera())
|
|
60
|
+
end))
|
|
61
|
+
end
|
|
37
62
|
|
|
38
63
|
return self
|
|
39
64
|
end
|
|
40
65
|
|
|
66
|
+
function RagdollClient:_setupCameraShake(impulseCamera)
|
|
67
|
+
-- TODO: Move out of this open source module
|
|
68
|
+
|
|
69
|
+
-- Use the upper torso instead of the head because the upper torso shakes a lot less so
|
|
70
|
+
-- we get a stronger response to full character movement.
|
|
71
|
+
|
|
72
|
+
self._maid:GiveTask(RxInstanceUtils.observePropertyBrio(self._obj, "Parent", function(character)
|
|
73
|
+
return character ~= nil
|
|
74
|
+
end):Pipe({
|
|
75
|
+
RxBrioUtils.switchMapBrio(function(character)
|
|
76
|
+
return RxBrioUtils.flatCombineLatestBrio({
|
|
77
|
+
upperTorso = RxR15Utils.observeCharacterPartBrio(character, "UpperTorso");
|
|
78
|
+
head = RxR15Utils.observeCharacterPartBrio(character, "Head")
|
|
79
|
+
}, function(state)
|
|
80
|
+
return state.upperTorso and state.head
|
|
81
|
+
end)
|
|
82
|
+
end);
|
|
83
|
+
}):Subscribe(function(brio)
|
|
84
|
+
if brio:IsDead() then
|
|
85
|
+
return
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
local maid = brio:ToMaid()
|
|
89
|
+
local state = brio:GetValue()
|
|
90
|
+
|
|
91
|
+
local function getEstimatedVelocityFromUpperTorso()
|
|
92
|
+
-- TODO: Consider neck attachments
|
|
93
|
+
local headOffset = state.upperTorso.Size*Vector3.new(0, 0.5, 0)
|
|
94
|
+
+ state.head.Size*Vector3.new(0, 0.5, 0)
|
|
95
|
+
local headPosition = state.upperTorso.CFrame:PointToWorldSpace(headOffset)
|
|
96
|
+
|
|
97
|
+
return state.upperTorso:GetVelocityAtPosition(headPosition)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
local lastVelocity = getEstimatedVelocityFromUpperTorso()
|
|
101
|
+
maid:GiveTask(RunService.Heartbeat:Connect(function()
|
|
102
|
+
|
|
103
|
+
debug.profilebegin("ragdollcamerashake")
|
|
104
|
+
|
|
105
|
+
local cameraCFrame = Workspace.CurrentCamera.CFrame
|
|
106
|
+
|
|
107
|
+
local velocity = getEstimatedVelocityFromUpperTorso()
|
|
108
|
+
local dVelocity = velocity - lastVelocity
|
|
109
|
+
if dVelocity.magnitude >= 0 then
|
|
110
|
+
if self._ragdollServiceClient:GetScreenShakeEnabled() then
|
|
111
|
+
impulseCamera:Impulse(cameraCFrame:vectorToObjectSpace(-0.1*cameraCFrame.lookVector:Cross(dVelocity)))
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
lastVelocity = velocity
|
|
116
|
+
debug.profileend()
|
|
117
|
+
end))
|
|
118
|
+
end))
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
function RagdollClient:_setupHapticFeedback()
|
|
122
|
+
local lastInputType = UserInputService:GetLastInputType()
|
|
123
|
+
if not HapticFeedbackUtils.setSmallVibration(lastInputType, 1) then
|
|
124
|
+
return
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
local alive = true
|
|
128
|
+
self._maid:GiveTask(function()
|
|
129
|
+
alive = false
|
|
130
|
+
end)
|
|
131
|
+
|
|
132
|
+
task.spawn(function()
|
|
133
|
+
for i=1, 0, -0.1 do
|
|
134
|
+
HapticFeedbackUtils.setSmallVibration(lastInputType, i)
|
|
135
|
+
task.wait(0.05)
|
|
136
|
+
end
|
|
137
|
+
HapticFeedbackUtils.setSmallVibration(lastInputType, 0)
|
|
138
|
+
|
|
139
|
+
if alive then
|
|
140
|
+
self._maid:GiveTask(function()
|
|
141
|
+
HapticFeedbackUtils.smallVibrate(lastInputType)
|
|
142
|
+
end)
|
|
143
|
+
end
|
|
144
|
+
end)
|
|
145
|
+
end
|
|
146
|
+
|
|
41
147
|
return Binder.new("Ragdoll", RagdollClient)
|
|
@@ -34,7 +34,7 @@ function RagdollableClient.new(humanoid, serviceBag)
|
|
|
34
34
|
self:_onRagdollChanged(ragdoll)
|
|
35
35
|
end))
|
|
36
36
|
|
|
37
|
-
self._maid:GiveTask(RagdollableInterface
|
|
37
|
+
self._maid:GiveTask(RagdollableInterface:Implement(self._obj, self))
|
|
38
38
|
|
|
39
39
|
return self
|
|
40
40
|
end
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
local require = require(script.Parent.loader).load(script)
|
|
9
9
|
|
|
10
|
-
local
|
|
10
|
+
local AttributeUtils = require("AttributeUtils")
|
|
11
|
+
local RagdollServiceConstants = require("RagdollServiceConstants")
|
|
11
12
|
|
|
12
13
|
local Players = game:GetService("Players")
|
|
13
14
|
|
|
@@ -31,10 +32,9 @@ function RagdollServiceClient:Init(serviceBag)
|
|
|
31
32
|
self._serviceBag:GetService(require("RagdollableClient"))
|
|
32
33
|
self._serviceBag:GetService(require("RagdollHumanoidOnDeathClient"))
|
|
33
34
|
self._serviceBag:GetService(require("RagdollHumanoidOnFallClient"))
|
|
34
|
-
self._serviceBag:GetService(require("RagdollCameraShakeClient"))
|
|
35
35
|
self._serviceBag:GetService(require("RagdollBindersClient"))
|
|
36
36
|
|
|
37
|
-
self._screenShakeEnabled =
|
|
37
|
+
self._screenShakeEnabled = true
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
--[=[
|
|
@@ -44,7 +44,7 @@ end
|
|
|
44
44
|
function RagdollServiceClient:SetScreenShakeEnabled(value)
|
|
45
45
|
assert(type(value) == "boolean", "Bad value")
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
Players.LocalPlayer:SetAttribute(RagdollServiceConstants.SCREEN_SHAKE_ENABLED_ATTRIBUTE, value)
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
--[=[
|
|
@@ -54,7 +54,8 @@ end
|
|
|
54
54
|
function RagdollServiceClient:GetScreenShakeEnabled()
|
|
55
55
|
assert(self._serviceBag, "Not initialized")
|
|
56
56
|
|
|
57
|
-
return
|
|
57
|
+
return AttributeUtils.getAttribute(Players.LocalPlayer, RagdollServiceConstants.SCREEN_SHAKE_ENABLED_ATTRIBUTE, true)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
|
|
60
61
|
return RagdollServiceClient
|
|
@@ -65,7 +65,7 @@ function Ragdollable.new(humanoid, serviceBag)
|
|
|
65
65
|
end))
|
|
66
66
|
self:_onRagdollChanged()
|
|
67
67
|
|
|
68
|
-
self._maid:GiveTask(RagdollableInterface
|
|
68
|
+
self._maid:GiveTask(RagdollableInterface:Implement(self._obj, self))
|
|
69
69
|
|
|
70
70
|
return self
|
|
71
71
|
end
|
|
@@ -30,7 +30,6 @@ function RagdollService:Init(serviceBag)
|
|
|
30
30
|
self._serviceBag:GetService(require("RagdollHumanoidOnDeath"))
|
|
31
31
|
self._serviceBag:GetService(require("RagdollHumanoidOnFall"))
|
|
32
32
|
self._serviceBag:GetService(require("UnragdollAutomatically"))
|
|
33
|
-
self._serviceBag:GetService(require("RagdollCameraShake"))
|
|
34
33
|
|
|
35
34
|
-- Configure
|
|
36
35
|
self._serviceBag:GetService(require("RagdollHumanoidOnDeath")):SetAutomaticTagging(false)
|
|
@@ -168,19 +168,21 @@ end
|
|
|
168
168
|
function RagdollMotorUtils.setupRagdollRootPartMotor(motor, part0, part1)
|
|
169
169
|
local maid = Maid.new()
|
|
170
170
|
|
|
171
|
-
local ragdollMotorData = RagdollMotorData:
|
|
171
|
+
local ragdollMotorData = RagdollMotorData:CreateValue(motor)
|
|
172
172
|
|
|
173
173
|
local lastTransformSpring = Spring.new(QFrame.fromCFrameClosestTo(motor.Transform, QFrame.new()))
|
|
174
174
|
lastTransformSpring.t = QFrame.new()
|
|
175
175
|
|
|
176
176
|
-- transform changed event doesn't fire, so let's use this to proxy it
|
|
177
|
-
local transformValue =
|
|
177
|
+
local transformValue = Instance.new("CFrameValue")
|
|
178
178
|
transformValue.Value = motor.Transform
|
|
179
|
+
maid:GiveTask(transformValue)
|
|
179
180
|
|
|
180
181
|
-- replacing this weld ensures interpolation for some reason
|
|
181
|
-
local weldContainer =
|
|
182
|
+
local weldContainer = Instance.new("Camera")
|
|
182
183
|
weldContainer.Name = "TempWeldContainer"
|
|
183
184
|
weldContainer.Parent = part0
|
|
185
|
+
maid:GiveTask(weldContainer)
|
|
184
186
|
|
|
185
187
|
local function setupWeld(weldType)
|
|
186
188
|
local weldMaid = Maid.new()
|
|
@@ -199,12 +201,12 @@ function RagdollMotorUtils.setupRagdollRootPartMotor(motor, part0, part1)
|
|
|
199
201
|
weld.C0 = innerState.C0 * innerState.Transform
|
|
200
202
|
end))
|
|
201
203
|
|
|
202
|
-
if weld:IsA("Motor6D") then
|
|
204
|
+
-- if weld:IsA("Motor6D") then
|
|
203
205
|
-- Suppress animations on any weld connection
|
|
204
|
-
weldMaid:GiveTask(RunService.Stepped:Connect(function()
|
|
205
|
-
|
|
206
|
-
end))
|
|
207
|
-
end
|
|
206
|
+
-- weldMaid:GiveTask(RunService.Stepped:Connect(function()
|
|
207
|
+
-- weld.Transform = CFrame.new()
|
|
208
|
+
-- end))
|
|
209
|
+
-- end
|
|
208
210
|
|
|
209
211
|
weldMaid:GiveTask(RxInstanceUtils.observeProperty(motor, "C1"):Subscribe(function(c1)
|
|
210
212
|
weld.C1 = c1
|
|
@@ -231,13 +233,13 @@ function RagdollMotorUtils.setupRagdollRootPartMotor(motor, part0, part1)
|
|
|
231
233
|
end))
|
|
232
234
|
|
|
233
235
|
-- Lerp smoothly to 0 to avoid jarring camera.
|
|
234
|
-
maid:GiveTask(RunService.Stepped:Connect(function()
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
end))
|
|
236
|
+
-- maid:GiveTask(RunService.Stepped:Connect(function()
|
|
237
|
+
-- local target = QFrame.toCFrame(lastTransformSpring.p)
|
|
238
|
+
-- if target then
|
|
239
|
+
-- transformValue.Value = target
|
|
240
|
+
-- motor.Transform = target
|
|
241
|
+
-- end
|
|
242
|
+
-- end))
|
|
241
243
|
|
|
242
244
|
motor.Enabled = false
|
|
243
245
|
|
|
@@ -255,7 +257,7 @@ function RagdollMotorUtils.setupRagdollMotor(motor, part0, part1)
|
|
|
255
257
|
maid:GiveTask(function()
|
|
256
258
|
local implemention = Motor6DStackInterface:FindFirstImplementation(motor)
|
|
257
259
|
if implemention then
|
|
258
|
-
local ragdollMotorData = RagdollMotorData:
|
|
260
|
+
local ragdollMotorData = RagdollMotorData:CreateValue(motor)
|
|
259
261
|
local initialTransform = (part0.CFrame * motor.C0):toObjectSpace(part1.CFrame * motor.C1)
|
|
260
262
|
local speed = ragdollMotorData.RagdollSpringReturnSpeed.Value
|
|
261
263
|
|
|
@@ -277,7 +279,7 @@ function RagdollMotorUtils.suppressJustRootPart(character, rigType)
|
|
|
277
279
|
|
|
278
280
|
local observable = RxR15Utils.observeRigMotorBrio(character, data.partName, data.motorName):Pipe({
|
|
279
281
|
RxBrioUtils.switchMapBrio(function(motor)
|
|
280
|
-
local ragdollMotorData = RagdollMotorData:
|
|
282
|
+
local ragdollMotorData = RagdollMotorData:CreateValue(motor)
|
|
281
283
|
|
|
282
284
|
return Rx.combineLatest({
|
|
283
285
|
motor = motor;
|
|
@@ -321,7 +323,7 @@ function RagdollMotorUtils.suppressMotors(character, rigType, velocityReadings)
|
|
|
321
323
|
for _, data in pairs(RagdollMotorUtils.getMotorData(rigType)) do
|
|
322
324
|
local observable = RxR15Utils.observeRigMotorBrio(character, data.partName, data.motorName):Pipe({
|
|
323
325
|
RxBrioUtils.switchMapBrio(function(motor)
|
|
324
|
-
local ragdollMotorData = RagdollMotorData:
|
|
326
|
+
local ragdollMotorData = RagdollMotorData:CreateValue(motor)
|
|
325
327
|
|
|
326
328
|
return RxBrioUtils.flatCombineLatest({
|
|
327
329
|
motor = motor;
|
|
@@ -71,74 +71,13 @@ function RxRagdollUtils.enforceHeadCollision(character)
|
|
|
71
71
|
return
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
-
local
|
|
75
|
-
head.CanCollide = true
|
|
76
|
-
|
|
77
|
-
maid:GiveTask(function()
|
|
78
|
-
head.CanCollide = false
|
|
79
|
-
end)
|
|
80
|
-
end))
|
|
81
|
-
|
|
82
|
-
return topMaid
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
function RxRagdollUtils.enforceHumanoidStateMachineOff(character, humanoid)
|
|
86
|
-
assert(typeof(character) == "Instance" and character:IsA("Model"), "Bad character")
|
|
87
|
-
|
|
88
|
-
local topMaid = Maid.new()
|
|
89
|
-
|
|
90
|
-
topMaid:GiveTask(RxInstanceUtils.observePropertyBrio(humanoid, "EvaluateStateMachine", function(evaluateStateMachine)
|
|
91
|
-
return not evaluateStateMachine
|
|
92
|
-
end):Subscribe(function(brio)
|
|
93
|
-
if brio:IsDead() then
|
|
94
|
-
return
|
|
95
|
-
end
|
|
96
|
-
|
|
74
|
+
local head = brio:GetValue()
|
|
97
75
|
local maid = brio:ToMaid()
|
|
98
|
-
maid:GiveTask(RxRagdollUtils.enforceLimbCollisions(character))
|
|
99
|
-
end))
|
|
100
|
-
|
|
101
|
-
return topMaid
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
function RxRagdollUtils.enforceLimbCollisions(character)
|
|
105
|
-
assert(typeof(character) == "Instance" and character:IsA("Model"), "Bad character")
|
|
106
76
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
local LIMB_NAMES = {
|
|
110
|
-
-- R6
|
|
111
|
-
["Left Arm"] = true;
|
|
112
|
-
["Right Arm"] = true;
|
|
113
|
-
["Left Leg"] = true;
|
|
114
|
-
["Right Leg"] = true;
|
|
115
|
-
|
|
116
|
-
-- R15
|
|
117
|
-
["LeftUpperArm"] = true;
|
|
118
|
-
["LeftLowerArm"] = true;
|
|
119
|
-
["LeftHand"] = true;
|
|
120
|
-
["LeftUpperLeg"] = true;
|
|
121
|
-
["LeftLowerLeg"] = true;
|
|
122
|
-
["LeftFoot"] = true;
|
|
123
|
-
["RightUpperArm"] = true;
|
|
124
|
-
["RightLowerArm"] = true;
|
|
125
|
-
["RightHand"] = true;
|
|
126
|
-
["RightUpperLeg"] = true;
|
|
127
|
-
["RightLowerLeg"] = true;
|
|
128
|
-
["RightFoot"] = true;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
topMaid:GiveTask(RxInstanceUtils.observeChildrenBrio(character, function(child)
|
|
132
|
-
return child:IsA("BasePart") and LIMB_NAMES[child.Name]
|
|
133
|
-
end):Subscribe(function(brio)
|
|
134
|
-
if brio:IsDead() then
|
|
135
|
-
return
|
|
136
|
-
end
|
|
77
|
+
head.CanCollide = true
|
|
137
78
|
|
|
138
|
-
local maid, part = brio:ToMaidAndValue()
|
|
139
|
-
part.CanCollide = true
|
|
140
79
|
maid:GiveTask(function()
|
|
141
|
-
|
|
80
|
+
head.CanCollide = false
|
|
142
81
|
end)
|
|
143
82
|
end))
|
|
144
83
|
|
|
@@ -169,7 +108,6 @@ function RxRagdollUtils.runLocal(humanoid)
|
|
|
169
108
|
|
|
170
109
|
maid:GiveTask(RxRagdollUtils.suppressRootPartCollision(character, rigType))
|
|
171
110
|
maid:GiveTask(RxRagdollUtils.enforceHeadCollision(character))
|
|
172
|
-
maid:GiveTask(RxRagdollUtils.enforceHumanoidStateMachineOff(character, humanoid))
|
|
173
111
|
|
|
174
112
|
-- Do motors
|
|
175
113
|
maid:GiveTask(RagdollMotorUtils.suppressMotors(character, rigType, velocityReadings))
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
--[=[
|
|
2
|
-
@class RagdollCameraShakeClient
|
|
3
|
-
]=]
|
|
4
|
-
|
|
5
|
-
local require = require(script.Parent.loader).load(script)
|
|
6
|
-
|
|
7
|
-
local Workspace = game:GetService("Workspace")
|
|
8
|
-
local UserInputService = game:GetService("UserInputService")
|
|
9
|
-
local RunService = game:GetService("RunService")
|
|
10
|
-
|
|
11
|
-
local BaseObject = require("BaseObject")
|
|
12
|
-
local Binder = require("Binder")
|
|
13
|
-
local CameraStackService = require("CameraStackService")
|
|
14
|
-
local HapticFeedbackUtils = require("HapticFeedbackUtils")
|
|
15
|
-
local Maid = require("Maid")
|
|
16
|
-
local RagdollMotorUtils = require("RagdollMotorUtils")
|
|
17
|
-
local RagdollServiceClient = require("RagdollServiceClient")
|
|
18
|
-
local RxBrioUtils = require("RxBrioUtils")
|
|
19
|
-
local RxCharacterUtils = require("RxCharacterUtils")
|
|
20
|
-
local RxInstanceUtils = require("RxInstanceUtils")
|
|
21
|
-
local RxR15Utils = require("RxR15Utils")
|
|
22
|
-
local RagdollClient = require("RagdollClient")
|
|
23
|
-
|
|
24
|
-
local RagdollCameraShakeClient = setmetatable({}, BaseObject)
|
|
25
|
-
RagdollCameraShakeClient.ClassName = "RagdollCameraShakeClient"
|
|
26
|
-
RagdollCameraShakeClient.__index = RagdollCameraShakeClient
|
|
27
|
-
|
|
28
|
-
function RagdollCameraShakeClient.new(humanoid, serviceBag)
|
|
29
|
-
local self = setmetatable(BaseObject.new(humanoid), RagdollCameraShakeClient)
|
|
30
|
-
|
|
31
|
-
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
32
|
-
self._ragdollServiceClient = self._serviceBag:GetService(RagdollServiceClient)
|
|
33
|
-
self._cameraStackService = self._serviceBag:GetService(CameraStackService)
|
|
34
|
-
self._ragdollBinder = self._serviceBag:GetService(RagdollClient)
|
|
35
|
-
|
|
36
|
-
-- While we've got a charater and we're ragdolled
|
|
37
|
-
self._maid:GiveTask(RxCharacterUtils.observeIsOfLocalCharacterBrio(self._obj):Pipe({
|
|
38
|
-
RxBrioUtils.switchMapBrio(function()
|
|
39
|
-
return self._ragdollBinder:ObserveBrio(self._obj)
|
|
40
|
-
end);
|
|
41
|
-
}):Subscribe(function(brio)
|
|
42
|
-
if brio:IsDead() then
|
|
43
|
-
return
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
local maid = brio:ToMaid()
|
|
47
|
-
|
|
48
|
-
maid:GiveTask(task.spawn(function()
|
|
49
|
-
-- Yield in the same way just to ensure no weird shakes.
|
|
50
|
-
RagdollMotorUtils.yieldUntilStepped()
|
|
51
|
-
|
|
52
|
-
maid:GiveTask(self:_setupHapticFeedback())
|
|
53
|
-
maid:GiveTask(self:_setupCameraShake(self._cameraStackService:GetImpulseCamera()))
|
|
54
|
-
end))
|
|
55
|
-
end))
|
|
56
|
-
|
|
57
|
-
return self
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
function RagdollCameraShakeClient:_setupCameraShake(impulseCamera)
|
|
61
|
-
local topMaid = Maid.new()
|
|
62
|
-
|
|
63
|
-
-- TODO: Move out of this open source module
|
|
64
|
-
|
|
65
|
-
-- Use the upper torso instead of the head because the upper torso shakes a lot less so
|
|
66
|
-
-- we get a stronger response to full character movement.
|
|
67
|
-
|
|
68
|
-
topMaid:GiveTask(RxInstanceUtils.observePropertyBrio(self._obj, "Parent", function(character)
|
|
69
|
-
return character ~= nil
|
|
70
|
-
end):Pipe({
|
|
71
|
-
RxBrioUtils.switchMapBrio(function(character)
|
|
72
|
-
return RxBrioUtils.flatCombineLatestBrio({
|
|
73
|
-
upperTorso = RxR15Utils.observeCharacterPartBrio(character, "UpperTorso");
|
|
74
|
-
head = RxR15Utils.observeCharacterPartBrio(character, "Head")
|
|
75
|
-
}, function(state)
|
|
76
|
-
return state.upperTorso and state.head
|
|
77
|
-
end)
|
|
78
|
-
end);
|
|
79
|
-
}):Subscribe(function(brio)
|
|
80
|
-
if brio:IsDead() then
|
|
81
|
-
return
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
local maid = brio:ToMaid()
|
|
85
|
-
local state = brio:GetValue()
|
|
86
|
-
|
|
87
|
-
local function getEstimatedVelocityFromUpperTorso()
|
|
88
|
-
-- TODO: Consider neck attachments
|
|
89
|
-
local headOffset = state.upperTorso.Size*Vector3.new(0, 0.5, 0)
|
|
90
|
-
+ state.head.Size*Vector3.new(0, 0.5, 0)
|
|
91
|
-
local headPosition = state.upperTorso.CFrame:PointToWorldSpace(headOffset)
|
|
92
|
-
|
|
93
|
-
return state.upperTorso:GetVelocityAtPosition(headPosition)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
local lastVelocity = getEstimatedVelocityFromUpperTorso()
|
|
97
|
-
maid:GiveTask(RunService.Heartbeat:Connect(function()
|
|
98
|
-
debug.profilebegin("ragdollcamerashake")
|
|
99
|
-
|
|
100
|
-
local cameraCFrame = Workspace.CurrentCamera.CFrame
|
|
101
|
-
|
|
102
|
-
local velocity = getEstimatedVelocityFromUpperTorso()
|
|
103
|
-
local dVelocity = velocity - lastVelocity
|
|
104
|
-
if dVelocity.magnitude >= 0 then
|
|
105
|
-
if self._ragdollServiceClient:GetScreenShakeEnabled() then
|
|
106
|
-
-- Defaults, but we should tune these
|
|
107
|
-
local speed = 20
|
|
108
|
-
local damper = 0.5
|
|
109
|
-
|
|
110
|
-
speed = 40
|
|
111
|
-
damper = 0.3
|
|
112
|
-
|
|
113
|
-
impulseCamera:Impulse(cameraCFrame:vectorToObjectSpace(-0.1*cameraCFrame.lookVector:Cross(dVelocity)), speed, damper)
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
lastVelocity = velocity
|
|
118
|
-
debug.profileend()
|
|
119
|
-
end))
|
|
120
|
-
end))
|
|
121
|
-
|
|
122
|
-
return topMaid
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
function RagdollCameraShakeClient:_setupHapticFeedback()
|
|
126
|
-
local maid = Maid.new()
|
|
127
|
-
|
|
128
|
-
local lastInputType = UserInputService:GetLastInputType()
|
|
129
|
-
if not HapticFeedbackUtils.setSmallVibration(lastInputType, 1) then
|
|
130
|
-
return maid
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
maid:GiveTask(task.spawn(function()
|
|
134
|
-
for i=1, 0, -0.1 do
|
|
135
|
-
HapticFeedbackUtils.setSmallVibration(lastInputType, i)
|
|
136
|
-
task.wait(0.05)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
HapticFeedbackUtils.setSmallVibration(lastInputType, 0)
|
|
140
|
-
|
|
141
|
-
maid:GiveTask(function()
|
|
142
|
-
HapticFeedbackUtils.smallVibrate(lastInputType)
|
|
143
|
-
end)
|
|
144
|
-
end))
|
|
145
|
-
|
|
146
|
-
return maid
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
return Binder.new("RagdollCameraShake", RagdollCameraShakeClient)
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
--[=[
|
|
2
|
-
Ragdolls the humanoid on death. This class exports a [Binder].
|
|
3
|
-
@server
|
|
4
|
-
@class RagdollCameraShake
|
|
5
|
-
]=]
|
|
6
|
-
|
|
7
|
-
local require = require(script.Parent.loader).load(script)
|
|
8
|
-
|
|
9
|
-
local BaseObject = require("BaseObject")
|
|
10
|
-
local Ragdoll = require("Ragdoll")
|
|
11
|
-
local PlayerHumanoidBinder = require("PlayerHumanoidBinder")
|
|
12
|
-
|
|
13
|
-
local RagdollCameraShake = setmetatable({}, BaseObject)
|
|
14
|
-
RagdollCameraShake.ClassName = "RagdollCameraShake"
|
|
15
|
-
RagdollCameraShake.__index = RagdollCameraShake
|
|
16
|
-
|
|
17
|
-
--[=[
|
|
18
|
-
Constructs a new RagdollCameraShake. This class exports a [Binder].
|
|
19
|
-
@param humanoid Humanoid
|
|
20
|
-
@param serviceBag ServiceBag
|
|
21
|
-
@return RagdollCameraShake
|
|
22
|
-
]=]
|
|
23
|
-
function RagdollCameraShake.new(humanoid, serviceBag)
|
|
24
|
-
local self = setmetatable(BaseObject.new(humanoid), RagdollCameraShake)
|
|
25
|
-
|
|
26
|
-
self._serviceBag = assert(serviceBag, "Bad serviceBag")
|
|
27
|
-
self._ragdollBinder = self._serviceBag:GetService(Ragdoll)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return self
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
return PlayerHumanoidBinder.new("RagdollCameraShake", RagdollCameraShake)
|