Ajoute une difficulté progressive au Vol d'Icare
Build and deploy / deploy (push) Successful in 1m36s

À 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.
This commit is contained in:
Valentin ROBIN
2026-08-01 22:40:54 +02:00
parent 7c5ce9dcb9
commit 4e22001ae0
4 changed files with 43 additions and 13 deletions
+25 -7
View File
@@ -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,
});
}