public var container : Transform = null; //Le gameobject dans lequel les objets seront ajoutés. Doit etre vide. public var sizeX : int = 100; //La largeur max en blocs de chaque étage généré. public var sizeY : int = 5; //Le nombre d'étages de la map générée. public var sizeZ : int = 80; //La longueur max en blocs de chaque étage généré. public var roomSize : int = 5; //La taille de chaque salle. (les salles doivent etre carrées) public var playerTransform : Transform; //Le transform du player, utilisé pour le replacer dans la salle de départ. public var startRoomAddon : Transform; //Le transform utilisé pour marquer la zone de départ. Une lumière colorée suffit. public var EndRoom : Transform[]; //Le transform de salle cul de sac. public var IRoom : Transform[]; //Le transform de salle en I public var LRoom : Transform[]; //Le transform de salle en L public var TRoom : Transform[]; //Le transform de salle en T public var OpenRoom : Transform[]; //Le transform de salle en croix public var seed : int = 123456; //Une seed similaire sur 2 postes fera toujours la meme map. private var roomsTable : int[,]; //Tableau disant quelle salle est prise ou pas private var numberOfRooms; //Nombre de salles existantes. private var coordinatesToGenerate : Vector2[]; //Les coordonnées des salles à générer, se vide et remplit tout seul. private var remainingRoomsToGenerate : int = 0; //Le nombre de salles restantes à générer. private var generating : boolean = false; function Start () { // Vérification qu'on a bien donné un container. Si non, on prend l'un des enfants de l'objet, s'il y a. Si y'a pas, ça buggera. if(container == null) { var allChildren = gameObject.GetComponentsInChildren(Transform); for (var child : Transform in allChildren) { if(child.name == "Blocks container") { container = child; } } } // Vérification de seed non nulle. Si seed nulle, elle est définie à 123456. if(seed < 1000) { seed += 1000; } // Finalement, on génère la map. regenerateMap(); } function Update () { } function OnGUI() { if(GUI.Button(Rect(10,10,100,100), "Regenerate")) regenerateMap(); if(!generating) seed = parseInt(GUI.TextField(Rect(10,120,100,20), seed+"")); else GUI.Label(Rect(10,120,100,20), seed+""); if(seed < 1000) { seed += 1000; } } /** * Fonction qui génère la map, en remplissant le tableau des blocs, et ajoutant les blocs correspondants dans le container. * Place également le joueur dans la salle de début (marquée par le firstRoomAddon) */ function regenerateMap() { generating = true; // On reset le tableau, et on vide le container. roomsTable = null; emptyContainer(); numberOfRooms = 0; // On crée le tableau, il faudra changer ça, c'est moche et pourri y'a une solution simple mais je connais pas la syntaxe et ai pas le net. roomsTable = new int[sizeX,sizeZ]; for(a = 0; a < sizeX; a++) { for(b = 0; b < sizeZ; b++) { setRoomsTable(a,b, -1); //Non défini } } // On choisit où on commence, et met les coordonnées dans startRoomPos. var startRoomPos : Vector2 = Vector2(randomWithSeed(0, sizeX - 1, 0),randomWithSeed(0, sizeZ - 1, 10)); setRoomsTable(startRoomPos.x, startRoomPos.y, 1); // On dit qu'on ne veut pas de salles autour (c'est une dead end après tout), y'aura juste la 2e qui la touchera. //Debug.Log(""+parseInt(startRoomPos.x)+" " + parseInt(startRoomPos.y)); setRoomsTable(startRoomPos.x+1, startRoomPos.y, 0); setRoomsTable(startRoomPos.x-1, startRoomPos.y, 0); setRoomsTable(startRoomPos.x, startRoomPos.y-1, 0); setRoomsTable(startRoomPos.x, startRoomPos.y+1, 0); // On choisit une direction de début, certaines peuvent etre bloquées si on est au bout de la map. var direction : int = 1; // Les directions sont : 0 bas; 1 gauche; 2 haut; 3 droite; if(startRoomPos.x == 0) // Si on est tout à gauche, vers la droite direction = 3; else if(startRoomPos.x == sizeX) // Si on est tout à droite, vers la gauche direction = 1; else if(startRoomPos.y == 0) // Si on est tout en bas, vers le haut direction = 2; else if(startRoomPos.y == sizeZ) // Si on est tout en haut, vers le bas direction = 0; else { // Si rien ne coince, choix random lié à la seed. direction = randomWithSeed(0, 3, 1); } // On instancie la première salle, avec l'addon pour la marquer. var startRoom : Transform = Instantiate(EndRoom[0], Vector3(startRoomPos.x * roomSize, 0, startRoomPos.y * roomSize), Quaternion.identity); startRoom.Rotate(Vector3(0,direction * 90, 0)); startRoom.name = "Start room"; var startRoomAddon : Transform = Instantiate(startRoomAddon, Vector3(startRoomPos.x * roomSize, 0, startRoomPos.y * roomSize), Quaternion.identity); startRoom.parent = container; startRoomAddon.parent = container; //Debug.Log("Première salle en (" + startRoomPos.x * roomSize + ", " + startRoomPos.y * roomSize + "), rotation " + (direction * 90) + "°"); // On bouge le joueur dedans. playerTransform.position = Vector3(startRoomPos.x * roomSize, 0.2f, startRoomPos.y * roomSize); // Du coup, la salle est ouverte d'un seul cote, on va commencer par la marquer comme "utilisée" dans roomsTable. var secondRoomPos : Vector2; if(direction == 0) { secondRoomPos.x = startRoomPos.x; secondRoomPos.y = startRoomPos.y - 1; } if(direction == 1) { secondRoomPos.x = startRoomPos.x - 1; secondRoomPos.y = startRoomPos.y; } if(direction == 2) { secondRoomPos.x = startRoomPos.x; secondRoomPos.y = startRoomPos.y + 1; } if(direction == 3) { secondRoomPos.x = startRoomPos.x + 1; secondRoomPos.y = startRoomPos.y; } // La première salle est complète, on va passer à la génération des autres salles. // Pour ça, on met les coordonnées à générer dans un tableau. // A chaque salle générée, on enlève ses coordonnées du tableau, et rajoute celles qui seront à coté de celle qu'on vient de faire. // La génération s'arrete quand le tableau est vidé, puisqu'on ne peut plus lier d'autres salles ! coordinatesToGenerate = null; coordinatesToGenerate = new Vector2[1000]; coordinatesToGenerate[0] = secondRoomPos; remainingRoomsToGenerate = 1; //Nombre de salles restantes à générer while(remainingRoomsToGenerate > 0) { var tempCoord = coordinatesToGenerate[0]; // On ajoute cette salle au nombre et à la liste de salles existantes. numberOfRooms++; setRoomsTable(tempCoord.x, tempCoord.y, 1); // Quel genre de salle celle-ci va etre ? var whatKindOfRoom : Vector2 = whatKindOfRoomWillItBe(tempCoord.x, tempCoord.y); // Ensuite on la crée. var createdRoom : Transform; switch(whatKindOfRoom.x) { case 1 : //Dead end createdRoom = Instantiate(EndRoom[randomWithSeed(0, EndRoom.Length-1, numberOfRooms)], Vector3(tempCoord.x * roomSize, 0.0f, tempCoord.y * roomSize), Quaternion.identity); createdRoom.name = "Dead end room"; break; case 2 : //I createdRoom = Instantiate(IRoom[randomWithSeed(0, IRoom.Length-1, numberOfRooms)], Vector3(tempCoord.x * roomSize, 0.0f, tempCoord.y * roomSize), Quaternion.identity); createdRoom.name = "I room"; break; case 3 : //L createdRoom = Instantiate(LRoom[randomWithSeed(0, LRoom.Length-1, numberOfRooms)], Vector3(tempCoord.x * roomSize, 0.0f, tempCoord.y * roomSize), Quaternion.identity); createdRoom.name = "L room"; break; case 4 : //T createdRoom = Instantiate(TRoom[randomWithSeed(0, TRoom.Length-1, numberOfRooms)], Vector3(tempCoord.x * roomSize, 0.0f, tempCoord.y * roomSize), Quaternion.identity); createdRoom.name = "T room"; break; case 5 : //Ouverte createdRoom = Instantiate(OpenRoom[randomWithSeed(0, OpenRoom.Length-1, numberOfRooms)], Vector3(tempCoord.x * roomSize, 0.0f, tempCoord.y * roomSize), Quaternion.identity); createdRoom.name = "Open room"; break; } createdRoom.name += " (" + numberOfRooms + ")"; // On l'oriente comme il faut. createdRoom.Rotate(Vector3(0,(1+whatKindOfRoom.y) * 90, 0)); // On la range. createdRoom.parent = container; //Debug.Log("Salle en (" + tempCoord.x * roomSize + ", " + tempCoord.y * roomSize + "), rotation " + (whatKindOfRoom.y * 90) + "°"); // Il nous reste un truc à faire, j'aurais pu grouper avec le switch d'avant mais c'est plus facile à comprendre et propre quand c'est à part. // On va voir si il faut ajouter des salles à générer dans la liste, et bloquer les endroits où il ne doit pas y en avoir. switch(whatKindOfRoom.x) { case 1 : //Dead end if(whatKindOfRoom.y != 0) setRoomsTable(tempCoord.x-1, tempCoord.y, 0); if(whatKindOfRoom.y != 1) setRoomsTable(tempCoord.x, tempCoord.y+1, 0); if(whatKindOfRoom.y != 2) setRoomsTable(tempCoord.x+1, tempCoord.y, 0); if(whatKindOfRoom.y != 3) setRoomsTable(tempCoord.x, tempCoord.y-1, 0); break; case 2 : //I if(whatKindOfRoom.y == 0 || whatKindOfRoom.y == 2) { //Horizontal setRoomsTable(tempCoord.x, tempCoord.y+1, 0); setRoomsTable(tempCoord.x, tempCoord.y-1, 0); if(-1 == isThereARoom(tempCoord.x + 1, tempCoord.y)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x + 1, tempCoord.y); remainingRoomsToGenerate++; } if(-1 == isThereARoom(tempCoord.x - 1, tempCoord.y)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x - 1, tempCoord.y); remainingRoomsToGenerate++; } } else { //Vertical setRoomsTable(tempCoord.x+1, tempCoord.y, 0); setRoomsTable(tempCoord.x-1, tempCoord.y, 0); if(-1 == isThereARoom(tempCoord.x, tempCoord.y + 1)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x, tempCoord.y + 1); remainingRoomsToGenerate++; } if(-1 == isThereARoom(tempCoord.x, tempCoord.y - 1)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x, tempCoord.y - 1); remainingRoomsToGenerate++; } } break; case 3 : //L if(whatKindOfRoom.y == 0) { //Gauche + haut setRoomsTable(tempCoord.x+1, tempCoord.y, 0); setRoomsTable(tempCoord.x, tempCoord.y-1, 0); if(-1 == isThereARoom(tempCoord.x - 1, tempCoord.y)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x - 1, tempCoord.y); remainingRoomsToGenerate++; } if(-1 == isThereARoom(tempCoord.x, tempCoord.y + 1)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x, tempCoord.y + 1); remainingRoomsToGenerate++; } } if(whatKindOfRoom.y == 1) { //Haut + droite setRoomsTable(tempCoord.x-1, tempCoord.y, 0); setRoomsTable(tempCoord.x, tempCoord.y-1, 0); if(-1 == isThereARoom(tempCoord.x, tempCoord.y + 1)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x, tempCoord.y + 1); remainingRoomsToGenerate++; } if(-1 == isThereARoom(tempCoord.x + 1, tempCoord.y)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x + 1, tempCoord.y); remainingRoomsToGenerate++; } } if(whatKindOfRoom.y == 2) { //Droite + bas setRoomsTable(tempCoord.x-1, tempCoord.y, 0); setRoomsTable(tempCoord.x, tempCoord.y+1, 0); if(-1 == isThereARoom(tempCoord.x + 1, tempCoord.y)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x + 1, tempCoord.y); remainingRoomsToGenerate++; } if(-1 == isThereARoom(tempCoord.x, tempCoord.y - 1)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x, tempCoord.y - 1); remainingRoomsToGenerate++; } } if(whatKindOfRoom.y == 3) { //Bas + gauche setRoomsTable(tempCoord.x+1, tempCoord.y, 0); setRoomsTable(tempCoord.x, tempCoord.y+1, 0); if(-1 == isThereARoom(tempCoord.x, tempCoord.y - 1)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x, tempCoord.y - 1); remainingRoomsToGenerate++; } if(-1 == isThereARoom(tempCoord.x - 1, tempCoord.y)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x - 1, tempCoord.y); remainingRoomsToGenerate++; } } break; case 4 : //T if(whatKindOfRoom.y != 0) { //Le mur de droite n'est pas fermé if(-1 == isThereARoom(tempCoord.x + 1, tempCoord.y)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x + 1, tempCoord.y); remainingRoomsToGenerate++; } } else { setRoomsTable(tempCoord.x+1, tempCoord.y, 0); } if(whatKindOfRoom.y != 1) { //Le mur du bas n'est pas fermé if(-1 == isThereARoom(tempCoord.x, tempCoord.y - 1)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x, tempCoord.y - 1); remainingRoomsToGenerate++; } } else { setRoomsTable(tempCoord.x, tempCoord.y-1, 0); } if(whatKindOfRoom.y != 2) { //Le mur de gauche n'est pas fermé if(-1 == isThereARoom(tempCoord.x - 1, tempCoord.y)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x - 1, tempCoord.y); remainingRoomsToGenerate++; } } else { setRoomsTable(tempCoord.x-1, tempCoord.y, 0); } if(whatKindOfRoom.y != 3) { //Le mur du haut n'est pas fermé if(-1 == isThereARoom(tempCoord.x, tempCoord.y + 1)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x, tempCoord.y + 1); remainingRoomsToGenerate++; } } else { setRoomsTable(tempCoord.x, tempCoord.y+1, 0); } break; case 5 : //Ouverte if(-1 == isThereARoom(tempCoord.x, tempCoord.y - 1)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x, tempCoord.y - 1); remainingRoomsToGenerate++; } if(-1 == isThereARoom(tempCoord.x, tempCoord.y + 1)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x, tempCoord.y + 1); remainingRoomsToGenerate++; } if(-1 == isThereARoom(tempCoord.x - 1, tempCoord.y)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x - 1, tempCoord.y); remainingRoomsToGenerate++; } if(-1 == isThereARoom(tempCoord.x + 1, tempCoord.y)) { coordinatesToGenerate[remainingRoomsToGenerate] = Vector2(tempCoord.x + 1, tempCoord.y); remainingRoomsToGenerate++; } break; } yield WaitForSeconds(0.005f); // Et c'est bon, on a fini pour cette salle ! On l'enlève de la liste, et on passe à la suivante. for(i = 0; i < remainingRoomsToGenerate - 1; i++) { coordinatesToGenerate[i] = coordinatesToGenerate[i + 1]; } remainingRoomsToGenerate -- ; } generating = false; Debug.Log("Génération terminée! " + numberOfRooms + " salles générées."); } /** * Fonction qui vide le container, pour la régénération de la map. * Destroy tous les enfants du container. */ function emptyContainer() { var allChildren = container.GetComponentsInChildren(Transform); for (var child : Transform in container) { Destroy(child.gameObject); } } /** * Fonction qui donne quel type de salle on aura aux coordonnées données. * Prend en compte la seed, les salles déjà présentes autour, et la taille du niveau. * Retourne un entier représentant le type de salle, et la direction. * Type : * 1 : dead end * 2 : I * 3 : L * 4 : T * 5 : ouverte * Direction à multiplier par 90 pour obtenir l'angle désiré. */ function whatKindOfRoomWillItBe(x : int, z : int) { // Dans quelles direction la salle sera-t-elle ouverte ? // On commence avec tout à true, on met à false pour fermer (parfois à tort), // et on corrige à la fin si on a fermé des murs qu'on devait pas, en fonction des salles adjacentes. var goRight : boolean = true; var goLeft : boolean = true; var goUp : boolean = true; var goDown : boolean = true; // Premièrement, on ajoute rien si on a déjà beaucoup de salles. Y'aura que les 2/3 de la map qui seront remplis. // On va tout passer à false, et on rouvrira là où il faut (pour les salles adjacentes) if(remainingRoomsToGenerate + numberOfRooms > sizeX * sizeZ * 2/3) { goRight = false; goLeft = false; goUp = false; goDown = false; } // Un petit coup de random, ouverture par ouverture. var chancesSur10 : int = 6; //66% de chances de fermer chaque passage étant encore ouvert, nombre à régler pour faire un donjon plus ou moins ouvert. // Cependant si le niveau n'est pas fini du tout et que y'a plus rien de prévu à créer, ben on ne ferme rien, pour agrandir de force. if(! (numberOfRooms < sizeX * sizeZ / 2 && remainingRoomsToGenerate < 3)) { if(goRight) { if(randomWithSeed(1, 10, numberOfRooms + 1) > 10 - chancesSur10) { goRight = false; } } if(randomWithSeed(1, 10, numberOfRooms + 2) > 10 - chancesSur10) { if(goLeft) { goLeft = false; } } if(randomWithSeed(1, 10, numberOfRooms + 3) > 10 - chancesSur10) { if(goUp) { goUp = false; } } if(randomWithSeed(1, 10, numberOfRooms + 4) > 10 - chancesSur10) { if(goDown) { goDown = false; } } } // Finalement, on corrige les ouvertures fermées qui doivent etre ouvertes. // on check d'abord si on est au bord de la map, puis si y'a une salle à coté. if(isThereARoom(x+1, z) == 1) { goRight = true; } if(isThereARoom(x-1, z) == 1) { goLeft = true; } if(isThereARoom(x, z+1) == 1) { goUp = true; } if(isThereARoom(x, z-1) == 1) { goDown = true; } if(isThereARoom(x+1, z) == 0) { goRight = false; } if(isThereARoom(x-1, z) == 0) { goLeft = false; } if(isThereARoom(x, z+1) == 0) { goUp = false; } if(isThereARoom(x, z-1) == 0) { goDown = false; } // On sait où on ouvre, où on n'ouvre pas, etc. Il est temps de définir ce qu'on a comme salle, et son orientation. var numberOfOpenings : int = 0; var direction : int = 0; // Les directions sont : 0 bas; 1 gauche; 2 haut; 3 droite; var roomTypeId : int = 0; // Les id de room sont : 1 : dead end; 2 : I; 3 : L; 4 : T; 5 : ouverte if(goRight) numberOfOpenings++; if(goLeft) numberOfOpenings++; if(goUp) numberOfOpenings++; if(goDown) numberOfOpenings++; switch(numberOfOpenings) { case 1 : //Dead end roomTypeId = 1; if(goRight) //Ouverture à droite direction = 2; if(goLeft) direction = 0; if(goDown) direction = 3; if(goUp) direction = 1; break; case 2 : //Soit I, soit L if(goRight && goLeft || goUp && goDown) { //I roomTypeId = 2; if(goRight) { direction = 0; } else { direction = 1; } } else { roomTypeId = 3; if(goRight && goUp) { direction = 1; } if(goDown && goRight) { direction = 2; } if(goLeft && goDown) { direction = 3; } if(goUp && goLeft) { direction = 0; } } break; case 3 : //T roomTypeId = 4; //Pour aller plus vite on teste celui qui est fermé. if(!goRight) { direction = 0; } if(!goDown) { direction = 1; } if(!goLeft) { direction = 2; } if(!goUp) { direction = 3; } break; case 4 : //Ouverte roomTypeId = 5; direction = 0; break; } return Vector2(roomTypeId, direction); } /** * Fonction qui teste si y'a besoin de générer une salle au x, z demandé. */ function isThereARoom(x : int, z : int) { var retour : int = -1; if(x == 0 || z == 0 || x == sizeX || z == sizeZ) { retour = 0; } else { // Si on va générer la salle for(j = 0; j < remainingRoomsToGenerate; j++) { if(coordinatesToGenerate[j].x == x && coordinatesToGenerate[j].y == z) { retour = 1; } } if(retour == -1) { //(Si elle va pas etre générée) // On teste voir si elle a déjà été générée if(roomsTable[x,z] == 1) { //Traitée, et elle existe retour = 1; } else { if(roomsTable[x,z] == 0) { //Traitée, et elle est désactivée retour = 0; } } } } // Sinon c'est qu'elle n'existe pas, ou pas encore. return retour; } /** * Fonction qui génère un nombre aléatoire entre start et end basé sur la seed. * end et start sont inclus dans le résultat ( randomWithSeed(0, 5, 0) donne 0,1,2,3,4 ou 5) * Change le résultat en fonction de kind, aussi, afin de pouvoir avoir des nombres aléatoires se chevauchant (0-3 et 0-10 par ex) qui ne donnent pas le meme resultat */ function randomWithSeed(start : int, end : int, kind : int) { if(start == end) return start; var tempSeed : int = seed; // Aléatoire fait un peu à l'arrache. if(start != 0) tempSeed *= ( start * 4 ) % ( start + 10 ) + 1; if(end != 0) tempSeed += ( end * 3 ) % ( end + 100 ) + 1; tempSeed += ( kind * 27 ) % ( kind + 13 ); var retour = tempSeed % (end - start + 1) + start; return retour; } function setRoomsTable(x : int, z : int, value : int) { if(x <= 0 || z <= 0 || x >= sizeX - 1 || z >= sizeZ -1) { return; } else { roomsTable[x,z] = value; } }