Day.png);">
Apprendre


Vous êtes
nouveau sur
Oniromancie?

Visite guidée
du site


Découvrir
RPG Maker


Apprendre
RPG Maker

Tutoriels
Guides
Making-of

Dans le
Forum

Section Entraide

Jeux: puie z / Jeux: Citymaime - Chapitre 1 / Jeux: Mer, îles et fous / News: Du neuf dans le making / News: Muma|Rope est disponible en (...) / Chat

Bienvenue
visiteur !




publicité RPG Maker!

Statistiques

Liste des
membres


Contact

Mentions légales

538 connectés actuellement

30732417 visiteurs
depuis l'ouverture

2396 visiteurs
aujourd'hui



Barre de séparation

Partenaires

Indiexpo

Akademiya RPG Maker

Blog Alioune Fall

Fairy Tail Constellations

Offgame

New RPG Maker

Level Up!

Kingdom Ultimate

RPG Maker Détente

Tous nos partenaires

Devenir
partenaire



Faire un A-RPG : programmer des déplacements libres

Sortez du déplacement en case par case par la force des variables !

Ecrit par Anton_ le 02/08/2022


❤ 4

Tuto CBS : Zelda 2D



Avant de commencer :

C'est une série de tutos centré sur de la prog événementielle assez intensive pour supporter un CBS à la Zelda 2D.
L'objectif de ces tutos est pas de vous bombarder de code qu'il faut copier (bien que du code il y en aura), mais plus de chercher à comprendre les fonctionnalités une à une qui forment l'ensemble d'un moteur que vous pourrez améliorer vous même.
L'intérêt est de pouvoir assimiler chaque concept employé et de se dire si oui ou non c'est vraiment utile de les réaliser.

J'utilise la version Steam de RM 2003, mais si ça se trouve, le patch Maniacs pourrait faciliter certaines parties, mais je n'y prendrai pas note.
Pour les pictures, je conseille un logiciel qui gère avec aisances des spritesheets en png 256 couleurs, Aseprite peut le faire, mais je me focaliserai sur ce qu'on attend des images en elle mêmes (dimensions, cases, organisation du contenu)



Fonctionnalités et limitations

+ Le héros contrôlable devient une picture qui peut se déplacer librement sur 2 axes, avec précision (subpixels).
- Une picture se cale entre 2 layers d'affichage, donc si on veut éviter que le perso s'affiche en dessous de certains trucs, c'est très compliqué.

+ On peut augmenter le nombre d'interactions possible avec les events : pousser, taper, sauter dessus, soulever/lancer.
+ Ajouter un CMS peut synergiser pas mal de critères dans le moteur (vitesse, gravité, friction, double sauts, vitesse de poussée, et d'autres)

- Plus on met de fonctionnalités, plus ça deviendra compliqué à gérer, pensez que votre game design doit rester concis.
- Les déplacements des charsets peuvent poser problème si on essaye de mettre la pause (ex : en navigant dans des menus)
- Au final on fait quasiment tout avec des variables et des pointeurs. Les pointeurs sont puissants mais peuvent faire de gros bugs.
- Pensez à l'optimisation, il ne faut pas dépasser 10 000 instructions pour chaque frame (pour que ça tourne sur EasyRPG)
(au passage, les lignes de commentaires comptent en tant qu'instruction, faut s'en passer dans les boucles qui passent à chaque frame.)



Organisation des variables, switches, base de données, et ressources

Variables et Switches
Les switchs sont un peu limités, principalement par le manque de pointeur.
Donc on ne peut ni accéder au switch n°(variable 153) en lecture ou en écriture.
L'avantage du switch c'est qu'il peut alterner entre ses 2 états en une ligne (toggle ON/OFF)

Dans certains bouts de code fourni, j'ai laissé mes numéros de variables. Le système peut occuper énormément de variables, et il est vraiment important de ne pas y mélanger nos variables de progression de l'aventure. Pour cela, nous réserverons les 1000 premières variables pour le système, et passé la variable 1000, les trucs utiles pour l'histoire.

On va allouer les première variables pour préparer le terrain pour le déplacement en subpixel, l'affichage d'un héros en picture avec cette position, et la détection et correction des murs.
Cela vous donne également un avant goût de ce qui vous attend. Il se pourrait que d'autres variables soient introduites plus tard :

Portion de code : Tout sélectionner

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
V 0001 = position x
V 0002 = position y
V 0003 = position z
V 0004 = speed x
V 0005 = speed y
V 0006 = speed z
V 0007 = next x
V 0008 = next y
V 0009 = next z
 
V 0016 = input H 
V 0017 = input V
V 0018 = input action
V 0019 = input cancel
V 0020 = input menu
---
 
V 0021 = tile x
V 0022 = tile y
V 0023 = modulo tile x
V 0024 = modulo tile y
V 0025 = delta tile x
V 0026 = delta tile y
V 0027 = adjacent x
V 0028 = adjacent y
V 0029 = check tile x
V 0030 = check tile y
 
V 31-34 : des variables temporaires pour des calculs.
 
V 0035 = modulo check x
V 0036 = modulo check y
V 0037 = get terrain ID
V 0038 = get event ID
----
 
V 0051 = hero picture x
V 0052 = hero picture y
V 0053 = hero sprite state
V 0054 = direction sprite
----
V 0096 = Page 1
V 0097 = Page 2
V 0098 = Page 3
V 0099 = Page 4
V 0100 = Debug
----
V 0201 = Hero speed
V 0202 = Hero size
V 0203 = Hero zesi (complément de size sur une case)



Base de données
Accordez vos n° Terrains avec vos Tilesets pour pouvoir directement savoir qu'est ce qui est un mur, qu'est ce qui est une zone de téléport, etc. Nous verrons les terrains petit à petit au fil des sections.
Pour les events communs, pareil, à organiser d'une manière qui vous permet de vous y retrouver. Le 1er event commun devrait être la boucle principale du moteur, juste en dessous, les sous fonctions de la boucle principale. Vous pouvez vous servir aussi d'un event commun pour stocker des instructions à copier coller.

Ressources
Le plus important des dossiers : Picture. En utilisant \"Show Picture\", il faut sélectionner une picture, qui se fait sur une liste triée par ordre alphabétique, donc donnez leur une convention de nommage qui vous va.
Pro Tip : en cours de sélection d'une picture, vous pouvez entrer les premières lettres pour que le curseur navigue sur la première occurence du nom trouvé.
Mais surtout, pas d'accents dans les noms de fichier, que des caractères ASCII !

Sur ce, attelons nous à la tâche, et démarrons sur les events communs.


La Boucle principale

Le système fonctionne sur un Event commun qui tourne continuellement si le Switch 1 (GAME) est actif, on pourra l'arrêter avec le Switch 2 pour par exemple laisser une cutscene prendre le dessus.
L'event commun est construit de la sorte :

Portion de code : Tout sélectionner

1
2
3
4
5
6
7
8
9
10
11
12
- Init systeme
- Init position
- Boucle
-  (Event commun) Afficher Picture Heros
-  (Event commun) Check la caméra
-  - Attendre 0.0s (= attendre 1 frame) (on peut attendre plus de frames pour simuler un ralentissement)
-  (Event commun) Obtenir les entrées clavier
-  (Event commun) Appliquer l action du clavier en fonction de l état du héros (changer la vitesse, agir sur un event, frapper, autres actions.)
-  (Event commun) Collision Sol/plafond
-  (Event commun) Collision Murs
-  - Mise à jour (MaJ) de la position après corrections.
- Fin Boucle (sauf si Switch 2 on, pour pouvoir quitter la boucle proprement)



Chacune de ces méthodes devrait être mis dans d'autress events communs, pour s'y retrouver quand on veut retoucher un aspect du moteur.

Autorun ou Processus Parallèle ? C'est au choix, les deux sont possibles, mais je suis bien plus à l'aise avec l'autorun.
Pensez bien que si la boucle principale est un processus parallèle, le héros peut se déplacer de lui même et interagir, ajoutez lui un \"Set Route (repeat) : Wait\" dans la tronche pour le calmer.
Mais si on sort de la boucle, il faudra penser à le déverouiller, pas de softlock.
Si vous utilisez l'autorun, les events ne bougeront pas d'eux même, on peut bien sur leur forcer la main avec des \"Set Route\".



Position et granularité

En premier lieu, on crée une position sur des axes : XY pour le plan au sol, et optionnellement, Z pour la hauteur.
Les directions de ces axes sont définis sur comment les tiles et la position écran sont interprétés par RM.
Fort heureusement, les deux vont dans le même sens :
- le tile 0 0 est celui tout en haut à gauche de la map entière.
- afficher une picture sur la position 0 0 la place centrée sur le coin en haut à gauche de l'écran.

X augmente vers la droite et diminue vers la gauche.
Y augmente vers le bas et diminue vers le haut. (faut bien faire attention là dessus à chaque fois qu'on touche à des directions et des positions)

Si on veut utiliser l'axe Z, ipar exemple pour sauter au dessus de trucs, comme une plume de roc, on ne va pas l'aborder dans ce tuto, mais vous pourrez déjà essayer des trucs si ça vous dit après avoir fini cette partie du tuto.

La première chose à penser est d'interpréter une position plus fine pour avoir des déplacements mieux contrôlable pour se déplacer en subpixels.
Pourquoi on utiliserait des subpixels ? A mon opinion, la différence entre se déplacer d'1px par frame et de 2px par frame est immense, et mon déplacement confortable est 1.5px par frame.
Comme RM ne traite qu'avec les nombres entiers en -9 999 999 et 9 999 999, il faut pouvoir gérer une position en subpixel exacte, qui sera traitée en un nombre en px pour positionner la picture et l'afficher.

La granularité, c'est ce que j'appelle le nombre de supixels dans un seul pixel. Libre à vous de faire ce choix, mais je conseille une valeur entre 10 et 100.
Dans ce tuto, j'utilise 100, mais prenez au moins une granularité divisible par 2. si vous voyez un multiple de 100 ou de 50 dans ce tuto, cela touchera à la position exacte en subpixels.
Un tile dans RM 2003 fait 16 px de coté, donc bouger d'un tile entier revient à changer la position de 16 x granularité, ici 1600.
Donc par exemple, avec la position x800 y1600, le héros se trouve sur le centre de la ligne entre les tiles x0 y0 et x0 y1.

Donc ce qu'il faut retenir, et qu'il ne faut pas mélanger sans les mettre au même ordre de grandeur :
- Position exacte, réelle du personnage
- Position en pixel par rapport au coin en haut à gauche de la map = position / 100
- Position d'un tile = position / (16 * 100)

Editez ceci dans les event communs :
Boucle principale, à la place de \"Init position\"

Portion de code : Tout sélectionner

1
2
3
4
5
6
7
8
9
// On a pour info initiale des tiles, le héros en charset quand on commence l aventure.
V1 Position x = Player X Coordinate
V2 Position y = Player Y Coordinate
 
// passer de la position en tile sur la position exacte :
Variables: [0001..0002] *= 1600
// on tombe sur le coin haut gauche du tile, on souhaite le centrer au milieu du tile
Variables: [0001..0002] += 800
 



Picture hero

Portion de code : Tout sélectionner

1
2
3
4
5
picture x = position x
picture y = position y
// passer de position exacte en position en pixels
picture x et y /= 100
Show picture : \"truc statique\", picture x y, 50% transparence



Pour l'instant, nous utiliseront uniquement des maps de 20x15, la \"caméra\" est donc toujours fixe, donc à tout moment sur ces maps, la Position en pixel qu'on obtient est directement utilisable pour afficher la picture du héros, ce que nous allons voir de suite.

Testez donc , pour vérifier que votre boucle fonctionne. Sur l'allumage du switch 1 (appuyez sur F9, entrée, entrée, echap, echap) qui affiche votre picture à l'endroit où le héros démarre.
Pour les playtests suivants, mettez un event autorun qui active le switch 1 puis s'efface pour ne pas faire la manip avec F9 à chaque fois.


Entrées du clavier

A chaque tour de boucle, utilisons \"Key Input Processing\" sans attente pour récupérer l'état du clavier maintenant.
Mais pas en une seule fois, on va utiliser plusieurs variables pour permettre plus de flexibilité sur l'appui simultané des touches :
Pour permettre les appuis en diagonale, il faut avoir 2 variables pour la direction :
1 appui pour Gauche ou Droite (2 ou 3)
1 appui pour Haut ou Bas (4 ou 1)

Les touches d'actions ont leur propre variable assignée, celles qui sont simple à prendre en main et déjà mappées sur une manette sont :
- 5 Action (Espace, Entrée, Z)
Utilisé pour interagir avec les events, et si rien ne se passe, on pourra décider d'une action arbitraire.
- 6 Annuler (Echap, X, C) : action spéciale/secondaire
- 7 Shift : ouvrir menu

Pour utiliser plus efficacement les inputs de déplacement, le format \"1 4\" et \"2 3\" n'est pas pratique.
Il faut convertir cela sur ce que ça représente pour les axes de notre position exacte, pour que l'input directionnelle soit plus facile à utiliser plus tard, nous allons directement modifier les variables des inputs qui ont étés changées par \"Key Input Processing\" :
Pour l'axe X :
-1 sens négatif de l'axe (gauche)
0 (ni gauche ni droite)
1 sens positif de l'axe (droite)

Pour l'axe Y :
-1 sens négatif de l'axe (haut)
0 (ni haut ni bas)
1 sens positif de l'axe (bas)

Comme ceci, dans l'event commun \"entrées clavier\" :

Portion de code : Tout sélectionner

1
2
3
4
5
6
7
8
9
10
11
Key Input Processing (Gauche et droite seulement) [dirH]
// valeurs possibles de dir H : (2 0 3)
 
if (dirH != 0)
  dirH * 2 // (4 0 6)
  dirH - 5 // (-1 0 1)
 
Key Input Processing (Haut et bas seulement) [dirV]
// valeurs possibles de dir V : (4 0 1)
 
if (dirV = 4) dirV = -1 // (-1 0 1) 



Avec dirH et dirV prêts à l'utilisation, introduisons la vitesse qui devra changer la position exacte du héros à chaque frame.
On utilise une constante \"heroSPD\" pour amplifier cette vitesse du déplacement.

Si on se déplace en diagonale, la vitesse de X et Y doivent être multipliées par un facteur de 1/SQRT(2) (~0.7071...)
C'est principalement pour cela que les subpixels sont utiles.

Portion de code : Tout sélectionner

1
2
3
4
5
spd x = dirH * heroSPD
spd y = dirV * heroSPD
if (dirH et dirV non nuls)
 spd xy *7
 spd xy /10



Maintenant que la vitesse est donnée, la position pour la prochaine frame peut être calculée :

Portion de code : Tout sélectionner

1
(Next xy) = (Position xy) + (spd xy)



Cette position next est ce qui sera sujet à des corrections de collisions pour ensuite s'appliquer comme nouvelle position exacte sur la fin de boucle.
Dans la boucle principale \"MaJ position\" (pas besoin d'event commun pour si peu de lignes)

Portion de code : Tout sélectionner

1
2
position x = next x
position y = next y



Suite à quoi la boucle principale reboucle, la picture s'affiche sur la nouvelle position, on attend 1 frame, puis on reprend les inputs clavier, et roulez manège !

Faites donc des tests pour voir si la picture se déplace au gré de vos appuis clavier dans les bonnes directions.


Premiers pas avec les spritesheets

Bon c'est bien beau le ptit carré mais on voudrait un héros qui soit un peu comme un charset.
Ok qu'est ce qu'on a besoin pour ajoutons une variable qui indique la direction du héros.

Ajouter la direction initiale sur \"Init position\" :

Portion de code : Tout sélectionner

1
2
direction affichage = Hero Direction (2bas 4gauche 6droite 8haut)
direction affichage /2 (1bas 2gauche 3droite 4haut)



Maintenant on va utiliser un spritesheet.
Avec une image de 40x100px, donnez dans chaque carré de 10x10 de la première ligne un bonhomme qui regarde dans les 4 directions bien dans l'ordre suivant :
(bas) (gauche) (droite) (haut)

Bon je vous aide un peu, vla une image simple.
image
Un petit gars en 10x10 dans un spritesheet de 10 lignes et 4 colonnes.
Pour l'instant on va se servir que de la 1ere ligne, où le gusse est rouge.

Editez dans l'event commun \"Picture Hero\"

Mettez \"sprite\" = direction
Puis éditez la commande Show picture doit utiliser un spritesheet, donnez y un découpage de 4x10 cases, et une nouvelle variable \"sprite\" dans le choix dynamique du sprite.

Si vous testez maintenant, le personnage regardera vers le bas, mais tout le temps vers le bas.
Il faut donc chercher à mettre à jour la direction en fonction des appuis clavier, ou de la vitesse, ou de critères arbitraires, j'ai envie de dire que c'est à vous de choisir, mais bon...
Le petit problème qui survient quand on se déplace en diagonale, comment décide-t-on de quelle direction est prioritaire ? C'est à vous de décider de ce choix de game design.
On peut commencer très simple, mais biaisée sur l'horizontalité :

Portion de code : Tout sélectionner

1
2
3
4
5
6
7
8
9
10
11
12
13
if dirV != 0
direction = dirV (valeur -1 ou 1)
  if direction = -1
    direction = 4
  end if
end if
if dirH != 0 (valeur -1 ou 1)
  if (dirH > 0)
    direction = 3
  else
    direction = 2
  end if
end if



Réflexions sur la direction...
Ce qui est important de garder en tête, c'est que la direction doit persister quand le clavier retourne une position neutre (dirH 0, dirV 0).
Aussi, on n'est pas obligé de choisir une direction en 1 2 3 4
On peut aussi choisir de le mettre sur une représentation de clavier numérique (2 4 6 8) et peut être même choisir 8 directions (1 2 3 4 (5) 6 7 8 9, avec 5 = neutre)
ou même centrer tout ça sur (0 = neutre ) pour avoir (-3 -1 1 3) pour 4 directions, et (-4 -3 -2 -1 (0) 1 2 3 4) pour 8 directions, et de permettre d'inverser la position en faisant un simple *(-1)
Mais bon, au final, le 1 2 3 4 c'est ce qui permet de directement sélectionner le bon sprite, donc d'une manière ou d'une autre, il faut minimum une variable pour ça... désolé de vous embrouiller comme ça, mais ça peut vous donner une idée si jamais vous souhaitez ajouter plus tard une gestion de 8 directions.


Les murs !

image

Le concept du mur, c'est pour pas pouvoir se déplacer n'importe où, bien évidemment.
L'objectif est de détecter si la position Next est dans un mur, et le corriger sur un endroit non muré si c'est le cas : Détection sur \"Next (xy)\", et Correction.
La méthode sera robuste mais cherchera à faire le strict minimum en nombre d'instructions.

On va considérer aussi que la vitesse max pour une frame dans un axe est de 1 case (16px, donc 1600 en déplacement de position), ce qui est quand même pas du tout contrôlable mais on peut considérer que ce sera la limite de vitesse à imposer à tout moment.

Avant de toucher tout de suite aux events communs, allez retoucher les terrains et tilesets dans la base de donnée.

Terrains :
Le terrain 1 est par défaut du sol marchable.
Je vais choisir de mettre le terrain 20 pour un mur, c'est pour prévoir plus tard quand on ajoutera d'autres types de sols communs et d'autres murs.
Au rappel, l'id terrain ne corrèle pas avec la passabilité normale de RM, on peut donc faire des murs pour seulement le héros en picture (ID Terrain 20), ou seulement les charsets (Passabilité X), ou les deux, mais préférablement un mur doit pouvoir bloquer les deux.

C'est la commande \"get Terrain ID\" qui permet de retrouver cet ID de valeur 20 d'un Tile demandé sur X Y. Rappel : Tile = position / 1600

Créez un event commun servira uniquement à détecter un mur à partir d'un tile.
Cet event commun prend en entrée les coordonnées d'un tile (x et y), et produit en sortie un Switch pour dire si oui ou non il y a un mur, et que le besoin de correction doit se faire.
C'est un tout petit event commun, mais avec beaucoup de potentiel latent.

Hero Wall check

Portion de code : Tout sélectionner

1
2
3
4
Switch IS WALL = OFF
Get Terrain ID: Check tile x y -> [get TerrainID]
IF [get TerrainID] >= 20
  Switch IS WALL = ON



Comment savoir quels sont les murs à détecter pour bloquer le joueur ?
Ma méthode possède 2 couches de détection, la première se base sur quand Position et Next sont sur des tiles différents.
Cette différence est notée dans les variables Delta x et Delta y
exemples :
Delta x = 1 : Next se trouve sur une case vers la droite.
Delta y = 1 : Next se trouve sur une case vers le bas.

pseudo code :
Si Delta x est != 0, check la case à coté (gauche / droite) et si c'est un mur, correction horizontale.
Si Delta y est != 0, check la case à coté (haut / bas) et si c'est un mur, correction verticale.
Si Delta x&y sont tous les deux non nuls, et qu'aucune correction de vitesse n'a été faite ci dessus, ça veut dire qu'on va dans un mur diagonalement. Pour ne pas se fouler avec un minimum de lignes :
- soit on choisit aléatoirement sur quel axe on va effectuer la correction
(code)
- soit on fait alternativement une correction puis l'autre en flip floppant un switch à chaque fois qu'une correction diagonale survient.
(code)
La solution idéale implique beaucoup plus de lignes, qui mérite son tuto tout entier.

Pour l'instant, la \"correction\" temporaire qu'on peut faire est d'annuler le déplacement sur un axe, donc de faire :
next x ou y = position x ou y

Rien qu'avec ces lignes, on peut effectivement se déplacer contre les murs en se frottant en diagonale.
Vérifiez que ça fonctionne bien avec les murs dans les 4 sens, en diagonale, etc
Le problème qui survient c'est qu'avec seulement le Delta comme moyen de détection, on ne détecte en réalité qu'un seul point contre les murs, ce qui ne convient pas pour notre héros avec une hitbox plus large qu'un simple point.

Utilisons la constante \"Hero Size\" entre 0 et 799 (je conseille 450) pour savoir notre dégré de correction hors d'un mur.
La constante Hero zesi (zesi = 1600 - size) permettra de faire moins d'instructions dans les corrections.
Pour ceux qui ont le sens du détail, si notre héros est large d'un nombre impair de pixels, il y aura une petite différence notable quand on se bloque sur un mur à gauche et à droite, une différence d'un pixel.
En changeant la constante Hero zesi d'un pixel (donc zesi = 1600 - size + 100), ça peut corriger ce petit défaut entre les deux murs horizontaux.
Il ne vaudrait pas toucher à Hero size s'il faut faire d'autres calculs avec genre des collisions d'ennemis. Mais le zesi sera seulement impliqué pour chercher et corriger les collisions de murs du héros.

Remplaçons les corrections temporaires par une correction qui prend en compte la taille du héros :
Ce bout de code gère à la fois les murs en haut et en bas, avec la correction appropriée.
Copiez le et faites de même avec les murs horizontaux.

Portion de code : Tout sélectionner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if :delta tile y  != 0
 check tile x = tile x
 check tile y = tile y
 check tile y += delta y
 Call Event: [Hero Wall check]
 if Switch [wall !] is ON
 // mur détecté ! corriger ici
     y next = tile y (de la position courante)
     y next *= 1600 (coin haut gauche du tile courant)
     if delta tile y > 0
      y next += Hero ZESI (coller le mur le plus éloigné du coin haut gauche)
     Else
      y next += Hero SIZE (coller le mur le plus proche du coin haut gauche)
 
 delta tile y = 0 // pour empecher la détection en diagonale.
End IF



Passons maintenant aux blocs adjacents : il faut savoir si on est assez près d un mur alors que P et N sont sur le même tile. Utilisons le modulo de la case.
Deux nouvelles variables sont mises en oeuvre : Adjacent x et Adjacent y
Le modulo de la position dans un tile est compris entre 0 et 1599
pour chaque dimension x et y :

Portion de code : Tout sélectionner

1
2
3
4
5
if modulo xy < size
  adj xy = -1
else if modulo xy > zesi
  adj xy = +1
else adj xy = 0



Et on fait les mêmes corrections x et y comme avec Delta.
Si Adj x est != 0, check la case à coté et si c'est un mur, correction horizontale.
Si Adj y est != 0, check la case à coté et si c'est un mur, correction verticale.
Et c'est tout, pas de corrections adjacentes en diagonale pour pas vous prendre la tête plus que ça.

Un détail important que j'ai oublié de mentionner, c'est qu'une correction delta réussi sur un axe fait qu'on a pas du tout besoin de corriger de manière adjacente.
J'utilise 2 switch pour dire si oui on non il faut poursuivre les corrections ou s'il n'y a plus besoin.

Voici à quoi ressemble le code de \"Collisions Murs\" au complet, le delta est fait en premier, puis l'adjacent :

Spoiler (cliquez pour afficher)



Enfin, playtestez et faites varier la hero size (pour plus de simplicité, mettez l'update de Hero zesi à l'intérieur de la boucle... mais que temporairement pour ces tests.
Avec F9, essayez donc avec de varier les valeurs de size en plein dans les murs et voyez ce qui peut mal se passer avec des sizes au delà de 800, ou des sizes négatives ? vous verrez ce que ça donne :)

Voilà, c'était un gros morceau. Si vous avez réussi à assimiler les méthodes de delta et d'adjacent, félicitez vous un peu, c'était pas de la tarte.
On va terminer sur des choses faciles pour boucler le tuto.


Utiliser l'appui bouton 5 de manière basique.

Dans RM de base, si on appuie sur Action 5 (Entrée, Espace, Z), le héros interagit avec les events en face de lui. On souhaite faire de même dans notre moteur.
à partir de la case actuelle (tile x y), on peut tout simplement avancer d'une case selon la Direction cardinale (-3 -1 1 3), et appelez la page 1 de l'ID Event.
Si on discute avec un PNJ, l'event peut se tourner vers la direction opposée à la vôtre pour vous faire face.
On peut soit faire 4 conditions, ou alors on utilise le charset du héros et on utilise \"regarder vers le héros\". Le charset du héros n'a qu'a viser la case de tile x y et de s'y déplacer en mode fantôme.

Un dernier détail, il faut utiliser l'event commun \"Interaction Heros\" que quand on commence à appuyer sur la touche.
J'utilise des variables pour compter le nombre de frames tant que la touche reste enfoncée, si pas appuyée, le compteur revient à 0.
Donc si la valeur d'un Hold est égale à 1, on vient à peine d'appuyer sur le bouton et l'interaction ne sera déclenchée qu'une seule fois par appui bouton.
V 0092 : Hold button 5
V 0093 : Hold button 6
V 0094 : Hold button 7

Interactions Heros

Portion de code : Tout sélectionner

1
2
3
4
5
6
7
8
9
en fonction de la direction du sprite du héros :
directionH = -1 0 ou 1
directionV = -1 0 ou 1
 
check tile x = tile x + directionH
check tile y = tile y + directionV
get Event ID (check tile xy)
if Event ID > 0
call Event ID, page (constant 1)




Mon event doit être un mur !

Ah cette fameuse détection des murs, il est temps de retoucher vite fait la sous fonction \"check mur\" qui prend en entrée un tile xy.
Nous allons y ajouter un \"get event ID\", et s'il trouve un event, d'y lancer la page 2.
L'event décide alors s'il doit mettre le switch de \"je suis un mur\" à ON.
Et maintenant la meilleure partie d'un event, mettre une condition si oui ou non l'event lui même est un mur ou pas, par exemple, s'il regarde vers le haut c'est un mur, sinon non.
Encore plus fou !! Mettez un event DANS un mur, et mettez le switch \"je suis un mur\" à OFF, et hop, vous pouvez passez à travers un terrain de mur.

Portion de code : Tout sélectionner

1
2
3
4
5
6
7
8
9
(A ajouter dans l'event commun [Hero Wall check])
// check tile x et check tile y sont prêts à l emploi !
get Event ID (check tile xy)
if Event ID > 0
call Event ID, page (constant 2)
 
(Page 2 Event)
if (this event) looks up :
Set Switch \"IS WALL\" ON



Vous pouvez ensuite tester ce qui se passe si l'event de mur se déplace sur vous, ou s'il a une routine et que vous êtes en plein milieu.
vu qu'on ne vérifie jamais la case actuelle sur laquelle on se trouve, on ne sera jamais complètement bloqué dans un mur.
(d'ailleurs si vous arrivez à glitcher dans un mur, pareil, si Next après correction passe dans un mur, pour une certaine raison (ou un forçace externe), le mur du tile actuel ne sera jamais vérifié.
On passera plus tard sur les vérifications Terrain et Event sur le tile sur lequel on se trouve plus tard, dans l'event \"Floor/Ceiling\" qui gère principalement les correction en Z.


Un petit coup de lifting !

Assez d'une picture rigide, améliorons notre héros avec des animations !
utilisons les lignes bleues de notre petite image d'exemple.
Il faut considérer chaque ligne comme une image unique qu'on accède sur un état particulier du héros.
Donc pour déterminer la bonne variable \"sprite\", on lui donne la ligne et la direction comme ceci :
Sprite = (4 * ligne) + direction(1|2|3|4)

Personnellement j'aime bien utiliser la ligne 0 pour indiquer que je me suis foiré en coloriant mon perso en rouge par exemple.
On attribue les lignes sur les positions suivantes :
0 : état d'erreur
1 : statique, ou milieu de marche
2 : marche, pied à gauche
3 : marche, pied à droite

Mais bon, les images, je préfère vraiment vous laisser le champ libre pour que vous choisissez comment vos états suivants évolueront.
Gardez de la place en matière de nombre de lignes, si vous avez plus de 100 lignes, vous pouvez faire 8 colonnes, X/2 lignes, ou 12 colonnes, X/3 lignes, etc.
Le grand max c'est 100 colonnes, X/25 lignes, pour un total de 2500 frames de héros différents.
C'est peut être beaucoup, faites comme vous le sentez. Mais rien ne nous empêche de dupliquer les mêmes images si ça vous fait réduire du code (ex : affichage d'attaques d'apparence similaires mais mécaniquement différentes)

Et maintenant, comment choisit-on dynamiquement notre ligne ?
Et bien ça dépend de vos choix, et de toutes les questions à se poser, du genre :
- Est-ce que marcher c'est quand speed est différent de 0 0 ?
- Ou alors est-ce que marcher c'est quand l'input de direction est différent de zéro ?
- Si on ajoute une mécanique de saut, c'est plus prioritaire que d'être au sol, donc les images de sauts prennent le dessus.
- Mais si on ajoute une image \"blessé\", est-ce plus prioritaire que d'être en l'air ? Est-ce qu'il faut une ligne \"blessé au sol\" et \"blessé en l'air\" ?
- etc ...

Ces problèmes là, je ne dois pas vous dire comment faire, c'est beaucoup trop arbitraire.
Ici et maintenant, on va s'occuper uniquement de mettre un pied en avant en alternance, ou d'afficher le héros de manière statique.

Faire avancer une animation, c'est représenté en comptant les frames (notre seule mesure tangible de temps dans RM).

Variables et Switches pour animer une marche :
Tant qu'on marche, on peut faire un compte de frames et de revenir à 0 quand on atteint la valeur limite.
Si on reste tranquille, on peut reset ce compteur à zéro si on veut.
Il faut aussi penser à alterner la marche du pas gauche au pas droit quand on franchit la limite.

image

V 0088 - Oscillation current
V 0089 - Oscillation milieu (150)
V 0090 - Oscillation limite (300)
V 0091 - Oscillation speed (10 de base, plus haut ça fait animer le perso plus vite)

S 0021 - IS STILL (est-ce que le héros reste planté au même endroit sans bouger ?
S 0024 - Oscillation flip (pour marcher sur le pas de gauche et de droite alternativement)

Portion de code : Tout sélectionner

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
IS STILL = ON si direction H et direction V sont 0
sinon IS STILL = OFF
 
if IS STILL = ON
 image = 1
 Oscillation flip = toggle ON/OFF (permet de changer le pas quand on s arrête, pour randomiser le prochain pas de marche )
else
 // compter sur le temps, 60 frames = 1 seconde.
 frames += 1
 if frames >= 30 // reset à chaque demi seconde
  frames = 0 
  Oscillation flip = toggle ON/OFF
 end if
 
 if frames >= 15 // le moment où on passe au milieu !
  image = 3 // marche 2/3
  // bonus, image = 5, marche 2bis/3 si ALTERNE LE PAS est ON, mais que si on a envie d une marche animée avec 4 lignes.
 else 
  if ALTERNE LE PAS = ON
    image = 4 // marche 3/3
  else
    image = 2 // marche 1/3
  end if
 end if
end if
 
// ça y est, \"image\" est pret a être utilisé dans \"sprite\".
sprite = image * (nb de colonnes) + direction
Show Picture spritesheet(4x10), \"sprite\"



Et vous connaissez la chanson, Testez, testez si tout va bien par rapport à ce que vous attendez.
Si la marche est trop lente ou trop rapide, changez les valeurs de vitesse, de milieu ou de limite.
il faut bien sur que la valeur de limite soie plus grande que la valeur de milieu, sinon, on n'affichera pas l'image de marche au milieu.


Changer de map

La chose suivante à penser est de pouvoir maintenir le moteur en marche tout en changeant de map.
Il y aura des trucs à faire pour gérer la caméra entre 2 maps, mais pour l'instant on va rester sur des maps de 20x15, dont la caméra n'a jamais besoin de bouger.

On va dédier un terrain uniquement pour y poser des TP. l'event en question possède beaucoup d'instructions, que nous pouvons réduire en 3 lignes :

Event commun \"Avant TP\"
Transfert Player : MAP02, X Y
Event commun \"Après TP\"

L'event commun \"Avant TP\" sert à retirer tout lien avec des entités de la map courante (comme des events d'ennemis, des infos traités dans les processus paralleles.)
L'event commun \"Après TP\" s'occupera principalement d'initialiser la caméra, et de réinitialiser la position par rapport à là où est positionné le charset.
Ou plus simplement call l'init système dans \"Après TP\", ça peut dépendre de si c'est vraiment nécessaire ou pas.

On peut aussi y intégrer une transition custom, fade out dans \"Avant TP\" et fade in dans \"Après TP\".


Coupez !

La dernière chose utile qui manque pour le moteur, c'est de pouvoir le couper à volonté pour pouvoir réaliser des mises en scènes, puis relancer le moteur une fois la mise en scène finie.
Le Switch 2 \"End\" permet de sortir de la boucle, mais il doit faire en sorte que tout autre process parallele se termine proprement.
Car c'est très important, un processus parallèle d'un event commun, s'il s'arrête en plein milieu en coupant son switch lié, il reprend là où il en était, et ça cause des effets de bords direct, c'est très dangereux.
Donc, à faire dans l'ordre quand on ordonne de sortir du moteur :
- Sortir de la boucle principale
- Attendre 1 frame en plus pour l'arret des autres process parallèles qui font partie du moteur (ex : hud, ennemis)
- Switch 1 = OFF, fin de l'event principal.

Essayez de faire passer le Switch 2 à ON via un event, et expérimentez comment vous voulez agencer vos mises en scènes.

Pourquoi ne pas toucher au Switch 1 directement ? Je rappelle qu'un event commun en processus parallèle mémorise où il en était. Il faut bien s'assurer de savoir qu'il s'arrête après avoir bien complété son tour de boucle.

De plus, sur une fin de boucle, vous serez tentés de changer le héros dans son équivalent en charset pour qu'il soit intégré dans la mise en scène plus facilement.


Fin !

Vous y êtes arrivé ? Vous avez là les bases d'un déplacement en pixel avec des murs et des events solides, avec des interactions d'event manuels (bouton d'Action) et automatiques (Téléports, cutscenes)
Il y a possibilité d'élaborer sur pas mal de points, mais je pense qu'on en a assez dit pour l'instant, les parties suivantes attaqueront d'autre problèmes plus techniques :
- L'axe Z : sauts, gravité, mouvements dans l'air
- Caméra : init, dimensionnement, déplacement, déclenchement d'event, intégration avec le changement de map.
- Pousser des choses : une interaction sans appuyer sur la touche interaction
- Mettre en place des ennemis
- Donner des coups
Cela dépend de vos envies de game design, et de votre ordre de grandeur pour le projet.


Aucun commentaire n'a été posté pour le moment.

Suite à de nombreux abus, le post en invités a été désactivé. Veuillez vous inscrire si vous souhaitez participer à la conversation.

Haut de page

Merci de ne pas reproduire le contenu de ce site sans autorisation.
Contacter l'équipe - Mentions légales

Plan du site

Communauté: Accueil | Forum | Chat | Commentaires | News | Flash-news | Screen de la semaine | Sorties | Tests | Gaming-Live | Interviews | Galerie | OST | Blogs | Recherche
Apprendre: Visite guidée | RPG Maker 95 | RPG Maker 2003 | RPG Maker XP | RPG Maker VX | RPG Maker MV | Tutoriels | Guides | Making-of
Télécharger: Programmes | Scripts/Plugins | Ressources graphiques / sonores | Packs de ressources | Midis | Eléments séparés | Sprites
Jeux: Au hasard | Notre sélection | Sélection des membres | Tous les jeux | Jeux complets | Le cimetière | RPG Maker 95 | RPG Maker 2000 | RPG Maker 2003 | RPG Maker XP | RPG Maker VX | RPG Maker VX Ace | RPG Maker MV | Autres | Proposer
Ressources RPG Maker 2000/2003: Chipsets | Charsets | Panoramas | Backdrops | Facesets | Battle anims | Battle charsets | Monstres | Systems | Templates
Ressources RPG Maker XP: Tilesets | Autotiles | Characters | Battlers | Window skins | Icônes | Transitions | Fogs | Templates
Ressources RPG Maker VX: Tilesets | Charsets | Facesets | Systèmes
Ressources RPG Maker MV: Tilesets | Characters | Faces | Systèmes | Title | Battlebacks | Animations | SV/Ennemis
Archives: Palmarès | L'Annuaire | Livre d'or | Le Wiki | Divers