[vraiment résolu]Déplacer le joueur avec un script puis lui rendre le contrôle des mouvements

Questions à propos du scripting. Hors Shader, GUI, Audio et Mobile.
Avatar de l’utilisateur
poupoule_h5n1
Messages : 48
Inscription : 28 Nov 2022 20:24

Re: [résolu mais entraine d'autre problémes ^^']Déplacer le joueur avec un script puis lui rendre le contrôle des mouvem

Message par poupoule_h5n1 » 16 Déc 2022 23:17

jmhoubre a écrit :
13 Déc 2022 13:01

Le mouvement du joueur utilise la physique, et celui géré par le script non : c'est étrange, et mérite réflexion.
Bien le bonjour,
J'ai essayer d'appliquer tes généreux conseils, j'ai été jusqu’à utiliser une autre méthode pour déplacer mon joueur et mes pnj en utilisant la physique. J'ai séparer mon script principale en plusieurs morceaux, en plusieurs classes ext ...
j'ai encore du mal avec les majuscule et les minuscule mais ça va venir ^^'

Un petit problème survient malgré tout.

Mes personnages n'atteignent plus leurs waypoints avec exactitude.
voici mon scripte de déplacement pour illustrer mes propos :

Code : Tout sélectionner

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PnjMove : MonoBehaviour
{
    public float speed = 500f;
    public bool move = false;
    public Transform[] waypoints;
    public int destPoint = 0;
    public Rigidbody2D rb;

    float horizontal;
    float vertical;
    public Transform Destination;
    private Vector3 velocity = Vector3.zero;

    public Animator animator;
    Vector2 lookDirection = new Vector2(1, 0);
    public AudioSource BruitDePas;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        animator = GetComponent<Animator>();
        Destination = waypoints[0];
    }

    void Update()
    {
        if (move)
        {
            Vector3 Position = rb.position;
            Vector2 Trajectoir = Destination.position - Position;
            Debug.Log(Vector3.Distance(Position, Destination.position));
            if (Vector3.Distance(Position, Destination.position) < 1.2f)
            {
                destPoint = (destPoint + 1) % waypoints.Length;
                Destination = waypoints[destPoint];
            }
            horizontal = Trajectoir.normalized.x;
            vertical = Trajectoir.normalized.y;
        }
        else
        {
            horizontal = 0;
            vertical = 0;
        }
    }

    void FixedUpdate()
    {
        float moveHorizontal = horizontal * speed * Time.deltaTime;
        float moveVertical = vertical * speed * Time.deltaTime;

        Mouvement(moveHorizontal, moveVertical);
        AnimationEtSons();
    }

    void Mouvement(float moveHorizontal, float moveVertical)
    {
        Vector3 targetVelocity = new Vector3(moveHorizontal, moveVertical, 0);
        rb.velocity = Vector3.SmoothDamp(rb.velocity, targetVelocity, ref targetVelocity, .05f);
    }

    void AnimationEtSons()
    {
        Vector2 regard = new Vector2(horizontal, vertical);

        if (!Mathf.Approximately(regard.x, 0.0f) || !Mathf.Approximately(regard.y, 0.0f))
        {
            lookDirection.Set(regard.x, regard.y);
            lookDirection.Normalize();
        }
        animator.SetFloat("Look X", lookDirection.x);
        animator.SetFloat("Look Y", lookDirection.y);
        animator.SetFloat("Speed", regard.magnitude);

        if (regard.magnitude > 0.1f)
        {
            if (!BruitDePas.isPlaying)
            {
                BruitDePas.Play();
            }
        }
        else
        {
            BruitDePas.Stop();
        }
    }
et un screen :
Image

Lorsque mon perso (ou un pnj) est lancé sur sa trajectoire, il atteint le 1er waypoint et reste dessus sans passés au suivant.
Le débug.Log m'apprend que, à ce moment la, la distance entre le rigidbody2d et le waypoint est de 2.222...
Je ne sais pas si c'est une marge normalement acceptable mais pour moi ça pose problème car
  • mon script attends normalement d’atteindre la distance de 1.2f entre le waypoint et le rigidbody pour passer au waypoint suivant et c'est la distance max que je puisse faire car
  • je travail en pixel art, mes dessins sont petit, 2.2f c'est beaucoup à mon échelle, c'est presque la largeur d'un perso.
    (dans sa vidéo, TUTO UNITY FR préconise même de mettre cette distance a 0.3f)
Dois-je encore changer de méthode pour faire bouger mes perso ?
Peut être que j'utilise mal celle-ci ?

Si vous avez besoin de plus d'information ou de précision, je reste a disposition pour vous répondre au plus vite.
Merci pour votre patience avec la débutante que je suis ^^'

Avatar de l’utilisateur
jmhoubre
Messages : 859
Inscription : 05 Oct 2019 22:05

Re: [résolu mais entraine d'autre problémes ^^']Déplacer le joueur avec un script puis lui rendre le contrôle des mouvem

Message par jmhoubre » 17 Déc 2022 00:49

Bonsoir,

je ne suis pas un pro de la physique, donc je ne suis pas certain de ce que j'avance :
  • 1.2f, ça me parait bien comment distance d'atteinte de but. Je met assez souvent 1, voir 0.25.
  • A mon avis, l'erreur est due à la vitesse du PNJ : 500f, c'est 500 m/s en IRL, soit 1800 km/h. A moins que ton PNJ soit un Rafale ou un F35, c'est trop élevé.
  • Ce qui se passe probablement avec cette vitesse, speed * Time.deltaTime vaut 5 (pour un FPS de 100). Ton PNJ avance donc de 5 en 5. S'il est à 2.5 m avant le WP, la frame d'après il dépassera de 2.5. Il fera donc demi-tour, dépassera de nouveau de 2.5m. Toi tu as une valeur de 2.22, mais cette valeur dépend du time.deltatime de ton jeu.
  • Comme tu affiches la distance, elle est toujours positive. Tu pourrais afficher aussi la vélocité du rigidbody du pnj, pour voir si elle change de sens.
  • Si tu confirmes la vitesse de 500m/s, il faut changer la méthode de détection du passage du WP, et comparer la vélocité à la frame d'avant (donc la stocker) et la nouvelle vélocité : un changement de sens indique que le PNJ a dépassé le WP.
Autre chose, tu te compliques l'existence à utiliser horizontal et vertical au lieu de ton Vector2 Trajectoir (qui est plus une direction, mais bon ...)
Dans l'update, tu normalises Trajectoir, dans le else tu écris Trajectoir = Vector2.zero.
Dans FixedUpdate, tu supprimes les 2 1ères lignes, et tu écris Mouvement(speed * Time.deltaTime * Trajectoir).
Tu modifies ta méthode :

Code : Tout sélectionner

private void Mouvement(Vector2 direction)
{
	Vector3 targetVelocity = new Vector3(direction.x, direction.y, 0);
	rb.velocity = Vector3.SmoothDamp(rb.velocity, targetVelocity, ref targetVelocity, .05f);
}

Avatar de l’utilisateur
jmhoubre
Messages : 859
Inscription : 05 Oct 2019 22:05

Re: [résolu mais entraine d'autre problémes ^^']Déplacer le joueur avec un script puis lui rendre le contrôle des mouvem

Message par jmhoubre » 17 Déc 2022 00:55

Arf, dans le screen, le Lord qui est le PNJ apparemment a une vitesse de 50. Donc une vitesse IRL de 180 km/h quand même. Et avance de 0.5 en 0.5 par frame pour un FPS de 100 fps. Je pense que le raisonnement est toujours valable, et que l'affichage de la vélocité pourra le confirmer ou l'infirmer.

Avatar de l’utilisateur
poupoule_h5n1
Messages : 48
Inscription : 28 Nov 2022 20:24

Re: [résolu mais entraine d'autre problémes ^^']Déplacer le joueur avec un script puis lui rendre le contrôle des mouvem

Message par poupoule_h5n1 » 17 Déc 2022 11:42

Bien le bonjour, j'avoue que je ne sais pas quoi penser.
Comme tu l'a proposer j'ai mis un Debug.Log("rb.velocity = " + rb.velocity);

voici le résultat :
Image


La vélocity ne dépasse pas 0.2. Elle va même dans les 0.02.

Par contre quant je met Debug.Log ("targetVelocity = " + targetVelocity);
voici le résultat :

Image

en effet target.Velocity est asses élevée mais je ne pense pas qu'il faut le prendre en compte car il est modérer dans le calcule pour mouvoir le rigidbody.

Code : Tout sélectionner

rb.velocity = Vector3.SmoothDamp(rb.velocity, targetVelocity, ref targetVelocity, .05f);
Pour la vitesse je conçois que c'est très élever mais si je met une vitesse normal (5f par exemple) le perso avance si lentement qu'on dirais qu'il ne bouge pas. Ce n'étais pas le cas quant j'utilisais un déplacement sans le système physique.

Comparer la vélocité a la frame d'avant peut être une bonne idée, je ne vois pas exactement comment mais je vais faire des tests.

Avatar de l’utilisateur
poupoule_h5n1
Messages : 48
Inscription : 28 Nov 2022 20:24

Re: [résolu mais entraine d'autre problémes ^^']Déplacer le joueur avec un script puis lui rendre le contrôle des mouvem

Message par poupoule_h5n1 » 17 Déc 2022 15:24

j'ai tester et j'ai pas encore trouver comment je pourrais faire.

j'ai essayé de créer deux vector3(tempo01 et tempo02), de leurs donner la vélocity du rigidbody à 1 frame d’intervalle, de les normalized, puis de tester l'angle entre les deux. Normalement, si l'angle était supérieur à 170 c'est que le pnj a fait demi tours, il devait donc normalement avoir ateint son waypoint et il était normalement temps de le changer. mais ... ça marche pas. :gene:

le perso change bien de direction mais a chaque changement de waypoint, l'angle est supérieur a 170, il change donc de waypoint a chaque frame en partant dans une sorte de vibration chelou.

voici le bout de code que j'ai tenté :

Code : Tout sélectionner

    private Vector3 tempo01 = new Vector3(0, 0, 0);
    private Vector3 tempo02 = new Vector3(0, 0, 0);

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        animator = GetComponent<Animator>();
        Destination = waypoints[0];
        tempo02.Set(tempo01.x, tempo01.y, tempo01.z);
    }

    void Update()
    {
        if (move)
        {
            Vector3 Position = rb.position;
            Vector2 Trajectoir = Destination.position - Position;

            float angle = Vector2.SignedAngle(tempo01.normalized, tempo02.normalized);
            if (angle < 170)
            {
                Debug.Log("on change de WP");
                destPoint = (destPoint + 1) % waypoints.Length;
                Destination = waypoints[destPoint];
            }

            tempo02.Set(tempo01.x, tempo01.y, tempo01.z);

            //if (Vector3.Distance(Position, Destination.position) < 1.2f)
            //{
            //    destPoint = (destPoint + 1) % waypoints.Length;
            //    Destination = waypoints[destPoint];
            //}
            horizontal = Trajectoir.normalized.x;
            vertical = Trajectoir.normalized.y;
        }
        else
        {
            horizontal = 0;
            vertical = 0;
        }
    }
Si quelqu'un a une meilleur idée je suis preneuse :gene:

Avatar de l’utilisateur
jmhoubre
Messages : 859
Inscription : 05 Oct 2019 22:05

Re: [résolu mais entraine d'autre problémes ^^']Déplacer le joueur avec un script puis lui rendre le contrôle des mouvem

Message par jmhoubre » 17 Déc 2022 17:20

Je ne sais pas si elle est meilleure, mais j'utiliserais le produit scalaire des vecteurs normalisés :

Code : Tout sélectionner

Vector3.Dot (oldVelocity.normalized, newVelocity.normalized );
Le produit scalaire renvoie 1 si les vecteurs ont la même direction, -1 s'ils ont des directions opposées, et 0 s'ils sont perpendiculaires. Tu devrais tester si le produit scalaire est plus petit que disons -0.8f avec une méthode comme :

Code : Tout sélectionner

private bool AreOppositeVectors(Vector2 v1, Vector2 v2)
{
	return Vector2.Dot (v1.normalized, v2.normalized) <= -0.8f;
}

Avatar de l’utilisateur
poupoule_h5n1
Messages : 48
Inscription : 28 Nov 2022 20:24

Re: [résolu mais entraine d'autre problémes ^^']Déplacer le joueur avec un script puis lui rendre le contrôle des mouvem

Message par poupoule_h5n1 » 17 Déc 2022 19:39

ça fonctionne ^^
Bien sur c'est pas parfait, il y a un une frame ou le pnj se retourne sur chaque waypoint, on le vois pas vraiment, mais on voit comme un bug visuel ou un tremblement du sprite.

Mais pour l'instant ça ira très bien
merci pour l'aide ça me fait très plaisir :-D

Avatar de l’utilisateur
jmhoubre
Messages : 859
Inscription : 05 Oct 2019 22:05

Re: [vraiment résolu]Déplacer le joueur avec un script puis lui rendre le contrôle des mouvements

Message par jmhoubre » 18 Déc 2022 00:09

En y réfléchissant, je me demande si tu ne devrais pas laisser tomber la physique pour déplacer ton PNJ. Des forces externes s'exercent-elles sur ce dernier ?
Pourquoi avoir choisi la physique ?

Avatar de l’utilisateur
poupoule_h5n1
Messages : 48
Inscription : 28 Nov 2022 20:24

Re: [vraiment résolu]Déplacer le joueur avec un script puis lui rendre le contrôle des mouvements

Message par poupoule_h5n1 » 18 Déc 2022 02:52

pour pouvoir interagir avec les éléments extérieurs comme les items où les énemis. Par exemple, avec certain waypoint qui possèdent une box collider qui leurs permettent de faire une pause dans leurs parcours, ou déclanchés des trigger de sénariots.
J'ai également ambitions de faire en sorte que les pnj puissent suivre le personnage principale, avoir des dégâts et une barre de vie, comme un allier.

Avatar de l’utilisateur
jmhoubre
Messages : 859
Inscription : 05 Oct 2019 22:05

Re: [vraiment résolu]Déplacer le joueur avec un script puis lui rendre le contrôle des mouvements

Message par jmhoubre » 18 Déc 2022 10:27

Mmmh.
Tu peux avoir tout cela sans utiliser le rigidbody pour déplacer le PNJ. L'intérêt de la physique, c'est d'appliquer des forces sur le PNJ, par exemple :
- une explosion ;
- un vent violent ;
- un projectile qui percute le PNJ.
Je ne dis pas qu'il faut enlever le rigidbody du PNJ, mais peut-être modifier son type.

Pour faire ce que tu demandes :
- interagir avec les éléments extérieurs comme les items ou les ennemis : il suffit que les items et les ennemis aient un collider en mode trigger, avec un script adapté à tes souhaits.

- avec certains waypoints qui possèdent une box collider qui permet de faire une pause dans le parcours du PNJ : le collider doit être en mode trigger et le script du WP doit tester si la collision est due à un PNJ ou à Poupoule, puis appeler une méthode publique du PNJ/joueur pour effectuer une pause.

- déclencher des trigger de scénarios : un empty + collider en mode trigger + script.

- suivre le personnage principal : si tu as déjà mis en place un navigation mesh, c'est assez facile. Le PNJ met sa destination sur poupoule, puis régulièrement (2 ou 3 fois par seconde), teste la distance PNJ-Poupoule et règle sa vitesse en fonction. Tu peux même ajouter au PNJ un champ distanceToPlayer, et le régler sur une certaine distance :

Code : Tout sélectionner

using UnityEngine;

public class FollowPlayer : MonoBehaviour
{
	private enum DistanceToPlayer { NEAR = 5, MEDIUM = 10, FAR = 20 }

	[SerializeField] private DistanceToPlayer distanceToPlayer = DistanceToPlayer.MEDIUM;
	[SerializeField] private bool MustFloowPlayer = false;

	private float minDistance;
	private float maxDistance;
	private Vector3 playerPosition;

	private void Awake ()
	{
		// Lance la méthode de vérification de distance toutes les 1/2 secondes.
		InvokeRepeating (nameof (Move), 0f, 0.5f);

		// On souhaite que le PNJ soit à +/- 10% de la distance programmée.
		minDistance = 0.9f * (float) distanceToPlayer;
		maxDistance = 1.1f * (float) distanceToPlayer;
	}

	private void Move ()
	{
		if (MustFloowPlayer == false) { return; }

		// Contrôle la distance.
		playerPosition = PoupouleControle.Instance.transform.position;
		float currentDistance = (playerPosition - transform.position).magnitude;

		// Si le PNJ est dans l'intervalle de distance, on ne fait rien.
		if (currentDistance > minDistance && currentDistance < maxDistance) { return; }

		// Modifie la vitesse.
		float modifier = 1f;
		if (currentDistance <= minDistance)
		{
			modifier = minDistance / currentDistance;
		}
		else if (currentDistance>=maxDistance)
		{
			modifier = currentDistance / maxDistance;
		}
		PoupouleControle.Instance.PlayerSpeed *= modifier;
	}
}
Remarque : pour accéder à la vitesse, j'ai ajouté dans PoupouleControle :

Code : Tout sélectionner

public float PlayerSpeed { get=>speed; set { speed = value; }}
- recevoir des dégâts et une barre de vie, comme un allié : c'est facile si tu as regrouper les fonctions liées à la santé dans un composant Health, qu'il suffit d'ajoueter au PNJ.

Répondre

Revenir vers « Scripting »