perf: 优化攻击位置
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user