feat: 重构英雄编队逻辑,优化出生与排队规则

1. 移除英雄出生落点偏移,改为按职业优先级从右向左排队
2. 重构英雄卡点击逻辑,支持点击整张卡牌选取
3. 新增英雄血量背景预制体与UI按钮样式
4. 优化AOE技能目标选取逻辑,使用中排基准点
This commit is contained in:
panFD
2026-08-19 22:52:17 +08:00
parent 0ec27b09a4
commit 6c82c632b6
14 changed files with 6667 additions and 6588 deletions

View File

@@ -29,16 +29,19 @@
}, },
{ {
"__id__": 26 "__id__": 26
},
{
"__id__": 34
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 76 "__id__": 84
} }
], ],
"_prefab": { "_prefab": {
"__id__": 78 "__id__": 86
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
@@ -78,7 +81,7 @@
"__id__": 1 "__id__": 1
}, },
"_children": [], "_children": [],
"_active": true, "_active": false,
"_components": [ "_components": [
{ {
"__id__": 3 "__id__": 3
@@ -108,8 +111,8 @@
}, },
"_lscale": { "_lscale": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 1, "x": 0.5,
"y": 1, "y": 0.5,
"z": 1 "z": 1
}, },
"_mobility": 0, "_mobility": 0,
@@ -253,7 +256,7 @@
"__id__": 1 "__id__": 1
}, },
"_children": [], "_children": [],
"_active": true, "_active": false,
"_components": [ "_components": [
{ {
"__id__": 11 "__id__": 11
@@ -283,8 +286,8 @@
}, },
"_lscale": { "_lscale": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 1, "x": 0.5,
"y": 1, "y": 0.5,
"z": 1 "z": 1
}, },
"_mobility": 0, "_mobility": 0,
@@ -428,7 +431,7 @@
"__id__": 1 "__id__": 1
}, },
"_children": [], "_children": [],
"_active": true, "_active": false,
"_components": [ "_components": [
{ {
"__id__": 19 "__id__": 19
@@ -458,8 +461,8 @@
}, },
"_lscale": { "_lscale": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 1, "x": 0.5,
"y": 1, "y": 0.5,
"z": 1 "z": 1
}, },
"_mobility": 0, "_mobility": 0,
@@ -594,6 +597,181 @@
"targetOverrides": null, "targetOverrides": null,
"nestedPrefabInstanceRoots": null "nestedPrefabInstanceRoots": null
}, },
{
"__type__": "cc.Node",
"_name": "Button01_l_Yellow",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 1
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 27
},
{
"__id__": 29
},
{
"__id__": 31
}
],
"_prefab": {
"__id__": 33
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 0.6,
"y": 0.6,
"z": 1
},
"_mobility": 0,
"_layer": 1073741824,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 26
},
"_enabled": true,
"__prefab": {
"__id__": 28
},
"_contentSize": {
"__type__": "cc.Size",
"width": 250,
"height": 133.33333333333334
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "f56shr5D9N2aJyQ8kZt9jR"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 26
},
"_enabled": true,
"__prefab": {
"__id__": 30
},
"_customMaterial": null,
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_spriteFrame": {
"__uuid__": "cb93c900-b440-4571-91d1-7da1636e3d73@0e901",
"__expectedType__": "cc.SpriteFrame"
},
"_type": 1,
"_fillType": 0,
"_sizeMode": 0,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "7828VPg/9KFqdBBkwVgGO8"
},
{
"__type__": "cc.Widget",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 26
},
"_enabled": true,
"__prefab": {
"__id__": 32
},
"_alignFlags": 45,
"_target": null,
"_left": 0,
"_right": 0,
"_top": 0,
"_bottom": 0,
"_horizontalCenter": 0,
"_verticalCenter": 0,
"_isAbsLeft": true,
"_isAbsRight": true,
"_isAbsTop": true,
"_isAbsBottom": true,
"_isAbsHorizontalCenter": true,
"_isAbsVerticalCenter": true,
"_originalWidth": 80,
"_originalHeight": 142,
"_alignMode": 2,
"_lockFlags": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "046yIkdV9ETpWKshcgcsYj"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "66oR8aJCtH4IcgUpTJF6an",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{ {
"__type__": "cc.Node", "__type__": "cc.Node",
"_name": "Node", "_name": "Node",
@@ -604,29 +782,29 @@
}, },
"_children": [ "_children": [
{ {
"__id__": 27 "__id__": 35
}, },
{ {
"__id__": 33 "__id__": 41
}, },
{ {
"__id__": 51 "__id__": 59
} }
], ],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 69 "__id__": 77
}, },
{ {
"__id__": 71 "__id__": 79
}, },
{ {
"__id__": 73 "__id__": 81
} }
], ],
"_prefab": { "_prefab": {
"__id__": 75 "__id__": 83
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
@@ -665,20 +843,20 @@
"mountedRoot": null "mountedRoot": null
}, },
"_parent": { "_parent": {
"__id__": 26 "__id__": 34
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 28 "__id__": 36
}, },
{ {
"__id__": 30 "__id__": 38
} }
], ],
"_prefab": { "_prefab": {
"__id__": 32 "__id__": 40
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
@@ -715,11 +893,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 27 "__id__": 35
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 29 "__id__": 37
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
@@ -743,11 +921,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 27 "__id__": 35
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 31 "__id__": 39
}, },
"_customMaterial": null, "_customMaterial": null,
"_srcBlendFactor": 2, "_srcBlendFactor": 2,
@@ -826,27 +1004,27 @@
"mountedRoot": null "mountedRoot": null
}, },
"_parent": { "_parent": {
"__id__": 26 "__id__": 34
}, },
"_children": [ "_children": [
{ {
"__id__": 34 "__id__": 42
},
{
"__id__": 40
}
],
"_active": true,
"_components": [
{
"__id__": 46
}, },
{ {
"__id__": 48 "__id__": 48
} }
], ],
"_active": true,
"_components": [
{
"__id__": 54
},
{
"__id__": 56
}
],
"_prefab": { "_prefab": {
"__id__": 50 "__id__": 58
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
@@ -883,20 +1061,20 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"_parent": { "_parent": {
"__id__": 33 "__id__": 41
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 35 "__id__": 43
}, },
{ {
"__id__": 37 "__id__": 45
} }
], ],
"_prefab": { "_prefab": {
"__id__": 39 "__id__": 47
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
@@ -933,11 +1111,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 34 "__id__": 42
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 36 "__id__": 44
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
@@ -961,11 +1139,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 34 "__id__": 42
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 38 "__id__": 46
}, },
"_customMaterial": null, "_customMaterial": null,
"_srcBlendFactor": 2, "_srcBlendFactor": 2,
@@ -1019,20 +1197,20 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"_parent": { "_parent": {
"__id__": 33 "__id__": 41
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 41 "__id__": 49
}, },
{ {
"__id__": 43 "__id__": 51
} }
], ],
"_prefab": { "_prefab": {
"__id__": 45 "__id__": 53
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
@@ -1069,11 +1247,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 40 "__id__": 48
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 42 "__id__": 50
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
@@ -1097,11 +1275,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 40 "__id__": 48
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 44 "__id__": 52
}, },
"_customMaterial": null, "_customMaterial": null,
"_srcBlendFactor": 2, "_srcBlendFactor": 2,
@@ -1178,11 +1356,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 33 "__id__": 41
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 47 "__id__": 55
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
@@ -1206,11 +1384,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 33 "__id__": 41
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 49 "__id__": 57
}, },
"_resizeMode": 1, "_resizeMode": 1,
"_layoutType": 1, "_layoutType": 1,
@@ -1259,27 +1437,27 @@
"mountedRoot": null "mountedRoot": null
}, },
"_parent": { "_parent": {
"__id__": 26 "__id__": 34
}, },
"_children": [ "_children": [
{ {
"__id__": 52 "__id__": 60
},
{
"__id__": 58
}
],
"_active": false,
"_components": [
{
"__id__": 64
}, },
{ {
"__id__": 66 "__id__": 66
} }
], ],
"_active": false,
"_components": [
{
"__id__": 72
},
{
"__id__": 74
}
],
"_prefab": { "_prefab": {
"__id__": 68 "__id__": 76
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
@@ -1316,20 +1494,20 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"_parent": { "_parent": {
"__id__": 51 "__id__": 59
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 53 "__id__": 61
}, },
{ {
"__id__": 55 "__id__": 63
} }
], ],
"_prefab": { "_prefab": {
"__id__": 57 "__id__": 65
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
@@ -1366,11 +1544,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 52 "__id__": 60
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 54 "__id__": 62
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
@@ -1394,11 +1572,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 52 "__id__": 60
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 56 "__id__": 64
}, },
"_customMaterial": null, "_customMaterial": null,
"_srcBlendFactor": 2, "_srcBlendFactor": 2,
@@ -1455,20 +1633,20 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"_parent": { "_parent": {
"__id__": 51 "__id__": 59
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 59 "__id__": 67
}, },
{ {
"__id__": 61 "__id__": 69
} }
], ],
"_prefab": { "_prefab": {
"__id__": 63 "__id__": 71
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
@@ -1505,11 +1683,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 58 "__id__": 66
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 60 "__id__": 68
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
@@ -1533,11 +1711,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 58 "__id__": 66
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 62 "__id__": 70
}, },
"_customMaterial": null, "_customMaterial": null,
"_srcBlendFactor": 2, "_srcBlendFactor": 2,
@@ -1614,11 +1792,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 51 "__id__": 59
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 65 "__id__": 73
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
@@ -1642,11 +1820,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 51 "__id__": 59
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 67 "__id__": 75
}, },
"_resizeMode": 1, "_resizeMode": 1,
"_layoutType": 1, "_layoutType": 1,
@@ -1693,11 +1871,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 26 "__id__": 34
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 70 "__id__": 78
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
@@ -1721,11 +1899,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 26 "__id__": 34
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 72 "__id__": 80
}, },
"_alignFlags": 45, "_alignFlags": 45,
"_target": null, "_target": null,
@@ -1757,11 +1935,11 @@
"_objFlags": 0, "_objFlags": 0,
"__editorExtras__": {}, "__editorExtras__": {},
"node": { "node": {
"__id__": 26 "__id__": 34
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 74 "__id__": 82
}, },
"_resizeMode": 1, "_resizeMode": 1,
"_layoutType": 2, "_layoutType": 2,
@@ -1812,7 +1990,7 @@
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 77 "__id__": 85
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,851 @@
[
{
"__type__": "cc.Prefab",
"_name": "hpbg",
"_objFlags": 0,
"__editorExtras__": {},
"_native": "",
"data": {
"__id__": 1
},
"optimizationPolicy": 0,
"persistent": false
},
{
"__type__": "cc.Node",
"_name": "hpbg",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": null,
"_children": [
{
"__id__": 2
},
{
"__id__": 10
},
{
"__id__": 18
},
{
"__id__": 26
}
],
"_active": true,
"_components": [
{
"__id__": 34
},
{
"__id__": 36
}
],
"_prefab": {
"__id__": 38
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1,
"y": 1,
"z": 1
},
"_mobility": 0,
"_layer": 1073741824,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.Node",
"_name": "Popup02~09_Topber_White_Bg",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 1
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 3
},
{
"__id__": 5
},
{
"__id__": 7
}
],
"_prefab": {
"__id__": 9
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1,
"y": 1,
"z": 1
},
"_mobility": 0,
"_layer": 1073741824,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 2
},
"_enabled": true,
"__prefab": {
"__id__": 4
},
"_contentSize": {
"__type__": "cc.Size",
"width": 720,
"height": 1280
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "bdp/P0YJBDNIRtzvt1ZYlH"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 2
},
"_enabled": true,
"__prefab": {
"__id__": 6
},
"_customMaterial": null,
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 229,
"g": 237,
"b": 242,
"a": 255
},
"_spriteFrame": {
"__uuid__": "cb93c900-b440-4571-91d1-7da1636e3d73@b94ea",
"__expectedType__": "cc.SpriteFrame"
},
"_type": 1,
"_fillType": 0,
"_sizeMode": 0,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "f6mrtQTPhBTr/oRv3n2y7H"
},
{
"__type__": "cc.Widget",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 2
},
"_enabled": true,
"__prefab": {
"__id__": 8
},
"_alignFlags": 45,
"_target": null,
"_left": 0,
"_right": 0,
"_top": 0,
"_bottom": 0,
"_horizontalCenter": 0,
"_verticalCenter": 0,
"_isAbsLeft": true,
"_isAbsRight": true,
"_isAbsTop": true,
"_isAbsBottom": true,
"_isAbsHorizontalCenter": true,
"_isAbsVerticalCenter": true,
"_originalWidth": 54,
"_originalHeight": 61,
"_alignMode": 2,
"_lockFlags": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "45Io/Pye5PoZwcjgb5AzVU"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "8czX9Q7aNPqKQ/I18JMThq",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.Node",
"_name": "Popup02~09_Topber_White_BgTop",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 1
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 11
},
{
"__id__": 13
},
{
"__id__": 15
}
],
"_prefab": {
"__id__": 17
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 561,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1,
"y": 1,
"z": 1
},
"_mobility": 0,
"_layer": 1073741824,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 10
},
"_enabled": true,
"__prefab": {
"__id__": 12
},
"_contentSize": {
"__type__": "cc.Size",
"width": 712,
"height": 150
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "be2QVw6n9LFYl0l8ppduaW"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 10
},
"_enabled": true,
"__prefab": {
"__id__": 14
},
"_customMaterial": null,
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 153,
"g": 22,
"b": 37,
"a": 255
},
"_spriteFrame": {
"__uuid__": "cb93c900-b440-4571-91d1-7da1636e3d73@4a5a3",
"__expectedType__": "cc.SpriteFrame"
},
"_type": 1,
"_fillType": 0,
"_sizeMode": 0,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "22EIS7o1lA4Kwmkz0o8wWF"
},
{
"__type__": "cc.Widget",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 10
},
"_enabled": true,
"__prefab": {
"__id__": 16
},
"_alignFlags": 41,
"_target": null,
"_left": 4,
"_right": 4,
"_top": 4,
"_bottom": 0,
"_horizontalCenter": 0,
"_verticalCenter": 0,
"_isAbsLeft": true,
"_isAbsRight": true,
"_isAbsTop": true,
"_isAbsBottom": true,
"_isAbsHorizontalCenter": true,
"_isAbsVerticalCenter": true,
"_originalWidth": 48,
"_originalHeight": 0,
"_alignMode": 2,
"_lockFlags": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "9elo28q4xGx5SRtvnP4h1I"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "325R/lW/ZKpKtAoLMy5t8a",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.Node",
"_name": "Popup02~09_Topber_White_BgTopLight",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 1
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 19
},
{
"__id__": 21
},
{
"__id__": 23
}
],
"_prefab": {
"__id__": 25
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 624,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1,
"y": 1,
"z": 1
},
"_mobility": 0,
"_layer": 1073741824,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 18
},
"_enabled": true,
"__prefab": {
"__id__": 20
},
"_contentSize": {
"__type__": "cc.Size",
"width": 712,
"height": 24
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "2fo59AaaRMSKRNKjh+ye/L"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 18
},
"_enabled": true,
"__prefab": {
"__id__": 22
},
"_customMaterial": null,
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 45
},
"_spriteFrame": {
"__uuid__": "cb93c900-b440-4571-91d1-7da1636e3d73@3d086",
"__expectedType__": "cc.SpriteFrame"
},
"_type": 1,
"_fillType": 0,
"_sizeMode": 0,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "abeLWbs05Hjp2RTnFx8XHJ"
},
{
"__type__": "cc.Widget",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 18
},
"_enabled": true,
"__prefab": {
"__id__": 24
},
"_alignFlags": 41,
"_target": null,
"_left": 4,
"_right": 4,
"_top": 4,
"_bottom": 173,
"_horizontalCenter": 0,
"_verticalCenter": 0,
"_isAbsLeft": true,
"_isAbsRight": true,
"_isAbsTop": true,
"_isAbsBottom": true,
"_isAbsHorizontalCenter": true,
"_isAbsVerticalCenter": true,
"_originalWidth": 50,
"_originalHeight": 24,
"_alignMode": 2,
"_lockFlags": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "7363RL0R9ErIoyiH6P9e4n"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "730isJJuJKuZ4TyYRIGiPl",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.Node",
"_name": "Popup03_Gradient",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 1
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 27
},
{
"__id__": 29
},
{
"__id__": 31
}
],
"_prefab": {
"__id__": 33
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 561,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1,
"y": 1,
"z": 1
},
"_mobility": 0,
"_layer": 1073741824,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 26
},
"_enabled": true,
"__prefab": {
"__id__": 28
},
"_contentSize": {
"__type__": "cc.Size",
"width": 700,
"height": 150
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "e3U+s1uAdCgJorqCcyhk3a"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 26
},
"_enabled": true,
"__prefab": {
"__id__": 30
},
"_customMaterial": null,
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 245,
"g": 51,
"b": 76,
"a": 255
},
"_spriteFrame": {
"__uuid__": "cb93c900-b440-4571-91d1-7da1636e3d73@7f910",
"__expectedType__": "cc.SpriteFrame"
},
"_type": 0,
"_fillType": 0,
"_sizeMode": 0,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "26DiJMkAFBWb1mvvZDUtMs"
},
{
"__type__": "cc.Widget",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 26
},
"_enabled": true,
"__prefab": {
"__id__": 32
},
"_alignFlags": 41,
"_target": null,
"_left": 10,
"_right": 10,
"_top": 4,
"_bottom": 0,
"_horizontalCenter": 0,
"_verticalCenter": 0,
"_isAbsLeft": true,
"_isAbsRight": true,
"_isAbsTop": true,
"_isAbsBottom": true,
"_isAbsHorizontalCenter": true,
"_isAbsVerticalCenter": true,
"_originalWidth": 473,
"_originalHeight": 0,
"_alignMode": 2,
"_lockFlags": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "24rC2H/9RBE6PRIrKWk/eF"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "3awTuW1YtIkJ0MAhOjyZM8",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 35
},
"_contentSize": {
"__type__": "cc.Size",
"width": 720,
"height": 1280
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "3e6xe2CYhAnaDRXFjAvMW1"
},
{
"__type__": "cc.Widget",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 37
},
"_alignFlags": 45,
"_target": null,
"_left": 0,
"_right": 0,
"_top": 0,
"_bottom": 0,
"_horizontalCenter": 0,
"_verticalCenter": 0,
"_isAbsLeft": true,
"_isAbsRight": true,
"_isAbsTop": true,
"_isAbsBottom": true,
"_isAbsHorizontalCenter": true,
"_isAbsVerticalCenter": true,
"_originalWidth": 100,
"_originalHeight": 100,
"_alignMode": 2,
"_lockFlags": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "d1teNkwJlBsLfuBqxWNv09"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "b4ICzk8RFDq6KzVMReui0b",
"instance": null,
"targetOverrides": null
}
]

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.50",
"importer": "prefab",
"imported": true,
"uuid": "49480ebf-d16b-4242-8983-f4ba12861caa",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "hpbg"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 997 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,8 @@
* @description 统一移动组件与系统(英雄 + 怪物) * @description 统一移动组件与系统(英雄 + 怪物)
* *
* 核心规则: * 核心规则:
* 1. 准备阶段:双方原地待命,不推进不索敌。 * 1. 准备阶段:不推进不索敌;英雄按职业优先级(坦克→战士→刺客→射手→法师→辅助)
* 从最右迎敌位向左排队移动,防止同类型落点重叠;怪物原地待命。
* 2. 战斗阶段双方向最近敌人推进进入攻击范围model.dis后停止移动并攻击。 * 2. 战斗阶段双方向最近敌人推进进入攻击范围model.dis后停止移动并攻击。
* 3. 场上无敌人:停止移动(战斗结束,由 MissionComp 进入结算)。 * 3. 场上无敌人:停止移动(战斗结束,由 MissionComp 进入结算)。
* 4. 击退/位移不会把单位推出屏幕HERO 不越过 BoxSet.LETF_ENDMON 不越过 BoxSet.RIGHT_END。 * 4. 击退/位移不会把单位推出屏幕HERO 不越过 BoxSet.LETF_ENDMON 不越过 BoxSet.RIGHT_END。
@@ -14,6 +15,7 @@ import { HeroViewComp } from "./HeroViewComp";
import { HeroAttrsComp } from "./HeroAttrsComp"; import { HeroAttrsComp } from "./HeroAttrsComp";
import { smc } from "../common/SingletonModuleComp"; import { smc } from "../common/SingletonModuleComp";
import { BoxSet, FacSet } from "../common/config/GameSet"; import { BoxSet, FacSet } from "../common/config/GameSet";
import { HRole } from "../common/config/heroSet";
import { Node } from "cc"; import { Node } from "cc";
@ecs.register('MoveComp') @ecs.register('MoveComp')
@@ -50,6 +52,18 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
private readonly renderEntries: { node: Node; bossPriority: number; frontScore: number; spawnOrder: number; eid: number; laneScore: number }[] = []; private readonly renderEntries: { node: Node; bossPriority: number; frontScore: number; spawnOrder: number; eid: number; laneScore: number }[] = [];
private renderEntryCount = 0; private renderEntryCount = 0;
// ======================== 英雄编队排队 ========================
/** 编队锚点:最右迎敌位(与 MissionHeroComp.HERO_ROW_FRONT 的 x 对齐,避免循环依赖在此独立定义) */
private static readonly QUEUE_ANCHOR_X = -200;
/** 编队槽位默认间隔(像素);人数超出边界宽度时自动压缩间距 */
private static readonly QUEUE_SPACING = 30;
/** 编队槽位重算节流间隔(秒):召唤/死亡后最多延迟一次重算 */
private readonly queueRebuildInterval = 0.1;
private lastQueueRebuildAt = 0;
/** 编队槽位缓存:按职业优先级排序后各英雄的目标 X */
private readonly queueSlots: { eid: number; targetX: number }[] = [];
private getHeroViewMatcher(): ecs.IMatcher { private getHeroViewMatcher(): ecs.IMatcher {
if (!this.heroViewMatcher) { if (!this.heroViewMatcher) {
this.heroViewMatcher = ecs.allOf(HeroAttrsComp, HeroViewComp); this.heroViewMatcher = ecs.allOf(HeroAttrsComp, HeroViewComp);
@@ -104,11 +118,15 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
} }
} }
/** 准备阶段:双方原地待命,不推进不索敌 */ /** 准备阶段:不推进不索敌;英雄向编队槽位排队移动,怪物原地待命 */
if (!smc.mission.in_fight) { if (!smc.mission.in_fight) {
this.clearCombatTarget(model); this.clearCombatTarget(model);
model.is_atking = false; model.is_atking = false;
if (view.status !== "atk") view.status_change("idle"); if (model.fac === FacSet.HERO) {
this.processHeroQueue(e, model, move, view);
} else if (view.status !== "atk") {
view.status_change("idle");
}
return; return;
} }
@@ -133,6 +151,90 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
model.enemy_in_cast_range = false; model.enemy_in_cast_range = false;
} }
// ======================== 英雄编队排队(准备阶段) ========================
/**
* 英雄编队排队:
* 按 坦克→战士→刺客→射手→法师→辅助 的职业优先级(即 HRole 枚举值升序),
* 从最右迎敌位QUEUE_ANCHOR_X向左依次排开防止同类型英雄落点重叠。
*
* @param entity 当前英雄实体
* @param model 英雄属性(取 speed / role
* @param move 移动组件(维护 direction
* @param view 视图组件(驱动位移与动画状态)
*/
private processHeroQueue(entity: ecs.Entity, model: HeroAttrsComp, move: MoveComp, view: HeroViewComp): void {
const targetX = this.resolveHeroQueueTargetX(entity);
const selfX = view.node.position.x;
/** 无槽位(未登记)或已到位:贴齐槽位并原地待命,恢复朝右迎敌 */
if (targetX === null || Math.abs(targetX - selfX) <= 2) {
if (targetX !== null && Math.abs(selfX - targetX) > 0.01) {
view.node.setPosition(targetX, view.node.position.y, 0);
}
view.scale = 1;
if (view.status !== "atk") view.status_change("idle");
return;
}
/** 未到位:向槽位推进(与战斗推进同速),朝移动方向转身 */
const dir = targetX > selfX ? 1 : -1;
move.direction = dir;
view.scale = dir;
const delta = (model.speed / 3) * this.dt * dir;
const newX = this.clampByFacBoundary(selfX + delta, model.fac);
if (Math.abs(newX - selfX) >= 0.01) {
view.node.setPosition(newX, view.node.position.y, 0);
view.status_change("move");
}
}
/** 查询当前英雄的编队槽位目标 X无槽位返回 null */
private resolveHeroQueueTargetX(entity: ecs.Entity): number | null {
const now = Date.now() / 1000;
if (now - this.lastQueueRebuildAt >= this.queueRebuildInterval) {
this.lastQueueRebuildAt = now;
this.rebuildHeroQueueSlots();
}
for (const slot of this.queueSlots) {
if (slot.eid === entity.eid) return slot.targetX;
}
return null;
}
/**
* 重算编队槽位:
* 收集全部存活英雄,按 职业优先级 → 出生序 → eid 稳定排序后,
* 从 QUEUE_ANCHOR_X 向左按间隔分配;人数过多时压缩间距,保证不越过 LETF_END。
*/
private rebuildHeroQueueSlots(): void {
const heroes: { eid: number; role: number; spawnOrder: number }[] = [];
ecs.query(this.getHeroViewMatcher()).forEach(e => {
const m = e.get(HeroAttrsComp);
if (!m || m.fac !== FacSet.HERO || m.is_dead) return;
const v = e.get(HeroViewComp);
if (!v?.node) return;
const move = e.get(MoveComp);
heroes.push({ eid: e.eid, role: m.role ?? HRole.Support, spawnOrder: move?.spawnOrder ?? 0 });
});
heroes.sort((a, b) =>
(a.role - b.role) ||
(a.spawnOrder - b.spawnOrder) ||
(a.eid - b.eid)
);
const count = heroes.length;
const maxWidth = MoveSystem.QUEUE_ANCHOR_X - BoxSet.LETF_END;
const spacing = count > 1
? Math.min(MoveSystem.QUEUE_SPACING, maxWidth / (count - 1))
: 0;
this.queueSlots.length = 0;
for (let i = 0; i < count; i++) {
this.queueSlots.push({ eid: heroes[i].eid, targetX: MoveSystem.QUEUE_ANCHOR_X - i * spacing });
}
}
private syncCombatTarget(model: HeroAttrsComp, selfView: HeroViewComp, enemyView: HeroViewComp): void { private syncCombatTarget(model: HeroAttrsComp, selfView: HeroViewComp, enemyView: HeroViewComp): void {
if (!enemyView || !enemyView.node || !enemyView.ent) { if (!enemyView || !enemyView.node || !enemyView.ent) {
this.clearCombatTarget(model); this.clearCombatTarget(model);

View File

@@ -769,11 +769,9 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
} }
return null; return null;
} else { } else {
// 怪物打英雄,英雄网格: // 怪物打英雄:以英雄编队中排基准点为 AOE 目标
// 列1: 0,1,2 (X=-210) // (英雄已改为按职业优先级从最右向左排队,中排 -260 即队伍中部)
// 列2: 3,4,5 (X=-300) return MissionHeroComp.HERO_ROW_MID.clone();
// 英雄只有2列第2列中路是 index 4
return MissionHeroComp.HERO_POSITIONS[4].clone();
} }
} }

View File

@@ -6,7 +6,7 @@
* 1. 渲染单个英雄卡的显示信息(名称、等级、描述、图标、费用), * 1. 渲染单个英雄卡的显示信息(名称、等级、描述、图标、费用),
* 展示方式与 SCardComp 一致(图集静态图标 + ★等级 + 描述词条)。 * 展示方式与 SCardComp 一致(图集静态图标 + ★等级 + 描述词条)。
* 同时支持动态升级卡SpecialUpgrade + target_hero_eid按英雄卡样式渲染。 * 同时支持动态升级卡SpecialUpgrade + target_hero_eid按英雄卡样式渲染。
* 2. 处理购买点击 → UseHeroCard 上限校验(guard) → 扣费 → 派发 CallHero 召唤英雄。 * 2. 处理卡牌点击(点击卡牌任意区域即选取) → UseHeroCard 上限校验(guard) → 派发 CallHero 召唤英雄。
* 特殊卡(升级/刷新)则派发 UseSpecialCard。 * 特殊卡(升级/刷新)则派发 UseSpecialCard。
* 3. 使用成功后派发 CardUsed 事件,通知 MissionCardComp 刷新英雄卡池。 * 3. 使用成功后派发 CardUsed 事件,通知 MissionCardComp 刷新英雄卡池。
* *
@@ -33,7 +33,7 @@ const { ccclass, property } = _decorator;
* CHeroComp —— 英雄卡池单个英雄卡项 * CHeroComp —— 英雄卡池单个英雄卡项
* *
* 由 MissionCardComp.cacheCardComps() 实例化。 * 由 MissionCardComp.cacheCardComps() 实例化。
* 以图标 + 名称 + 描述词条 + 购买按钮的形式呈现。 * 以图标 + 名称 + 描述词条 + 费用角标的形式呈现,点击卡牌任意区域即选取
*/ */
@ccclass('CHeroComp') @ccclass('CHeroComp')
@ecs.register('CHeroComp', false) @ecs.register('CHeroComp', false)
@@ -72,7 +72,7 @@ export class CHeroComp extends CCComp {
@property(Label) @property(Label)
private level_label: Label = null; private level_label: Label = null;
/** 购买按钮节点 */ /** 费用显示节点(原购买按钮,现仅展示费用,不再承担点击) */
@property({ type: Node }) @property({ type: Node })
private buy_node: Node = null; private buy_node: Node = null;
@@ -118,9 +118,10 @@ export class CHeroComp extends CCComp {
fied3: !!this.fied3, fied3: !!this.fied3,
buy_node: !!this.buy_node buy_node: !!this.buy_node
}); });
this.buy_node?.on(NodeEventType.TOUCH_START, this.onBuyTouchStart, this); // 点击整张卡牌选取英雄:监听挂载在卡牌根节点,命中区域由根节点 UITransformer 决定
this.buy_node?.on(NodeEventType.TOUCH_END, this.onBuyClick, this); this.node.on(NodeEventType.TOUCH_START, this.onCardTouchStart, this);
this.buy_node?.on(NodeEventType.TOUCH_CANCEL, this.onBuyTouchCancel, this); this.node.on(NodeEventType.TOUCH_END, this.onCardClick, this);
this.node.on(NodeEventType.TOUCH_CANCEL, this.onCardTouchCancel, this);
// 缓存透明度组件(用于淡入淡出动画) // 缓存透明度组件(用于淡入淡出动画)
this.opacityComp = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity); this.opacityComp = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
@@ -134,10 +135,10 @@ export class CHeroComp extends CCComp {
onDestroy() { onDestroy() {
super.onDestroy(); super.onDestroy();
if (this.buy_node && this.buy_node.isValid) { if (this.node && this.node.isValid) {
this.buy_node.off(NodeEventType.TOUCH_START, this.onBuyTouchStart, this); this.node.off(NodeEventType.TOUCH_START, this.onCardTouchStart, this);
this.buy_node.off(NodeEventType.TOUCH_END, this.onBuyClick, this); this.node.off(NodeEventType.TOUCH_END, this.onCardClick, this);
this.buy_node.off(NodeEventType.TOUCH_CANCEL, this.onBuyTouchCancel, this); this.node.off(NodeEventType.TOUCH_CANCEL, this.onCardTouchCancel, this);
} }
} }
@@ -303,7 +304,7 @@ export class CHeroComp extends CCComp {
this.updateHeroAnimation(this.icon_node, renderUuid, this.iconVisualToken); this.updateHeroAnimation(this.icon_node, renderUuid, this.iconVisualToken);
} }
// 购买按钮费用显示 // 费用显示(原购买按钮节点,现仅作费用角标)
if (this.buy_node) { if (this.buy_node) {
this.buy_node.active = true; this.buy_node.active = true;
const costLabel = this.cost_label const costLabel = this.cost_label
@@ -372,7 +373,7 @@ export class CHeroComp extends CCComp {
/** /**
* 播放购买动画卡牌放大scale 1→1.1+ 淡出,结束后隐藏节点。 * 播放购买动画卡牌放大scale 1→1.1+ 淡出,结束后隐藏节点。
* 动画结束由 onBuyClick 的后续逻辑CardUsed 刷新卡池)接管。 * 动画结束由 onCardClick 的后续逻辑CardUsed 刷新卡池)接管。
*/ */
private playPurchaseAnim() { private playPurchaseAnim() {
Tween.stopAllByTarget(this.node); Tween.stopAllByTarget(this.node);
@@ -413,44 +414,44 @@ export class CHeroComp extends CCComp {
} }
} }
// ======================== 按钮动画 ======================== // ======================== 点击反馈动画 ========================
/** 按钮正常缩放 */ /** 卡牌正常缩放 */
private readonly btnNormalScale: number = 1; private readonly cardNormalScale: number = 1;
/** 按钮按下缩放 */ /** 卡牌按下缩放 */
private readonly btnPressScale: number = 0.92; private readonly cardPressScale: number = 0.92;
/** 按钮点击回弹峰值缩放 */ /** 卡牌点击回弹峰值缩放 */
private readonly btnClickScale: number = 1.08; private readonly cardClickScale: number = 1.08;
/** 购买按钮按下:缩小反馈 */ /** 卡牌按下:缩小反馈 */
private onBuyTouchStart() { private onCardTouchStart() {
this.playBtnScaleTo(this.btnPressScale, 0.06); this.playCardScaleTo(this.cardPressScale, 0.06);
} }
/** 购买按钮取消:恢复缩放 */ /** 卡牌取消(滑出卡牌区域):恢复缩放 */
private onBuyTouchCancel() { private onCardTouchCancel() {
this.playBtnScaleTo(this.btnNormalScale, 0.08); this.playCardScaleTo(this.cardNormalScale, 0.08);
} }
/** 按钮缩放动画(通用) */ /** 卡牌缩放动画(通用) */
private playBtnScaleTo(scale: number, duration: number) { private playCardScaleTo(scale: number, duration: number) {
if (!this.buy_node || !this.buy_node.isValid) return; if (!this.node || !this.node.isValid) return;
Tween.stopAllByTarget(this.buy_node); Tween.stopAllByTarget(this.node);
tween(this.buy_node) tween(this.node)
.to(duration, { scale: new Vec3(scale, scale, 1) }) .to(duration, { scale: new Vec3(scale, scale, 1) })
.start(); .start();
} }
/** 按钮点击回弹动画:先弹到峰值再回到正常 */ /** 卡牌点击回弹动画:先弹到峰值再回到正常 */
private playBtnClickAnim(onComplete?: () => void) { private playCardClickAnim(onComplete?: () => void) {
if (!this.buy_node || !this.buy_node.isValid) { if (!this.node || !this.node.isValid) {
onComplete?.(); onComplete?.();
return; return;
} }
Tween.stopAllByTarget(this.buy_node); Tween.stopAllByTarget(this.node);
tween(this.buy_node) tween(this.node)
.to(0.05, { scale: new Vec3(this.btnClickScale, this.btnClickScale, 1) }) .to(0.05, { scale: new Vec3(this.cardClickScale, this.cardClickScale, 1) })
.to(0.08, { scale: new Vec3(this.btnNormalScale, this.btnNormalScale, 1) }) .to(0.08, { scale: new Vec3(this.cardNormalScale, this.cardNormalScale, 1) })
.call(() => onComplete?.()) .call(() => onComplete?.())
.start(); .start();
} }
@@ -561,20 +562,20 @@ export class CHeroComp extends CCComp {
// ======================== 购买逻辑 ======================== // ======================== 购买逻辑 ========================
/** /**
* 购买点击处理: * 卡牌点击处理(点击卡牌任意区域即选取)
* 1. 播放按钮点击回弹动画。 * 1. 播放卡牌点击回弹动画。
* 2. 英雄卡先通过 UseHeroCard 事件做数量上限校验guard 模式,可被取消)。 * 2. 英雄卡先通过 UseHeroCard 事件做数量上限校验guard 模式,可被取消)。
* 3. 按卡牌类型分发效果:英雄卡 → CallHero 召唤英雄;特殊卡 → UseSpecialCard。 * 3. 按卡牌类型分发效果:英雄卡 → CallHero 召唤英雄;特殊卡 → UseSpecialCard。
* 4. 派发 CardUsed 通知 MissionCardComp 刷新英雄卡池。 * 4. 派发 CardUsed 通知 MissionCardComp 刷新英雄卡池。
* 5. 标记已购买,隐藏购买按钮 * 5. 标记已购买,隐藏费用角标
* *
* 注:购买不花费金币(免费获取),原扣费逻辑已移除。 * 注:购买不花费金币(免费获取),原扣费逻辑已移除。
*/ */
private onBuyClick() { private onCardClick() {
if (!this.cardData || this.isPurchased) return; if (!this.cardData || this.isPurchased) return;
// 播放按钮点击回弹动画(视觉反馈,不阻塞后续逻辑) // 播放卡牌点击回弹动画(视觉反馈,不阻塞后续逻辑)
this.playBtnClickAnim(); this.playCardClickAnim();
oops.audio.playEffect("music/button"); oops.audio.playEffect("music/button");

View File

@@ -17,10 +17,11 @@
* revive 系技能为"濒死回血"(血量归零时消耗次数回血续命),不触发真死。 * revive 系技能为"濒死回血"(血量归零时消耗次数回血续命),不触发真死。
* *
* 出生点规则: * 出生点规则:
* 按攻击类型HType分三排:近战 → 前排x=-200最靠右迎敌 * 按攻击类型HType排:近战 → 前排x=-200最靠右迎敌
* 中距 → 中排x=-260远程 → 后排x=-320最靠左 * 中距 → 中排x=-260远程 → 后排x=-320最靠左
* 同类型重复召唤时,从该类型基准点向两侧 ±SLOT_OFFSET 依次交替展开,避免落点重叠 * 同类型落点不再做 ± 偏移展开,落点重叠由 MoveSystem 的编队排队解决
* posIndex 取 heroGrid 全局空闲下标0~9用于网格登记/索敌,落点偏移与 posIndex、职业均解耦 * (准备阶段按职业优先级 坦克→战士→刺客→射手→法师→辅助 从最右向左排开)
* posIndex 取 heroGrid 全局空闲下标0~9用于网格登记/索敌,与落点解耦。
* *
* 依赖: * 依赖:
* - Herohero/Hero.ts—— 英雄 ECS 实体类 * - Herohero/Hero.ts—— 英雄 ECS 实体类
@@ -39,7 +40,7 @@ import { HeroInfo, HType } from "../common/config/heroSet";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops"; import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { HeroAttrsComp } from "../hero/HeroAttrsComp"; import { HeroAttrsComp } from "../hero/HeroAttrsComp";
import { FacSet, BoxSet } from "../common/config/GameSet"; import { FacSet, BoxSet } from "../common/config/GameSet";
import { HeroViewComp } from "../hero/HeroViewComp"; import { MoveComp } from "../hero/MoveComp";
const { ccclass } = _decorator; const { ccclass } = _decorator;
/** /**
@@ -63,19 +64,9 @@ export class MissionHeroComp extends CCComp {
public static readonly HERO_ROW_MID = v3(-260, BoxSet.GAME_LINE, 0) public static readonly HERO_ROW_MID = v3(-260, BoxSet.GAME_LINE, 0)
public static readonly HERO_ROW_BACK = v3(-320, BoxSet.GAME_LINE, 0) public static readonly HERO_ROW_BACK = v3(-320, BoxSet.GAME_LINE, 0)
/**
* 英雄登场占位点(兼容旧引用,等价于后/中/前三排基准点):
* - index 0=后排、1=中排、2=前排。
*/
public static readonly HERO_POSITIONS: Vec3[] = [
MissionHeroComp.HERO_ROW_BACK,
MissionHeroComp.HERO_ROW_MID,
MissionHeroComp.HERO_ROW_FRONT,
];
/** /**
* 攻击类型 → 出生排映射: * 攻击类型 → 出生排映射:
* 近战 → 前排,中距 → 中排,远程 → 后排。 * 近战 → 前排,中距 → 中排(优先中间位),远程 → 后排。
*/ */
private static readonly TYPE_ROW: Record<HType, Vec3> = { private static readonly TYPE_ROW: Record<HType, Vec3> = {
[HType.Melee]: MissionHeroComp.HERO_ROW_FRONT, [HType.Melee]: MissionHeroComp.HERO_ROW_FRONT,
@@ -83,9 +74,6 @@ export class MissionHeroComp extends CCComp {
[HType.Long]: MissionHeroComp.HERO_ROW_BACK, [HType.Long]: MissionHeroComp.HERO_ROW_BACK,
}; };
/** 同类型重复召唤时的落点横向间隔(像素) */
private static readonly SLOT_OFFSET = 30
/** 英雄出生时的掉落高度(从空中落到地面的像素差) */ /** 英雄出生时的掉落高度(从空中落到地面的像素差) */
private static readonly HERO_DROP_HEIGHT = 260 private static readonly HERO_DROP_HEIGHT = 260
@@ -103,6 +91,8 @@ export class MissionHeroComp extends CCComp {
is_processing_queue: boolean = false is_processing_queue: boolean = false
/** 召唤请求队列:保证召唤按顺序串行执行 */ /** 召唤请求队列:保证召唤按顺序串行执行 */
summon_queue: { uuid: number; hero_lv: number; card_lv: number }[] = [] summon_queue: { uuid: number; hero_lv: number; card_lv: number }[] = []
/** 英雄出生序计数器(编队排队/渲染排序的稳定决胜) */
private heroSpawnOrder = 0
/** 预留英雄列表 */ /** 预留英雄列表 */
heros: any = [] heros: any = []
@@ -188,42 +178,26 @@ export class MissionHeroComp extends CCComp {
/** /**
* 为英雄分配出生槽位: * 为英雄分配出生槽位:
* 按攻击类型取三排基准点(近战前排、中距中排、远程后排), * 按攻击类型取三排基准点(近战前排、中距优先中排、远程后排),同类型不再做横向偏移展开;
* 同类型第 1 个落基准点,之后按 ±SLOT_OFFSET 交替展开0, +30, -30, +60, -60 …) * 落点重叠由 MoveSystem 的编队排队(按职业优先级从最右向左排开)负责分开
* *
* 槽位编码posIndex 直接取 heroGrid 的全局空闲下标0~9与落点无映射。 * 槽位编码posIndex 直接取 heroGrid 的全局空闲下标0~9与落点无映射。
* 落点偏移由「同攻击类型已占位英雄的 x 偏移」推展开序计算,与 posIndex、职业均解耦。
* *
* @param type 英雄攻击类型HType决定所属排 * @param type 英雄攻击类型HType决定所属排
* @param heroes 当前存活英雄列表spawnHeroAt 在实体创建前快照,避免复用池残留实体干扰槽位判定) * @param heroes 当前存活英雄列表spawnHeroAt 在实体创建前快照,避免复用池残留实体干扰网格判定)
* @returns { posIndex heroGrid 空闲下标(-1 表示满员未登记), landingPos 落地点 } * @returns { posIndex heroGrid 空闲下标(-1 表示满员未登记), landingPos 落地点 }
*/ */
private pickSpawnSlotForHero(type: HType, heroes: Hero[]): { posIndex: number; landingPos: Vec3 } { private pickSpawnSlotForHero(type: HType, heroes: Hero[]): { posIndex: number; landingPos: Vec3 } {
const basePos = MissionHeroComp.TYPE_ROW[type] ?? MissionHeroComp.HERO_ROW_MID; const basePos = MissionHeroComp.TYPE_ROW[type] ?? MissionHeroComp.HERO_ROW_MID;
// 同攻击类型已占位的 x 偏移集合(依据落点 x 反推,与 posIndex 解耦 // 已登记的网格下标集合(仅用于 posIndex 分配
const usedOffsets = new Set<number>();
const occupiedGrid = new Set<number>(); const occupiedGrid = new Set<number>();
for (const h of heroes) { for (const h of heroes) {
const m = h.get(HeroAttrsComp)!; const m = h.get(HeroAttrsComp)!;
if (m.posIndex >= 0) occupiedGrid.add(m.posIndex); if (m.posIndex >= 0) occupiedGrid.add(m.posIndex);
if (m.type !== type) continue;
const view = h.get(HeroViewComp);
if (view?.node?.isValid) {
usedOffsets.add(Math.round(view.node.position.x - basePos.x));
}
} }
// 展开序 n0=居中1=+302=-303=+60 …,找到第一个未被同类型占用的偏移 const landingPos = v3(basePos.x, basePos.y, 0);
// 上限取 heroGrid 容量,同类型超过该数量时复用基准点(不再向外展开)
let offsetX = 0;
for (let n = 0; n < smc.mission.heroGrid.length; n++) {
const step = Math.ceil(n / 2);
const dir = n % 2 === 1 ? 1 : -1;
const candidate = n === 0 ? 0 : dir * step * MissionHeroComp.SLOT_OFFSET;
if (!usedOffsets.has(candidate)) { offsetX = candidate; break; }
}
const landingPos = v3(basePos.x + offsetX, basePos.y, 0);
// posIndex 取 heroGrid 全局空闲下标,仅用于网格登记/索敌,与落点无映射 // posIndex 取 heroGrid 全局空闲下标,仅用于网格登记/索敌,与落点无映射
let posIndex = -1; let posIndex = -1;
@@ -235,8 +209,9 @@ export class MissionHeroComp extends CCComp {
/** /**
* 生成一个英雄: * 生成一个英雄:
* - 按攻击类型自动分配三排槽位并展开落点 * - 按攻击类型落排基准点(近战前排 / 中距中排 / 远程后排),不做横向偏移
* - 计算出生点(空中)和落点(地面),播放掉落动画。 * - 计算出生点(空中)和落点(地面),播放掉落动画。
* - 落地后由 MoveSystem 按职业优先级从最右向左排队分开。
* *
* @param uuid 英雄 UUID * @param uuid 英雄 UUID
* @param hero_lv 英雄等级 * @param hero_lv 英雄等级
@@ -259,6 +234,12 @@ export class MissionHeroComp extends CCComp {
let spawnPos: Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0); let spawnPos: Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0);
hero.load(spawnPos, scale, uuid, landingPos.y, hero_lv, card_lv, finalPosIndex); hero.load(spawnPos, scale, uuid, landingPos.y, hero_lv, card_lv, finalPosIndex);
// 登记出生序,供编队排队/渲染排序做稳定决胜
const move = hero.get(MoveComp);
if (move) {
move.spawnOrder = ++this.heroSpawnOrder;
}
// 召唤完成后,派发事件以更新英雄面板 // 召唤完成后,派发事件以更新英雄面板
const model = hero.get(HeroAttrsComp); const model = hero.get(HeroAttrsComp);
if (model) { if (model) {