From 4e22001ae033be70d52fcb8a83f73d02ae3243a9 Mon Sep 17 00:00:00 2001 From: Valentin ROBIN Date: Sat, 1 Aug 2026 22:40:54 +0200 Subject: [PATCH] =?UTF-8?q?Ajoute=20une=20difficult=C3=A9=20progressive=20?= =?UTF-8?q?au=20Vol=20d'Icare?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit À partir du score 10, la vitesse de défilement augmente et l'écart entre les colonnes se resserre progressivement, jusqu'à un palier à score 50 (vitesse ×1.6, écart réduit à 75%) pour ne pas devenir injouable sur une longue partie. Le gap est maintenant stocké par colonne (plutôt qu'une constante globale) pour que les colonnes déjà en place ne changent pas de taille rétroactivement. --- src/app/icare/icarus-game.tsx | 32 +++++++++++++++++++++++++------- src/lib/icarus/constants.ts | 14 ++++++++++++-- src/lib/icarus/physics.ts | 5 ++--- src/lib/icarus/types.ts | 5 ++++- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/app/icare/icarus-game.tsx b/src/app/icare/icarus-game.tsx index bfcea5d..6e3b388 100644 --- a/src/app/icare/icarus-game.tsx +++ b/src/app/icare/icarus-game.tsx @@ -6,13 +6,17 @@ import { COLUMN_GAP_MARGIN, COLUMN_SPACING, COLUMN_WIDTH, + DIFFICULTY_RAMP_SPAN, + DIFFICULTY_START_SCORE, FIXED_DT_MS, FLAP_IMPULSE, FORWARD_SPEED, + GAP_MIN_MULTIPLIER, GROUND_Y, ICARUS_RADIUS, ICARUS_X, MAX_FRAME_DT_MS, + SPEED_MAX_MULTIPLIER, TILT_MAX_RADIANS, VIEWPORT_HEIGHT, VIEWPORT_WIDTH, @@ -29,9 +33,16 @@ type Status = "idle" | "flying" | "dead"; const CONFETTI_COLORS = ["#E7C560", "#C9A227", "#F4ECD8", "#A5342A", "#5E6B3B", "#2E6E7E", "#B08D57"]; -function randomGapCenter(): number { - const minCenter = COLUMN_GAP / 2 + COLUMN_GAP_MARGIN; - const maxCenter = GROUND_Y - COLUMN_GAP / 2 - COLUMN_GAP_MARGIN; +// 0 avant DIFFICULTY_START_SCORE, monte jusqu'à 1 sur DIFFICULTY_RAMP_SPAN +// points, puis reste à 1 (palier, pour ne pas devenir injouable). +function difficultyProgress(score: number): number { + if (score <= DIFFICULTY_START_SCORE) return 0; + return Math.min(1, (score - DIFFICULTY_START_SCORE) / DIFFICULTY_RAMP_SPAN); +} + +function randomGapCenter(gap: number): number { + const minCenter = gap / 2 + COLUMN_GAP_MARGIN; + const maxCenter = GROUND_Y - gap / 2 - COLUMN_GAP_MARGIN; return minCenter + Math.random() * Math.max(0, maxCenter - minCenter); } @@ -112,8 +123,8 @@ function drawColumnShaft( } function drawColumn(ctx: CanvasRenderingContext2D, column: Column) { - const gapTop = column.gapCenterY - COLUMN_GAP / 2; - const gapBottom = column.gapCenterY + COLUMN_GAP / 2; + const gapTop = column.gapCenterY - column.gap / 2; + const gapBottom = column.gapCenterY + column.gap / 2; drawColumnShaft(ctx, column.x, 0, gapTop, true); drawColumnShaft(ctx, column.x, gapBottom, GROUND_Y - gapBottom, false); } @@ -203,8 +214,14 @@ export function IcarusGame({ onFinish }: { onFinish: (score: number) => void }) } icarusRef.current = stepIcarus(icarusRef.current, FIXED_DT_MS); + // Difficulté progressive : vitesse en hausse et écart resserré au + // fil du score, jusqu'à un palier (voir difficultyProgress). + const progress = difficultyProgress(scoreRef.current); + const currentSpeed = FORWARD_SPEED * (1 + (SPEED_MAX_MULTIPLIER - 1) * progress); + const currentGap = COLUMN_GAP * (1 - (1 - GAP_MIN_MULTIPLIER) * progress); + for (const column of columnsRef.current) { - column.x -= FORWARD_SPEED * (FIXED_DT_MS / 1000); + column.x -= currentSpeed * (FIXED_DT_MS / 1000); if (!column.scored && column.x + COLUMN_WIDTH < ICARUS_X - ICARUS_RADIUS) { column.scored = true; scoreRef.current += 1; @@ -217,7 +234,8 @@ export function IcarusGame({ onFinish }: { onFinish: (score: number) => void }) if (!last || last.x < VIEWPORT_WIDTH - COLUMN_SPACING) { columnsRef.current.push({ x: last ? last.x + COLUMN_SPACING : VIEWPORT_WIDTH, - gapCenterY: randomGapCenter(), + gapCenterY: randomGapCenter(currentGap), + gap: currentGap, scored: false, }); } diff --git a/src/lib/icarus/constants.ts b/src/lib/icarus/constants.ts index e1a105a..e7f52a3 100644 --- a/src/lib/icarus/constants.ts +++ b/src/lib/icarus/constants.ts @@ -27,12 +27,22 @@ export const WING_IDLE_PERIOD_MS = 480; export const WING_FLAP_MAX_RADIANS = (75 * Math.PI) / 180; export const WING_FLAP_ANIM_MS = 260; -export const FORWARD_SPEED = 130; // unités/seconde, vitesse de défilement des colonnes +export const FORWARD_SPEED = 130; // unités/seconde, vitesse de défilement des colonnes de base export const COLUMN_WIDTH = 46; -export const COLUMN_GAP = 128; +export const COLUMN_GAP = 128; // écart de base entre les colonnes d'une paire export const COLUMN_SPACING = 210; export const COLUMN_GAP_MARGIN = 50; // distance mini entre le centre du trou et le sol/plafond +// Difficulté progressive : au-delà de DIFFICULTY_START_SCORE, la vitesse +// augmente et l'écart entre les colonnes se resserre progressivement, +// jusqu'à un palier atteint à DIFFICULTY_START_SCORE + DIFFICULTY_RAMP_SPAN +// (au-delà, la difficulté n'augmente plus — évite un jeu qui devient +// littéralement impossible sur une très longue partie). +export const DIFFICULTY_START_SCORE = 10; +export const DIFFICULTY_RAMP_SPAN = 40; +export const SPEED_MAX_MULTIPLIER = 1.6; +export const GAP_MIN_MULTIPLIER = 0.75; + export const FIXED_DT_MS = 1000 / 60; export const MAX_FRAME_DT_MS = 250; // évite la "spirale de la mort" après un onglet en arrière-plan diff --git a/src/lib/icarus/physics.ts b/src/lib/icarus/physics.ts index 72a3c41..5bf7d92 100644 --- a/src/lib/icarus/physics.ts +++ b/src/lib/icarus/physics.ts @@ -1,5 +1,4 @@ import { - COLUMN_GAP, COLUMN_WIDTH, GRAVITY, GROUND_Y, @@ -31,7 +30,7 @@ export function collidesWithColumn(icarusY: number, column: Column): boolean { const overlapsX = columnRight > ICARUS_X - ICARUS_RADIUS && columnLeft < ICARUS_X + ICARUS_RADIUS; if (!overlapsX) return false; - const gapTop = column.gapCenterY - COLUMN_GAP / 2; - const gapBottom = column.gapCenterY + COLUMN_GAP / 2; + const gapTop = column.gapCenterY - column.gap / 2; + const gapBottom = column.gapCenterY + column.gap / 2; return icarusY - ICARUS_RADIUS < gapTop || icarusY + ICARUS_RADIUS > gapBottom; } diff --git a/src/lib/icarus/types.ts b/src/lib/icarus/types.ts index 54abdc2..7253eec 100644 --- a/src/lib/icarus/types.ts +++ b/src/lib/icarus/types.ts @@ -1,3 +1,6 @@ -export type Column = { x: number; gapCenterY: number; scored: boolean }; +// gap est stocké par colonne (pas une constante globale) : la difficulté +// progressive resserre l'écart des colonnes créées après un certain score, +// sans changer la forme des colonnes déjà en place. +export type Column = { x: number; gapCenterY: number; gap: number; scored: boolean }; export type IcarusState = { y: number; velocityY: number };