[DB]Lier l'intensité d'une PointLight avec la HealthBar

Questions à propos du scripting. Hors Shader, GUI, Audio et Mobile.
Gérard le Graviton
Messages : 5
Inscription : 04 Juil 2021 18:35

[DB]Lier l'intensité d'une PointLight avec la HealthBar

Message par Gérard le Graviton » 04 Juil 2021 19:22

Bonjour, Bonsoir

Après plusieurs jours de recherche j'ai besoin de votre aide..

Dans un projet Unity 2D (ver2021.1.13 ) J'ai mis en place un système qui fait perdre automatiquement des points de vies au joueur lorsqu'il ne se trouve pas dans une "HealZone", le jeu est plongé dans l'obscurité mais une pointLight2D accroché au joueur permet d'éclairer les alentours.

J'aimerai que l'intensité de la PointLight diminue lorsque le joueur perd des points de vies et inversement.

Ma question est donc la suivante : Comment faire pour synchroniser l'intensité de la PointLight 2D avec le nombre de point de vie du joueur?

merci d'avance pour vos réponses ;>

( Ps je ne sais pas si sa change quelque chose mais l'Universal Render Pipeline est activé dans mon projet )

Avatar de l’utilisateur
Max
Messages : 8764
Inscription : 30 Juil 2011 13:57
Contact :

Re: [DB]Lier l'intensité d'une PointLight avec la HealthBar

Message par Max » 04 Juil 2021 20:52

Bonsoir,
Gérard le Graviton a écrit :
04 Juil 2021 19:22
Ma question est donc la suivante : Comment faire pour synchroniser l'intensité de la PointLight 2D avec le nombre de point de vie du joueur?
Je dirais que, en sachant quelle est la plage utile des points de vie de ton joueur (par exemple de 0 à 100), tu fais varier en proportion l'intensité de ta Light2D (de 0 à 1).
Après si c'est un soucis de mise en oeuvre de script, poste éventuellement ce que tu as pu déjà faire pour voir ce qui pourrait clocher ou être améliorer.
Image
Pas d'aide par MP, le forum est là pour ça.
En cas de doute sur les bonnes pratiques à adopter sur le forum, consulter la Charte et sa FAQ

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

Re: [DB]Lier l'intensité d'une PointLight avec la HealthBar

Message par jmhoubre » 04 Juil 2021 22:42

Bonsoir,

c'est une règle de trois...
Personnellement, je ferais comme ceci :
1) définir 4 variables privées (avec attribut [SerializedField] pour pouvoir les modifier dans l'inspecteur) :

Code : Tout sélectionner

float minHealth; //à priori zéro.
float maxHealth;
float minLight;
float maxLight;
2) tu as sans doute une variable donnant la santé actuelle :

Code : Tout sélectionner

private float currentHealth;
3) tu as l'intensité actuelle de la lumière dans le composant Light, que je met en cache dans la fonction Awake () :

Code : Tout sélectionner

private float lightIntensity;
private Lignt lightComponent;

private void Awake ()
{
	// A modifier selon le rattachement du script et de composant. Je suppose ici qu'ils sont sur le même GameObject.
	lightComponent = GetComponent<Light> ();
	
	// Au cas où... afin d'éviter une division par zéro.
	if (maxHealth == minHealth)
	{
		Debug.LogError ("La santé minimale et la santé maximale doivent avoir des valeurs différentes);
}
4) idéalement, il faudrait utiliser un delegate déclenché par une modification de la santé. Plus simplement, chaque fois que dans ton code tu modifies la santé, tu appelles la fonction UpdateLightIntensity () :

Code : Tout sélectionner

private void UpdateIntensity ()
{
	lightIntensity = minLight + ((currentHealth - minHealth) * (maxLight - minLight) / (maxHealth - minHealth));
	lightComponent.intensity = lightIntensity ;
}
Dans la documentation, je n'ai pas lu que l'intensité d'une Light2D soit comprise entre 0 et 1.

Avatar de l’utilisateur
Max
Messages : 8764
Inscription : 30 Juil 2011 13:57
Contact :

Re: [DB]Lier l'intensité d'une PointLight avec la HealthBar

Message par Max » 04 Juil 2021 23:44

jmhoubre a écrit :
04 Juil 2021 22:42
Dans la documentation, je n'ai pas lu que l'intensité d'une Light2D soit comprise entre 0 et 1.
heu non en effet. Les valeurs données avaient juste valeur d'exemple.
Image
Pas d'aide par MP, le forum est là pour ça.
En cas de doute sur les bonnes pratiques à adopter sur le forum, consulter la Charte et sa FAQ

Gérard le Graviton
Messages : 5
Inscription : 04 Juil 2021 18:35

Re: [DB]Lier l'intensité d'une PointLight avec la HealthBar

Message par Gérard le Graviton » 05 Juil 2021 00:23

jmhoubre a écrit :
04 Juil 2021 22:42
Bonsoir,

c'est une règle de trois...
Personnellement, je ferais comme ceci :
1) définir 4 variables privées (avec attribut [SerializedField] pour pouvoir les modifier dans l'inspecteur) :

Code : Tout sélectionner

float minHealth; //à priori zéro.
float maxHealth;
float minLight;
float maxLight;
2) tu as sans doute une variable donnant la santé actuelle :

Code : Tout sélectionner

private float currentHealth;
3) tu as l'intensité actuelle de la lumière dans le composant Light, que je met en cache dans la fonction Awake () :

Code : Tout sélectionner

private float lightIntensity;
private Lignt lightComponent;

private void Awake ()
{
	// A modifier selon le rattachement du script et de composant. Je suppose ici qu'ils sont sur le même GameObject.
	lightComponent = GetComponent<Light> ();
	
	// Au cas où... afin d'éviter une division par zéro.
	if (maxHealth == minHealth)
	{
		Debug.LogError ("La santé minimale et la santé maximale doivent avoir des valeurs différentes);
}
4) idéalement, il faudrait utiliser un delegate déclenché par une modification de la santé. Plus simplement, chaque fois que dans ton code tu modifies la santé, tu appelles la fonction UpdateLightIntensity () :

Code : Tout sélectionner

private void UpdateIntensity ()
{
	lightIntensity = minLight + ((currentHealth - minHealth) * (maxLight - minLight) / (maxHealth - minHealth));
	lightComponent.intensity = lightIntensity ;
}
Dans la documentation, je n'ai pas lu que l'intensité d'une Light2D soit comprise entre 0 et 1.

merci beaucoup pour ta réponse riche en information !

Malheureusement le système ne marche pas dans mon projet.. j'imagine que c'est dû à la programmation très bancale de mon système de damage zone et de Heal zone

je post le code que j'ai utilisé pour les dégâts de Heal et Damage zone, avec un peu ( beaucoup ) de chance verrez en un coup d'œil le grain de sable qui fait coincer la machine ;>

Code : Tout sélectionner

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

public class DarknessSystem : MonoBehaviour
{      
    //DARKNESS = HEALTH
    public float Darkness;
    public Slider DarknessSlider;

    public bool changeIntensity = true;

    //public Component lightComponent;

    public float maxIntensity = 1f;

    public float minDarkness = 0;
    public float maxDarkness = 300;

    public float minLight = 0;
    public float maxLight = 1;

    private float lightIntensity;

    private Light lightComponent;

   
 //script liaison lumière avec health
    // private void Awake (){
    //     lightComponent = GetComponent<Light> ();

    //     if (maxDarkness == minDarkness){
    //         Debug.LogError ("La santé minimale et la santé maximale doivent avoir des valeurs différentes");

    //     }
    // }
    // private void UpdateIntensity ()
    // {
	// lightIntensity = minLight + ((Darkness - minDarkness) * (maxLight - minLight) / (maxDarkness - minDarkness));
	// lightComponent.intensity = lightIntensity ;
    // }



// SCRIPT DANIMATION DE MORT DU PLAYER
    public void Die(){
        Debug.Log("Le joueur est éliminé");
        // bloquer les mouvements du personnages
        PlayerMouvement.instance.enabled=false;
        //jouer l'animation de mort
        PlayerMouvement.instance.animator.SetTrigger("Die");
        //empecher les interactions physiques avec les autre éléments de la scene
        PlayerMouvement.instance.rb.bodyType = RigidbodyType2D.Kinematic;
        PlayerMouvement.instance.playerCollider.enabled = false;
    }    

   

    // Start is called before the first frame update
    void Start()
    {                 
    }

    // Update is called once per frame
    void Update()
    {
       
        //SCRIPT BAISSE AUTOMATIQUE POINT DE VIE  

        DarknessSlider.value = Darkness;

        Darkness -= 0.05f;

        

        if(Darkness <0){
            Darkness = 0;
        }
        if(Darkness <= 0){
            Die();
            return;
        }
    
    }

    //script permettant de regagner des points de vie sur heal zone

    void OnTriggerEnter2D(Collider2D other){
        Debug.Log ("Object Entered The Trigger");

        if (other.gameObject.tag == "Player"){
            
            DarknessSlider.value = Darkness;

            Darkness += 30f * Time.deltaTime;

            if(Darkness >300){
            Darkness = 300;
            }
        
        }
        
    }  
   

    void OnTriggerStay2D(Collider2D other){
        Debug.Log ("Object Stay in The Trigger");

        if (other.gameObject.tag == "Player"){
            DarknessSlider.value = Darkness;

            Darkness += 30f * Time.deltaTime;

            if(Darkness >300){
            Darkness = 300;
            }
        }
        
    }

    void OnTriggerExit2D(Collider2D other){
        Debug.Log ("Object Exit The Trigger");

        if (other.gameObject.tag == "Player"){
            DarknessSlider.value = Darkness;

            Darkness += 30f * Time.deltaTime;
        }
    }
}

Avatar de l’utilisateur
Max
Messages : 8764
Inscription : 30 Juil 2011 13:57
Contact :

Re: [DB]Lier l'intensité d'une PointLight avec la HealthBar

Message par Max » 05 Juil 2021 07:49

Bonjour,

au vu de ta description initiale, tu sembles passer les Light2D. Donc c'est sur ce type de composant qu'il faut travailler.
Dans ton code, cela peut se traduire par:
(doc de l'API Light2D)

Code : Tout sélectionner

using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;

...

    private Light2D lightComponent;
...

    // Start is called before the first frame update
    void Awake()
    {
        lightComponent = GetComponent<Light2D>();
        lightComponent.intensity = 0.2f;            // exemple de changement de valeur de l'intensité
    }
PS: pour savoir comment intégrer tes scripts dans tes messages, je t'invite à consulter la Charte et sa FAQ (lien dans ma signature).
,
Image
Pas d'aide par MP, le forum est là pour ça.
En cas de doute sur les bonnes pratiques à adopter sur le forum, consulter la Charte et sa FAQ

Gérard le Graviton
Messages : 5
Inscription : 04 Juil 2021 18:35

Re: [DB]Lier l'intensité d'une PointLight avec la HealthBar

Message par Gérard le Graviton » 05 Juil 2021 22:52

Max a écrit :
05 Juil 2021 07:49
Bonjour,

au vu de ta description initiale, tu sembles passer les Light2D. Donc c'est sur ce type de composant qu'il faut travailler.
Dans ton code, cela peut se traduire par:
(doc de l'API Light2D)

Code : Tout sélectionner

using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;

...

    private Light2D lightComponent;
...

    // Start is called before the first frame update
    void Awake()
    {
        lightComponent = GetComponent<Light2D>();
        lightComponent.intensity = 0.2f;            // exemple de changement de valeur de l'intensité
    }
PS: pour savoir comment intégrer tes scripts dans tes messages, je t'invite à consulter la Charte et sa FAQ (lien dans ma signature).
,
rebonsoir ;>

Oui effectivement merci de me prévenir je n'étais pas au courant de l'existence de cette "formule" ( UnityEngine.Experimental.Rendering.Universal )

Malheureusement même après avoir inclut ceci à mon code ainsi que d'autres petites "mises à jour" l'intensité de la Light2D ne bouge toujours pas d'un pouce..

c'est comme si le Component Light2D ne reconnaissait pas le script qu'il lui est dédié, même lorsque je change directement des données comme "l'intensity" dans le script les valeurs ne se mettent pas à jour dans l'inspector de la Light2D~
( je sauvegarde bien mon script à chaque manip donc sa ne vient pas de la ;>)

je tiens à préciser au cas ou c'est important que le script de la Light2D se trouve directement dans le GameObject de la light2D qui elle même se trouve être l'enfant du Parent "Player" qui contrôle les points de vie du joueur ainsi que ses déplacements

je ne sais vraiment pas quoi faire pour régler ce problème le script me semble correct désormais :*<

j'inclus ci dessous le script ainsi que quelques screens :

Code : Tout sélectionner

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Experimental.Rendering.Universal;

[System.Serializable]
public class healthLight : MonoBehaviour
{

    UnityEngine.Experimental.Rendering.Universal.Light2D LightComponent;

    //DARKNESS = HEALTH
    public float minDarkness = 0;
    public float maxDarkness = 300;

    public float Darkness = 300;

    public float minLight = 0;
    public float maxLight = 1;

    public float lightIntensity;

    public Light2D lightComponent;

    public Slider DarknessSlider;

    // public void minLight(){
    //     lightComponent.intensity= 0;
    // }
    // public void maxLight(){
    //     lightComponent.intensity= 1;
    // }

    private void Awake (){
        
        lightComponent = GetComponent<UnityEngine.Experimental.Rendering.Universal.Light2D>();
        lightComponent = GetComponent<Light2D> ();
        lightComponent.intensity = 1;

        if (maxDarkness == minDarkness){
            Debug.LogError ("La santé minimale et la santé maximale doivent avoir des valeurs différentes");

        }
    }
    public void UpdateIntensity ()
    {
	lightIntensity = minLight + ((Darkness - minDarkness) * (maxLight - minLight) / (maxDarkness - minDarkness));
	lightComponent.intensity = lightIntensity ;
    }

    


    // Start is called before the first frame update
    void Start()
    {

        lightComponent = GetComponent<UnityEngine.Experimental.Rendering.Universal.Light2D>();      
        
       
        
    }

    // Update is called once per frame
    void Update()
    {
        lightComponent = GetComponent<UnityEngine.Experimental.Rendering.Universal.Light2D>();

        DarknessSlider.value = Darkness;

        Darkness -= 0.05f;


    }
}    
screenshots:

Image
Image
Image
Image

EmileF
Messages : 673
Inscription : 18 Mars 2017 19:39

Re: [DB]Lier l'intensité d'une PointLight avec la HealthBar

Message par EmileF » 06 Juil 2021 10:41

Salut,

Dans ton code, j'ai remarqué 2 ou 3 petites choses qui m'étonnent.

Dans Awake():

lightComponent = GetComponent<UnityEngine.Experimental.Rendering.Universal.Light2D>();
lightComponent = GetComponent<Light2D> ();
tu instancies 2 fois lightComponent. A mon avis une fois est suffisant et entre nous je préfère la deuxième. J'ai remarqué que tu instancies de nouveau lightComponent dans le Start et l'Update. C'est inutile. Une fois suffit.

La deuxième chose que j'ai remarqué c'est que nulle part tu appelles ta fonction UpdateIntensity ().
Je pense que ce serait bien d'ajouter cet appel dans l'Update après la modification du Darkness.

Au plaisir
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.

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

Re: [DB]Lier l'intensité d'une PointLight avec la HealthBar

Message par jmhoubre » 06 Juil 2021 14:12

Bonjour,
j'ai fini par faire un projet. Il fonctionne bien :
Vidéo

J'ajoute un toto de Brackeys sur les lumières 2D. Il n'est pas tout à fait à jour, mais je le trouve clair et bien réalisé.
Dernière édition par jmhoubre le 06 Juil 2021 17:32, édité 1 fois.

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

Re: [DB]Lier l'intensité d'une PointLight avec la HealthBar

Message par jmhoubre » 06 Juil 2021 17:31

Le player :
- capsule collider2D + rigidbody2D pour détecter les collisions
- Light2D (nécessite le package Universel Render Pipeline)
- script PlayerMovement (renseigner la vitesse)

Code : Tout sélectionner

using UnityEngine;

/// <summary>
/// Gestion du mouvement de joueur.
/// </summary>
public class PlayerMovement : MonoBehaviour
{
    [SerializeField] private float speed = 5f;

    private void Update ()
    {
        HandleMovement ();
    }

    private void HandleMovement ()
    {
        float xInput = Input.GetAxis ("Horizontal");

        Vector2 position = transform.position;

        position.x += speed * Time.deltaTime * xInput;
        // Limite le mouvement à l'écran (pour ma résolution).
        position.x = Mathf.Clamp (position.x, -8f, 8f);

        transform.position = position;
    }
}

- script PlayerHealth (renseigner santé mini et maxi)

Code : Tout sélectionner

using UnityEngine;

/// <summary>
/// Gestion de la santé et des soins/dégâts.
/// </summary>
public class PlayerHealth : MonoBehaviour
{
    [SerializeField] private float minHealth = 0f;
    [SerializeField] private float maxHealth = 100f;

    private float currentHealth;
    private PlayerLight playerLight;

    private void Awake ()
    {
        playerLight = GetComponent<PlayerLight> ();      
    }

    private void Start ()
    {
        // On divise par 2 pour tester plus vite.
        currentHealth = maxHealth / 2;

        // Pour éviter une éventuelle division par zéro.
        if (minHealth == maxHealth)
        {
            minHealth = 0f;
            maxHealth = 50f;
        }

        // On ne modifie l'intensité que dans le Start pour être sûr que PlayerLight a fini son Awake.
        playerLight.LightUpdate ();
    }

    public void TakeZoneEffect (float amount)
    {
        currentHealth += amount;

        // Limite la santé à minHealth et maxHealth.
        currentHealth = Mathf.Clamp (currentHealth, minHealth, maxHealth);

        playerLight.LightUpdate ();

        if (currentHealth <= minHealth)
        {
            Die ();
        }
    }

    public float GetCurrentHealth => currentHealth;
    public float GetMinHealth => minHealth;
    public float GetMaxHealth => maxHealth;


    private void Die ()
    {
        //Destroy (gameObject);
    }
}
- script PlayerLight (renseigner light mini et maxi)

Code : Tout sélectionner

using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;

/// <summary>
/// Gestion de la lumière du joueur.
/// </summary>
public class PlayerLight : MonoBehaviour
{
    [Header ("Lumière")]
    [SerializeField] private float minLight;
    [SerializeField] private float maxLight;

    private PlayerHealth playerHealth;
    private Light2D playerLight;

    private void Awake ()
    {
        playerHealth = gameObject.GetComponent<PlayerHealth> ();
        playerLight = GetComponent<Light2D> ();
    }

    public void LightUpdate ()
    {
        // Application d'une règle de 3 pour calculer une intensité en rapport avec la santé.
        playerLight.intensity = minLight + (
            (playerHealth.GetCurrentHealth - playerHealth.GetMinHealth) * (maxLight - minLight)
            / (playerHealth.GetMaxHealth - playerHealth.GetMinHealth));

        // On peut également renforcer l'effet en jouant sur le rayon extérieur de la lumière
        // (en gros, le range des lumières 3D).
        playerLight.pointLightOuterRadius = playerLight.intensity;
    }
}
Les zones d'effet sont des sprites square avec BoxCollider2D en mode trigger, et le script ZoneEffect (renseigner le nombre de PV par seconde, négatif pour des dégâts, positif pour des soins). Le matérial a pour shader URP/Lit surface type à transparent. Pour accélérer l'effet, on peut soit augmenter EffectPerSecond, soir ajouter une vitesse dans le script (dans la ligne du deltaTime).

Code : Tout sélectionner

using UnityEngine;

/// <summary>
/// Gestion des effets de zone.
/// </summary>
public class ZoneEffect : MonoBehaviour
{
    [Tooltip("Nombre négatif pour des dégâts, positif pour des soins")]
    [SerializeField] private float effectPerSecond = 0f;

    private void OnTriggerStay2D (Collider2D _collider)
    {
        // Ne pas oublier de passer les collider en mode Trigger.
        // Ne pas oublier de passer le SleepingMode du rigidbody2D du player à NeverSleep.
        if (_collider.CompareTag ("Player"))
        {
            _collider.gameObject.GetComponent<PlayerHealth> ().TakeZoneEffect (effectPerSecond * Time.deltaTime);
        }
    }
}
Il y a une UI, avec deux textes (TextMeshPro) et un script pour mettre à jour l'affichage.

Code : Tout sélectionner

using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
using TMPro;

/// <summary>
/// Affiche l'intensité et la santé. Utilise TextMeshPro.
/// </summary>
public class DebugUI : MonoBehaviour
{
    [SerializeField] private TMP_Text lightIntensityText;
    [SerializeField] private TMP_Text playerHealthText;

    private PlayerHealth playerHealth;
    private Light2D playerLight;

    private void Awake ()
    {
        GameObject player = GameObject.Find ("Player");
        playerHealth = player.GetComponent<PlayerHealth> ();
       
        playerLight = player.GetComponent<Light2D> ();        
    }

    // L'utilisation d'un delegate Action serait plus pertinent.
    private void Update ()
    {
        lightIntensityText.text = $"Intensité : {playerLight.intensity:0.00}";
        playerHealthText.text = $"Santé : {playerHealth.GetCurrentHealth:0.00}";
    }
}
Ne pas oublier une image de fond, de préférence plutôt sombre.

Pour l'ambiance, j'ai ajouté une global light de couleur rouge, elle est superflue.

Répondre

Revenir vers « Scripting »