@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 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.478.88914d9.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/ragdoll@15.6.0...@quenty/ragdoll@15.6.1-canary.478.88914d9.0) (2024-09-12)
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
- * Upgrade ragdoll to support humanoid state machine off ([fed4769](https://github.com/Quenty/NevermoreEngine/commit/fed47690c13fdda689b80158cf9f28c8a3a00a47))
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.478.88914d9.0",
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.1-canary.478.88914d9.0",
29
- "@quenty/attributeutils": "14.3.1-canary.478.88914d9.0",
30
- "@quenty/baseobject": "10.3.1-canary.478.88914d9.0",
31
- "@quenty/binder": "14.4.1-canary.478.88914d9.0",
32
- "@quenty/brio": "14.3.1-canary.478.88914d9.0",
33
- "@quenty/camera": "14.5.1-canary.478.88914d9.0",
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.1-canary.478.88914d9.0",
36
- "@quenty/draw": "7.3.1-canary.478.88914d9.0",
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.1-canary.478.88914d9.0",
40
- "@quenty/loader": "10.3.1-canary.478.88914d9.0",
41
- "@quenty/maid": "3.2.1-canary.478.88914d9.0",
42
- "@quenty/motor6d": "7.5.1-canary.478.88914d9.0",
43
- "@quenty/physicsutils": "8.3.1-canary.478.88914d9.0",
44
- "@quenty/playerhumanoidbinder": "14.4.1-canary.478.88914d9.0",
45
- "@quenty/promise": "10.3.1-canary.478.88914d9.0",
46
- "@quenty/qframe": "10.3.1-canary.478.88914d9.0",
47
- "@quenty/r15utils": "13.3.1-canary.478.88914d9.0",
48
- "@quenty/remoting": "12.4.1-canary.478.88914d9.0",
49
- "@quenty/rx": "13.3.1-canary.478.88914d9.0",
50
- "@quenty/rxbinderutils": "14.4.1-canary.478.88914d9.0",
51
- "@quenty/rxsignal": "7.3.1-canary.478.88914d9.0",
52
- "@quenty/spring": "10.3.1-canary.478.88914d9.0",
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.1-canary.478.88914d9.0",
56
- "@quenty/valuebaseutils": "13.3.1-canary.478.88914d9.0",
57
- "@quenty/valueobject": "13.3.1-canary.478.88914d9.0"
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": "88914d9021cfc9d91111f4d825341271dc292ec4"
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.Client:Implement(self._obj, self))
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 AttributeValue = require("AttributeValue")
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 = AttributeValue.new(Players.LocalPlayer, "RagdollScreenShakeEnabled", true)
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
- self._screenShakeEnabled.Value = value
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 self._screenShakeEnabled.Value
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.Server:Implement(self._obj, self))
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:Create(motor)
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 = maid:Add(Instance.new("CFrameValue"))
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 = maid:Add(Instance.new("Camera"))
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
- weld.Transform = CFrame.new()
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
- local target = QFrame.toCFrame(lastTransformSpring.p)
236
- if target then
237
- transformValue.Value = target
238
- motor.Transform = target
239
- end
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:Create(motor)
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:Create(motor)
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:Create(motor)
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 maid, head = brio:ToMaidAndValue()
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
- local topMaid = Maid.new()
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
- part.CanCollide = false
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)