Les jeux de plateforme

Accrocher le joueur aux plateformes animées

Le problème

Par défaut, en utilisant le FPSController fourni par Unity, lorsque l'on saute sur une plateforme animée, le FPSController ne suit pas le mouvement de celle-ci ; il reste sur place alors que la plateforme subit des déplacements horizontaux (lors de déplacements verticaux, le problème ne se pose pas).

La solution

L'idée est qu'il faut "parenter" le FPSController temporairement à la plateforme. Il subira alors toutes les transformations de celle-ci (position, rotation, et même scale).

Un problème vient se poser alors : les seules informations dont nous avons besoin sont la position et la rotation. Nous allons devoir compenser le scale...

 

Plutôt que de longues explications, voici le script que nous allons pouvoir utiliser, accompagné de commentaires permettant de comprendre les calculs effectués :

  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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/* PlatefromGrip v1.2
 * 
 * * EDIT : 28/01/2017
 * - Prise en charge du cas de contact simultané avec plusieurs plateformes
 * 
 * 
 * Lors de l'utilisation du FPSController dans un jeu de plateforme, nous sommes évidemment tenté de le faire sauter
 * sur des plateformes animées.
 * Sans ce script, le FPSController ne suit pas le mouvement de la plateforme et tombe pendant son animation (notamment
 * lors de translations horizontales).
 * 
 * Ce script part de l'idée de parenter temporairement le FPSController à la plateforme touchée, lui transmettant donc
 * toute transformation (position, rotation & scale).
 * L'intérêt étant d'utiliser la position de la plateforme, il nous faudra donc en revanche compenser le scale et la rotation.
 * 
 * Le FPSController est ensuite dé-parenté lorsqu'il quitte le contacte de la plateforme.
 * Mais une compensation de rotation doit êre maintenue ; il ne faut donc pas enlever ce script du personnage.
 * 
 * Utilisation :
 * - Ce script doit se placer sur le Player (ou FPSController)
 * - Le gameObject Player doit être équipé d'un Trigger
 * - Ne pas oublié de donner le nom du tag utilisé pour les plateformes
 * - Les plateformes n'ont besoin que d'un simple collider
 * 
 * 
 * sylvain@oldtree.studio - 31/08/16
 */

using UnityEngine;


public class PlatefromGrip : MonoBehaviour
{
        #region UNITY PART

        //Variables à renseigner via l'inspecteur
        public string       plateformTag = "Plateform";                  //Nom de tag utilisé ; initialisée à "Plateform"


        //Initialisation
        void Start ()
        {
                InitCompensation ();
        }

        //Se passe APRES l'habituel Update de Unity
        void LateUpdate ()
        {
                MaintainRotationOffset ();
        }

        //Lorsqu'un objet entre en contact avec le déclencheur
        void OnTriggerEnter ( Collider col )
        {
                //Si c'est une plateforme
                if ( col.tag == plateformTag )
                        OnPlateformEnter ( col.transform );
        }

        //Lorsque l'objet quitte le contact d'un déclencheur
        void OnTriggerExit ( Collider col )
        {
                //Si c'est une plateforme
                if ( col.tag == plateformTag )
                        OnPlateformExit ( col.transform );
        }

        #endregion


        #region INTERNAL LOGIC

        //Variables servant à la prise en charge du contact simultané avec d'autres plateformes
        private bool        _isOnPlatform;                                                        //Savoir si le Player est actuellement en contact ou non avec une plateforme
        private Transform   _actualPlateformTr;                                                  //Référence au Transform de la plateforme actuellement utilisée pour la compensation
        private bool        _forcedExit;                                                  //Savoir si l'on a été obligé de de forcer la sortie d'une plateforme
                                                                                                //(le cas lorsque le Player entre en contact avec une plateforme alors qu'il est déjà sur une autre)

        //Variable nécessaires aux calculs du script
        private Vector3     _rotationOffset;                                                     //Contiendra la compensation de rotation à maintenir de façon permanente
        private Vector3     _originalScale;                                                      //Sauvegarder le scale original du gameobject
        private Vector3     _parentScale;                                                        //Contiendra les données de scale de la plateforme


        /// <summary>
        /// Initialisation des variables de compensation
        /// </summary>
        private void InitCompensation ()
        {
                _originalScale = this.transform.localScale;                                      //Enregistrement du scale originale du FPSController
                _rotationOffset = Vector3.zero;                                                 //Mise à zéro de la compensation de rotation
        }

        /// <summary>
        /// Maintien de l'offset (à executer en continu sur le Player)
        /// </summary>
        private void MaintainRotationOffset ()
        {
                this.transform.localEulerAngles += _rotationOffset;                              //Maintien permanent de la compensation en rotation
        }

        /// <summary>
        /// Lorsque le Player entre en contact avec une plateforme
        /// </summary>
        /// <param name="plateformTr">Transform de la plateforme contactée</param>
        private void OnPlateformEnter ( Transform plateformTr )
        {
                if ( _isOnPlatform )                                                             //Si le Player est actuellement sur une plateforme
                {
                        UnparentFromPlatform ( _actualPlateformTr );                            //Forcer le "dé-parentage" de clui-ci
                        _forcedExit = true;                                                      //L'indiquer au script
                }

                //Puis, dans tous les cas...
                _actualPlateformTr = plateformTr;                                               //Indiquer au script le transform que l'on va utiliser pour les calculs de compensation
                ParentToPlateform ( _actualPlateformTr );                                       //Initier le "parentage" du Player à cette plateforme
        }

        /// <summary>
        /// Lorsque le Player perd le contact avec une plateforme
        /// </summary>
        /// <param name="plateformTr">Transform de la plateforme quittée</param>
        private void OnPlateformExit ( Transform plateformTr )
        {
                if ( !_forcedExit )                                                              //Si aucun "dé-parentage" n'a été au préalable forcé
                        UnparentFromPlatform ( plateformTr );                                   //Dé-parenter normalement le Player

                else if ( plateformTr != _actualPlateformTr )                                     //Sinon, si la plateforme actuellement quittée est différente de la plateforme sur laquelle le joueur est
                        _forcedExit = false;                                                     //Ne rien faire, si ce n'est réinitialiser la variable d'état comme quoi le joueur
                                                                                                //n'a pas été forcé de dé-parenter une plateforme
        }

        /// <summary>
        /// Parenter le Player à la plateforme
        /// </summary>
        /// <param name="plateformTr">Tranform de la plateforme à laquelle se parenter</param>
        private void ParentToPlateform ( Transform plateformTr )
        {
                //Indiquer que le Player est actuellement en contact avec une plateforme
                _isOnPlatform = true;

                //Avant de parenter le Player à la plateforme,
                //Calcul de la compensation en scale à appliquer pour éviter toute déformation
                _parentScale = plateformTr.localScale;
                Vector3 newScale = new Vector3
                {
                        x = this.transform.localScale.x * ( 1 / _parentScale.x ),
                        y = this.transform.localScale.y * ( 1 / _parentScale.y ),
                        z = this.transform.localScale.z * ( 1 / _parentScale.z ),
                };

                //Ajout de la valeur de rotation de la plateforme au vecteur de compensations de rotation
                AddRotationOffset ( -plateformTr.rotation.eulerAngles );

                //Le Player est parenté à la plateforme
                this.transform.SetParent ( plateformTr );

                //Application du scale précédemment calculé
                this.transform.localScale = newScale;
        }

        /// <summary>
        /// Dé-parenter le Player de la plateforme
        /// </summary>
        /// <param name="plateformTr">Transform de la plateforme de laquelle se dé-parenter</param>
        private void UnparentFromPlatform ( Transform plateformTr )
        {
                //Indiquer que le Player vient de quitter le contact avec une plateforme
                _isOnPlatform = false;

                //Ajout de la valeur de rotation par rapport au monde 3D de la plateforme au vecteur de compensations de rotation
                AddRotationOffset ( plateformTr.rotation.eulerAngles );

                //Dé-parenter l'objet
                this.transform.parent = null;

                //Application du scale d'origine
                this.transform.localScale = _originalScale;
        }

        /// <summary>
        /// Ajoute et régule le vecteur de compensation de rotation
        /// </summary>
        /// <param name="rotationToAdd">Valeur de rotation à ajouter</param>
        private void AddRotationOffset ( Vector3 rotationToAdd )
        {
                //Remise à zéro de la rotation sur l'axe Z, dans le cas où il y en aurait une
                rotationToAdd.z = 0;

                //Ajout de la nouvelle valeur de rotation
                _rotationOffset += rotationToAdd;

                //Eviter que les valeurs d'angles Euler dépassent 360°
                if ( _rotationOffset.x > 360 )
                        _rotationOffset.x -= 360;

                if ( _rotationOffset.y > 360 )
                        _rotationOffset.y -= 360;
        }

        #endregion
}

Écrire commentaire

Commentaires: 0