diff --git a/src/game/Table.ts b/src/game/Table.ts index bd9d50e..f2d6aa9 100644 --- a/src/game/Table.ts +++ b/src/game/Table.ts @@ -226,25 +226,28 @@ export class Table { } /** - * Проверка: гол игрока (низ). Триггерит когда EDGE мяча (не центр) пересёк - * линию ворот — чтобы не получалось «попал в ворота, но отскочил обратно». - * ballRadius default 22 (BALL_RADIUS). + * Проверка: гол игрока (низ). Edge-based по обеим осям: + * - Y: edge мяча пересёк линию (ballY + radius > goalY) + * - X: центр мяча в gap ± half-radius (lenient — даёт пройти мячу когда + * значительная часть в gap, симметрично с Y). */ isInPlayerGoal(ballX: number, ballY: number, ballRadius = 22): boolean { const g = this.geometry; + const xTolerance = ballRadius / 2; return ( ballY + ballRadius > g.playerGoalY && - ballX > g.goalLeftX && - ballX < g.goalRightX + ballX > g.goalLeftX - xTolerance && + ballX < g.goalRightX + xTolerance ); } isInAIGoal(ballX: number, ballY: number, ballRadius = 22): boolean { const g = this.geometry; + const xTolerance = ballRadius / 2; return ( ballY - ballRadius < g.aiGoalY && - ballX > g.goalLeftX && - ballX < g.goalRightX + ballX > g.goalLeftX - xTolerance && + ballX < g.goalRightX + xTolerance ); } } diff --git a/src/scenes/MatchScene.ts b/src/scenes/MatchScene.ts index 10935d1..c352d77 100644 --- a/src/scenes/MatchScene.ts +++ b/src/scenes/MatchScene.ts @@ -931,16 +931,20 @@ export class MatchScene extends Phaser.Scene { // (mobile-friendly), в нижних углах вне playfield/guard rails. // Те же handlers что и keyboard A/D/S. В aiming-фазе L/R = rotate aim. - // Bottom Y — у самого края canvas, ниже flipper rest и guard rails (которые - // теперь продолжают линию флипперов под углом 30°) - const buttonY = GAME_HEIGHT - 40; - const sideButtonR = 52; // диаметр 104 — выше mobile threshold 72-88 - const centerButtonR = 44; // BOOST чуть меньше, центр + // Layout: + // L/R r=48 на y=H-55=1225 → top 1177 (ниже rails), bottom 1273 (выше canvas edge); + // overlap x не пересекает goal line (которая drawn только в gap 306..414). + // BOOST r=36 на y=H-85=1195 → выше goal line (1260), не overlap'ит её визуально. + // Asymmetric Y — trade-off за чистый visual. + const sideButtonR = 48; // диаметр 96 — выше mobile threshold 72-88 + const centerButtonR = 36; // BOOST меньше, поднят выше чтобы не overlap goal line + const sideButtonY = GAME_HEIGHT - 55; + const centerButtonY = GAME_HEIGHT - 85; // Player L — левый нижний угол this.createCornerButton({ x: 70, - y: buttonY, + y: sideButtonY, radius: sideButtonR, color: PALETTE.player, label: 'L', @@ -957,7 +961,7 @@ export class MatchScene extends Phaser.Scene { // Player R — правый нижний угол this.createCornerButton({ x: GAME_WIDTH - 70, - y: buttonY, + y: sideButtonY, radius: sideButtonR, color: PALETTE.player, label: 'R', @@ -971,10 +975,10 @@ export class MatchScene extends Phaser.Scene { }, }); - // BOOST — центр, отдельной кнопкой + // BOOST — центр, выше L/R чтобы не пересекать drawn goal line this.createCornerButton({ x: GAME_WIDTH / 2, - y: buttonY, + y: centerButtonY, radius: centerButtonR, color: PALETTE.accent, label: 'B',