changement d'interface pour le joueur et début d'animation du ballon

This commit is contained in:
2024-10-13 19:05:17 +00:00
parent b8428a2e5b
commit f51a4001d8
2 changed files with 109 additions and 81 deletions

View File

@@ -86,7 +86,7 @@
(mousemove)="onTimelineMouseMove($event)"
(mouseup)="onTimelineMouseUp()">
<div *ngFor="let stepPlayer of player.timeline; let i = index"
<div *ngFor="let stepPlayer of player.steps; let i = index"
class="timeline-block"
[style.left]="calculateLeftPosition(stepPlayer)"
[style.width]="calculateBlockWidth(stepPlayer)"
@@ -104,7 +104,7 @@
<h3>Timeline du joueur sélectionné</h3>
<div *ngIf="selectedPlayer">
<ul>
<li *ngFor="let step of selectedPlayer.timeline; let i = index">
<li *ngFor="let step of selectedPlayer.steps; let i = index">
Étape {{ i + 1 }} : Départ ({{ step.startX }}, {{ step.startY }}), Arrivée ({{ step.endX }}, {{ step.endY }}), Durée : ({{ step.startTime }}, {{ step.endTime }}: {{ step.duration }})
</li>
</ul>

View File

@@ -60,7 +60,7 @@ interface Ball {
progress: number; // Paramètre de progression sur la courbe
steps: TimelineStep[]; // Stockage des étapes d'animation
currentStepIndex: number; // Suivre l'étape actuelle dans la timeline
attachToPlayer: Circle | null; // Le joueur auquel le ballon est attaché
attachedToPlayer: Player | null; // Le joueur auquel le ballon est attaché
}
interface Player {
@@ -94,9 +94,9 @@ export class FootballFieldComponent {
//private lastY = 0;
private startX = 0;
private startY = 0;
private draggingPlayer: Circle | null = null;
private draggingPlayer: Player | null = null;
private draggingElement: any = null; // Element (plot, piquet) en cours de déplacement sur le terrain
private attachedPlayer: Circle | null = null;
private attachedPlayer: Player | null = null;
private magnetRadius: number = 35; // Distance à laquelle le ballon s'attache au joueur
//private attachedToPlayer: boolean = false; // indique si le ballon est attache a un joueur
private offsetX: number = 0;
@@ -125,7 +125,7 @@ export class FootballFieldComponent {
public playerCount: number = 8; // Nombre de joueurs par défaut
public plotCount: number = 2; // Nombre de plots par défaut
public piquetCount: number = 2; // Nombre de plots par défaut
public players: Circle[] = [];
public players: Player[] = [];
public plots: Plot[] = [];
public piquets: Piquet[] = [];
/*public ball: Circle = { id: 0,
@@ -148,15 +148,15 @@ export class FootballFieldComponent {
progress: 0,
steps: [],
currentStepIndex: 0,
attachToPlayer: null };
public selectedPlayer: Circle | null = null;
attachedToPlayer: null };
public selectedPlayer: Player | null = null;
public isDrawingLine: boolean = false;
public loopAnimation: boolean = false; // Pour répéter l'animation
public debugInfo: string = ""; // Variable pour les informations de debogage
public interactionMode: 'move' | 'animate' = 'move'; // Mode d'interaction sélectionné
public isTimelineDragging: boolean = false;
public draggedTimelinePlayer: Circle | null = null;
public draggedTimelinePlayer: Player | null = null;
public draggedTimelineIndex: number = -1;
// La largeur totale de la timeline en pixels.
// Cela représente la taille visuelle de la timeline dans laquelle
@@ -270,18 +270,27 @@ export class FootballFieldComponent {
// Mettre à jour la position de tous les joueurs en suivant leur timeline
this.players.forEach(player => {
if (this.isAnimating) {
if (player.timeline.length > 0) {
if (player.steps.length > 0) {
this.updatePlayerPosition(player);
}
} else if (this.isPlaying) {
player.timeline.forEach(step => {
player.steps.forEach(step => {
if (currentTime >= step.startTime && currentTime <= step.endTime) {
const progress = ((currentTime - step.startTime) / (step.endTime - step.startTime));
player.x = step.startX + (step.endX - step.startX) * progress;
player.y = step.startY + (step.endY - step.startY) * progress;
player.design.x = step.startX + (step.endX - step.startX) * progress;
player.design.y = step.startY + (step.endY - step.startY) * progress;
hasAnimation = true; // Indiquer qu'il y a encore de l'animation
}
});
// Déplacer le ballon si attaché à un joueur
if (this.ball.attachedToPlayer) {
const player = this.ball.attachedToPlayer;
// Le ballon suit le joueur avec un léger décalage (ajustable)
this.ball.design.x = player.design.x + player.design.radius + this.ball.design.radius + 5; // Décalage à droite du joueur
this.ball.design.y = player.design.y; // Même hauteur que le joueur
}
// Mettre à jour la position du trait de visualisation du temps
this.updateTimeIndicator(currentTime);
}
@@ -330,12 +339,26 @@ export class FootballFieldComponent {
}
}
private updatePlayerPosition(player: Circle) {
if (player.timeline.length === 0 || player.currentStepIndex >= player.timeline.length) {
// Méthode pour attacher le ballon à un joueur
attachBallToPlayer(player: Player) {
this.ball.attachedToPlayer = player;
player.hasBall = true;
}
// Méthode pour détacher le ballon (par exemple, quand on clique dessus)
detachBall() {
if (this.ball.attachedToPlayer) {
this.ball.attachedToPlayer.hasBall = false;
this.ball.attachedToPlayer = null;
}
}
private updatePlayerPosition(player: Player) {
if (player.steps.length === 0 || player.currentStepIndex >= player.steps.length) {
return; // Pas d'animation si la timeline est vide
}
const step = player.timeline[player.currentStepIndex];
const step = player.steps[player.currentStepIndex];
// Avancer le joueur sur la ligne avec un LERP (Linear Interpolation)
player.progress += this.animationSpeed;
// Temps actuel
@@ -354,12 +377,12 @@ export class FootballFieldComponent {
if (player.progress >= 1) {
player.progress = 0; // Réinitialiser la progression
player.x = step.endX;
player.y = step.endY;
player.design.x = step.endX;
player.design.y = step.endY;
player.currentStepIndex++; // Passer à l'étape suivante
// Si on atteint la fin de la timeline, recommencer ou arrêter
if (player.currentStepIndex >= player.timeline.length) {
if (player.currentStepIndex >= player.steps.length) {
if (this.loopAnimation) {
player.currentStepIndex = 0; // Boucle l'animation
} else {
@@ -368,15 +391,22 @@ export class FootballFieldComponent {
}
} else {
const t = player.progress;
player.x = (1 - t) * step.startX + t * step.endX;
player.y = (1 - t) * step.startY + t * step.endY;
player.design.x = (1 - t) * step.startX + t * step.endX;
player.design.y = (1 - t) * step.startY + t * step.endY;
// Attacher le ballon si un joueur se rapproche suffisamment
const nearestPlayer = this.getNearestPlayerToBall();
if (nearestPlayer && this.getDistance(this.ball.design, nearestPlayer.design) < this.magnetRadius) {
//this.attachedPlayer = nearestPlayer;
this.updateBallPositionOnPlayer(nearestPlayer);
}
}
}
// Mettre à jour debugInfo avec les positions des joueurs et du ballon
private updateDebugInfo() {
const playerPositions = this.players.map((player, index) =>
`Joueur ${index + 1}: (${player.x.toFixed(1)}, ${player.y.toFixed(1)})`
`Joueur ${index + 1}: (${player.design.x.toFixed(1)}, ${player.design.y.toFixed(1)})`
).join(', ');
const ballPosition = `Ballon: (${this.ball.design.x.toFixed(1)}, ${this.ball.design.y.toFixed(1)})`;
@@ -391,15 +421,15 @@ export class FootballFieldComponent {
const x = i * (2 * radius) + radius;
const y = radius;
this.players.push({ id: i + 1,
x, y,
radius,
design: { x, y, radius },
isDragging: false,
isMoving:false,
isMoving: false,
progress: 0,
startX: 0,
startY: 0,
timeline: [],
currentStepIndex: 0 });
steps: [],
currentStepIndex: 0,
hasBall: false });
}
this.drawField();
}
@@ -412,9 +442,7 @@ export class FootballFieldComponent {
const x = (this.canvas.nativeElement.width - radius) - (i) * (2 * radius);
const y = radius;
this.plots.push({ id: i + 1,
design: {
x, y,
radius },
design: { x, y, radius },
isDragging: false });
}
this.drawField();
@@ -428,8 +456,8 @@ export class FootballFieldComponent {
const height = 100;
const x = (i * width);
const y = (this.canvas.nativeElement.height - height);
this.piquets.push({ id: i+1,
design: {x, y, width, height},
this.piquets.push({ id: i + 1,
design: { x, y, width, height },
isDragging: false });
}
this.drawField();
@@ -451,8 +479,8 @@ export class FootballFieldComponent {
// Dessiner les cercles numéroe representant les joueurs
private drawPlayers() {
this.players.forEach(circle => {
this.drawPlayer(circle);
this.players.forEach(player => {
this.drawPlayer(player);
});
}
@@ -509,12 +537,12 @@ export class FootballFieldComponent {
}
// Dessiner un joueur avec un numér
private drawPlayer(circle: Circle) {
private drawPlayer(player: Player) {
const ctx = this.ctx;
// Dessiner le cercle
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
ctx.arc(player.design.x, player.design.y, player.design.radius, 0, Math.PI * 2);
ctx.fillStyle = '#ffff00';
ctx.fill();
ctx.strokeStyle = '#000000';
@@ -525,7 +553,7 @@ export class FootballFieldComponent {
ctx.font = 'bold 12px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(circle.id.toString(), circle.x, circle.y);
ctx.fillText(player.id.toString(), player.design.x, player.design.y);
}
onCanvasMouseDown(event: MouseEvent) {
@@ -534,7 +562,7 @@ export class FootballFieldComponent {
console.log("[Mouse Down] animate elements");
this.selectedPlayer = null;
for (const player of this.players) {
if (this.isInsideCircle(player, x, y)) {
if (this.isInsideCircle(player.design, x, y)) {
// Sélectionner le joueur
this.selectedPlayer = player;
console.log("[Mouse Down|Animate] selecting player :", this.selectedPlayer);
@@ -545,8 +573,8 @@ export class FootballFieldComponent {
if (!this.selectedPlayer) {
this.selectPlayer(x, y);
} else if (!this.isDrawingLine) {
this.startX = this.selectedPlayer.x;
this.startY = this.selectedPlayer.y;
this.startX = this.selectedPlayer.design.x;
this.startY = this.selectedPlayer.design.y;
this.isDrawingLine = true;
console.log("[Mouse Down|Animate] drawing line - Start:(",
this.startX,",",
@@ -584,13 +612,13 @@ export class FootballFieldComponent {
this.isDrawingLine = false;
// Ajouter l'étape dans la timeline du joueur
let prevStartTime:number = 0;
if (this.selectedPlayer.timeline && this.selectedPlayer.timeline.length > 0) {
prevStartTime = this.selectedPlayer.timeline[this.selectedPlayer.timeline.length - 1].endTime;
if (this.selectedPlayer.steps && this.selectedPlayer.steps.length > 0) {
prevStartTime = this.selectedPlayer.steps[this.selectedPlayer.steps.length - 1].endTime;
} else {
prevStartTime = 0;
}
this.selectedPlayer.timeline.push({
this.selectedPlayer.steps.push({
startTime: prevStartTime,
endTime: prevStartTime + 1000,
startX: this.startX,
@@ -599,7 +627,7 @@ export class FootballFieldComponent {
endY: this.endY,
duration: 1000 // Durée d'animation arbitraire, peut être ajustée
});
console.log("timeline:", this.selectedPlayer.timeline);
console.log("timeline:", this.selectedPlayer.steps);
//this.selectedPlayer.currentStepIndex = 0; // Réinitialiser au début de la timeline
this.startAnimation();
}
@@ -610,10 +638,10 @@ export class FootballFieldComponent {
// Si on déplace un joueur alors que celui-ci a une animation,
// on supprime toute sa timeline
for (const player of this.players) {
if (this.isInsideCircle(player, x, y)) {
if (this.isInsideCircle(player.design, x, y)) {
// Sélectionner le joueur
this.selectedPlayer = player;
this.selectedPlayer.timeline = [];
this.selectedPlayer.steps = [];
break;
}
}
@@ -643,12 +671,12 @@ export class FootballFieldComponent {
}
this.draggingPlayer = this.players.find(player => {
return this.isInsideCircle(player, x, y);
return this.isInsideCircle(player.design, x, y);
});
if (this.draggingPlayer) {
this.draggingPlayer.isDragging = true;
this.offsetX = x - this.draggingPlayer.x;
this.offsetY = y - this.draggingPlayer.y;
this.offsetX = x - this.draggingPlayer.design.x;
this.offsetY = y - this.draggingPlayer.design.y;
this.test = true;
}
}
@@ -665,14 +693,14 @@ export class FootballFieldComponent {
if (this.draggingPlayer && this.draggingPlayer.isDragging) {
console.log("dragging player");
this.draggingPlayer.x = x - this.offsetX;
this.draggingPlayer.y = y - this.offsetY;
this.draggingPlayer.design.x = x - this.offsetX;
this.draggingPlayer.design.y = y - this.offsetY;
// Attacher le ballon si un joueur se rapproche suffisamment
const nearestPlayer = this.getNearestPlayerToBall();
if (nearestPlayer && this.getDistance(this.ball.design, nearestPlayer) < this.magnetRadius) {
this.attachedPlayer = nearestPlayer;
this.updateBallPositionOnPlayer();
if (nearestPlayer && this.getDistance(this.ball.design, nearestPlayer.design) < this.magnetRadius) {
//this.attachedPlayer = nearestPlayer;
this.updateBallPositionOnPlayer(nearestPlayer);
}
// // Si le ballon est attaché au joueur deplacer le ballon avec lui en calculant
@@ -714,12 +742,12 @@ export class FootballFieldComponent {
}
}
private updateBallPositionOnPlayer() {
const player = this.attachedPlayer;
private updateBallPositionOnPlayer(player: Player) {
//const player = this.attachedPlayer;
if (!player) return;
const dx = this.ball.design.x - player.x;
const dy = this.ball.design.y - player.y;
const dx = this.ball.design.x - player.design.x;
const dy = this.ball.design.y - player.design.y;
let angle = 0;
if (this.test) {
angle = Math.atan2(dy, dx);
@@ -729,15 +757,15 @@ export class FootballFieldComponent {
angle = this.prevAngle;
}
this.ball.design.x = player.x + (player.radius + this.ball.design.radius) * Math.cos(angle);
this.ball.design.y = player.y + (player.radius + this.ball.design.radius) * Math.sin(angle);
this.ball.design.x = player.design.x + (player.design.radius + this.ball.design.radius) * Math.cos(angle);
this.ball.design.y = player.design.y + (player.design.radius + this.ball.design.radius) * Math.sin(angle);
}
private selectPlayer(x: number, y: number) {
this.selectedPlayer = this.getNearestPlayer(x, y);
if (this.selectedPlayer) {
this.selectedPlayer.startX = this.selectedPlayer.x; // Sauvegarder le point de départ
this.selectedPlayer.startY = this.selectedPlayer.y;
this.selectedPlayer.startX = this.selectedPlayer.design.x; // Sauvegarder le point de départ
this.selectedPlayer.startY = this.selectedPlayer.design.y;
}
}
@@ -748,12 +776,12 @@ export class FootballFieldComponent {
}
}
private getNearestPlayerToBall(): Circle | null {
let nearestPlayer: Circle | null = null;
private getNearestPlayerToBall(): Player | null {
let nearestPlayer: Player | null = null;
let minDistance = this.magnetRadius;
this.players.forEach(player => {
const distance = this.getDistance(this.ball.design, player);
const distance = this.getDistance(this.ball.design, player.design);
if (distance < minDistance) {
minDistance = distance;
nearestPlayer = player;
@@ -762,13 +790,13 @@ export class FootballFieldComponent {
return nearestPlayer;
}
private getNearestPlayer(x: number, y: number): Circle | null {
let nearestPlayer: Circle | null = null;
private getNearestPlayer(x: number, y: number): Player | null {
let nearestPlayer: Player | null = null;
let minDistance = Infinity;
this.players.forEach(player => {
const distance = Math.sqrt((player.x - x) ** 2 + (player.y - y) ** 2);
if (distance < minDistance && distance <= player.radius) {
const distance = Math.sqrt((player.design.x - x) ** 2 + (player.design.y - y) ** 2);
if (distance < minDistance && distance <= player.design.radius) {
minDistance = distance;
nearestPlayer = player;
}
@@ -878,13 +906,13 @@ export class FootballFieldComponent {
return (time / totalDuration) * timelineWidth;
}
onTimelineMouseDown(event: MouseEvent, player: Circle) {
onTimelineMouseDown(event: MouseEvent, player: Player) {
// Récupérer la position de la souris lors du clic
const { x } = this.getMousePosOnTimeline(event);
// Chercher si un bloc (une étape du joueur) a été cliqué
for (let i = 0; i < player.timeline.length; i++) {
const step = player.timeline[i];
for (let i = 0; i < player.steps.length; i++) {
const step = player.steps[i];
//console.log("[OnTimelineMouseDown | Joueur",player.id,"] - Timeline[",i,"]:", step);
// Calculer la position du bloc en pixels sur la timeline
const blockStartX = this.calculateBlockPixelPosition(step.startTime);
@@ -910,7 +938,7 @@ export class FootballFieldComponent {
}
}
onTimelineBlockMouseDown(event: MouseEvent, player: Circle, index: number) {
onTimelineBlockMouseDown(event: MouseEvent, player: Player, index: number) {
console.log("[onTimelineBlockMouseDown] index:", index);
this.isTimelineDragging = true;
this.draggedTimelinePlayer = player;
@@ -930,18 +958,18 @@ export class FootballFieldComponent {
" - newStartX:", newStartX,
" - newStartTime:", newStartTime,
" - dragged Index:", this.draggedTimelineIndex,
" - timeline len:", this.draggedTimelinePlayer.timeline.length,
" - timeline len:", this.draggedTimelinePlayer.steps.length,
);
// Mettre à jour l'étape avec la nouvelle position
const step = this.draggedTimelinePlayer.timeline[this.draggedTimelineIndex];
const step = this.draggedTimelinePlayer.steps[this.draggedTimelineIndex];
let prevStep:TimelineStep = null;
// Le bloc N ne peut pas dépasser le bloc N+1 si celui-ci existe
// De même, le bloc N ne peut pas dépasser le bloc N-1 si celui-ci existe
if ((this.draggedTimelineIndex + 1) < this.draggedTimelinePlayer.timeline.length) {
const nextStep = this.draggedTimelinePlayer.timeline[this.draggedTimelineIndex + 1];
if ((this.draggedTimelineIndex + 1) < this.draggedTimelinePlayer.steps.length) {
const nextStep = this.draggedTimelinePlayer.steps[this.draggedTimelineIndex + 1];
if ((this.draggedTimelineIndex - 1) >= 0) {
prevStep = this.draggedTimelinePlayer.timeline[this.draggedTimelineIndex - 1];
prevStep = this.draggedTimelinePlayer.steps[this.draggedTimelineIndex - 1];
}
if ((((newStartTime + step.duration) < nextStep.startTime) &&
prevStep === null) ||
@@ -952,8 +980,8 @@ export class FootballFieldComponent {
step.endTime = Math.max(0, step.startTime + step.duration); // Ne pas permettre les valeurs négatives
}
} else if (((this.draggedTimelineIndex - 1) >= 0) &&
(this.draggedTimelinePlayer.timeline[this.draggedTimelineIndex - 1])) {
const prevStep = this.draggedTimelinePlayer.timeline[this.draggedTimelineIndex - 1];
(this.draggedTimelinePlayer.steps[this.draggedTimelineIndex - 1])) {
const prevStep = this.draggedTimelinePlayer.steps[this.draggedTimelineIndex - 1];
if ((prevStep.endTime <= newStartTime) &&
((newStartTime + step.duration) <= (this.getTotalTimelineDuration()))) {
//console.log("PLOP1.1: (",prevStep.endTime, "/", newStartTime,"),(",newStartTime + step.duration,"/",this.getTotalTimelineDuration(),")");
@@ -980,7 +1008,7 @@ export class FootballFieldComponent {
onTimelineMouseUp() {
console.log("onMouseUp");
if (this.isTimelineDragging) {
console.log("timeline:", this.draggedTimelinePlayer.timeline);
console.log("timeline:", this.draggedTimelinePlayer.steps);
this.isTimelineDragging = false;
this.draggedTimelinePlayer = null;
this.draggedTimelineIndex = -1;