[DB] Un jeu 2D façon Painter
-
- Messages : 33
- Inscription : 03 Avr 2019 15:09
- Localisation : Liège (B)
- Contact :
[DB] Un jeu 2D façon Painter
Bonjour,
Je cherche à recréer un petit jeu 2D dans l’idée de « Painter » (si ma mémoire est bonne), un jeu des années ’80 que l’on trouvait sur les bornes arcade dans les bistrots.
En voici le principe, dans les grandes lignes :
Le joueur (représenté par un point dans l’exemple ci-dessous) se déplace en permanence et à vitesse constante le long de la ligne représentant le cadre de la scène. Il peut être contrôlé à l’aide du clavier pour quitter son trajet perpendiculairement à la ligne qu’il suit. Chaque déplacement en-dehors du trajet existant crée une nouvelle ligne. Lorsque la nouvelle ligne rejoint un tracé existant, un nouvelle surface est ajoutée.
Le but étant de remplir au maximum l’espace de la scène pour piéger l’ennemi (ou remplir l’espace de jeu avec le pourcentage de surface le plus élevé).
Cet ennemi se déplace librement dans l’espace vide de la scène, rebondissant contre les tracés qu’il rencontre. S’il touche un tracé en cous de création (donc incomplet et n’ayant pas encore créé une nouvelle surface), le joueur est détruit.
un petit visuel pour mieux comprendre :
Je pense utiliser un TrailRenderer sur le joueur. Mais…
• Comment créer une surface lorsque la ligne en construction rencontre une ligne existante ?
• Comment faire pour que le joueur se déplace uniquement sur le contour de ces surfaces (lignes vertes dans l’exemple) ?
• Comment déterminer qu’une ligne est en construction et que l’ennemi peut la détruire (ligne noire dans l’exemple) ?
• Comment…
• Comment…
Bref, quelles sont les pistes à suivre pour créer cela ?
Bon weekend à vous !
Je cherche à recréer un petit jeu 2D dans l’idée de « Painter » (si ma mémoire est bonne), un jeu des années ’80 que l’on trouvait sur les bornes arcade dans les bistrots.
En voici le principe, dans les grandes lignes :
Le joueur (représenté par un point dans l’exemple ci-dessous) se déplace en permanence et à vitesse constante le long de la ligne représentant le cadre de la scène. Il peut être contrôlé à l’aide du clavier pour quitter son trajet perpendiculairement à la ligne qu’il suit. Chaque déplacement en-dehors du trajet existant crée une nouvelle ligne. Lorsque la nouvelle ligne rejoint un tracé existant, un nouvelle surface est ajoutée.
Le but étant de remplir au maximum l’espace de la scène pour piéger l’ennemi (ou remplir l’espace de jeu avec le pourcentage de surface le plus élevé).
Cet ennemi se déplace librement dans l’espace vide de la scène, rebondissant contre les tracés qu’il rencontre. S’il touche un tracé en cous de création (donc incomplet et n’ayant pas encore créé une nouvelle surface), le joueur est détruit.
un petit visuel pour mieux comprendre :
Je pense utiliser un TrailRenderer sur le joueur. Mais…
• Comment créer une surface lorsque la ligne en construction rencontre une ligne existante ?
• Comment faire pour que le joueur se déplace uniquement sur le contour de ces surfaces (lignes vertes dans l’exemple) ?
• Comment déterminer qu’une ligne est en construction et que l’ennemi peut la détruire (ligne noire dans l’exemple) ?
• Comment…
• Comment…
Bref, quelles sont les pistes à suivre pour créer cela ?
Bon weekend à vous !
Re: [DB] Un jeu 2D façon Painter
Bonjour,
Perso, plutôt que les TrailRenderers, je passerais par les LineRenderers.
Après, concernant les grands principes de réalisation, je ne sais pas.
Ton plan serait considéré comme un système cartésien orthogonal à deux dimensions, avec un ensemble de droites et de points. L'approche mathématique pourrait être une des solutions ?
Perso, plutôt que les TrailRenderers, je passerais par les LineRenderers.
Après, concernant les grands principes de réalisation, je ne sais pas.
Ton plan serait considéré comme un système cartésien orthogonal à deux dimensions, avec un ensemble de droites et de points. L'approche mathématique pourrait être une des solutions ?
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
-
- Messages : 33
- Inscription : 03 Avr 2019 15:09
- Localisation : Liège (B)
- Contact :
Re: [DB] Un jeu 2D façon Painter
Merci Max !
En fait, je pense au TrailRenderer pour afficher le tracé en construction. Et comme tu le suggères, un LineRenderer pour les tracés complétés. Je suppose qu’il sera possible de remplacer le premier par le second une fois que le tracé est complété, donc une fois que le Player a rejoint un tracé existant ou l’un des bords de l’espace de jeu.
Un système cartésien orthogonal… Cela veux dire qu’il va falloir fonctionner avec des coordonnées d’espace 2D dans un tableau ?
Donc, la position du Player est récupérée en permanence et lorsqu’il quitte un tracé existant, sa position devient le premier point du LineRenderer (tracé en construction) et un nouveau point est défini à chaque changement de direction, le point final étant l’intersection entre le joueur et le tracé existant qu’il rencontre.
La question qui me turlupine en ce moment est : comment faire pour que le Player se déplace en suivant les tracés, tant que le joueur n’appuie pas sur une touche pour le faire entamer un nouveau tracé. Comment déterminer cette position ?
Tout ça me semble compliqué et reste assez flou dans mon esprit.
En fait, je pense au TrailRenderer pour afficher le tracé en construction. Et comme tu le suggères, un LineRenderer pour les tracés complétés. Je suppose qu’il sera possible de remplacer le premier par le second une fois que le tracé est complété, donc une fois que le Player a rejoint un tracé existant ou l’un des bords de l’espace de jeu.
Un système cartésien orthogonal… Cela veux dire qu’il va falloir fonctionner avec des coordonnées d’espace 2D dans un tableau ?
Donc, la position du Player est récupérée en permanence et lorsqu’il quitte un tracé existant, sa position devient le premier point du LineRenderer (tracé en construction) et un nouveau point est défini à chaque changement de direction, le point final étant l’intersection entre le joueur et le tracé existant qu’il rencontre.
La question qui me turlupine en ce moment est : comment faire pour que le Player se déplace en suivant les tracés, tant que le joueur n’appuie pas sur une touche pour le faire entamer un nouveau tracé. Comment déterminer cette position ?
Tout ça me semble compliqué et reste assez flou dans mon esprit.
Re: [DB] Un jeu 2D façon Painter
Oui, en effet techniquement cela ne pose pas de problème de la jouer comme ça.JollyStone a écrit : ↑17 Juin 2019 10:15En fait, je pense au TrailRenderer pour afficher le tracé en construction. Et comme tu le suggères, un LineRenderer pour les tracés complétés. Je suppose qu’il sera possible de remplacer le premier par le second une fois que le tracé est complété, donc une fois que le Player a rejoint un tracé existant ou l’un des bords de l’espace de jeu.
La base est que ton Player doit se déplacer sur des droites, dont tu connais les deux points qui les définissent (les deux extrémités dans ce cas). Abordable surtout avec des droites qui sont soit verticales soit horizontales. Idem pour les fonctions permettant de savoir si un point est sur une droite, et quand il y a intersection entre deux droites.JollyStone a écrit : ↑17 Juin 2019 10:15La question qui me turlupine en ce moment est : comment faire pour que le Player se déplace en suivant les tracés, tant que le joueur n’appuie pas sur une touche pour le faire entamer un nouveau tracé. Comment déterminer cette position ?
Ça c'est l'approche disons mathématique. Elle a l'avantage d'être indépendant du rendu.
Après tu as l'approche graphique, à l'ancienne je dirais. A coup de SetPixel et GetPixel, sur une surface de couleur neutre. Ton Player se déplace en ligne droite (V ou H) et fait un SetPixel à chaque pas. Très vite tu as visuellement une ligne qui se dessine d'une couleur choisie. En même temps, avant d'attaquer la position suivante, tu testes si le pixel est d'une autre couleur que la couleur neutre. Si c'est le cas, alors c'est qu'il a rencontré une autre ligne (ou une zone fermée). Là tu adapte le comportement du Player (changement de direction, fermer une zone, etc..). Et ainsi de suite.
C'est pas évident en fait. Un jeu qui parait simple de prime abord, mais qui est plus retors qu'il n'y parait en terme de conception
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
-
- Messages : 33
- Inscription : 03 Avr 2019 15:09
- Localisation : Liège (B)
- Contact :
Re: [DB] Un jeu 2D façon Painter
En effet, plus j'y réfléchi et moins je trouve le chemin à suivre. C'est peut-être trop complexe pour le débutant que je suis.
La première méthode que tu suggères me semble la plus intéressante, sans doute moins gourmande en ressources.
Donc, le processus ressemblerait à ceci :
• Initialiser les quatre angles (coordonnées X et Y) et tracer le LineRenderer pour constituer le cadre au lancement de la partie.
• Placer le Player sur le point supérieur gauche et le mettre en mouvement dans le sens horaire.
• Au premier changement de direction (touche activée par le joueur), capter la position du Player qui sera le point d’origine d’un nouveau tracé. Ensuite, à chaque changement de direction, la position actuelle représente le point suivant, et ainsi de suite jusqu’à la rencontre d’un tracé existant, ce point étant le dernier du nouveau tracé.
Mes réflexions m'amènent à un tas de questions… En voici quelques unes :
• De manière générale, comment construire / organiser la mécanique du jeu (scripts, GameObjects, LineRenderer, …) ?
• Comment fonctionner avec le(s) tableau(x) qui sont sensés se compléter au fur et à mesure que le joueur construits les surfaces ?
• Comment déterminer au fur et à mesure le point du tracé vers lequel le Player doit se déplacer (tant qu’un changement de direction n’est pas effectué par le joueur) ?
• Comment désactiver les points par lesquels le Player ne doit plus passer lorsqu’ils ne sont plus accessibles (ne font plus partie du contour intérieur de l’espace de jeu) ?
• Comment transformer les LineRenderer en surface (mesh ?) lorsque tracé est compléter ?
Je cherche, je teste, je supprime, je recommence…
C'est sûr, j'ai besoin d'un coup de paluche pour trouver comment procéder.
Bon weekend !
La première méthode que tu suggères me semble la plus intéressante, sans doute moins gourmande en ressources.
Donc, le processus ressemblerait à ceci :
• Initialiser les quatre angles (coordonnées X et Y) et tracer le LineRenderer pour constituer le cadre au lancement de la partie.
• Placer le Player sur le point supérieur gauche et le mettre en mouvement dans le sens horaire.
• Au premier changement de direction (touche activée par le joueur), capter la position du Player qui sera le point d’origine d’un nouveau tracé. Ensuite, à chaque changement de direction, la position actuelle représente le point suivant, et ainsi de suite jusqu’à la rencontre d’un tracé existant, ce point étant le dernier du nouveau tracé.
Mes réflexions m'amènent à un tas de questions… En voici quelques unes :
• De manière générale, comment construire / organiser la mécanique du jeu (scripts, GameObjects, LineRenderer, …) ?
• Comment fonctionner avec le(s) tableau(x) qui sont sensés se compléter au fur et à mesure que le joueur construits les surfaces ?
• Comment déterminer au fur et à mesure le point du tracé vers lequel le Player doit se déplacer (tant qu’un changement de direction n’est pas effectué par le joueur) ?
• Comment désactiver les points par lesquels le Player ne doit plus passer lorsqu’ils ne sont plus accessibles (ne font plus partie du contour intérieur de l’espace de jeu) ?
• Comment transformer les LineRenderer en surface (mesh ?) lorsque tracé est compléter ?
Je cherche, je teste, je supprime, je recommence…
C'est sûr, j'ai besoin d'un coup de paluche pour trouver comment procéder.
Bon weekend !
Re: [DB] Un jeu 2D façon Painter
salut, juste une petite question
La précision de résolution de ton plan, c'est le pixel, ou penses-tu utiliser une grille de 5x5 ou 10x10 pixels par exemple pour que le chemin de ton player soit plus visible?
Si c'est le pixel tu peux utiliser une texture avec get et setPixel, ça me paraît plus confortable à utiliser.
Avec des résolutions plus grande tu pourrais créer une grille de quad, mais ça me parait plus difficile à gérer.
J'ai du mal à voir comment utiliser les LineRenderer pour faire ce que tu veux faire, surtout quand il s'agira de colorer l’intérieur de tes rectangles.
La précision de résolution de ton plan, c'est le pixel, ou penses-tu utiliser une grille de 5x5 ou 10x10 pixels par exemple pour que le chemin de ton player soit plus visible?
Si c'est le pixel tu peux utiliser une texture avec get et setPixel, ça me paraît plus confortable à utiliser.
Avec des résolutions plus grande tu pourrais créer une grille de quad, mais ça me parait plus difficile à gérer.
J'ai du mal à voir comment utiliser les LineRenderer pour faire ce que tu veux faire, surtout quand il s'agira de colorer l’intérieur de tes rectangles.
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.
Re: [DB] Un jeu 2D façon Painter
Tiens, ton projet a titiller ma curiosité,
Voilà ce que j'ai pondu,
Dans ta scène tu mets un quad correspondant à la taille de ta grille.
et tu lui mets ce script.
Le player se déplace avec les flèches du clavier.
Je ne connais pas les règles de ton jeu
C'est loin d'être parfait, mais c'est un début
j'ai commenter le script pour essayer d'être plus clair
J'espère que ça pourra t'aider
Voilà ce que j'ai pondu,
Dans ta scène tu mets un quad correspondant à la taille de ta grille.
et tu lui mets ce script.
Le player se déplace avec les flèches du clavier.
Je ne connais pas les règles de ton jeu
C'est loin d'être parfait, mais c'est un début
j'ai commenter le script pour essayer d'être plus clair
J'espère que ça pourra t'aider
Code : Tout sélectionner
using System.Collections.Generic;
using UnityEngine;
public class Grille : MonoBehaviour
{
//Les directions possibles de déplacement du player
enum Direction
{
right,
down,
left,
up
}
List<Color> Colors = new List<Color>() { Color.white, //le fond de la grille
Color.blue, //le player
Color.green, //les bords
Color.black, //le chemin du player
Color.red, //les bords dans les zones colorés
Color.yellow }; //les zones colorées
Vector2Int posStart;//la position de départ
int y = 255; //position verticale du player au départ
int x = 0; //position horizontale du player au départ
Texture2D texture; //la texture à colorer
int[,] grille; //la grille qui contiendra les index des couleurs pour la texture
Direction direction;//la direction à appliquer pour le mouvement du player
Vector2Int Mini; //le coin bas gauche du rectangle à colorer
Vector2Int Maxi; //le coin haut droit du rectangle à colorer
List<int> exColor; //les index des couleurs sous le player
Vector2Int exPos; //la dermière position du player
bool move; //si true player en mouvement
bool stop; //true quand le player est arrêté dans sa course
void Start()
{
grille = new int[256, 256]; //la grille à la taille de la texture
texture = new Texture2D(256, 256); //la texture même taille que la grille
GetComponent<Renderer>().material.mainTexture = texture; //appliquer la texture au matérial
for (int i = 0; i < 256; i++) //Tracer les bords
{
grille[i, 0] = 2;
grille[i, 255] = 2;
grille[0, i] = 2;
grille[255, i] = 2;
}
AfficheTexture(); //appliquer la modification à la texture
exColor = new List<int>() { 0, 0, 0, 0, 0 }; //les couleurs de la grille sous le player
posStart = new Vector2Int(0, 255); //la position de départ du player
grille[posStart.x, posStart.y] = 3; //appliquer la couleur du player dans la grille
Mini = new Vector2Int(255, 255); //les valeurs du mini au départ
}
private void Update()
{
ChargeInput();
if (move)
{
MovePlayer();
if (stop)
{
StoppePlayer();
}
}
AfficheTexture();
}
//lit les flêches du clavier pour donner la direction au player
private void ChargeInput()
{
if (Input.GetKey(KeyCode.RightArrow))
{
direction = Direction.right;
move = true;
}
else if (Input.GetKey(KeyCode.DownArrow))
{
direction = Direction.down;
move = true;
}
else if (Input.GetKey(KeyCode.LeftArrow))
{
direction = Direction.left;
move = true;
}
else if (Input.GetKey(KeyCode.UpArrow))
{
direction = Direction.up;
move = true;
}
}
//Déplace le player tant que possible
void MovePlayer()
{
EffacePlayer();
if (direction == Direction.right && x < 255)
{
x++;
}
if (direction == Direction.down && y > 0)
{
y--;
}
if (direction == Direction.left && x > 0)
{
x--;
}
if (direction == Direction.up && y < 255)
{
y++;
}
stop = false;
//A ce stade x et y contiennent la nouvelle position du player
//si le grille est vide() on continue
//ou si la grille est verte, si c'est le bord, on continue
//sinon qu'elle que soit la couleur on arrête
if (grille[x, y] == 0 || (grille[x, y] == 2 && (x == 0 || x == 255 || y == 0 || y == 255)))
{
//on met à jour les valeurs mii et maxi du rectangle à colorer
if (x < Mini.x) Mini.x = x;
if (y < Mini.y) Mini.y = y;
if (x > Maxi.x) Maxi.x = x;
if (y > Maxi.y) Maxi.y = y;
//si on arrive vers un bord on arrête
if (direction == Direction.left && x == 0) stop = true;
if (direction == Direction.right && x == 255) stop = true;
if (direction == Direction.up && y == 255) stop = true;
if (direction == Direction.down && y == 0) stop = true;
//on affiche le player dans sa nouvelle position
AffichePlayer(x, y);
//et on colore la case courante en noir
grille[x, y] = 3;
exColor[2] = 3;
}
else
{
stop = true;
}
}
//quand le player doit s'arrêter
void StoppePlayer()
{
//on arrête le mouvement
move = false;
stop = false;
//on parcours le rectangle du déplacement du player
for (int l = Mini.y; l < Maxi.y + 1; l++)
{
for (int c = Mini.x; c < Maxi.x + 1; c++)
{
if (grille[c, l] == 0) //si la case est vide, on la peint en jaune
grille[c, l] = 5;
else if (grille[l, c] == 3) // si la case est moire, on la peint en vert
grille[c, l] = 2;
else if (grille[l, c] == 2) // si la case est verte, on la peint en rouge
grille[c, l] = 4;
}
}
//on réinitialise la position du player et le rectangle
posStart = new Vector2Int(x, y);
Mini = new Vector2Int(255, 255);
Maxi = new Vector2Int(0, 0);
AffichePlayer(x, y);
}
void EffacePlayer()
{
//le player occupe 5 cases en croix, on réinitialise les couleurs de la grille
for (int i = 0; i < 5; i++)
{
int xx = exPos.x;
int yy = exPos.y;
if (i == 0) yy++;
if (i == 1) xx--;
if (i == 3) xx++;
if (i == 4) yy--;
if (yy >= 0 && yy <= 255 && xx >= 0 && xx <= 255)
{
if (exColor[i] > -1)
grille[xx, yy] = exColor[i];
}
}
}
void AffichePlayer(int x, int y)
{
//on colore la crois occupée par le player en bleu,
//mais on récupère avent la couleur de la case dans exColor
//et sa position dans exPos
exPos = new Vector2Int(x, y);
for (int i = 0; i < 5; i++)
{
int xx = x;
int yy = y;
if (i == 0) yy++;
if (i == 1) xx--;
if (i == 3) xx++;
if (i == 4) yy--;
if (yy >= 0 && yy <= 255 && xx >= 0 && xx <= 255)
{
exColor[i] = grille[xx, yy];
grille[xx, yy] = 1;
}
}
}
private void AfficheTexture()
{
//parcours la grille et applique au pixels de la texture
//les couleurs dont l'index est dans la grille
for (int x = 0; x < 256; x++)
{
for (int y = 0; y < 256; y++)
{
texture.SetPixel(x, y, Colors[grille[x, y]]);
}
}
texture.Apply();
}
}
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.
Re: [DB] Un jeu 2D façon Painter
Hello,
Tu t'es éclaté à ce que je vois Emile
En tous les cas merci pour @JollyStone, cela constitue une vrai base de travail
Tu t'es éclaté à ce que je vois Emile
En tous les cas merci pour @JollyStone, cela constitue une vrai base de travail
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
Re: [DB] Un jeu 2D façon Painter
Houa, , pour moi, cela équivaut à une véritable félicitation de ta part.
merci Max
merci Max
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.
Re: [DB] Un jeu 2D façon Painter
Yop !
Je me suis déjà penché sur ce type de jeu il y a quelques années.
L'approche mathématique est la plus optimale.
Il faut bosser avec une liste de croisements et une liste de segments (chaque segment étant identifié par ses extrémités, donc deux croisements).
Chaque croisement est une instance d'une classe ayant des références vers les 4 autres croisements adjacents (donc qui sont reliés par des segments du tracé)
Quand le joueur part du milieu d'un segment afin de débuter un nouveau tracé, s'il parvient à terminer ce tracé, un nouveau croisement est créé à la position du point de départ sur le segment initial, et celui-ci est donc coupé en deux segments.
Une fois que le tracé est fermé, il faut le découper mathématiquement la surface qu'il forme en plusieurs rectangles, afin de facilement calculer la surface totale que cela représente.
Il y a une subtilité ici, car à chaque nouveau tracé, on la le choix entre deux surfaces : celle qui contient l'ennemi et celle qui est vide (et qui doit donc être remplie)
Je ne me souviens plus comment j'avais solutionné ça, mais là le plus simple est de diviser les deux zones en rectangles (et identifiant bien quel rectangle appartient à quelle zone) et dès qu'on trouve qu'un rectangle contient la position de l'ennemi, c'est que ce rectangle et tous ceux dans la même liste correspondent à la zone à laisser "libre"
Quand tu as pu déterminer quelle est la zone libre, tu peux en déduire quels sont les segments qui forment sont contour... et donc les marquer comme étant ceux qui définissent le parcours autorisé.
Voilà en gros ce dont je me souviens
Je me suis déjà penché sur ce type de jeu il y a quelques années.
L'approche mathématique est la plus optimale.
Il faut bosser avec une liste de croisements et une liste de segments (chaque segment étant identifié par ses extrémités, donc deux croisements).
Chaque croisement est une instance d'une classe ayant des références vers les 4 autres croisements adjacents (donc qui sont reliés par des segments du tracé)
Quand le joueur part du milieu d'un segment afin de débuter un nouveau tracé, s'il parvient à terminer ce tracé, un nouveau croisement est créé à la position du point de départ sur le segment initial, et celui-ci est donc coupé en deux segments.
Une fois que le tracé est fermé, il faut le découper mathématiquement la surface qu'il forme en plusieurs rectangles, afin de facilement calculer la surface totale que cela représente.
Il y a une subtilité ici, car à chaque nouveau tracé, on la le choix entre deux surfaces : celle qui contient l'ennemi et celle qui est vide (et qui doit donc être remplie)
Je ne me souviens plus comment j'avais solutionné ça, mais là le plus simple est de diviser les deux zones en rectangles (et identifiant bien quel rectangle appartient à quelle zone) et dès qu'on trouve qu'un rectangle contient la position de l'ennemi, c'est que ce rectangle et tous ceux dans la même liste correspondent à la zone à laisser "libre"
Quand tu as pu déterminer quelle est la zone libre, tu peux en déduire quels sont les segments qui forment sont contour... et donc les marquer comme étant ceux qui définissent le parcours autorisé.
Voilà en gros ce dont je me souviens