feat(skill): 重做普攻弹道逻辑,优化攻击命中表现

1. 新增atk_ci普攻预制体,替换原有的atk_light预制体配置
2. 调整普攻预制体的位置、缩放属性,更新技能配置的sp名
3. 重构施法目标坐标计算,修正Y轴高度偏移以命中目标中心
4. 优化线性弹道移动逻辑,统一处理弹道延长和旋转计算
5. 更新攻击动画的帧时长和精灵贴图资源
This commit is contained in:
panw
2026-05-18 10:53:21 +08:00
parent 79cf3c1a62
commit b01a3d2b84
7 changed files with 438 additions and 52 deletions

View File

@@ -11,7 +11,7 @@
"speed": 1,
"wrapMode": 2,
"enableTrsBlending": false,
"_duration": 0.3,
"_duration": 0.13333333333333333,
"_hash": 500763545,
"_tracks": [
{
@@ -71,48 +71,23 @@
0,
0.03333333333333333,
0.06666666666666667,
0.1,
0.13333333333333333,
0.16666666666666666,
0.2,
0.23333333333333334,
0.26666666666666666
0.1
],
"_values": [
{
"__uuid__": "2423272e-e63b-4736-b15b-30b40cf98a23@dce69",
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@c85a2",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2423272e-e63b-4736-b15b-30b40cf98a23@f3a1e",
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@1526a",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2423272e-e63b-4736-b15b-30b40cf98a23@d5671",
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@fa8fc",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2423272e-e63b-4736-b15b-30b40cf98a23@ad18c",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2423272e-e63b-4736-b15b-30b40cf98a23@7e2c5",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2423272e-e63b-4736-b15b-30b40cf98a23@dd096",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2423272e-e63b-4736-b15b-30b40cf98a23@5f8fc",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2423272e-e63b-4736-b15b-30b40cf98a23@386fd",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2423272e-e63b-4736-b15b-30b40cf98a23@d4c73",
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@0d06a",
"__expectedType__": "cc.SpriteFrame"
}
]

View File

@@ -0,0 +1,371 @@
[
{
"__type__": "cc.Prefab",
"_name": "atk_ci",
"_objFlags": 0,
"__editorExtras__": {},
"_native": "",
"data": {
"__id__": 1
},
"optimizationPolicy": 0,
"persistent": false
},
{
"__type__": "cc.Node",
"_name": "atk_ci",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": null,
"_children": [
{
"__id__": 2
}
],
"_active": true,
"_components": [
{
"__id__": 8
},
{
"__id__": 10
},
{
"__id__": 12
},
{
"__id__": 14
},
{
"__id__": 16
}
],
"_prefab": {
"__id__": 18
},
"_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": "Node",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 1
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 3
},
{
"__id__": 5
}
],
"_prefab": {
"__id__": 7
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 7.98,
"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": 64,
"height": 64
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "93pNmvtQlLSqtTgIepyEmA"
},
{
"__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": 255,
"g": 255,
"b": 255,
"a": 255
},
"_spriteFrame": {
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@c85a2",
"__expectedType__": "cc.SpriteFrame"
},
"_type": 0,
"_fillType": 0,
"_sizeMode": 1,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": {
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6",
"__expectedType__": "cc.SpriteAtlas"
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "12eKc8gltBz50frJCS5+ww"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "3arqAMBz1MvoXBzeDaL5M/",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 9
},
"_contentSize": {
"__type__": "cc.Size",
"width": 50,
"height": 50
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
},
{
"__type__": "cc.Animation",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 11
},
"playOnLoad": true,
"_clips": [
{
"__uuid__": "c09c3e6a-dd75-47ec-9db8-cd72c0f9fc59",
"__expectedType__": "cc.AnimationClip"
}
],
"_defaultClip": {
"__uuid__": "c09c3e6a-dd75-47ec-9db8-cd72c0f9fc59",
"__expectedType__": "cc.AnimationClip"
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "c2RDvksalG2acL3tyGCY0t"
},
{
"__type__": "57aabs7TE1J5obTAZczc+64",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 13
},
"atk_x": 10,
"atk_y": 15,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "60LInmZXxDtKu79AshRG9j"
},
{
"__type__": "cc.RigidBody2D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 15
},
"enabledContactListener": true,
"bullet": false,
"awakeOnLoad": true,
"_group": 1,
"_type": 1,
"_allowSleep": false,
"_gravityScale": 1,
"_linearDamping": 0,
"_angularDamping": 0,
"_linearVelocity": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_angularVelocity": 0,
"_fixedRotation": false,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "e1yBA625RLwLhzo6bLYW7j"
},
{
"__type__": "cc.BoxCollider2D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 17
},
"tag": 0,
"_group": 1,
"_density": 1,
"_sensor": true,
"_friction": 0.2,
"_restitution": 0,
"_offset": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_size": {
"__type__": "cc.Size",
"width": 50,
"height": 50
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "43ZDJfXX9AX73gyytKQZWm"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
"instance": null,
"targetOverrides": null
}
]

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.50",
"importer": "prefab",
"imported": true,
"uuid": "6954a335-aa5e-4fb5-b0ad-64af6f0f4da1",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "atk_ci"
}
}

View File

@@ -95,7 +95,7 @@
},
"_lpos": {
"__type__": "cc.Vec3",
"x": -37.68,
"x": 7.98,
"y": 0,
"z": 0
},
@@ -108,8 +108,8 @@
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 0.8,
"y": 1.8,
"x": 0.5,
"y": 1,
"z": 1
},
"_mobility": 0,
@@ -282,7 +282,6 @@
},
"atk_x": 10,
"atk_y": 15,
"debugMode": true,
"_id": ""
},
{

View File

@@ -172,7 +172,7 @@ export const SkillUpList = {
export const SkillSet: Record<number, SkillConfig> = {
// ========== 基础技能 ==========
6001: {
uuid:6001,name:"普通攻击",sp_name:"atk",icon:"1026",TGroup:TGroup.Enemy,readyAnm:"",endAnm:"",act:"atk",
uuid:6001,name:"普通攻击",sp_name:"atk_ci",icon:"1026",TGroup:TGroup.Enemy,readyAnm:"",endAnm:"",act:"atk",
DTType:DTType.single,ap:100,hit_count:1,hitcd:0.2,speed:720,with:0,ready:0.2,EAnm:0,DAnm:"",IType:IType.Melee,
RType:RType.linear,EType:EType.collision,buffs:[],info:"造成攻击力100%的伤害",
},

View File

@@ -679,9 +679,22 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
/** 生成沿目标方向的施法目标坐标 */
private buildEnemyCastTargetPos(caster: HeroViewComp, target: HeroViewComp, castRange: number): Vec3 {
const casterPos = caster.node.position;
const targetPos = target.node.position;
const direction = targetPos.x >= casterPos.x ? 1 : -1;
return new Vec3(casterPos.x + direction * castRange, casterPos.y, casterPos.z);
// 直接返回目标的真实坐标,保留其 Y 轴信息,确保能向目标真实所在位置发射
// 考虑到目前角色的 y 坐标都是脚底(碰撞体底部),为了命中身体中心,给目标 y 加上高度的一半
let halfHeight = 0;
if (target.node) {
const transform = target.node.getComponent('cc.UITransform') as any;
if (transform) {
halfHeight = transform.height / 2;
} else {
halfHeight = 40; // 如果没有 UITransform给一个默认高度偏移
}
}
const pos = target.node.position.clone();
pos.y += halfHeight;
// 至于最终投射物是否要飞出屏幕(例如线性弹道延长至 +-500由 SMoveSystem 统一处理
return pos;
}
}

View File

@@ -64,31 +64,46 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
switch(moveComp.runType) {
case RType.linear:
// linear类型根据atk_x和atk_y调整位置
// linear类型根据atk_x和atk_y调整起始位置
const adjustedStartPos = v3(
moveComp.startPos.x + moveComp.atk_x,
moveComp.startPos.x + moveComp.atk_x * moveComp.scale,
moveComp.startPos.y + moveComp.atk_y,
moveComp.startPos.z
);
const adjustedTargetPos = v3(
moveComp.targetPos.x + moveComp.atk_x,
moveComp.targetPos.y + moveComp.atk_y,
const originTargetPos = v3(
moveComp.targetPos.x,
moveComp.targetPos.y,
moveComp.targetPos.z
);
// 如果开启水平移动开关强制目标Y等于起点Y
if (moveComp.isHorizontal) {
adjustedTargetPos.y = adjustedStartPos.y;
const direction = new Vec3();
Vec3.subtract(direction, originTargetPos, adjustedStartPos);
// 延长终止点统一消亡点的x坐标为 +-500
const targetX = moveComp.scale > 0 ? 500 : -500;
let targetY = originTargetPos.y;
if (Math.abs(direction.x) > 0.01) {
const slope = direction.y / direction.x;
targetY = adjustedStartPos.y + slope * (targetX - adjustedStartPos.x);
}
const adjustedTargetPos = v3(
targetX,
targetY,
originTargetPos.z
);
moveComp.startPos.set(adjustedStartPos);
moveComp.targetPos.set(adjustedTargetPos);
node.setPosition(adjustedStartPos);
// 设置旋转角度
const direction = new Vec3();
Vec3.subtract(direction, moveComp.targetPos, moveComp.startPos);
if (direction.length() > 0.01) {
const angle = Math.atan2(direction.y, direction.x) * (180 / Math.PI);
const dirForAngle = new Vec3();
Vec3.subtract(dirForAngle, adjustedTargetPos, adjustedStartPos);
if (dirForAngle.length() > 0.01) {
const angle = Math.atan2(dirForAngle.y, dirForAngle.x) * (180 / Math.PI);
node.angle = angle;
}
break;