import { BALL_RADIUS, BALL_COLOR } from '../constants.js'; /** * Ball - 小球实体 * 由玩家发射,碰撞方块和边缘时反弹 */ export class Ball { /** * @param {number} x - 初始 x 坐标 * @param {number} y - 初始 y 坐标 * @param {number} [radius=BALL_RADIUS] - 半径 * @param {string} [color=BALL_COLOR] - 颜色 */ constructor(x, y, radius = BALL_RADIUS, color = BALL_COLOR) { this.x = x; this.y = y; this.radius = radius; this.color = color; this.vx = 0; this.vy = 0; this.active = true; } /** * 每帧更新位置(子步移动,fraction 为 0~1 的比例) * 处理左/右/顶部边缘反弹,底部停止 * @param {number} _deltaTime - 未使用,保留接口兼容 * @param {number} boardWidth - 面板宽度 * @param {number} boardHeight - 面板高度 * @param {number} [fraction=1] - 移动比例(用于子步进防穿透) */ update(_deltaTime, boardWidth, boardHeight, fraction = 1) { if (!this.active) return; // 移动 fraction 比例的距离 this.x += this.vx * fraction; this.y += this.vy * fraction; // 左侧边缘反弹 if (this.x - this.radius <= 0) { this.x = this.radius; this.reflect('x'); } // 右侧边缘反弹 if (this.x + this.radius >= boardWidth) { this.x = boardWidth - this.radius; this.reflect('x'); } // 顶部边缘反弹 if (this.y - this.radius <= 0) { this.y = this.radius; this.reflect('y'); } // 底部:停止运动 if (this.isAtBottom(boardHeight)) { this.y = boardHeight - this.radius; this.active = false; } } /** * 反射:反转指定轴的速度分量 * @param {'x'|'y'} axis - 'x' 反转 vx,'y' 反转 vy */ reflect(axis) { if (axis === 'x') { this.vx = -this.vx; } else if (axis === 'y') { this.vy = -this.vy; } } /** * 判断球是否到达底部 * @param {number} boardHeight - 面板高度 * @returns {boolean} */ isAtBottom(boardHeight) { return this.y + this.radius >= boardHeight; } /** * 获取碰撞矩形(AABB) * @returns {{ x: number, y: number, width: number, height: number }} */ getRect() { const diameter = this.radius * 2; return { x: this.x - this.radius, y: this.y - this.radius, width: diameter, height: diameter }; } }