IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

Incrémenter la position d'un objet après un nombre de frames


Sujet :

JavaScript

  1. #1
    Membre régulier
    Homme Profil pro
    sans
    Inscrit en
    Mai 2023
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : sans

    Informations forums :
    Inscription : Mai 2023
    Messages : 145
    Points : 83
    Points
    83
    Par défaut Incrémenter la position d'un objet après un nombre de frames
    Bonjour !

    J'ai fais un code qui me permet de faire bouger un objet à partir d'un nombre de frames différent des autres objets mais il n’est pas complet, je ne sais pas comment leur attribuer le tour.
    Je pensais utiliser un switch case mais il me manque quelque chose et j'arrive pas à voir quoi.

    timevague est le numéro de la frame en cours, il varie de 0 à 299 puis recommence à 0.
    Tchemin contient les points XY que chaque objet va prendre mais pas en même temps, sauf pour le premier points en Tchemin[0].
    En gros, tous les 4 frames je fais partir un vaisseau de la liste Tennemis[].

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    function Ennemis1_UpDate(){
     
    	if(indx<=Tchemin.length-1){
     
    		// Met tous les vaisseaux de la vague à la meme position de départ.
    		Tennemis.forEach(vaiss =>{
    			vaiss.X=Tchemin[0].X;
    			vaiss.Y=Tchemin[0].Y;
    		})
     
    		Tennemis.forEach(vaiss =>{
    			switch(timevague){
    				case 0:
     
    					break;
    				case 4:
    					break;
    				case 8:
    					break;
    				case 12:
    					break;
    				case 16:
    					break;
    				case 20:
    					break;
    				case 24:
    					break;
    				case 28:
    					break;
    				case 32:
    					break;
    				case 36:
    					break;
    				case 40:
    					break;
     
    			}
     
                       // C'est la partie ci-dessous que je voudrais appliquer à chaque vaisseau en fonction de la case du switch.
     
    			// Incrémente X des vaisseaux de la vague.
    			if(vaiss.X<Tchemin[indx].X-vaiss.Vit){
    				vaiss.X+=vaiss.Vit;
    			}
     
    			if(vaiss.X>Tchemin[indx].X-vaiss.Vit){
    				vaiss.X-=vaiss.Vit;
    			}
     
    			// Incrémente Y des vaisseaux de la vague.
    			if(vaiss.Y<Tchemin[indx].Y-vaiss.Vit){
    				vaiss.Y+=vaiss.Vit;
    			}
     
    			if(vaiss.Y>Tchemin[indx].Y-vaiss.Vit){
    				vaiss.Y-=vaiss.Vit;
    			}
     
    		})
    	}
     
    	indx +=1;
     
    }

    Peut-être qu'il me faudrait faire un compteur jusqu'à 4 frames et ainsi faire partir un vaisseau puis incrémenter le numéro du vaisseau qui doit partir après.
    Ce serait plus simple

    JE M'APPROCHE ....

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    // A mettre dans une fonction : Met tous les vaisseaux de la vague à la meme position de départ.
    Tennemis.forEach(vaiss =>{
    	vaiss.X=Tchemin[0].X;
    	vaiss.Y=Tchemin[0].Y;
    })
     
    function xorgons1_UpDate(){
    	// Déplace ennemi2 toutes les 50 frames. 
    	if(indx<=Tchemin.length-1){
     
    		let vaiss=Tennemis[i];
     
    		console.log(timevague);
     
    		if(timevague==1){ // Chiffre UN.
     
    			// Incrémente X du vaisseau Tennemis[i].
    			if(vaiss.X<Tchemin[indx].X-vaiss.Vit){
    				vaiss.X+=vaiss.Vit;
    			}
     
    			if(vaiss.X>Tchemin[indx].X-vaiss.Vit){
    				vaiss.X-=vaiss.Vit;
    			}
     
    			// Incrémente X du vaisseau Tennemis[i].
    			if(vaiss.Y<Tchemin[indx].Y-vaiss.Vit){
    				vaiss.Y+=vaiss.Vit;
    			}
     
    			if(vaiss.Y>Tchemin[indx].Y-vaiss.Vit){
    				vaiss.Y-=vaiss.Vit;
    			}
     
    		}
     
    		// Passe au vaisseau suivant.
    		i++;
     
                    // Passe au point suivant du chemin si on a incrémenté tous les vaisseaux.
                    // Remet aussi le compteur du numéro de vaisseau à 0.
    		if(i>=Tennemis.length-1){
    			i=0;
    			// Passe au point suivant.
    			indx +=1;
    			if(indx>=Tchemin.length-1){
    				// La vague d'ennemis.
    				indx=0; // Remise à 0 du compteur de points du chemin.
    				Tchemin=[]; // Vidage du chemin de points.
    				face_sel();  // Fonction choisissant le coté de départ de la vague et le point de départ.
    				trace(); // Fonction traçant le chemin de points et qui le mémorise dans le tableau Tchemin.
     
    			}
    		}
     
    	}
     
     
    }

  2. #2
    Membre régulier
    Homme Profil pro
    sans
    Inscrit en
    Mai 2023
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : sans

    Informations forums :
    Inscription : Mai 2023
    Messages : 145
    Points : 83
    Points
    83
    Par défaut
    c'est vraiment dur de créer des vagues de vaisseaux :/

    Faire parcourir un vaisseau sur quelques points avant de faire avancer le vaisseau suivant.
    donner l'impression que les vaisseaux partent du même point mais qu'ensuite ils se suivent en partant l'un aprés l'autre, rapidement.
    Faut être fort en algo j'imagine :/ c'est pas mon fort, j'essaie

    Quelqu'un a une idée où je pourrais trouver ça sur le net ? google donne rien.

  3. #3
    Expert confirmé
    Avatar de Doksuri
    Profil pro
    Développeur Web
    Inscrit en
    Juin 2006
    Messages
    2 476
    Détails du profil
    Informations personnelles :
    Âge : 54
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 476
    Points : 4 687
    Points
    4 687
    Par défaut
    pour se caller sur des frame, tu peux utiliser la function requestAnimationFrame developer.mozilla.org/fr/docs/Web/API/window/requestAnimationFrame

  4. #4
    Membre régulier
    Homme Profil pro
    sans
    Inscrit en
    Mai 2023
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : sans

    Informations forums :
    Inscription : Mai 2023
    Messages : 145
    Points : 83
    Points
    83
    Par défaut
    Merci !

    Je marque en résolu, j'abandonne, c'est un projet de professionnels du jeu ce genre de technique, ils donnent ce genre de code que dans les bouquins de prog de jeu de pros et en anglais en plus.

  5. #5
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 098
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 098
    Points : 44 675
    Points
    44 675
    Par défaut
    Bonjour,
    c'est un projet de professionnels du jeu ce genre de technique,
    Ce genre de technique, comme tu dis, consiste à bien avoir la vision de ce que tu veux faire.

    Comme déjà surement dit il faut diviser pour mieux régner.

    Un façon de lancer ta flotte
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    function startFlotteNavires(navires) {
      const decalage = 500;  	// en ms
      navires.forEach((navire, ind) => {
        setTimeout(() => {
          navire.deplace();		// fonction de déplacement définie dans l'objet Navire par exemple
        }, ind * decalage);
      });
    }
    ... et en anglais en plus.
    les liens sur MDN sont pour la plupart traduit, quand cela fonctionne

  6. #6
    Membre régulier
    Homme Profil pro
    sans
    Inscrit en
    Mai 2023
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : sans

    Informations forums :
    Inscription : Mai 2023
    Messages : 145
    Points : 83
    Points
    83
    Par défaut
    Merci No Smoking !!!

    Je vais voir comment comprendre et adapter ça

    Pour ce qui est de la vision de l'effet je l'ai mais j'ai pas l'expérience de l'algorythmie ou de la prog en général, mais j'essaie de progresser avec des exemples de jeux car j'aime bien la programmation de jeux, même si je suis pas fort
    Ah oui MDN, j'avais regardé mais souvent j'ai du mal à comprendre les exemples qu'ils donnent, ils font souvent compliqué quand ils pourraient faire simple, je trouve, enfin c'est comme ça que je le perçois, car des fois sur d'autres liens je trouve des exemples plus simples que ce que propose MDN, mais l'avantage de MDN c'est quil y a pas mal de choses c'est sûr, mais c'est pour les gens avancés e prog pas les débutants comme moi.
    Enfin, parfois ça va.
    Merci pour ton code, je te dirais ce que j'en obtiens


    à Quoi sert la valeur ind ?

    Mes vaisseaux doivent suivre un chemin de points contenu dans un tableau.
    Je fais un frame % T pour lancer la fonction de déplacement de la flotte.
    frame c'est le numéro de la frame en cours dans XhtmlRequest().
    T c'est un temps en entier choisi au pf pour essayer jusqu'à fonctionnement. C'est le pas de déplacement en fait, pour passer d'une position à une autre, le temps quoi.


    Voici les deux fonctions qui me permettent de définir un coté de l'écran pour démarrer le chemin et tracer le chemin.

    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    // Le choix du coté de l'écran pour la vague d'assaux des ennemis.
    function face_sel(){
    	let face=Math.floor(Math.random()*3); // donne au hazard 0, 1, ou 2.
     
    	switch(face){
     
    		case 0: // Coté gauche de l'écran.
    			point.X=0; point.Y=Math.floor(Math.random()*(hauteur-200));// Un point au hasard sur le coté gauche de l'écran.
    			if(point.Y>Mi_Height){
    				point.DirX=+1; point.DirY=-1;
    			}else{
    				point.DirX=+1; point.DirY=+1;
    			}
    			break;
     
    		case 1: // Coté haut de l'écran.
    			point.X=Math.floor(Math.random()*largeur); point.Y=0;// Un point au hasard sur le coté haut de l'écran.
    			if(point.X>Mi_Width){
    				point.DirX=-1; point.DirY=+1;
    			}else{
    				point.DirX=+1; point.DirY=+1;
    			}
    			break;
     
    		case 2: // Coté droit de l'écran.
    			point.X=largeur; point.Y=Math.floor(Math.random()*(hauteur-200));// Un point au hasard sur le coté droit de l'écran.
    			if(point.Y>Mi_Height){
    				point.DirX=-1; point.DirY=-1;
    			}else{
    				point.DirX=-1; point.DirY=+1;
    			}
    			break;
     
    	}
     
     
    }
     
    /* Enregistre dans Tchemin tous les localpoints du chemin. */
    function trace(){
     
    	// La limite haute du localpoint courant.
    	point.limitV=Math.floor(Math.random(100));
     
    	// Départ de la boucle de test.
    	point.fin=false;
     
    	while(point.fin==false){
     
    		var localpoint=new Clocalpoint();
    		localpoint.X=point.X;
    		localpoint.Y=point.Y;
     
    		// Stabilisation de Y à la limite haute.
    		if(localpoint.Y<=point.limitV){
    			localpoint.Y=point.limitV;
    		}
     
    		// Selon la direction de X, la fin du tracé pour ce localpoint sera largeur écran ou 0.
    		if(point.DirX==+1){
     
    			if(localpoint.X>=largeur){
    				point.fin=true;
    			}
     
    		}else{
     
    			if(localpoint.X<=-100){ // -100 pour cacher la largeur du vaisseau de la vague.
    				point.fin=true;
    			}
     
    		}
     
    		// Mémorise le localpoint courant.
    		Tchemin.push(localpoint);
     
     
    		// Passe au prochain localpoint X. (le 60 sert d'écrat de distance visuelle entre deux vaisseaus d'une vague)
    		point.X+=(60*point.DirX);
     
    		// Stabilise Y s'il descend trop bas sur l'écran.
    		if(point.Y>=(hauteur-250)){
    			point.Y=hauteur-250;
    		}else{
     
    			// Passe au prochain localpoint Y.
    			point.Y+=(60*point.DirY);
    		}
     
    	}
    	/* Voilà, il ne reste plus qu'à faire suivre le chemin tracé dans Tchemin.
    	On fait ça dans l'update des vaisseaux ennemis.
    	*/
     
    }


    Avec mon code, je suis arrivé déjà à obtenir une vague de vaisseau qui suivent un chemin et l'un après l'autre, mais comme c'est la frame qui fait leur déplacement cela va trop vite.

    J'essaie de voir comment adapter ton code car j'ai essayé mon code de plusieurs manieres pour ralentir le passage de la vague mais elle passe en deux secondes...


    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    // La fonction qui initialise les variables de la vague et le tracé de la vague.
    function Vague_init(){
    	indx=0;
    	i=0;
    	Tchemin=[];
    	face_sel();
    	trace();
    }
     
     
    // La fonction de la vague qui déplace les vaisseaux l'un aprés l'autre..
    function Vague(){
     
    	if(timevague==0){
     
    		pas=0;
    		i++;
     
    	}else{
     
    		pas+=1;
     
    		if(i<=Tennemis1.length-1){
     
    			if(indx<=Tchemin.length-1){
     
     
    				Tennemis1[i].Deplace(Tchemin[indx].X,Tchemin[indx].Y);
     
    				indx++;
    			}
     
     
    		}
     
     
    	}
     
    }
     
     
     
    // En début de la boucle des frames : (Ces variables servent à démarrer en temps voulus la vague_init et la vague.
     
    		frequence_vague=frame % 40;
    		timevague=frame % T;
     
    		// Fréquence de la vague d'assaut.
    		if(frequence_vague==0){
    			Vague_init(); // Initialisation de la vague. 
    		}
     
                    // La vague d'assaut, qui est trop rapide.
    		Vague();

  7. #7
    Membre régulier
    Homme Profil pro
    sans
    Inscrit en
    Mai 2023
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : sans

    Informations forums :
    Inscription : Mai 2023
    Messages : 145
    Points : 83
    Points
    83
    Par défaut
    RESOLU



    Nom : image_2023-07-04_202221175.png
Affichages : 80
Taille : 3,4 Ko

    Comme on peut le voir, ce sont 5 rectangles de tailles différentes (5 objets) qui défilent en rase-motte de l'écran suivant un tracé défini aléatoirement dans un tableau.

    Cela permet de créer sois-même si besoin des figures à faire pour les vagues de vaisseaux....

    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    /* Enregistre dans Tchemin tous les localpoints du chemin. */
    function trace(){
     
    	// La limite haute du localpoint courant.
    	point.limitV=Math.floor(Math.random(100));
     
    	// Départ de la boucle de test.
    	point.fin=false;
     
    	while(point.fin==false){
     
    		var localpoint=new Clocalpoint();
    		localpoint.X=point.X;
    		localpoint.Y=point.Y;
     
    		// Stabilisation de Y à la limite haute.
    		if(localpoint.Y<=point.limitV){
    			localpoint.Y=point.limitV;
    		}
     
    		// Selon la direction de X, la fin du tracé pour ce localpoint sera largeur écran ou 0.
    		if(point.DirX==+1){
     
    			if(localpoint.X>=largeur*2){  // "largeur * 2" pour cacher la vague sur la droite de l'écran.
    				point.fin=true;
    			}
     
    		}else{
     
    			if(localpoint.X<=-largeur){ // "-largeur de l'écran" pour cacher la vague sur la gauche de l'écran.
    				point.fin=true;
    			}
     
    		}
     
    		// Mémorise le localpoint courant.
    		Tchemin.push(localpoint);
     
     
    		// Passe au prochain localpoint X.
    		point.X+=(20*point.DirX); // 20 c'est l'écart entre vaisseaux)
     
    		// Stabilise Y s'il descend trop bas sur l'écran.
    		if(point.Y>=(hauteur-250)){
    			point.Y=hauteur-250;
    		}else{
     
    			// Passe au prochain localpoint Y.
    			point.Y+=(20*point.DirY);
    		}
     
    	}
    	/* Voilà, il ne reste plus qu'à faire suivre le chemin tracé dans Tchemin.
    	On fait ça dans l'update des vaisseaux ennemis.
    	*/
     
    }
     
     
    // Initialisation de la vague.
    function Vague_init(){
    	indx=0;
    	i=0;
    	Tchemin=[];
    	face_sel();
    	trace();
    }
     
     
    // une vague d'ennemis, façon nouby ;).
     
    function Vague(){
     
    	if(timevague==0){
     
    		i++;
     
    	}else{
     
     
    		if(i<=Tennemis1.length-1){
     
    			if(indx<=Tchemin.length-1){
     
    				Tennemis1[i].Deplace(Tchemin[indx].X,Tchemin[indx].Y);
     
    			}
     
    			indx++;
     
     
    		}else{i=0;}
     
     
    	}
     
    }
     
     
    // Dans la boucle des frames :
     
    frequence_vague=frame % 100;
    timevague=frame % 5;
     
    // Fréquence de la vague d'assaut. Fixée par la variable "frequence_vague".
    if(frequence_vague==0){
    	Vague_init()
    }
     
    // Vague de vaisseaux.
    Vague();

    Concernant la partie de code où je cache la vague derrière l'écran, à gauche ou à droite, il y a deux solutions pour éviter ça :

    Soit détruire un vaisseau de la vague dés qu'il déborde de l'écran, soit empêcher son affichage, sa méthode "objet.Draw()".

    La première solution est bourrine et alourdit le programme pour rien, tandis que la seconde est élégante, et rapide.

    Voilà, voilà...

  8. #8
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 098
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 098
    Points : 44 675
    Points
    44 675
    Par défaut
    Citation Envoyé par nouby
    RESOLU
    pas sûr d'avoir tout bien compris après lecture rapide , mais si c'est résolu c'est tant mieux !

  9. #9
    Membre régulier
    Homme Profil pro
    sans
    Inscrit en
    Mai 2023
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : sans

    Informations forums :
    Inscription : Mai 2023
    Messages : 145
    Points : 83
    Points
    83
    Par défaut
    Oui mon texte était pas trop clair, lol

    C'était en fait des objets, qu'on peut imaginer des vaisseaux, qui viennent d'un coté aléatoire de l'écran (sauf le bas), et volent en rase-motte vers le bas de l'écran parfois (hasard), afin d'attaquer le bas de l'écran, ensuite ils filent vers le haut ou vers l'autre coté de l'écran ou les deux.
    Chaque vaisseau avance d'un pas avant les autres, et non en même temps, donc on a l'impression d'une suite de vaisseaux qui défilent.
    J'ai mis les codes au cas où quelqu'un veut les utiliser.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Générer un événement d'après la position d'un objet
    Par topo.design dans le forum Général JavaScript
    Réponses: 8
    Dernier message: 20/02/2014, 10h53
  2. incrémenter le numéro de l'objet
    Par lesafir dans le forum Langage
    Réponses: 5
    Dernier message: 07/06/2007, 15h41
  3. changer la position d'un objet dans la scene
    Par Lsong dans le forum DirectX
    Réponses: 3
    Dernier message: 21/03/2007, 18h41
  4. [java3D] detecter la position d'un objet.
    Par apesle dans le forum 3D
    Réponses: 3
    Dernier message: 14/04/2006, 17h30
  5. [C#]Bouger la position d'un objet
    Par fantomchris dans le forum Windows Forms
    Réponses: 5
    Dernier message: 06/03/2006, 11h50

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo