perf: 优化攻击位置

This commit is contained in:
richarjiang
2025-10-10 15:17:58 +08:00
parent 455cca40b0
commit 71231ad759
2 changed files with 133 additions and 19 deletions

View File

@@ -3748,8 +3748,8 @@
"_prefab": null,
"_lpos": {
"__type__": "cc.Vec3",
"x": 310.51,
"y": -233.009,
"x": 283.495,
"y": -254.621,
"z": 0
},
"_lrot": {

View File

@@ -1,4 +1,4 @@
import { _decorator, Component, Node, Vec3, input, Input, EventTouch, Camera, view, tween, Animation, Collider2D, Contact2DType, Label, Color, Canvas, UITransform, AudioSource, Sprite, director, PhysicsSystem2D, EPhysics2DDrawFlags } from 'cc';
import { _decorator, Component, Node, Vec3, input, Input, EventTouch, Camera, view, tween, Animation, Collider2D, BoxCollider2D, Contact2DType, Label, Color, Canvas, UITransform, AudioSource, Sprite, director, PhysicsSystem2D, EPhysics2DDrawFlags } from 'cc';
import { TiledMapPathfinder } from './TiledMapPathfinder';
const { ccclass, property } = _decorator;
@@ -58,6 +58,7 @@ export class PlayerController extends Component {
private lastPosition: Vec3 = new Vec3(); // 上一帧位置
private hasWinTimes = 0
private readonly attackAlignGap = 10; // 玩家与怪物对阵时的额外左右间距,单位:像素
// 道具列表
private props: Node[] = [];
@@ -575,26 +576,127 @@ export class PlayerController extends Component {
// 禁用碰撞器,防止重复触发
otherCollider.enabled = false;
if (otherCollider.node.name.startsWith('guai_')) {
this.handleAttack(otherCollider);
void this.handleAttack(selfCollider, otherCollider);
} else if (otherCollider.node.name.startsWith('box_')) {
this.handleBoxCollision(otherCollider);
}
}
/**
* 将玩家移动到怪物正对位置,确保攻击前双方站位合理
*/
private alignPlayerForAttack(selfCollider: Collider2D, monsterCollider: Collider2D): Promise<void> {
return new Promise((resolve) => {
if (!this.player || !selfCollider || !monsterCollider || !monsterCollider.node || !monsterCollider.node.isValid) {
resolve();
return;
}
const playerNode = this.player;
const monsterNode = monsterCollider.node;
const playerWorldPos = playerNode.worldPosition.clone();
const monsterWorldPos = monsterNode.worldPosition.clone();
const playerBox = selfCollider instanceof BoxCollider2D ? selfCollider : null;
const monsterBox = monsterCollider instanceof BoxCollider2D ? monsterCollider : null;
const playerScale = playerNode.worldScale;
const monsterScale = monsterNode.worldScale;
const playerHalfWidth = playerBox ? (playerBox.size.x * Math.abs(playerScale.x)) / 2 : 40;
const monsterHalfWidth = monsterBox ? (monsterBox.size.x * Math.abs(monsterScale.x)) / 2 : 60;
const playerOffsetX = playerBox ? playerBox.offset.x * playerScale.x : 0;
const playerOffsetY = playerBox ? playerBox.offset.y * playerScale.y : 0;
const monsterOffsetX = monsterBox ? monsterBox.offset.x * monsterScale.x : 0;
const monsterOffsetY = monsterBox ? monsterBox.offset.y * monsterScale.y : 0;
const playerCenterX = playerWorldPos.x + playerOffsetX;
const monsterCenterX = monsterWorldPos.x + monsterOffsetX;
const standOnLeft = playerCenterX <= monsterCenterX;
const totalHalfWidth = playerHalfWidth + monsterHalfWidth + this.attackAlignGap;
const directionMultiplier = standOnLeft ? -1 : 1;
const targetWorldPos = new Vec3(
monsterWorldPos.x + monsterOffsetX + directionMultiplier * totalHalfWidth - playerOffsetX,
monsterWorldPos.y + monsterOffsetY - playerOffsetY,
playerWorldPos.z
);
const targetLocalPos = this.convertWorldToParentSpace(playerNode, targetWorldPos);
const currentLocalPos = playerNode.position.clone();
const distance = Vec3.distance(currentLocalPos, targetLocalPos);
if (distance < 1) {
playerNode.setPosition(targetLocalPos);
this.currentDirection = standOnLeft ? 5 : 3;
resolve();
return;
}
const movingRight = targetLocalPos.x >= currentLocalPos.x;
const moveAnimation = movingRight ? 'walk5' : 'walk3';
this.switchAnimation(moveAnimation);
const baseDuration = this.moveSpeed > 0 ? distance / this.moveSpeed : 0.2;
const duration = Math.min(Math.max(baseDuration, 0.12), 0.45);
tween(playerNode)
.to(duration, { position: targetLocalPos }, {
easing: 'smooth',
onComplete: () => {
playerNode.setPosition(targetLocalPos);
this.currentDirection = standOnLeft ? 5 : 3;
resolve();
}
})
.start();
});
}
private convertWorldToParentSpace(node: Node, worldPos: Vec3): Vec3 {
const parent = node.parent;
if (!parent) {
return worldPos.clone();
}
const parentTransform = parent.getComponent(UITransform);
if (parentTransform) {
return parentTransform.convertToNodeSpaceAR(worldPos);
}
const fallback = worldPos.clone();
fallback.subtract(parent.worldPosition);
return fallback;
}
/**
* 处理攻击逻辑
*/
private handleAttack(otherCollider: Collider2D) {
private async handleAttack(selfCollider: Collider2D, otherCollider: Collider2D) {
if (this.isAttacking) {
return
return;
}
this.isAttacking = true;
if (!this.player || !otherCollider || !otherCollider.node || !otherCollider.node.isValid) {
return;
}
this.isAttacking = true;
this.stopMovement();
await this.alignPlayerForAttack(selfCollider, otherCollider);
if (!this.player || !otherCollider.node || !otherCollider.node.isValid) {
this.isAttacking = false;
return;
}
console.log('开始攻击,怪物名称:', otherCollider.node.name);
this.stopMovement();
// 获取玩家和怪物的生命值
const playerHpLabel = this.player.getChildByName('hp');
const monsterHpLabel = otherCollider.node.getChildByName('hp');
@@ -629,25 +731,35 @@ export class PlayerController extends Component {
}
// 播放攻击动画
const monsterAnimation = otherCollider.node.getChildByName('Anim').getComponent(Animation);
const animNode = otherCollider.node.getChildByName('Anim');
const monsterAnimation = animNode ? animNode.getComponent(Animation) : null;
if (monsterAnimation) {
monsterAnimation.play(`${otherCollider.node.name}_attack`);
}
this.switchAnimation(this.currentDirection === 3 ? 'attack3' : 'attack5');
// this.switchAnimation('attack3');
// 1.2秒后判定攻击结果
this.scheduleOnce(async () => {
if (!this.player || !playerLabel.isValid || !monsterLabel.isValid) {
this.isAttacking = false;
if (this.attackAudio) {
const audioSource = this.attackAudio.getComponent(AudioSource);
if (audioSource) {
audioSource.stop();
}
}
return;
}
// 比较生命值,判断输赢
console.log('判定攻击结果玩家HP:', playerHp, '怪物HP:', monsterHp);
if (playerHp >= monsterHp) {
const hit = otherCollider.node.getChildByName('Hit')
const hit = otherCollider.node.getChildByName('Hit');
if (hit) {
hit.active = true;
}
this.hasWinTimes++
this.hasWinTimes++;
// 玩家获胜
console.log('玩家获胜!更新玩家生命值为:', playerHp + monsterHp);
@@ -665,19 +777,21 @@ export class PlayerController extends Component {
// 如果是攻击 guai_2 并且成功,创建道具飞向 player 的动画
if (otherCollider.node.name === 'guai_2') {
await this.createPropsFlyToPlayerAnimation()
await this.createPropsFlyToPlayerAnimation();
}
// 1秒后怪物消失
this.scheduleOnce(() => {
console.log('怪物已消失');
console.log('otherCollider', otherCollider);
if (!otherCollider.node || !otherCollider.node.isValid) {
return;
}
otherCollider.node?.destroy();
console.log('怪物已消失');
otherCollider.node.destroy();
if (this.hasWinTimes === 7) {
this.isWin = true
this.showBonusPopup()
this.isWin = true;
this.showBonusPopup();
}
}, 1);