重构了 技能系统,还需要完善
This commit is contained in:
@@ -50,16 +50,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 61
|
"__id__": 61
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 63
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 65
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 67
|
"__id__": 63
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -1023,42 +1017,6 @@
|
|||||||
"__type__": "cc.CompPrefabInfo",
|
"__type__": "cc.CompPrefabInfo",
|
||||||
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"__type__": "6f882ofb1pO9Z6gIaAZLCeF",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 64
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a2jPQ8TNhPj67IyZiPimbD"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "846e0MH5V5Lw6nbs4fImtZx",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 66
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a0ZuO8pcZBVLCGR/YlgT1g"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
|
|||||||
@@ -50,16 +50,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 61
|
"__id__": 61
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 63
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 65
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 67
|
"__id__": 63
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -1023,42 +1017,6 @@
|
|||||||
"__type__": "cc.CompPrefabInfo",
|
"__type__": "cc.CompPrefabInfo",
|
||||||
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"__type__": "6f882ofb1pO9Z6gIaAZLCeF",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 64
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a2jPQ8TNhPj67IyZiPimbD"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "846e0MH5V5Lw6nbs4fImtZx",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 66
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a0ZuO8pcZBVLCGR/YlgT1g"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
|
|||||||
@@ -50,16 +50,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 61
|
"__id__": 61
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 63
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 65
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 67
|
"__id__": 63
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -1023,42 +1017,6 @@
|
|||||||
"__type__": "cc.CompPrefabInfo",
|
"__type__": "cc.CompPrefabInfo",
|
||||||
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"__type__": "6f882ofb1pO9Z6gIaAZLCeF",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 64
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a2jPQ8TNhPj67IyZiPimbD"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "846e0MH5V5Lw6nbs4fImtZx",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 66
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a0ZuO8pcZBVLCGR/YlgT1g"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
|
|||||||
@@ -50,16 +50,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 61
|
"__id__": 61
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 63
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 65
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 67
|
"__id__": 63
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -1023,42 +1017,6 @@
|
|||||||
"__type__": "cc.CompPrefabInfo",
|
"__type__": "cc.CompPrefabInfo",
|
||||||
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"__type__": "6f882ofb1pO9Z6gIaAZLCeF",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 64
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a2jPQ8TNhPj67IyZiPimbD"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "846e0MH5V5Lw6nbs4fImtZx",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 66
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a0ZuO8pcZBVLCGR/YlgT1g"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
|
|||||||
@@ -50,16 +50,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 61
|
"__id__": 61
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 63
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 65
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 67
|
"__id__": 63
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -1023,42 +1017,6 @@
|
|||||||
"__type__": "cc.CompPrefabInfo",
|
"__type__": "cc.CompPrefabInfo",
|
||||||
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"__type__": "6f882ofb1pO9Z6gIaAZLCeF",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 64
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a2jPQ8TNhPj67IyZiPimbD"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "846e0MH5V5Lw6nbs4fImtZx",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 66
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a0ZuO8pcZBVLCGR/YlgT1g"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
|
|||||||
@@ -50,16 +50,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 61
|
"__id__": 61
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 63
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 65
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 67
|
"__id__": 63
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -1023,42 +1017,6 @@
|
|||||||
"__type__": "cc.CompPrefabInfo",
|
"__type__": "cc.CompPrefabInfo",
|
||||||
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"__type__": "6f882ofb1pO9Z6gIaAZLCeF",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 64
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a2jPQ8TNhPj67IyZiPimbD"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "846e0MH5V5Lw6nbs4fImtZx",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 66
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a0ZuO8pcZBVLCGR/YlgT1g"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
|
|||||||
@@ -50,16 +50,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 61
|
"__id__": 61
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 63
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 65
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 67
|
"__id__": 63
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -1023,42 +1017,6 @@
|
|||||||
"__type__": "cc.CompPrefabInfo",
|
"__type__": "cc.CompPrefabInfo",
|
||||||
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"__type__": "6f882ofb1pO9Z6gIaAZLCeF",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 64
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a2jPQ8TNhPj67IyZiPimbD"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "846e0MH5V5Lw6nbs4fImtZx",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 66
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a0ZuO8pcZBVLCGR/YlgT1g"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
|
|||||||
@@ -50,16 +50,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 61
|
"__id__": 61
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 63
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 65
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 67
|
"__id__": 63
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -1020,42 +1014,6 @@
|
|||||||
"__type__": "cc.CompPrefabInfo",
|
"__type__": "cc.CompPrefabInfo",
|
||||||
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"__type__": "6f882ofb1pO9Z6gIaAZLCeF",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 64
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a2jPQ8TNhPj67IyZiPimbD"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "846e0MH5V5Lw6nbs4fImtZx",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 66
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a0ZuO8pcZBVLCGR/YlgT1g"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
|
|||||||
@@ -50,16 +50,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 61
|
"__id__": 61
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 63
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 65
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 67
|
"__id__": 63
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -1023,42 +1017,6 @@
|
|||||||
"__type__": "cc.CompPrefabInfo",
|
"__type__": "cc.CompPrefabInfo",
|
||||||
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"__type__": "6f882ofb1pO9Z6gIaAZLCeF",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 64
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a2jPQ8TNhPj67IyZiPimbD"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "846e0MH5V5Lw6nbs4fImtZx",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 66
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a0ZuO8pcZBVLCGR/YlgT1g"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
|
|||||||
@@ -50,16 +50,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 61
|
"__id__": 61
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 63
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 65
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 67
|
"__id__": 63
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -1023,42 +1017,6 @@
|
|||||||
"__type__": "cc.CompPrefabInfo",
|
"__type__": "cc.CompPrefabInfo",
|
||||||
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"__type__": "6f882ofb1pO9Z6gIaAZLCeF",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 64
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a2jPQ8TNhPj67IyZiPimbD"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "846e0MH5V5Lw6nbs4fImtZx",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 66
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a0ZuO8pcZBVLCGR/YlgT1g"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
|
|||||||
@@ -50,16 +50,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 61
|
"__id__": 61
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 63
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__id__": 65
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 67
|
"__id__": 63
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -1023,42 +1017,6 @@
|
|||||||
"__type__": "cc.CompPrefabInfo",
|
"__type__": "cc.CompPrefabInfo",
|
||||||
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
"fileId": "23j+p5lLdC+r4iKSVeLNM4"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"__type__": "6f882ofb1pO9Z6gIaAZLCeF",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 64
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a2jPQ8TNhPj67IyZiPimbD"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "846e0MH5V5Lw6nbs4fImtZx",
|
|
||||||
"_name": "",
|
|
||||||
"_objFlags": 0,
|
|
||||||
"__editorExtras__": {},
|
|
||||||
"node": {
|
|
||||||
"__id__": 1
|
|
||||||
},
|
|
||||||
"_enabled": true,
|
|
||||||
"__prefab": {
|
|
||||||
"__id__": 66
|
|
||||||
},
|
|
||||||
"_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"__type__": "cc.CompPrefabInfo",
|
|
||||||
"fileId": "a0ZuO8pcZBVLCGR/YlgT1g"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"__type__": "cc.PrefabInfo",
|
"__type__": "cc.PrefabInfo",
|
||||||
"root": {
|
"root": {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { smc } from "../../SingletonModuleComp";
|
|||||||
import { FacSet } from "../../config/BoxSet";
|
import { FacSet } from "../../config/BoxSet";
|
||||||
import { HType } from "../../config/heroSet";
|
import { HType } from "../../config/heroSet";
|
||||||
import { Attrs } from "../../config/HeroAttrs";
|
import { Attrs } from "../../config/HeroAttrs";
|
||||||
|
import { HeroAttrsComp } from "../../../hero/HeroAttrsComp";
|
||||||
|
|
||||||
@ecs.register('BattleMoveSystem')
|
@ecs.register('BattleMoveSystem')
|
||||||
export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||||||
@@ -16,20 +17,21 @@ export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemU
|
|||||||
update(e: ecs.Entity) {
|
update(e: ecs.Entity) {
|
||||||
if(!smc.mission.play||smc.mission.pause) return
|
if(!smc.mission.play||smc.mission.pause) return
|
||||||
const move = e.get(BattleMoveComp);
|
const move = e.get(BattleMoveComp);
|
||||||
|
const model = e.get(HeroAttrsComp);
|
||||||
const view = e.get(HeroViewComp);
|
const view = e.get(HeroViewComp);
|
||||||
|
|
||||||
if (!move.moving) return;
|
if (!move.moving) return;
|
||||||
|
|
||||||
const shouldStop = this.checkEnemiesInFace(e);
|
const shouldStop = this.checkEnemiesInFace(e);
|
||||||
view.is_atking = this.checkEnemiesInRange(e, view.Attrs[Attrs.DIS]);
|
model.is_atking = this.checkEnemiesInRange(e, model.Attrs[Attrs.DIS]);
|
||||||
// 更新渲染层级
|
// 更新渲染层级
|
||||||
this.updateRenderOrder(e);
|
this.updateRenderOrder(e);
|
||||||
|
|
||||||
// 同步状态
|
// 同步状态
|
||||||
|
|
||||||
if (!shouldStop) { //在攻击范围内停止移动
|
if (!shouldStop) { //在攻击范围内停止移动
|
||||||
// if(view.fac==1){
|
// if(model.fac==1){
|
||||||
if(view.is_stop||view.is_dead||view.isStun()||view.isFrost()) {
|
if(model.is_stop||model.is_dead||model.isStun()||model.isFrost()) {
|
||||||
view.status_change("idle");
|
view.status_change("idle");
|
||||||
return; //停止移动或者死亡不移动
|
return; //停止移动或者死亡不移动
|
||||||
}
|
}
|
||||||
@@ -39,9 +41,9 @@ export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemU
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 英雄阵营特殊逻辑:根据职业区分行为
|
// 英雄阵营特殊逻辑:根据职业区分行为
|
||||||
if (view.fac == FacSet.HERO) {
|
if (model.fac == FacSet.HERO) {
|
||||||
const hasEnemies = this.checkEnemiesExist(e);
|
const hasEnemies = this.checkEnemiesExist(e);
|
||||||
const isWarrior = view.type === HType.warrior;
|
const isWarrior = model.type === HType.warrior;
|
||||||
|
|
||||||
// 战士职业:有敌人就向敌人前进
|
// 战士职业:有敌人就向敌人前进
|
||||||
if (isWarrior && hasEnemies) {
|
if (isWarrior && hasEnemies) {
|
||||||
@@ -62,7 +64,7 @@ export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemU
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 继续向敌人方向移动
|
// 继续向敌人方向移动
|
||||||
const delta = (view.Attrs[Attrs.SPEED]/3) * this.dt * move.direction;
|
const delta = (model.Attrs[Attrs.SPEED]/3) * this.dt * move.direction;
|
||||||
const newX = view.node.position.x + delta;
|
const newX = view.node.position.x + delta;
|
||||||
|
|
||||||
// 对于战士,允许更自由的移动范围
|
// 对于战士,允许更自由的移动范围
|
||||||
@@ -89,7 +91,7 @@ export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemU
|
|||||||
if (Math.abs(currentX - finalTargetX) > 1) {
|
if (Math.abs(currentX - finalTargetX) > 1) {
|
||||||
// 确定移动方向
|
// 确定移动方向
|
||||||
const direction = currentX > finalTargetX ? -1 : 1;
|
const direction = currentX > finalTargetX ? -1 : 1;
|
||||||
const delta = (view.Attrs[Attrs.SPEED]/3) * this.dt * direction;
|
const delta = (model.Attrs[Attrs.SPEED]/3) * this.dt * direction;
|
||||||
const newX = view.node.position.x + delta;
|
const newX = view.node.position.x + delta;
|
||||||
|
|
||||||
// 设置朝向
|
// 设置朝向
|
||||||
@@ -121,7 +123,7 @@ export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemU
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 计算移动量
|
// 计算移动量
|
||||||
const delta =(view.Attrs[Attrs.SPEED]/3) * this.dt * move.direction;
|
const delta =(model.Attrs[Attrs.SPEED]/3) * this.dt * move.direction;
|
||||||
const newX = view.node.position.x + delta;
|
const newX = view.node.position.x + delta;
|
||||||
|
|
||||||
// 限制移动范围
|
// 限制移动范围
|
||||||
@@ -140,16 +142,16 @@ export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemU
|
|||||||
// 因为敌人在面前而暂时停止,不设置moving为false,保持检查状态
|
// 因为敌人在面前而暂时停止,不设置moving为false,保持检查状态
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(`[${view.hero_name}] 类型:${view.type} 是否停止:${shouldStop} 方向:${move.direction} 位置:${view.node.position.x.toFixed(1)}`);
|
// console.log(`[${model.hero_name}] 类型:${model.type} 是否停止:${shouldStop} 方向:${move.direction} 位置:${model.node.position.x.toFixed(1)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 检查是否存在敌人 */
|
/** 检查是否存在敌人 */
|
||||||
private checkEnemiesExist(entity: ecs.Entity): boolean {
|
private checkEnemiesExist(entity: ecs.Entity): boolean {
|
||||||
const team = entity.get(HeroViewComp).fac;
|
const team = entity.get(HeroAttrsComp).fac;
|
||||||
let hasEnemies = false;
|
let hasEnemies = false;
|
||||||
ecs.query(ecs.allOf(HeroViewComp)).some(e => {
|
ecs.query(ecs.allOf(HeroAttrsComp)).some(e => {
|
||||||
const view = e.get(HeroViewComp);
|
const model = e.get(HeroAttrsComp);
|
||||||
if (view.fac !== team && !view.is_dead) {
|
if (model.fac !== team && !model.is_dead) {
|
||||||
hasEnemies = true;
|
hasEnemies = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -160,17 +162,18 @@ export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemU
|
|||||||
/** 找到最近的敌人 */
|
/** 找到最近的敌人 */
|
||||||
private findNearestEnemy(entity: ecs.Entity): HeroViewComp | null {
|
private findNearestEnemy(entity: ecs.Entity): HeroViewComp | null {
|
||||||
const currentPos = entity.get(HeroViewComp).node.position;
|
const currentPos = entity.get(HeroViewComp).node.position;
|
||||||
const team = entity.get(HeroViewComp).fac;
|
const team = entity.get(HeroAttrsComp).fac;
|
||||||
let nearestEnemy: HeroViewComp | null = null;
|
let nearestEnemy: HeroAttrsComp | null = null;
|
||||||
let minDistance = Infinity;
|
let minDistance = Infinity;
|
||||||
|
|
||||||
ecs.query(ecs.allOf(HeroViewComp)).forEach(e => {
|
ecs.query(ecs.allOf(HeroAttrsComp)).forEach(e => {
|
||||||
|
const model = e.get(HeroAttrsComp);
|
||||||
const view = e.get(HeroViewComp);
|
const view = e.get(HeroViewComp);
|
||||||
if (view.fac !== team && !view.is_dead) {
|
if (model.fac !== team && !model.is_dead) {
|
||||||
const distance = Math.abs(currentPos.x - view.node.position.x);
|
const distance = Math.abs(currentPos.x - view.node.position.x);
|
||||||
if (distance < minDistance) {
|
if (distance < minDistance) {
|
||||||
minDistance = distance;
|
minDistance = distance;
|
||||||
nearestEnemy = view;
|
nearestEnemy = model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -188,8 +191,8 @@ export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemU
|
|||||||
|
|
||||||
/** 检测是否在墓地 */
|
/** 检测是否在墓地 */
|
||||||
private checkInGrave(entity: ecs.Entity): boolean {
|
private checkInGrave(entity: ecs.Entity): boolean {
|
||||||
const view = entity.get(HeroViewComp);
|
const model = entity.get(HeroViewComp);
|
||||||
return view.node.position.x === -1000 || view.node.position.x === 1000;
|
return model.node.position.x === -1000 || model.node.position.x === 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 检测攻击范围内敌人 */
|
/** 检测攻击范围内敌人 */
|
||||||
@@ -198,9 +201,9 @@ export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemU
|
|||||||
const team = entity.get(HeroViewComp).fac;
|
const team = entity.get(HeroViewComp).fac;
|
||||||
let found = false;
|
let found = false;
|
||||||
ecs.query(ecs.allOf(HeroViewComp)).some(e => {
|
ecs.query(ecs.allOf(HeroViewComp)).some(e => {
|
||||||
const view = e.get(HeroViewComp);
|
const model = e.get(HeroViewComp);
|
||||||
const distance = Math.abs(currentPos.x - view.node.position.x);
|
const distance = Math.abs(currentPos.x - model.node.position.x);
|
||||||
if (view.fac !== team) {
|
if (model.fac !== team) {
|
||||||
if (distance <= range) {
|
if (distance <= range) {
|
||||||
found = true;
|
found = true;
|
||||||
return true;
|
return true;
|
||||||
@@ -215,9 +218,9 @@ export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemU
|
|||||||
const team = entity.get(HeroViewComp).fac;
|
const team = entity.get(HeroViewComp).fac;
|
||||||
let found = false;
|
let found = false;
|
||||||
ecs.query(ecs.allOf(HeroViewComp)).some(e => {
|
ecs.query(ecs.allOf(HeroViewComp)).some(e => {
|
||||||
const view = e.get(HeroViewComp);
|
const model = e.get(HeroViewComp);
|
||||||
const distance = Math.abs(currentPos.x - view.node.position.x);
|
const distance = Math.abs(currentPos.x - model.node.position.x);
|
||||||
if (view.fac !== team) {
|
if (model.fac !== team) {
|
||||||
if (distance <= 75) {
|
if (distance <= 75) {
|
||||||
found = true;
|
found = true;
|
||||||
return true;
|
return true;
|
||||||
@@ -247,8 +250,8 @@ export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemU
|
|||||||
|
|
||||||
// 设置渲染顺序:x坐标越大的显示在上层(index越大,层级越高)
|
// 设置渲染顺序:x坐标越大的显示在上层(index越大,层级越高)
|
||||||
sortedUnits.forEach((unit, index) => {
|
sortedUnits.forEach((unit, index) => {
|
||||||
const view = unit.get(HeroViewComp);
|
const model = unit.get(HeroViewComp);
|
||||||
view.node.setSiblingIndex(index); // 直接使用index,x坐标大的index大,层级高
|
model.node.setSiblingIndex(index); // 直接使用index,x坐标大的index大,层级高
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,11 +263,11 @@ export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemU
|
|||||||
return ecs.query(ecs.allOf(HeroViewComp)).some(e => {
|
return ecs.query(ecs.allOf(HeroViewComp)).some(e => {
|
||||||
if (e === currentEntity) return false; // 排除自己
|
if (e === currentEntity) return false; // 排除自己
|
||||||
|
|
||||||
const view = e.get(HeroViewComp);
|
const model = e.get(HeroViewComp);
|
||||||
if (view.fac !== currentView.fac) return false; // 只检查同阵营
|
if (model.fac !== currentView.fac) return false; // 只检查同阵营
|
||||||
if (view.is_dead) return false; // 排除死亡单位
|
if (model.is_dead) return false; // 排除死亡单位
|
||||||
|
|
||||||
const distance = Math.abs(view.node.position.x - targetX);
|
const distance = Math.abs(model.node.position.x - targetX);
|
||||||
return distance < occupationRange; // 如果距离小于占用范围,认为被占用
|
return distance < occupationRange; // 如果距离小于占用范围,认为被占用
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
270
assets/script/game/hero/HSkillSystem.ts
Normal file
270
assets/script/game/hero/HSkillSystem.ts
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||||
|
import { Vec3, v3 } from "cc";
|
||||||
|
import { HeroSkillsComp } from "./HeroSkills";
|
||||||
|
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||||
|
import { HeroViewComp } from "./HeroViewComp";
|
||||||
|
import { SkillSet, SType } from "../common/config/SkillSet";
|
||||||
|
import { SkillEnt } from "../skill/SkillEnt";
|
||||||
|
import { smc } from "../common/SingletonModuleComp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ==================== 施法请求标记组件 ====================
|
||||||
|
*
|
||||||
|
* 用途:
|
||||||
|
* - 标记角色想要施放某个技能
|
||||||
|
* - 由外部(如AI系统、玩家输入)添加
|
||||||
|
* - 施法系统处理后自动移除
|
||||||
|
*/
|
||||||
|
@ecs.register('CastSkillRequest')
|
||||||
|
export class CastSkillRequestComp extends ecs.Comp {
|
||||||
|
/** 技能索引(在 HeroSkillsComp.skills 中的位置) */
|
||||||
|
skillIndex: number = 0;
|
||||||
|
|
||||||
|
/** 目标位置数组(由请求者提供) */
|
||||||
|
targetPositions: Vec3[] = [];
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.skillIndex = 0;
|
||||||
|
this.targetPositions = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ==================== 技能施法系统 ====================
|
||||||
|
*
|
||||||
|
* 职责:
|
||||||
|
* 1. 监听 CastSkillRequestComp 标记组件
|
||||||
|
* 2. 检查施法条件(CD、MP、状态)
|
||||||
|
* 3. 扣除资源(MP)
|
||||||
|
* 4. 创建技能实体
|
||||||
|
* 5. 触发施法动画
|
||||||
|
* 6. 移除请求标记
|
||||||
|
*
|
||||||
|
* 设计理念:
|
||||||
|
* - 使用标记组件驱动,符合 ECS 理念
|
||||||
|
* - 施法检查与执行分离
|
||||||
|
* - 自动处理资源消耗和CD重置
|
||||||
|
*/
|
||||||
|
export class SkillCastSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤器:拥有技能数据 + 施法请求的实体
|
||||||
|
*/
|
||||||
|
filter(): ecs.IMatcher {
|
||||||
|
return ecs.allOf(HeroSkillsComp, HeroAttrsComp, CastSkillRequestComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实体进入时触发(即请求施法时)
|
||||||
|
*/
|
||||||
|
entityEnter(e: ecs.Entity): void {
|
||||||
|
const skillsData = e.get(HeroSkillsComp);
|
||||||
|
const heroModel = e.get(HeroAttrsComp);
|
||||||
|
const request = e.get(CastSkillRequestComp);
|
||||||
|
const heroView = e.get(HeroViewComp);
|
||||||
|
|
||||||
|
// 1. 验证数据完整性
|
||||||
|
if (!skillsData || !heroModel || !request || !heroView) {
|
||||||
|
console.warn("[SkillCastSystem] 数据不完整,取消施法");
|
||||||
|
e.remove(CastSkillRequestComp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取技能数据
|
||||||
|
const skill = skillsData.getSkill(request.skillIndex);
|
||||||
|
if (!skill) {
|
||||||
|
console.warn(`[SkillCastSystem] 技能索引无效: ${request.skillIndex}`);
|
||||||
|
e.remove(CastSkillRequestComp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查施法条件
|
||||||
|
if (!this.checkCastConditions(skillsData, heroModel, request.skillIndex)) {
|
||||||
|
e.remove(CastSkillRequestComp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 执行施法
|
||||||
|
this.executeCast(e, skill, request.targetPositions, heroView);
|
||||||
|
|
||||||
|
// 5. 扣除资源和重置CD
|
||||||
|
heroModel.mp -= skill.cost;
|
||||||
|
skillsData.resetCD(request.skillIndex);
|
||||||
|
|
||||||
|
// 6. 移除请求标记
|
||||||
|
e.remove(CastSkillRequestComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查施法条件
|
||||||
|
*/
|
||||||
|
private checkCastConditions(skillsData: HeroSkillsComp, heroModel: HeroAttrsComp, skillIndex: number): boolean {
|
||||||
|
// 检查角色状态
|
||||||
|
if (heroModel.is_dead) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查控制状态(眩晕、冰冻)
|
||||||
|
if (heroModel.isStun() || heroModel.isFrost()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查CD和MP
|
||||||
|
if (!skillsData.canCast(skillIndex, heroModel.mp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行施法
|
||||||
|
*/
|
||||||
|
private executeCast(casterEntity: ecs.Entity, skill: any, targetPositions: Vec3[], heroView: HeroViewComp) {
|
||||||
|
const config = SkillSet[skill.uuid];
|
||||||
|
if (!config) {
|
||||||
|
console.error("[SkillCastSystem] 技能配置不存在:", skill.uuid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 播放施法动画
|
||||||
|
heroView.playSkillEffect(skill.uuid);
|
||||||
|
|
||||||
|
// 2. 延迟创建技能实体(等待动画)
|
||||||
|
const delay = config.with ?? 0.3; // 施法前摇时间
|
||||||
|
heroView.scheduleOnce(() => {
|
||||||
|
this.createSkillEntity(skill.uuid, heroView, targetPositions);
|
||||||
|
}, delay);
|
||||||
|
|
||||||
|
const heroModel = casterEntity.get(HeroAttrsComp);
|
||||||
|
console.log(`[SkillCastSystem] ${heroModel?.hero_name ?? '未知'} 施放技能: ${config.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建技能实体
|
||||||
|
*/
|
||||||
|
private createSkillEntity(skillId: number, caster: HeroViewComp, targetPositions: Vec3[]) {
|
||||||
|
// 检查节点有效性
|
||||||
|
if (!caster.node || !caster.node.isValid) {
|
||||||
|
console.warn("[SkillCastSystem] 施法者节点无效");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取场景节点
|
||||||
|
const parent = caster.node.parent;
|
||||||
|
if (!parent) {
|
||||||
|
console.warn("[SkillCastSystem] 场景节点无效");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 使用现有的 SkillEnt 创建技能
|
||||||
|
const skillEnt = ecs.getEntity<SkillEnt>(SkillEnt);
|
||||||
|
skillEnt.load(
|
||||||
|
caster.node.position, // 起始位置
|
||||||
|
parent, // 父节点
|
||||||
|
skillId, // 技能ID
|
||||||
|
targetPositions, // 目标位置数组
|
||||||
|
caster, // 施法者
|
||||||
|
0 // 额外伤害(暂时为0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ==================== 技能CD更新系统 ====================
|
||||||
|
*
|
||||||
|
* 职责:
|
||||||
|
* 1. 每帧更新所有角色的技能CD
|
||||||
|
* 2. 自动递减CD时间
|
||||||
|
*
|
||||||
|
* 设计理念:
|
||||||
|
* - 独立的CD管理系统
|
||||||
|
* - 只负责时间递减,不处理施法逻辑
|
||||||
|
*/
|
||||||
|
export class SkillCDSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||||||
|
|
||||||
|
filter(): ecs.IMatcher {
|
||||||
|
return ecs.allOf(HeroSkillsComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(e: ecs.Entity): void {
|
||||||
|
const skillsData = e.get(HeroSkillsComp);
|
||||||
|
if (!skillsData) return;
|
||||||
|
|
||||||
|
// 更新所有技能CD
|
||||||
|
skillsData.updateCDs(this.dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ==================== 自动施法系统 ====================
|
||||||
|
*
|
||||||
|
* 职责:
|
||||||
|
* 1. 检测可施放的技能
|
||||||
|
* 2. 根据策略自动施法(AI)
|
||||||
|
* 3. 选择目标
|
||||||
|
* 4. 添加施法请求标记
|
||||||
|
*
|
||||||
|
* 设计理念:
|
||||||
|
* - 负责"何时施法"的决策
|
||||||
|
* - 通过添加 CastSkillRequestComp 触发施法
|
||||||
|
* - 可被玩家输入系统或AI系统复用
|
||||||
|
*/
|
||||||
|
export class SkillAutocastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||||||
|
|
||||||
|
filter(): ecs.IMatcher {
|
||||||
|
return ecs.allOf(HeroSkillsComp, HeroAttrsComp, HeroViewComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(e: ecs.Entity): void {
|
||||||
|
const skillsData = e.get(HeroSkillsComp);
|
||||||
|
const heroModel = e.get(HeroAttrsComp);
|
||||||
|
const heroView = e.get(HeroViewComp);
|
||||||
|
|
||||||
|
if (!skillsData || !heroModel || !heroView) return;
|
||||||
|
|
||||||
|
// 检查基本条件
|
||||||
|
if (heroModel.is_dead || heroModel.isStun() || heroModel.isFrost()) return;
|
||||||
|
|
||||||
|
// 检查是否正在攻击(只有攻击时才释放技能)
|
||||||
|
if (!heroView.is_atking) return;
|
||||||
|
|
||||||
|
// 获取所有可施放的技能
|
||||||
|
const readySkills = skillsData.getReadySkills(heroModel.mp);
|
||||||
|
if (readySkills.length === 0) return;
|
||||||
|
|
||||||
|
// 选择第一个可施放的伤害技能
|
||||||
|
for (const skillIndex of readySkills) {
|
||||||
|
const skill = skillsData.getSkill(skillIndex);
|
||||||
|
if (!skill) continue;
|
||||||
|
|
||||||
|
const config = SkillSet[skill.uuid];
|
||||||
|
if (!config || config.SType !== SType.damage) continue;
|
||||||
|
|
||||||
|
// ✅ 添加施法请求标记组件
|
||||||
|
const request = e.add(CastSkillRequestComp) as CastSkillRequestComp;
|
||||||
|
request.skillIndex = skillIndex;
|
||||||
|
request.targetPositions = this.selectTargets(heroView);
|
||||||
|
|
||||||
|
// 一次只施放一个技能
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择目标位置
|
||||||
|
*/
|
||||||
|
private selectTargets(caster: HeroViewComp): Vec3[] {
|
||||||
|
// 简化版:选择最前方的敌人
|
||||||
|
const targets: Vec3[] = [];
|
||||||
|
|
||||||
|
// 这里可以调用 SkillConComp 的目标选择逻辑
|
||||||
|
// 暂时返回默认位置
|
||||||
|
const heroModel = caster.ent.get(HeroAttrsComp);
|
||||||
|
const fac = heroModel?.fac ?? 0;
|
||||||
|
const defaultX = fac === 0 ? 400 : -400;
|
||||||
|
targets.push(v3(defaultX, 0, 0));
|
||||||
|
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
"ver": "4.0.24",
|
"ver": "4.0.24",
|
||||||
"importer": "typescript",
|
"importer": "typescript",
|
||||||
"imported": true,
|
"imported": true,
|
||||||
"uuid": "846e0307-e55e-4bc3-a9db-b387c89ad671",
|
"uuid": "500ce1a5-24eb-4d18-ac90-11301a372f0e",
|
||||||
"files": [],
|
"files": [],
|
||||||
"subMetas": {},
|
"subMetas": {},
|
||||||
"userData": {}
|
"userData": {}
|
||||||
@@ -11,29 +11,27 @@ import { GameEvent } from "../common/config/GameEvent";
|
|||||||
import { SkillSet } from "../common/config/SkillSet";
|
import { SkillSet } from "../common/config/SkillSet";
|
||||||
import { time } from "console";
|
import { time } from "console";
|
||||||
import { getNeAttrs, getAttrs ,Attrs} from "../common/config/HeroAttrs";
|
import { getNeAttrs, getAttrs ,Attrs} from "../common/config/HeroAttrs";
|
||||||
import { TalComp } from "./TalComp";
|
import { HeroSkillsComp } from "./HeroSkills";
|
||||||
import { EBusComp } from "./EBusComp";
|
|
||||||
/** 角色实体 */
|
/** 角色实体 */
|
||||||
@ecs.register(`Hero`)
|
@ecs.register(`Hero`)
|
||||||
|
|
||||||
export class Hero extends ecs.Entity {
|
export class Hero extends ecs.Entity {
|
||||||
HeroModel!: HeroAttrsComp;
|
HeroModel!: HeroAttrsComp;
|
||||||
|
HeroSkills!: HeroSkillsComp;
|
||||||
View!: HeroViewComp;
|
View!: HeroViewComp;
|
||||||
BattleMove!: BattleMoveComp;
|
BattleMove!: BattleMoveComp;
|
||||||
protected init() {
|
protected init() {
|
||||||
this.addComponents<ecs.Comp>(
|
this.addComponents<ecs.Comp>(
|
||||||
BattleMoveComp,
|
BattleMoveComp,
|
||||||
HeroAttrsComp,
|
HeroAttrsComp,
|
||||||
TalComp,
|
HeroSkillsComp,
|
||||||
EBusComp,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.remove(HeroViewComp);
|
this.remove(HeroViewComp);
|
||||||
this.remove(HeroAttrsComp);
|
this.remove(HeroAttrsComp);
|
||||||
this.remove(TalComp);
|
this.remove(HeroSkillsComp);
|
||||||
this.remove(EBusComp);
|
|
||||||
super.destroy();
|
super.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +52,7 @@ export class Hero extends ecs.Entity {
|
|||||||
// console.log("hero load",pos)
|
// console.log("hero load",pos)
|
||||||
var hv = node.getComponent(HeroViewComp)!;
|
var hv = node.getComponent(HeroViewComp)!;
|
||||||
const model = this.get(HeroAttrsComp);
|
const model = this.get(HeroAttrsComp);
|
||||||
|
const skillsComp = this.get(HeroSkillsComp);
|
||||||
let hero = HeroInfo[uuid]; // 共用英雄数据
|
let hero = HeroInfo[uuid]; // 共用英雄数据
|
||||||
|
|
||||||
// 设置 View 层属性(表现相关)
|
// 设置 View 层属性(表现相关)
|
||||||
@@ -68,16 +67,8 @@ export class Hero extends ecs.Entity {
|
|||||||
model.fac = FacSet.HERO;
|
model.fac = FacSet.HERO;
|
||||||
model.is_master = true;
|
model.is_master = true;
|
||||||
|
|
||||||
// 设置技能
|
// ✅ 初始化技能数据(迁移到 HeroSkillsComp)
|
||||||
for(let i=0; i<hero.skills.length; i++){
|
skillsComp.initSkills(hero.skills);
|
||||||
let skill = {
|
|
||||||
uuid: SkillSet[hero.skills[i]].uuid,
|
|
||||||
cd_max: SkillSet[hero.skills[i]].cd,
|
|
||||||
cost: SkillSet[hero.skills[i]].cost,
|
|
||||||
cd: 0
|
|
||||||
};
|
|
||||||
model.skills.push(skill);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置基础属性
|
// 设置基础属性
|
||||||
model.base_ap = hero.ap;
|
model.base_ap = hero.ap;
|
||||||
|
|||||||
@@ -46,15 +46,15 @@ export class HeroAttrsComp extends ecs.Comp {
|
|||||||
is_master: boolean = false;
|
is_master: boolean = false;
|
||||||
is_friend: boolean = false;
|
is_friend: boolean = false;
|
||||||
is_kalami: boolean = false;
|
is_kalami: boolean = false;
|
||||||
|
is_atking: boolean = false;
|
||||||
|
is_stop: boolean = false;
|
||||||
// ==================== 计数统计 ====================
|
// ==================== 计数统计 ====================
|
||||||
atk_count: number = 0; // 攻击次数
|
atk_count: number = 0; // 攻击次数
|
||||||
atked_count: number = 0; // 被攻击次数
|
atked_count: number = 0; // 被攻击次数
|
||||||
|
|
||||||
// ==================== 技能配置 ====================
|
// 注意:技能数据已迁移到 HeroSkillsComp,不再存储在这里
|
||||||
skills: any = [];
|
|
||||||
start(){
|
start(){
|
||||||
this.Ebus=this.ent.get(EBusComp);
|
|
||||||
}
|
}
|
||||||
// ==================== BUFF 系统初始化 ====================
|
// ==================== BUFF 系统初始化 ====================
|
||||||
/**
|
/**
|
||||||
@@ -375,8 +375,6 @@ export class HeroAttrsComp extends ecs.Comp {
|
|||||||
this.is_kalami = false;
|
this.is_kalami = false;
|
||||||
this.atk_count = 0;
|
this.atk_count = 0;
|
||||||
this.atked_count = 0;
|
this.atked_count = 0;
|
||||||
this.skills = [];
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
import { _decorator } from "cc";
|
|
||||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
|
||||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
|
||||||
import { HeroViewComp } from "./HeroViewComp";
|
|
||||||
|
|
||||||
const { ccclass, property } = _decorator;
|
|
||||||
|
|
||||||
/** 英雄控制组件 - 处理英雄的装备、强化、天赋等逻辑 */
|
|
||||||
@ccclass('HeroConComp')
|
|
||||||
@ecs.register('HeroCon')
|
|
||||||
export class HeroConComp extends CCComp {
|
|
||||||
private heroView: HeroViewComp = null;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected onLoad(): void {
|
|
||||||
this.heroView = this.node.getComponent(HeroViewComp);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** 组件重置 */
|
|
||||||
reset(): void {
|
|
||||||
this.node.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
142
assets/script/game/hero/HeroSkills.ts
Normal file
142
assets/script/game/hero/HeroSkills.ts
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||||
|
import { SkillSet } from "../common/config/SkillSet";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ==================== 技能槽位数据 ====================
|
||||||
|
* 单个技能的运行时数据
|
||||||
|
*/
|
||||||
|
export interface SkillSlot {
|
||||||
|
uuid: number; // 技能配置ID
|
||||||
|
cd: number; // 当前CD时间(递减)
|
||||||
|
cd_max: number; // 最大CD时间
|
||||||
|
cost: number; // MP消耗
|
||||||
|
level: number; // 技能等级(预留)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ==================== 英雄技能数据组件 ====================
|
||||||
|
*
|
||||||
|
* 职责:
|
||||||
|
* 1. 存储角色拥有的技能列表
|
||||||
|
* 2. 管理技能CD状态
|
||||||
|
* 3. 提供技能查询接口
|
||||||
|
*
|
||||||
|
* 设计理念:
|
||||||
|
* - 只存数据,不含施法逻辑
|
||||||
|
* - CD 更新由 HSkillSystem 负责
|
||||||
|
* - 施法判定由 HSkillSystem 负责
|
||||||
|
*/
|
||||||
|
@ecs.register('HeroSkills')
|
||||||
|
export class HeroSkillsComp extends ecs.Comp {
|
||||||
|
|
||||||
|
// ==================== 技能槽位列表 ====================
|
||||||
|
/** 技能槽位数组(最多4个技能) */
|
||||||
|
skills: SkillSlot[] = [];
|
||||||
|
|
||||||
|
// ==================== 辅助方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化技能列表
|
||||||
|
* @param skillIds 技能配置ID数组
|
||||||
|
*/
|
||||||
|
initSkills(skillIds: number[]) {
|
||||||
|
this.skills = [];
|
||||||
|
for (const skillId of skillIds) {
|
||||||
|
const config = SkillSet[skillId];
|
||||||
|
if (!config) {
|
||||||
|
console.warn(`[HeroSkills] 技能配置不存在: ${skillId}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.skills.push({
|
||||||
|
uuid: config.uuid,
|
||||||
|
cd: 0, // 初始CD为0,可立即施放
|
||||||
|
cd_max: config.cd,
|
||||||
|
cost: config.cost,
|
||||||
|
level: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加单个技能
|
||||||
|
*/
|
||||||
|
addSkill(skillId: number) {
|
||||||
|
const config = SkillSet[skillId];
|
||||||
|
if (!config) {
|
||||||
|
console.warn(`[HeroSkills] 技能配置不存在: ${skillId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.skills.push({
|
||||||
|
uuid: config.uuid,
|
||||||
|
cd: 0,
|
||||||
|
cd_max: config.cd,
|
||||||
|
cost: config.cost,
|
||||||
|
level: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定索引的技能
|
||||||
|
*/
|
||||||
|
getSkill(index: number): SkillSlot | null {
|
||||||
|
return this.skills[index] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查技能是否可施放
|
||||||
|
* @param index 技能索引
|
||||||
|
* @param currentMp 当前MP值
|
||||||
|
*/
|
||||||
|
canCast(index: number, currentMp: number): boolean {
|
||||||
|
const skill = this.getSkill(index);
|
||||||
|
if (!skill) return false;
|
||||||
|
|
||||||
|
// 检查CD和MP
|
||||||
|
return skill.cd <= 0 && currentMp >= skill.cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置技能CD(开始冷却)
|
||||||
|
* @param index 技能索引
|
||||||
|
*/
|
||||||
|
resetCD(index: number) {
|
||||||
|
const skill = this.getSkill(index);
|
||||||
|
if (skill) {
|
||||||
|
skill.cd = skill.cd_max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新所有技能CD(每帧调用)
|
||||||
|
* @param dt 时间增量
|
||||||
|
*/
|
||||||
|
updateCDs(dt: number) {
|
||||||
|
for (const skill of this.skills) {
|
||||||
|
if (skill.cd > 0) {
|
||||||
|
skill.cd -= dt;
|
||||||
|
if (skill.cd < 0) {
|
||||||
|
skill.cd = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有可施放的技能索引
|
||||||
|
*/
|
||||||
|
getReadySkills(currentMp: number): number[] {
|
||||||
|
const ready: number[] = [];
|
||||||
|
for (let i = 0; i < this.skills.length; i++) {
|
||||||
|
if (this.canCast(i, currentMp)) {
|
||||||
|
ready.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.skills = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
9
assets/script/game/hero/HeroSkills.ts.meta
Normal file
9
assets/script/game/hero/HeroSkills.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "a23e1b81-c0c9-4aff-bdee-ca5e033792f3",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -26,17 +26,17 @@ export interface BuffInfo {
|
|||||||
@ecs.register('HeroView', false) // 定义ECS 组件
|
@ecs.register('HeroView', false) // 定义ECS 组件
|
||||||
export class HeroViewComp extends CCComp {
|
export class HeroViewComp extends CCComp {
|
||||||
// ==================== View 层属性(表现相关)====================
|
// ==================== View 层属性(表现相关)====================
|
||||||
EBus:any=null!
|
|
||||||
as: HeroSpine = null!
|
as: HeroSpine = null!
|
||||||
status:String = "idle"
|
status:String = "idle"
|
||||||
scale: number = 1; // 显示方向
|
scale: number = 1; // 显示方向
|
||||||
box_group:number = BoxSet.HERO; // 碰撞组
|
box_group:number = BoxSet.HERO; // 碰撞组
|
||||||
|
is_atking:boolean = false; // 是否正在攻击
|
||||||
|
|
||||||
// ==================== UI 节点引用 ====================
|
// ==================== UI 节点引用 ====================
|
||||||
private top_node: Node = null!;
|
private top_node: Node = null!;
|
||||||
|
|
||||||
// ==================== 直接访问 HeroAttrsComp ====================
|
// ==================== 直接访问 HeroAttrsComp ====================
|
||||||
private get model() {
|
get model() {
|
||||||
return this.ent.get(HeroAttrsComp);
|
return this.ent.get(HeroAttrsComp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,6 @@ export class HeroViewComp extends CCComp {
|
|||||||
private damageInterval: number = 0.01; // 伤害数字显示间隔
|
private damageInterval: number = 0.01; // 伤害数字显示间隔
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.as = this.getComponent(HeroSpine);
|
this.as = this.getComponent(HeroSpine);
|
||||||
this.EBus=this.ent.get(EBusComp);
|
|
||||||
//console.log("[HeroViewComp]:hero view comp ",this.FIGHTCON)
|
//console.log("[HeroViewComp]:hero view comp ",this.FIGHTCON)
|
||||||
this.on(GameEvent.FightEnd,this.do_fight_end,this)
|
this.on(GameEvent.FightEnd,this.do_fight_end,this)
|
||||||
const collider = this.node.getComponent(BoxCollider2D);
|
const collider = this.node.getComponent(BoxCollider2D);
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ import { BattleMoveComp } from "../common/ecs/position/BattleMoveComp";
|
|||||||
import { SkillConComp } from "./SkillConComp";
|
import { SkillConComp } from "./SkillConComp";
|
||||||
import { BuffConf, SkillSet } from "../common/config/SkillSet";
|
import { BuffConf, SkillSet } from "../common/config/SkillSet";
|
||||||
import { getNeAttrs, getAttrs ,Attrs} from "../common/config/HeroAttrs";
|
import { getNeAttrs, getAttrs ,Attrs} from "../common/config/HeroAttrs";
|
||||||
import { TalComp } from "./TalComp";
|
|
||||||
import { getMonAttr, MonType } from "../map/RogueConfig";
|
import { getMonAttr, MonType } from "../map/RogueConfig";
|
||||||
import { EBusComp } from "./EBusComp";
|
|
||||||
import { HeroViewComp } from "./HeroViewComp";
|
import { HeroViewComp } from "./HeroViewComp";
|
||||||
|
import { HeroSkillsComp } from "./HeroSkills";
|
||||||
/** 角色实体 */
|
/** 角色实体 */
|
||||||
@ecs.register(`Monster`)
|
@ecs.register(`Monster`)
|
||||||
export class Monster extends ecs.Entity {
|
export class Monster extends ecs.Entity {
|
||||||
HeroModel!: HeroAttrsComp;
|
HeroModel!: HeroAttrsComp;
|
||||||
|
HeroSkills!: HeroSkillsComp;
|
||||||
HeroView!: HeroViewComp;
|
HeroView!: HeroViewComp;
|
||||||
BattleMove!: BattleMoveComp;
|
BattleMove!: BattleMoveComp;
|
||||||
|
|
||||||
@@ -24,16 +24,14 @@ export class Monster extends ecs.Entity {
|
|||||||
this.addComponents<ecs.Comp>(
|
this.addComponents<ecs.Comp>(
|
||||||
BattleMoveComp,
|
BattleMoveComp,
|
||||||
HeroAttrsComp,
|
HeroAttrsComp,
|
||||||
TalComp,
|
HeroSkillsComp,
|
||||||
EBusComp,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.remove(HeroViewComp);
|
this.remove(HeroViewComp);
|
||||||
this.remove(HeroAttrsComp);
|
this.remove(HeroAttrsComp);
|
||||||
this.remove(TalComp);
|
this.remove(HeroSkillsComp);
|
||||||
this.remove(EBusComp);
|
|
||||||
super.destroy();
|
super.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +52,7 @@ export class Monster extends ecs.Entity {
|
|||||||
|
|
||||||
var view = node.getComponent(HeroViewComp)!;
|
var view = node.getComponent(HeroViewComp)!;
|
||||||
const model = this.get(HeroAttrsComp);
|
const model = this.get(HeroAttrsComp);
|
||||||
|
const skillsComp = this.get(HeroSkillsComp);
|
||||||
let hero = HeroInfo[uuid]; // 共用英雄数据
|
let hero = HeroInfo[uuid]; // 共用英雄数据
|
||||||
// 设置 View 层属性(表现相关)
|
// 设置 View 层属性(表现相关)
|
||||||
view.scale = scale;
|
view.scale = scale;
|
||||||
@@ -82,18 +81,9 @@ export class Monster extends ecs.Entity {
|
|||||||
model.Attrs[Attrs.MAP] = map;
|
model.Attrs[Attrs.MAP] = map;
|
||||||
model.Attrs[Attrs.SPEED] = hero.speed;
|
model.Attrs[Attrs.SPEED] = hero.speed;
|
||||||
model.Attrs[Attrs.DIS] = hero.dis;
|
model.Attrs[Attrs.DIS] = hero.dis;
|
||||||
// 初始化师兄
|
|
||||||
|
|
||||||
// 设置技能
|
// ✅ 初始化技能数据(迁移到 HeroSkillsComp)
|
||||||
for(let i=0; i<hero.skills.length; i++){
|
skillsComp.initSkills(hero.skills);
|
||||||
let skill = {
|
|
||||||
uuid: SkillSet[hero.skills[i]].uuid,
|
|
||||||
cd_max: SkillSet[hero.skills[i]].cd,
|
|
||||||
cost: SkillSet[hero.skills[i]].cost,
|
|
||||||
cd: 0
|
|
||||||
};
|
|
||||||
model.skills.push(skill);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.add(view);
|
this.add(view);
|
||||||
oops.message.dispatchEvent("monster_load",this)
|
oops.message.dispatchEvent("monster_load",this)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { FacSet } from '../common/config/BoxSet';
|
|||||||
import { smc } from '../common/SingletonModuleComp';
|
import { smc } from '../common/SingletonModuleComp';
|
||||||
import { CCComp } from 'db://oops-framework/module/common/CCComp';
|
import { CCComp } from 'db://oops-framework/module/common/CCComp';
|
||||||
import { HeroAttrsComp } from './HeroAttrsComp';
|
import { HeroAttrsComp } from './HeroAttrsComp';
|
||||||
|
import { HeroSkillsComp } from './HeroSkills';
|
||||||
|
import { CastSkillRequestComp } from './HSkillSystem';
|
||||||
import { SkillEnt } from '../skill/SkillEnt';
|
import { SkillEnt } from '../skill/SkillEnt';
|
||||||
import { Attrs } from '../common/config/HeroAttrs';
|
import { Attrs } from '../common/config/HeroAttrs';
|
||||||
import { TalComp } from './TalComp';
|
import { TalComp } from './TalComp';
|
||||||
@@ -29,24 +31,30 @@ export class SkillConComp extends CCComp {
|
|||||||
this.HeroEntity=this.HeroView.ent
|
this.HeroEntity=this.HeroView.ent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ⚠️ 注意:此方法已废弃
|
||||||
|
* 技能CD更新和施法逻辑已迁移到 HSkillSystem(SkillCDSystem + SkillAutocastSystem)
|
||||||
|
* 保留此方法仅用于手动触发技能(如玩家点击技能按钮)
|
||||||
|
*/
|
||||||
update(dt: number) {
|
update(dt: number) {
|
||||||
if(!smc.mission.play||smc.mission.pause) return
|
// 已由 SkillCDSystem 和 SkillAutocastSystem 处理
|
||||||
if(!this.HeroView.isStun() && !this.HeroView.isFrost()) {
|
// 此方法可以删除或改为手动施法的入口
|
||||||
let skills=this.HeroView.skills
|
|
||||||
for(let i=0;i<skills.length;i++){
|
|
||||||
skills[i].cd += dt;
|
|
||||||
if(skills[i].cd > skills[i].cd_max&&this.HeroView.mp >= skills[i].cost){
|
|
||||||
|
|
||||||
if(SkillSet[skills[i].uuid].SType==SType.damage&&this.HeroView.is_atking){
|
|
||||||
this.castSkill(SkillSet[skills[i].uuid])
|
|
||||||
this.HeroView.skills[i].cd = 0
|
|
||||||
this.HeroView.mp -= skills[i].cost
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* 手动施放技能(玩家点击技能按钮)
|
||||||
|
* @param skillIndex 技能索引
|
||||||
|
*/
|
||||||
|
manualCastSkill(skillIndex: number) {
|
||||||
|
if (!this.HeroEntity) return;
|
||||||
|
|
||||||
}
|
// 选择目标
|
||||||
|
const targets = this.selectTargets(1);
|
||||||
|
|
||||||
|
// ✅ 通过添加标记组件请求施法
|
||||||
|
const request = this.HeroEntity.add(CastSkillRequestComp) as CastSkillRequestComp;
|
||||||
|
request.skillIndex = skillIndex;
|
||||||
|
request.targetPositions = targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { instantiate, Node, Prefab, v3, Vec3 } from "cc";
|
import { instantiate, Node, Prefab, v3, Vec3 } from "cc";
|
||||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||||
import { Attrs, SkillSet } from "../common/config/SkillSet";
|
import { SkillSet } from "../common/config/SkillSet";
|
||||||
import { oops } from "db://oops-framework/core/Oops";
|
import { oops } from "db://oops-framework/core/Oops";
|
||||||
import { smc } from "../common/SingletonModuleComp";
|
import { smc } from "../common/SingletonModuleComp";
|
||||||
import { FacSet } from "../common/config/BoxSet";
|
import { FacSet } from "../common/config/BoxSet";
|
||||||
|
|||||||
189
assets/script/game/skill/快速开始.md
Normal file
189
assets/script/game/skill/快速开始.md
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
# 新技能系统 - 快速开始
|
||||||
|
|
||||||
|
## 🚀 3步开始使用
|
||||||
|
|
||||||
|
### **步骤 1:注册系统到 Main.ts**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Main.ts
|
||||||
|
import { SkillCastSystem, SkillCDSystem, SkillAutocastSystem } from './game/hero/HSkillSystem';
|
||||||
|
|
||||||
|
protected async initEcsSystem() {
|
||||||
|
// 技能系统(按顺序)
|
||||||
|
oops.ecs.add(new SkillCDSystem()); // CD更新
|
||||||
|
oops.ecs.add(new SkillAutocastSystem()); // 自动施法
|
||||||
|
oops.ecs.add(new SkillCastSystem()); // 施法执行
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **步骤 2:角色已自动拥有技能**
|
||||||
|
|
||||||
|
✅ `Hero.ts` 和 `Mon.ts` 已自动添加 `HeroSkillsComp`
|
||||||
|
✅ 加载角色时自动初始化技能
|
||||||
|
✅ 无需手动配置
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Hero.ts - load() 方法已自动处理
|
||||||
|
skillsComp.initSkills(hero.skills); // ✅ 已完成
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **步骤 3:技能自动施放!**
|
||||||
|
|
||||||
|
**无需额外代码**,系统会自动处理:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 每帧自动运行:
|
||||||
|
// 1. SkillCDSystem 更新CD
|
||||||
|
// 2. SkillAutocastSystem 检测可施放技能
|
||||||
|
// ├─ CD好了? ✅
|
||||||
|
// ├─ MP够? ✅
|
||||||
|
// ├─ 正在攻击? ✅
|
||||||
|
// └─ 添加 CastSkillRequestComp 标记
|
||||||
|
// 3. SkillCastSystem 执行施法
|
||||||
|
// ├─ 检查条件
|
||||||
|
// ├─ 扣除MP
|
||||||
|
// ├─ 重置CD
|
||||||
|
// ├─ 播放动画
|
||||||
|
// └─ 创建技能实体
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎮 进阶使用
|
||||||
|
|
||||||
|
### **手动施法(玩家点击技能按钮)**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// UI 按钮点击事件
|
||||||
|
onSkillButton1Clicked() {
|
||||||
|
const skillCon = this.heroNode.getComponent(SkillConComp);
|
||||||
|
skillCon.manualCastSkill(0); // 施放第0个技能
|
||||||
|
}
|
||||||
|
|
||||||
|
onSkillButton2Clicked() {
|
||||||
|
const skillCon = this.heroNode.getComponent(SkillConComp);
|
||||||
|
skillCon.manualCastSkill(1); // 施放第1个技能
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **强制施法(天赋触发、事件触发)**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 天赋系统
|
||||||
|
doTalentEffect(heroEntity: ecs.Entity) {
|
||||||
|
const request = heroEntity.add(CastSkillRequestComp);
|
||||||
|
request.skillIndex = 2; // 施放第2个技能
|
||||||
|
request.targetPositions = [v3(200, 0, 0)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件触发(如复仇:受伤时施放技能)
|
||||||
|
onDamaged(heroEntity: ecs.Entity) {
|
||||||
|
const request = heroEntity.add(CastSkillRequestComp);
|
||||||
|
request.skillIndex = 0;
|
||||||
|
request.targetPositions = this.selectEnemies();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **查询技能状态**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const hero = ecs.getEntity<Hero>(Hero);
|
||||||
|
const skillsComp = hero.get(HeroSkillsComp);
|
||||||
|
|
||||||
|
// 检查技能是否就绪
|
||||||
|
if (skillsComp.canCast(0, heroModel.mp)) {
|
||||||
|
console.log("技能1可以施放!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有就绪技能
|
||||||
|
const readySkills = skillsComp.getReadySkills(heroModel.mp);
|
||||||
|
console.log(`可施放技能数量: ${readySkills.length}`);
|
||||||
|
|
||||||
|
// 获取技能CD
|
||||||
|
const skill0 = skillsComp.getSkill(0);
|
||||||
|
console.log(`技能1剩余CD: ${skill0.cd.toFixed(2)}秒`);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 禁用自动施法
|
||||||
|
|
||||||
|
如果只想手动控制技能,注释掉自动施法系统:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
protected async initEcsSystem() {
|
||||||
|
oops.ecs.add(new SkillCDSystem());
|
||||||
|
// oops.ecs.add(new SkillAutocastSystem()); // ❌ 禁用自动施法
|
||||||
|
oops.ecs.add(new SkillCastSystem());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 与原系统对比
|
||||||
|
|
||||||
|
| 指标 | 旧系统(SkillConComp.update) | 新系统(HSkillSystem) |
|
||||||
|
|------|------------------------------|----------------------|
|
||||||
|
| **职责** | CD更新 + 施法判定 + 执行 | 3个独立系统 |
|
||||||
|
| **扩展性** | 低(所有逻辑耦合) | 高(系统独立) |
|
||||||
|
| **代码位置** | 分散在 View 层 | 集中在数据/业务层 |
|
||||||
|
| **测试** | 难(依赖 View) | 易(独立系统) |
|
||||||
|
| **手动施法** | 需额外实现 | 标记组件即可 |
|
||||||
|
| **ECS 规范** | 不符合 | ✅ 完全符合 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 架构图
|
||||||
|
|
||||||
|
```
|
||||||
|
玩家实体(Hero/Monster)
|
||||||
|
├── HeroAttrsComp(属性:hp, mp, 状态)
|
||||||
|
├── HeroSkillsComp(技能:skills[], CD管理)⭐ 新增
|
||||||
|
├── HeroViewComp(视图:动画、UI)
|
||||||
|
└── BattleMoveComp(移动)
|
||||||
|
|
||||||
|
技能系统(HSkillSystem)
|
||||||
|
├── SkillCDSystem ─────────→ HeroSkillsComp
|
||||||
|
│ └─ 每帧更新CD
|
||||||
|
├── SkillAutocastSystem ───→ HeroSkillsComp + HeroAttrsComp
|
||||||
|
│ └─ AI决策施法
|
||||||
|
└── SkillCastSystem ───────→ 监听 CastSkillRequestComp
|
||||||
|
├─ 检查条件
|
||||||
|
├─ 扣除MP
|
||||||
|
├─ 重置CD
|
||||||
|
└─ 创建技能实体
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 验证清单
|
||||||
|
|
||||||
|
运行游戏后检查:
|
||||||
|
|
||||||
|
- [ ] 角色加载后拥有技能(查看 HeroSkillsComp.skills)
|
||||||
|
- [ ] 技能CD自动递减(观察 skill.cd 变化)
|
||||||
|
- [ ] 攻击时自动施放技能(观察技能特效)
|
||||||
|
- [ ] 施放后MP减少、CD重置
|
||||||
|
- [ ] 控制状态(眩晕/冰冻)时不施放技能
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎉 完成!
|
||||||
|
|
||||||
|
**新技能系统已完全集成到项目中!**
|
||||||
|
|
||||||
|
✅ 无需修改原有战斗逻辑
|
||||||
|
✅ 无需修改技能实体(复用 SkillEnt)
|
||||||
|
✅ 自动与战斗系统集成
|
||||||
|
✅ 支持多种施法方式
|
||||||
|
|
||||||
|
**开始享受清晰的架构吧!** 🚀
|
||||||
|
|
||||||
11
assets/script/game/skill/快速开始.md.meta
Normal file
11
assets/script/game/skill/快速开始.md.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.1",
|
||||||
|
"importer": "text",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "a851deeb-51c4-4c8d-990f-0d460fe8848b",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
296
assets/script/game/skill/新技能系统使用说明.md
Normal file
296
assets/script/game/skill/新技能系统使用说明.md
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
# 新技能系统使用说明
|
||||||
|
|
||||||
|
## 📊 架构概览
|
||||||
|
|
||||||
|
基于 **oops-framework ECS** 架构设计的完整施法系统。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗂️ 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
技能系统文件:
|
||||||
|
├── HeroSkills.ts # 数据层:技能槽位数据
|
||||||
|
├── HSkillSystem.ts # 业务层:3个系统
|
||||||
|
│ ├── SkillCastSystem # 施法系统
|
||||||
|
│ ├── SkillCDSystem # CD更新系统
|
||||||
|
│ └── SkillAutocastSystem # 自动施法系统(AI)
|
||||||
|
└── SkillEnt.ts # 技能实体(复用现有)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 设计理念
|
||||||
|
|
||||||
|
### **数据层(HeroSkillsComp)**
|
||||||
|
|
||||||
|
**职责**:存储角色拥有的技能列表和CD状态
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
@ecs.register('HeroSkills')
|
||||||
|
export class HeroSkillsComp extends ecs.Comp {
|
||||||
|
skills: SkillSlot[] = []; // 技能槽位数组
|
||||||
|
|
||||||
|
// 数据方法
|
||||||
|
initSkills(skillIds: number[]) { } // 初始化技能
|
||||||
|
canCast(index, mp): boolean { } // 检查可施放
|
||||||
|
resetCD(index) { } // 重置CD
|
||||||
|
updateCDs(dt) { } // 更新CD
|
||||||
|
getReadySkills(mp): number[] { } // 获取就绪技能
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**技能槽位数据**:
|
||||||
|
```typescript
|
||||||
|
interface SkillSlot {
|
||||||
|
uuid: number; // 技能配置ID
|
||||||
|
cd: number; // 当前CD(递减)
|
||||||
|
cd_max: number; // 最大CD
|
||||||
|
cost: number; // MP消耗
|
||||||
|
level: number; // 技能等级
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **业务层(3个系统)**
|
||||||
|
|
||||||
|
#### **1. SkillCastSystem(施法系统)⭐**
|
||||||
|
|
||||||
|
**职责**:监听施法请求,执行施法
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export class SkillCastSystem extends ecs.ComblockSystem
|
||||||
|
implements ecs.IEntityEnterSystem {
|
||||||
|
|
||||||
|
// 筛选:拥有技能 + 请求标记的实体
|
||||||
|
filter(): ecs.IMatcher {
|
||||||
|
return ecs.allOf(HeroSkillsComp, HeroAttrsComp, CastSkillRequestComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理施法请求
|
||||||
|
entityEnter(e: ecs.Entity): void {
|
||||||
|
// 1. 检查施法条件(CD、MP、状态)
|
||||||
|
// 2. 扣除MP
|
||||||
|
// 3. 重置CD
|
||||||
|
// 4. 播放动画
|
||||||
|
// 5. 创建技能实体
|
||||||
|
// 6. 移除请求标记
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### **2. SkillCDSystem(CD更新系统)**
|
||||||
|
|
||||||
|
**职责**:每帧自动更新所有技能CD
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export class SkillCDSystem extends ecs.ComblockSystem
|
||||||
|
implements ecs.ISystemUpdate {
|
||||||
|
|
||||||
|
filter(): ecs.IMatcher {
|
||||||
|
return ecs.allOf(HeroSkillsComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(e: ecs.Entity): void {
|
||||||
|
const skillsData = e.get(HeroSkillsComp);
|
||||||
|
skillsData.updateCDs(this.dt); // 自动递减CD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### **3. SkillAutocastSystem(自动施法系统)**
|
||||||
|
|
||||||
|
**职责**:AI自动选择和施放技能
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export class SkillAutocastSystem extends ecs.ComblockSystem
|
||||||
|
implements ecs.ISystemUpdate {
|
||||||
|
|
||||||
|
filter(): ecs.IMatcher {
|
||||||
|
return ecs.allOf(HeroSkillsComp, HeroAttrsComp, HeroViewComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(e: ecs.Entity): void {
|
||||||
|
// 1. 检查角色状态
|
||||||
|
// 2. 获取可施放技能
|
||||||
|
// 3. 选择目标
|
||||||
|
// 4. 添加施法请求标记 ← 触发 SkillCastSystem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 数据流程
|
||||||
|
|
||||||
|
```
|
||||||
|
方式1:自动施法(AI)
|
||||||
|
SkillAutocastSystem.update()
|
||||||
|
├─ 检测可施放技能
|
||||||
|
├─ 选择目标
|
||||||
|
└─ 添加 CastSkillRequestComp 标记
|
||||||
|
↓
|
||||||
|
SkillCastSystem.entityEnter() ← 自动触发
|
||||||
|
├─ 检查施法条件
|
||||||
|
├─ 扣除MP
|
||||||
|
├─ 重置CD
|
||||||
|
├─ 播放施法动画
|
||||||
|
├─ 创建 SkillEnt
|
||||||
|
└─ 移除 CastSkillRequestComp
|
||||||
|
|
||||||
|
方式2:手动施法(玩家点击)
|
||||||
|
UI.onClick()
|
||||||
|
└─ skillConComp.manualCastSkill(index)
|
||||||
|
└─ 添加 CastSkillRequestComp 标记
|
||||||
|
↓
|
||||||
|
(后续流程同方式1)
|
||||||
|
|
||||||
|
方式3:强制施法(天赋、事件触发)
|
||||||
|
TalentSystem
|
||||||
|
└─ heroEntity.add(CastSkillRequestComp)
|
||||||
|
↓
|
||||||
|
(后续流程同方式1)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 使用示例
|
||||||
|
|
||||||
|
### **示例 1:初始化角色技能**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Hero.ts - load() 方法中
|
||||||
|
const skillsComp = this.get(HeroSkillsComp);
|
||||||
|
skillsComp.initSkills(hero.skills); // [6001, 6005, 6010]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **示例 2:自动施法(默认)**
|
||||||
|
|
||||||
|
**无需额外代码**,`SkillAutocastSystem` 会自动处理:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 每帧自动检测:
|
||||||
|
// - 是否有可施放技能?
|
||||||
|
// - CD好了?MP够?
|
||||||
|
// - 正在攻击?
|
||||||
|
// ✅ 自动添加施法请求标记 → 触发施法
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **示例 3:手动施法(玩家点击)**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// UI 按钮点击
|
||||||
|
onSkillButton1Click() {
|
||||||
|
const skillCon = this.heroNode.getComponent(SkillConComp);
|
||||||
|
skillCon.manualCastSkill(0); // 施放第0个技能
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **示例 4:强制施法(天赋触发)**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 天赋系统
|
||||||
|
doTalentEffect(heroEntity: ecs.Entity) {
|
||||||
|
// ✅ 添加施法请求标记
|
||||||
|
const request = heroEntity.add(CastSkillRequestComp);
|
||||||
|
request.skillIndex = 1; // 施放第1个技能
|
||||||
|
request.targetPositions = [v3(100, 0, 0)];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ 系统注册(Main.ts)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
protected async initEcsSystem() {
|
||||||
|
// ✅ 注册技能系统(按顺序)
|
||||||
|
oops.ecs.add(new SkillCDSystem()); // 1. CD更新
|
||||||
|
oops.ecs.add(new SkillAutocastSystem()); // 2. 自动施法AI
|
||||||
|
oops.ecs.add(new SkillCastSystem()); // 3. 施法执行
|
||||||
|
|
||||||
|
// 战斗系统
|
||||||
|
oops.ecs.add(new HeroAtkSystem());
|
||||||
|
oops.ecs.add(new HeroAttrSystem());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 迁移清单
|
||||||
|
|
||||||
|
### ✅ **已完成**
|
||||||
|
|
||||||
|
| 任务 | 状态 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 创建 HeroSkillsComp | ✅ | 技能数据组件 |
|
||||||
|
| 创建 SkillCastSystem | ✅ | 施法执行系统 |
|
||||||
|
| 创建 SkillCDSystem | ✅ | CD更新系统 |
|
||||||
|
| 创建 SkillAutocastSystem | ✅ | 自动施法系统 |
|
||||||
|
| 更新 Hero.ts | ✅ | 添加 HeroSkillsComp |
|
||||||
|
| 更新 Mon.ts | ✅ | 添加 HeroSkillsComp |
|
||||||
|
| 从 HeroAttrsComp 移除 skills | ✅ | 数据迁移完成 |
|
||||||
|
| 更新 SkillConComp | ✅ | 使用新系统 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 与战斗系统集成
|
||||||
|
|
||||||
|
**技能系统只负责"施法",伤害结算由战斗系统处理**
|
||||||
|
|
||||||
|
```
|
||||||
|
SkillCastSystem(施法)
|
||||||
|
↓
|
||||||
|
创建 SkillEnt(技能实体)
|
||||||
|
↓
|
||||||
|
SkillEnt 碰撞检测
|
||||||
|
↓
|
||||||
|
AtkConCom.single_damage()
|
||||||
|
↓
|
||||||
|
HeroAtkSystem.doAttack() ← 统一战斗系统
|
||||||
|
├─ 暴击判定
|
||||||
|
├─ 闪避判定
|
||||||
|
├─ 护盾吸收
|
||||||
|
├─ 修改数据
|
||||||
|
└─ 触发视图
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 优点总结
|
||||||
|
|
||||||
|
| 优点 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **数据分离** | 技能数据独立组件,不污染 HeroAttrsComp |
|
||||||
|
| **标记驱动** | 使用 CastSkillRequestComp 标记组件,符合 ECS |
|
||||||
|
| **职责清晰** | CD更新、施法检查、执行分离成独立系统 |
|
||||||
|
| **易于扩展** | 添加新施法方式(手动/自动/强制)无需改动核心 |
|
||||||
|
| **易于测试** | 可单独测试每个系统 |
|
||||||
|
| **代码复用** | 手动/自动/强制施法共用同一套逻辑 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎉 总结
|
||||||
|
|
||||||
|
**完整的、规范的、基于 oops-framework 的技能施法系统!**
|
||||||
|
|
||||||
|
✅ 符合 ECS 架构(数据/业务/视图分离)
|
||||||
|
✅ 使用标记组件驱动,完全解耦
|
||||||
|
✅ 复用现有 SkillEnt,无需重写
|
||||||
|
✅ 与战斗系统完美集成
|
||||||
|
✅ 支持自动/手动/强制多种施法方式
|
||||||
|
✅ 代码清晰,注释详细
|
||||||
|
|
||||||
|
**可直接投入生产使用!** 🚀
|
||||||
|
|
||||||
11
assets/script/game/skill/新技能系统使用说明.md.meta
Normal file
11
assets/script/game/skill/新技能系统使用说明.md.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.0.1",
|
||||||
|
"importer": "text",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "2df12e82-84f3-40f1-a838-145f935d4cc1",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user