@quenty/ik 15.34.3 → 15.34.4
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 +12 -0
- package/package.json +10 -10
- package/src/Shared/Arm/ArmIKBase.lua +1 -0
- package/src/Shared/Arm/ArmIKUtils.lua +4 -1
- package/src/Shared/Grip/IKGripBase.lua +22 -12
- package/src/Shared/IKDataService.lua +1 -0
- package/src/Shared/IKUtils.lua +8 -0
- package/src/Shared/Resources/IKResource.lua +40 -22
- package/src/Shared/Resources/IKResourceUtils.lua +9 -1
- package/src/Shared/Torso/TorsoIKBase.lua +33 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
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.34.4](https://github.com/Quenty/NevermoreEngine/compare/@quenty/ik@15.34.3...@quenty/ik@15.34.4) (2025-12-28)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* Retrieve priority ([ade11b6](https://github.com/Quenty/NevermoreEngine/commit/ade11b6743f9d9dc7ad2324bcbb31d58669f048e))
|
|
12
|
+
* Typecheck some more things ([41eaaa6](https://github.com/Quenty/NevermoreEngine/commit/41eaaa6d053c469f7548810da72d05dfbf997b26))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
6
18
|
## [15.34.3](https://github.com/Quenty/NevermoreEngine/compare/@quenty/ik@15.34.2...@quenty/ik@15.34.3) (2025-12-13)
|
|
7
19
|
|
|
8
20
|
**Note:** Version bump only for package @quenty/ik
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/ik",
|
|
3
|
-
"version": "15.34.
|
|
3
|
+
"version": "15.34.4",
|
|
4
4
|
"description": "Inverse Kinematics for characters on Roblox",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -28,28 +28,28 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@quenty/acceltween": "^2.5.3",
|
|
30
30
|
"@quenty/baseobject": "^10.9.0",
|
|
31
|
-
"@quenty/binder": "^14.25.
|
|
31
|
+
"@quenty/binder": "^14.25.3",
|
|
32
32
|
"@quenty/brio": "^14.20.1",
|
|
33
|
-
"@quenty/camera": "^14.26.
|
|
33
|
+
"@quenty/camera": "^14.26.3",
|
|
34
34
|
"@quenty/characterutils": "^12.22.2",
|
|
35
|
-
"@quenty/humanoidtracker": "^13.21.
|
|
35
|
+
"@quenty/humanoidtracker": "^13.21.3",
|
|
36
36
|
"@quenty/instanceutils": "^13.20.2",
|
|
37
37
|
"@quenty/loader": "^10.9.0",
|
|
38
38
|
"@quenty/maid": "^3.5.0",
|
|
39
39
|
"@quenty/math": "^2.7.3",
|
|
40
|
-
"@quenty/motor6d": "^7.28.
|
|
40
|
+
"@quenty/motor6d": "^7.28.3",
|
|
41
41
|
"@quenty/optional": "^11.9.0",
|
|
42
42
|
"@quenty/promise": "^10.12.0",
|
|
43
43
|
"@quenty/qframe": "^10.12.1",
|
|
44
44
|
"@quenty/r15utils": "^13.21.2",
|
|
45
|
-
"@quenty/ragdoll": "^15.33.
|
|
45
|
+
"@quenty/ragdoll": "^15.33.3",
|
|
46
46
|
"@quenty/remoting": "^12.21.2",
|
|
47
47
|
"@quenty/rx": "^13.20.0",
|
|
48
|
-
"@quenty/servicebag": "^11.13.
|
|
48
|
+
"@quenty/servicebag": "^11.13.2",
|
|
49
49
|
"@quenty/signal": "^7.11.1",
|
|
50
50
|
"@quenty/table": "^3.8.0",
|
|
51
|
-
"@quenty/tie": "^10.26.
|
|
52
|
-
"@quenty/valueobject": "^13.21.
|
|
51
|
+
"@quenty/tie": "^10.26.3",
|
|
52
|
+
"@quenty/valueobject": "^13.21.3"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@quenty/rigbuilderutils": "^10.22.3"
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"publishConfig": {
|
|
58
58
|
"access": "public"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "821c9336020dfbcf5cd2f67f2dfe61624233ee06"
|
|
61
61
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
@class ArmIKUtils
|
|
3
4
|
]=]
|
|
@@ -10,7 +11,9 @@ local RxR15Utils = require("RxR15Utils")
|
|
|
10
11
|
|
|
11
12
|
local ArmIKUtils = {}
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
export type ArmName = "Left" | "Right"
|
|
15
|
+
|
|
16
|
+
function ArmIKUtils.ensureMotorAnimated(character: Model, armName: ArmName): Maid.Maid
|
|
14
17
|
local topMaid = Maid.new()
|
|
15
18
|
|
|
16
19
|
local function disable(brio)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
Meant to be used with a binder
|
|
3
4
|
@class IKGripBase
|
|
@@ -16,11 +17,23 @@ local IKGripBase = setmetatable({}, BaseObject)
|
|
|
16
17
|
IKGripBase.ClassName = "IKGripBase"
|
|
17
18
|
IKGripBase.__index = IKGripBase
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
export type IKGripBase =
|
|
21
|
+
typeof(setmetatable(
|
|
22
|
+
{} :: {
|
|
23
|
+
_obj: Instance,
|
|
24
|
+
_serviceBag: ServiceBag.ServiceBag,
|
|
25
|
+
_attachment: Attachment,
|
|
26
|
+
_ikRigPromise: Promise.Promise<any>,
|
|
27
|
+
},
|
|
28
|
+
{} :: typeof({ __index = IKGripBase })
|
|
29
|
+
))
|
|
30
|
+
& BaseObject.BaseObject
|
|
31
|
+
|
|
32
|
+
function IKGripBase.new(objectValue: ObjectValue, serviceBag: ServiceBag.ServiceBag): IKGripBase
|
|
33
|
+
local self: IKGripBase = setmetatable(BaseObject.new(objectValue) :: any, IKGripBase)
|
|
21
34
|
|
|
22
35
|
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
23
|
-
self._attachment = self._obj.Parent
|
|
36
|
+
self._attachment = self._obj.Parent :: any
|
|
24
37
|
|
|
25
38
|
assert(self._obj:IsA("ObjectValue"), "Not an object value")
|
|
26
39
|
assert(self._attachment:IsA("Attachment"), "Not parented to an attachment")
|
|
@@ -28,15 +41,15 @@ function IKGripBase.new(objectValue: ObjectValue, serviceBag: ServiceBag.Service
|
|
|
28
41
|
return self
|
|
29
42
|
end
|
|
30
43
|
|
|
31
|
-
function IKGripBase
|
|
44
|
+
function IKGripBase.GetPriority(_self: IKGripBase): number
|
|
32
45
|
return 1
|
|
33
46
|
end
|
|
34
47
|
|
|
35
|
-
function IKGripBase
|
|
36
|
-
return self.
|
|
48
|
+
function IKGripBase.GetAttachment(self: IKGripBase): Attachment?
|
|
49
|
+
return self._attachment
|
|
37
50
|
end
|
|
38
51
|
|
|
39
|
-
function IKGripBase
|
|
52
|
+
function IKGripBase.PromiseIKRig(self: IKGripBase): Promise.Promise<any>
|
|
40
53
|
if self._ikRigPromise then
|
|
41
54
|
return self._ikRigPromise
|
|
42
55
|
end
|
|
@@ -48,10 +61,7 @@ function IKGripBase:PromiseIKRig()
|
|
|
48
61
|
ikService = self._serviceBag:GetService((require :: any)("IKServiceClient"))
|
|
49
62
|
end
|
|
50
63
|
|
|
51
|
-
|
|
52
|
-
self._maid:GiveTask(promise)
|
|
53
|
-
|
|
54
|
-
self._ikRigPromise = promise:Then(function(humanoid)
|
|
64
|
+
self._ikRigPromise = self._maid:Add(promisePropertyValue(self._obj, "Value")):Then(function(humanoid)
|
|
55
65
|
if not humanoid:IsA("Humanoid") then
|
|
56
66
|
warn("[IKGripBase.PromiseIKRig] - Humanoid in link is not a humanoid")
|
|
57
67
|
return Promise.rejected()
|
|
@@ -60,7 +70,7 @@ function IKGripBase:PromiseIKRig()
|
|
|
60
70
|
return self._maid:GivePromise(ikService:PromiseRig(humanoid))
|
|
61
71
|
end)
|
|
62
72
|
|
|
63
|
-
return self._ikRigPromise
|
|
73
|
+
return self._ikRigPromise :: any
|
|
64
74
|
end
|
|
65
75
|
|
|
66
76
|
return IKGripBase
|
package/src/Shared/IKUtils.lua
CHANGED
|
@@ -6,6 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
local IKUtils = {}
|
|
8
8
|
|
|
9
|
+
--[=[
|
|
10
|
+
Returns a function that dampens angles approaching maxAngle.
|
|
11
|
+
|
|
12
|
+
@param maxAngle number -- The maximum angle allowed
|
|
13
|
+
@param dampenAreaAngle number -- The area over which to dampen
|
|
14
|
+
@param dampenAreaFactor number? -- The factor to use for dampening. Defaults to dampenAreaAngle.
|
|
15
|
+
@return (number) -> number -- A function that takes an angle and returns a dampened angle
|
|
16
|
+
]=]
|
|
9
17
|
function IKUtils.getDampenedAngleClamp(
|
|
10
18
|
maxAngle: number,
|
|
11
19
|
dampenAreaAngle: number,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
@class IKResource
|
|
3
4
|
]=]
|
|
@@ -5,29 +6,46 @@
|
|
|
5
6
|
local require = require(script.Parent.loader).load(script)
|
|
6
7
|
|
|
7
8
|
local BaseObject = require("BaseObject")
|
|
9
|
+
local IKResourceUtils = require("IKResourceUtils")
|
|
8
10
|
local Maid = require("Maid")
|
|
11
|
+
local Signal = require("Signal")
|
|
9
12
|
local ValueObject = require("ValueObject")
|
|
10
13
|
|
|
11
14
|
local IKResource = setmetatable({}, BaseObject)
|
|
12
15
|
IKResource.ClassName = "IKResource"
|
|
13
16
|
IKResource.__index = IKResource
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
export type IKResource =
|
|
19
|
+
typeof(setmetatable(
|
|
20
|
+
{} :: {
|
|
21
|
+
_data: IKResourceUtils.IKResourceData,
|
|
22
|
+
_instance: Instance?,
|
|
23
|
+
_childResourceMap: { [string]: IKResource },
|
|
24
|
+
_descendantLookupMap: { [string]: IKResource },
|
|
25
|
+
_ready: ValueObject.ValueObject<boolean>,
|
|
26
|
+
|
|
27
|
+
ReadyChanged: Signal.Signal<boolean>,
|
|
28
|
+
},
|
|
29
|
+
{} :: typeof({ __index = IKResource })
|
|
30
|
+
))
|
|
31
|
+
& BaseObject.BaseObject
|
|
32
|
+
|
|
33
|
+
function IKResource.new(data: IKResourceUtils.IKResourceData): IKResource
|
|
34
|
+
local self: IKResource = setmetatable(BaseObject.new() :: any, IKResource)
|
|
17
35
|
|
|
18
36
|
self._data = assert(data, "Bad data")
|
|
19
37
|
assert(data.name, "Bad data.name")
|
|
20
38
|
assert(data.robloxName, "Bad data.robloxName")
|
|
21
39
|
|
|
22
40
|
self._instance = nil
|
|
23
|
-
self._childResourceMap = {} -- [robloxName] =
|
|
41
|
+
self._childResourceMap = {} -- [robloxName] = IKResource
|
|
24
42
|
self._descendantLookupMap = {
|
|
25
43
|
[data.name] = self,
|
|
26
44
|
}
|
|
27
45
|
|
|
28
46
|
self._ready = self._maid:Add(ValueObject.new(false, "boolean"))
|
|
29
47
|
|
|
30
|
-
self.ReadyChanged = self._ready.Changed
|
|
48
|
+
self.ReadyChanged = self._ready.Changed :: any
|
|
31
49
|
|
|
32
50
|
if self._data.children then
|
|
33
51
|
for _, childData in self._data.children do
|
|
@@ -38,15 +56,15 @@ function IKResource.new(data)
|
|
|
38
56
|
return self
|
|
39
57
|
end
|
|
40
58
|
|
|
41
|
-
function IKResource
|
|
59
|
+
function IKResource.GetData(self: IKResource): IKResourceUtils.IKResourceData
|
|
42
60
|
return self._data
|
|
43
61
|
end
|
|
44
62
|
|
|
45
|
-
function IKResource
|
|
63
|
+
function IKResource.IsReady(self: IKResource): boolean
|
|
46
64
|
return self._ready.Value
|
|
47
65
|
end
|
|
48
66
|
|
|
49
|
-
function IKResource
|
|
67
|
+
function IKResource.Get(self: IKResource, descendantName: string)
|
|
50
68
|
local resource = self._descendantLookupMap[descendantName]
|
|
51
69
|
if not resource then
|
|
52
70
|
error(string.format("[IKResource.Get] - Resource %q does not exist", tostring(descendantName)))
|
|
@@ -60,9 +78,9 @@ function IKResource:Get(descendantName: string)
|
|
|
60
78
|
return result
|
|
61
79
|
end
|
|
62
80
|
|
|
63
|
-
function IKResource
|
|
81
|
+
function IKResource.GetInstance(self: IKResource): Instance?
|
|
64
82
|
if self._data.isLink then
|
|
65
|
-
if self._instance then
|
|
83
|
+
if self._instance and self._instance:IsA("ObjectValue") then
|
|
66
84
|
return self._instance.Value
|
|
67
85
|
else
|
|
68
86
|
return nil
|
|
@@ -72,7 +90,7 @@ function IKResource:GetInstance(): Instance?
|
|
|
72
90
|
return self._instance
|
|
73
91
|
end
|
|
74
92
|
|
|
75
|
-
function IKResource
|
|
93
|
+
function IKResource.SetInstance(self: IKResource, instance: Instance?)
|
|
76
94
|
if self._instance == instance then
|
|
77
95
|
return
|
|
78
96
|
end
|
|
@@ -91,7 +109,7 @@ function IKResource:SetInstance(instance: Instance?)
|
|
|
91
109
|
end
|
|
92
110
|
|
|
93
111
|
if instance and self._data.isLink then
|
|
94
|
-
assert(instance:IsA("ObjectValue"))
|
|
112
|
+
assert(instance:IsA("ObjectValue"), "Bad instance for link IKResource")
|
|
95
113
|
|
|
96
114
|
self._maid:GiveTask(instance.Changed:Connect(function()
|
|
97
115
|
self:_updateReady()
|
|
@@ -102,11 +120,11 @@ function IKResource:SetInstance(instance: Instance?)
|
|
|
102
120
|
self:_updateReady()
|
|
103
121
|
end
|
|
104
122
|
|
|
105
|
-
function IKResource
|
|
123
|
+
function IKResource.GetLookupTable(self: IKResource)
|
|
106
124
|
return self._descendantLookupMap
|
|
107
125
|
end
|
|
108
126
|
|
|
109
|
-
function IKResource
|
|
127
|
+
function IKResource._startListening(self: IKResource, maid, instance)
|
|
110
128
|
for _, child in instance:GetChildren() do
|
|
111
129
|
self:_handleChildAdded(child)
|
|
112
130
|
end
|
|
@@ -119,7 +137,7 @@ function IKResource:_startListening(maid, instance)
|
|
|
119
137
|
end))
|
|
120
138
|
end
|
|
121
139
|
|
|
122
|
-
function IKResource
|
|
140
|
+
function IKResource._addResource(self: IKResource, ikResource: IKResource)
|
|
123
141
|
local data = ikResource:GetData()
|
|
124
142
|
assert(data.name, "Bad data.name")
|
|
125
143
|
assert(data.robloxName, "Bad data.robloxName")
|
|
@@ -144,7 +162,7 @@ function IKResource:_addResource(ikResource)
|
|
|
144
162
|
end
|
|
145
163
|
end
|
|
146
164
|
|
|
147
|
-
function IKResource
|
|
165
|
+
function IKResource._handleChildAdded(self: IKResource, child)
|
|
148
166
|
local resource = self._childResourceMap[child.Name]
|
|
149
167
|
if not resource then
|
|
150
168
|
return
|
|
@@ -153,7 +171,7 @@ function IKResource:_handleChildAdded(child)
|
|
|
153
171
|
resource:SetInstance(child)
|
|
154
172
|
end
|
|
155
173
|
|
|
156
|
-
function IKResource
|
|
174
|
+
function IKResource._handleChildRemoved(self: IKResource, child)
|
|
157
175
|
local resource = self._childResourceMap[child.Name]
|
|
158
176
|
if not resource then
|
|
159
177
|
return
|
|
@@ -164,28 +182,28 @@ function IKResource:_handleChildRemoved(child)
|
|
|
164
182
|
end
|
|
165
183
|
end
|
|
166
184
|
|
|
167
|
-
function IKResource
|
|
168
|
-
for _, child in self._childResourceMap do
|
|
185
|
+
function IKResource._clearChildren(self: IKResource)
|
|
186
|
+
for _, child: any in self._childResourceMap do
|
|
169
187
|
child:SetInstance(nil)
|
|
170
188
|
end
|
|
171
189
|
end
|
|
172
190
|
|
|
173
|
-
function IKResource
|
|
191
|
+
function IKResource._updateReady(self: IKResource)
|
|
174
192
|
self._ready.Value = self:_calculateIsReady()
|
|
175
193
|
end
|
|
176
194
|
|
|
177
|
-
function IKResource
|
|
195
|
+
function IKResource._calculateIsReady(self: IKResource)
|
|
178
196
|
if not self._instance then
|
|
179
197
|
return false
|
|
180
198
|
end
|
|
181
199
|
|
|
182
200
|
if self._data.isLink then
|
|
183
|
-
if not self._instance.Value then
|
|
201
|
+
if self._instance and self._instance:IsA("ObjectValue") and not self._instance.Value then
|
|
184
202
|
return false
|
|
185
203
|
end
|
|
186
204
|
end
|
|
187
205
|
|
|
188
|
-
for _, child in self._childResourceMap do
|
|
206
|
+
for _, child: any in self._childResourceMap do
|
|
189
207
|
if not child:IsReady() then
|
|
190
208
|
return false
|
|
191
209
|
end
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
@class IKResourceUtils
|
|
3
4
|
]=]
|
|
4
5
|
|
|
5
6
|
local IKResourceUtils = {}
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
export type IKResourceData = {
|
|
9
|
+
name: string,
|
|
10
|
+
isLink: boolean?,
|
|
11
|
+
robloxName: string,
|
|
12
|
+
children: { IKResourceData }?,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function IKResourceUtils.createResource(data: IKResourceData): IKResourceData
|
|
8
16
|
assert(type(data) == "table", "Bad data")
|
|
9
17
|
assert(data.name, "Bad data.name")
|
|
10
18
|
assert(data.robloxName, "Bad data.robloxName")
|
|
@@ -16,8 +16,38 @@ local TorsoIKBase = setmetatable({}, BaseObject)
|
|
|
16
16
|
TorsoIKBase.__index = TorsoIKBase
|
|
17
17
|
TorsoIKBase.ClassName = "TorsoIKBase"
|
|
18
18
|
|
|
19
|
+
export type TorsoIKBase =
|
|
20
|
+
typeof(setmetatable(
|
|
21
|
+
{} :: {
|
|
22
|
+
_humanoid: Humanoid,
|
|
23
|
+
_resources: IKResource.IKResource,
|
|
24
|
+
_waistY: AccelTween.AccelTween,
|
|
25
|
+
_waistZ: AccelTween.AccelTween,
|
|
26
|
+
_headY: AccelTween.AccelTween,
|
|
27
|
+
_headZ: AccelTween.AccelTween,
|
|
28
|
+
_relWaistTransform: CFrame?,
|
|
29
|
+
_relNeckTransform: CFrame?,
|
|
30
|
+
_lastValidWaistTransform: CFrame,
|
|
31
|
+
_lastWaistTransform: CFrame,
|
|
32
|
+
_lastValidNeckTransform: CFrame,
|
|
33
|
+
_lastNeckTransform: CFrame,
|
|
34
|
+
_target: Vector3?,
|
|
35
|
+
|
|
36
|
+
Pointed: Signal.Signal<Vector3?>,
|
|
37
|
+
|
|
38
|
+
Update: (self: TorsoIKBase) -> (),
|
|
39
|
+
UpdateTransformOnly: (self: TorsoIKBase) -> (),
|
|
40
|
+
Point: (self: TorsoIKBase, position: Vector3?) -> (),
|
|
41
|
+
GetAimPosition: (self: TorsoIKBase) -> Vector3?,
|
|
42
|
+
GetTargetUpperTorsoCFrame: (self: TorsoIKBase) -> CFrame?,
|
|
43
|
+
GetUpperTorsoCFrame: (self: TorsoIKBase) -> CFrame?,
|
|
44
|
+
},
|
|
45
|
+
{} :: typeof({ __index = TorsoIKBase })
|
|
46
|
+
))
|
|
47
|
+
& BaseObject.BaseObject
|
|
48
|
+
|
|
19
49
|
function TorsoIKBase.new(humanoid: Humanoid)
|
|
20
|
-
local self = setmetatable(BaseObject.new(), TorsoIKBase)
|
|
50
|
+
local self: TorsoIKBase = setmetatable(BaseObject.new() :: any, TorsoIKBase)
|
|
21
51
|
|
|
22
52
|
self._humanoid = humanoid or error("No humanoid")
|
|
23
53
|
|
|
@@ -25,7 +55,8 @@ function TorsoIKBase.new(humanoid: Humanoid)
|
|
|
25
55
|
|
|
26
56
|
self._resources = IKResource.new(IKResourceUtils.createResource({
|
|
27
57
|
name = "Character",
|
|
28
|
-
|
|
58
|
+
-- TODO: Fix this potentail error
|
|
59
|
+
robloxName = (self._humanoid :: any).Parent.Name,
|
|
29
60
|
children = {
|
|
30
61
|
IKResourceUtils.createResource({
|
|
31
62
|
name = "RootPart",
|