.
=
+
+
<
>
X
@

NOUS LES DEVS

Créer des unités

A la conquête du jeu vidéo partie 16

Niveau : expert(e)
</> </> </>

Bâtiment

Pour construire des unités, j’ai besoin de pouvoir cliquer sur des casernes et pour le moment il n’y a pas de notion de bâtiment. Ce que je fais donc, c’est définir tous les bâtiments de la carte, dans la base de données, et pour faire ça j’utilise le shell MongoDB.

Je stocke les différents bâtiments dans l’objet « building » et pour chacun je renseigne les coordonnées, le type de bâtiment (caserne, quartier générale, ville) ainsi que l’appartenance des bâtiments (-1 pour neutre, 0 pour rouge, 1 pour bleu, etc…).

db.maps.update({_id:ObjectId("57261c65c55d81e2aacb052e")},{ "nom" : "Intro", "nbr_joueur" : 2, "map" : [ [ "086", "105", "084", "084", "084", "084", "107", "084", "084", "084", "084", "084", "084", "084", "104" ], [ "105", "085", "022", "168", "150", "005", "008", "005", "002", "022", "168", "159", "022", "159", "073" ], [ "075", "022", "022", "168", "149", "168", "063", "022", "021", "157", "022", "022", "140", "159", "073" ], [ "075", "047", "047", "001", "018", "058", "096", "060", "021", "022", "025", "022", "021", "159", "073" ], [ "075", "022", "022", "021", "022", "073", "105", "085", "021", "047", "025", "047", "149", "022", "073" ], [ "075", "158", "150", "018", "157", "083", "085", "168", "151", "005", "005", "005", "020", "047", "073" ], [ "075", "158", "139", "022", "022", "022", "022", "168", "168", "101", "058", "059", "024", "078", "106" ], [ "095", "060", "158", "158", "022", "022", "022", "022", "022", "022", "073", "085", "022", "022", "073" ], [ "105", "108", "059", "060", "025", "157", "025", "168", "157", "101", "008", "022", "157", "157", "073" ], [ "095", "094", "086", "095", "059", "059", "059", "059", "059", "059", "096", "059", "059", "059", "094" ] ], "building" : { "1-11" : [ "caserne", 1 ], "1-13" : [ "caserne", 1 ], "2-9" : [ "ville", -1 ], "2-12" : [ "qg", 1 ], "2-13" : [ "caserne", 1 ], "3-1" : [ "ville", -1 ], "3-2" : [ "ville", -1 ], "3-13" : [ "caserne", 1 ], "4-9" : [ "ville", -1 ], "4-11" : [ "ville", -1 ], "5-1" : [ "caserne", 0 ], "5-4" : [ "ville", -1 ], "5-13" : [ "ville", -1 ], "6-1" : [ "caserne", 0 ], "6-2" : [ "qg", 0 ], "7-2" : [ "caserne", 0 ], "7-3" : [ "caserne", 0 ], "8-5" : [ "ville", -1 ], "8-8" : [ "ville", -1 ], "8-12" : [ "ville", -1 ], "8-13" : [ "ville", -1 ] }, "height" : 10, "width" : 15 });

Grille curseur

Je mets à jour la construction des différentes grilles et je lance la méthode construct_cursor_grid qui va me permettre de construire la grille du curseur, et j’ajoute le curseur via set_cursor. J’ai récupéré le code de la partie « commande clavier » que j’avais déjà fait.

construct_map(params){
	this.construct_grid(params.map.map);
	this.construct_unit_grid(params.map.map);
	this.construct_cursor_grid(params.map.map,params.index_player,params.map.building);
	this.set_cursor(params.map.width, params.map.height );
}

Je modifie la méthode de construction de la grille pour y ajouter les nouvelles données des bâtiments, si jamais une case contient un bâtiment alors j’ajoute une classe qui contient le nom du type de bâtiment, si le bâtiment est neutre, alors j’ajoute la classe « neutral« , et si jamais le bâtiment peut être capturé alors j’ajoute la classe « catchable« .

construct_cursor_grid(map,index_players,building){
	//ajout de la grid des unités
	$("#grid").append('<div id="map_cursor"></div>');

	map.forEach( (line, index_line) => {
		$("#map_cursor").append('<div class="tr">');
		line.forEach( (col, index_col) => {
			let td_class = "td";
			if(building[index_line+"-"+index_col] != undefined ){
				if( building[index_line+"-"+index_col][1] == index_players ){
					//batiments du joueurs
					td_class += " to_me " + building[index_line+"-"+index_col][0];
				}else if( building[index_line+"-"+index_col][1] == -1 ){
					//case ville vide
					td_class += " " + building[index_line+"-"+index_col][0] + " neutral catchable";
				}else if( building[index_line+"-"+index_col][1] != index_players ){
					//batiment enemis
					td_class += " " + building[index_line+"-"+index_col][0] + " enemies catchable";
				}
			}
			$(`#map_cursor .tr:eq(${index_line})`).append(`<div class="${td_class}" data-x="${index_col}" data-y="${index_line}"><div class="bg"></div></div>`);		
		});
		$("#map_cursor").append('</div>');		
	});
}

Menu

J’ajoute dans l’interface du jeu une balise qui va me permettre de stocker la liste des unités disponibles quand je clique sur une caserne.

<div id="interface_jeu">
	<span id="money"></span>
	<a href="#" id="action_end_turn">Finir le tour</a>
	<ul id="list_units"></ul>
</div>

Création d'unité

Je crée une nouvelle collection dans MongoDB que j’appelle unit et qui me sert à stocker les propriétés des unités comme le nom, les pm (points de mouvement), le prix, etc… Et je crée deux unités, l’infanterie et le tank.

db.unit.insert({"name":"infanterie","type":1,"price":1000,"pm":3});
db.unit.insert({"name":"tank","type":2,"price":2000,"pm":5});

Récupération des unités

Je crée une nouvelle méthode au niveau du serveur qui va me permettre de récupérer l’ensemble des unités.

get_list_unit(){
	return this.mongodb.collection('unit').find({},{"_id":0}).toArray();
}

Je suis passé en mode « chainage de promesses« , ça me permet de faire un code plus propre et de récupérer les unités avant le lancement du serveur.

this.start_db().then( () => {
	e("BDD > Démarrage de la base de données");
	return this.get_list_maps();
}).then( (response) => {
	e("BDD > Chargement des cartes éffectuées");
	this.maps = response;
	return this.get_list_co();
}).then( (response) => {
	e("BDD > Chargement des CO éffectuer");
	this.list_co = this.format_list_co(response);
	return this.get_list_unit();
}).then( (response) => {
	e("BDD > Chargement des unités éffectuer");
	this.units_store = response;
	return this.restore_parties();
}).then( (response) => {
	e("> Parties récupérés");
	if(response != undefined){
		if( response.length > 0 ){
			this.parties = response[0].json_data;
		}
	}
	//initialisation http
	this.init();
});

Envoi des unités

Je renvoie la liste des unités stockées préalablement dans units_store au renvoi de la réponse du lancement d’une partie.

this.send_to_specific_client( this.users[token].socket_id, "message", { action : "response_launch_partie", params : { 
	result 			: true, 
	map 			: this.parties[ params.partie_id ].map,
	aquiletour_index 	: this.parties[ params.partie_id ].courant.aquiletour_index,
	aquiletour  		: this.parties[ params.partie_id ].courant.aquiletour,
	jour			: this.parties[ params.partie_id ].courant.jour,
	money			: this.parties[ params.partie_id ].list_players[ token ].money,
	units_store		: this.units_store,
	index_player    	: this.parties[ params.partie_id ].list_players[ token ].index
} });

Création de la liste

Juste après la création des différentes grilles, je lance la méthode construct_list_units qui va me permettre de remplir la liste des unités.

construct_list_units(units_store){
	let interface_list_units = $("#list_units");
	for(let index in units_store){
		$('<li></li>',{
			"data-type" : units_store[index].name,
			text : units_store[index].name + ' ('+units_store[index].price+')'
		}).appendTo(interface_list_units);	
	}
}

Intéraction avec la carte

Maintenant, je dois faire en sorte de pouvoir cliquer sur une caserne pour afficher la liste des unités disponibles ! Au début du tour d’un joueur et après la création des grilles, j’appelle la méthode event_click_on_map.

launch_start_turn(params){
	
	//...
	
	//bindage des click
	this.event_click_on_map();
}

Dans cette méthode, je détecte juste le clique d’une case et je récupère la position X et Y de celle-ci, et si cette case est une caserne, alors j’affiche la liste des unités disponibles, sinon je masque la liste.

event_click_on_map(){
	$("#map_cursor").off("click.on_map").on("click.on_map",".td", (event) => {
		let elem = $(event.currentTarget);

		this.click_map_position_x = parseInt( elem.attr("data-x") );
		this.click_map_position_y = parseInt( elem.attr("data-y") );
		
		//masqué la liste des unités
		$("#interface_jeu #list_units").hide();
		
		//type click
		if( elem.hasClass("caserne") && elem.hasClass("to_me") ){
			//affichage de la liste
			$("#interface_jeu #list_units").show();	
		}
	});
}

Acheter une unité

Directement après l’affichage de la liste, je crée un système d’événements qui va me permettre de lancer la méthode construct_units pour construire l’unité, et je passe comme argument X, Y et le type d’unité que je veux construire.

//click list
$("#interface_jeu #list_units").off("click.create_unit").on("click.create_unit","li", (event) => {
	//requete de création d'unité
	this.construct_units(this.click_map_position_x, this.click_map_position_y, $(event.currentTarget).attr("data-type"));
});

Et je balance au serveur la requête.

construct_units(x, y, type){
	this.send_to_server("message",{ action : "request_construct_units" , params : { token : this.token, x : x, y : y, type : type } });
}

Achat serveur

Je construis la méthode request_construct_units et je vérifie que le joueur peut acheter l’unité et que la position de la case est valide. Je génère un ID unique pour l’unité et je l’ajoute dans le tableau des unités de la partie. Enfin, j’envoie la nouvelle unité à tout les joueurs de la partie.

request_construct_units(user_socket, params){
	//propriété de l'unité		
	let unit_properties = this.get_properties_unit(params.type);

	// test si argent suffisant et position libre
	if( this.is_unit_can_buying(params, unit_properties) ){
		if( this.is_position_available_for_making_unit(params, unit_properties) ){
			//id de l'unité
			let unit_id = this.get_random_unit_id(this.parties[ this.users[ params.token ].current_partie ].courant.units);

			//ajout unité
			this.parties[ this.users[ params.token ].current_partie ].courant.units[ unit_id ] = {
				x 			: parseInt(params.x),
				y 			: parseInt(params.y),
				owner 		: this.parties[ this.users[ params.token ].current_partie ].list_players[ params.token ].index,
				properties 	: unit_properties
			}
			//décrémentation de l'argent
			this.parties[ this.users[ params.token ].current_partie ].list_players[ params.token ].money -= unit_properties.price;
		
			//envois à tout le monde de la nouvelle unité
			for(let token in this.parties[ this.users[ params.token ].current_partie ].list_players ){
				this.send_to_specific_client( this.users[token].socket_id, "message", { action : "response_request_construct_units", params : { 
					result 				: true, 
					unit				: this.parties[ this.users[ params.token ].current_partie ].courant.units[ unit_id ]
				} });	
			}
		}
	}else{
		//erreur argent insuffisant
		e("Erreur création unité argent insufisant");
	}
}

La méthode is_unit_can_buying me permet de tester si le joueur a assez d’argent pour acheter l’unité.

is_unit_can_buying(params, unit_properties){
	return ( this.parties[ this.users[ params.token ].current_partie ].list_players[ params.token ].money >= unit_properties.price );
}

La méthode building_info me permet de savoir si la case est bien un bâtiment, qu’il appartient au joueur et qu’il a la capacité de construire.

building_info(index_player, building, coordinate){
	if( building[coordinate] == undefined ){
		e("Aucun batiment au coordonnees");
		return false;
	}else{
		let type 		= building[coordinate][0];
		let index_building 	= building[coordinate][1];
		
		if( index_building == index_player ){
			if( type == "caserne" ){
				return true;
			}else{
				e("Ce n'est pas un batiment de construction");
				return false;
			}
		}else{
			e("Ce batiment n'appartient pas au joueur");
			return false;
		}
	}
}

La méthode case_occuped me permet de savoir s’il n’y a pas déjà une unité sur cette case.

case_occuped(units, coordinate){
	if( Object.keys(units).length != 0 ){
		for(let index in units){
			let current_unit_coordinate = units[ index ].y + "-" + units[ index ].x;
			if( coordinate == current_unit_coordinate ){
				e("La case est occupé");
				return true;
			}
		}
	}
	return false;
}

Réponse serveur

Je crée la méthode response_request_construct_units pour gérer la réponse de la création d’unité et je lance directement la fonction add_unit_to_unit_grid qui me permet d’ajouter la nouvelle unité sur la grille adéquate.

response_request_construct_units(params){
	if(params.result){
		this.add_unit_to_unit_grid( params.unit.properties.name + " owner_" + params.unit.owner, params.unit.x, params.unit.y );
	}
}
add_unit_to_unit_grid(unit_class, x, y){
	$(`#map_units .td[data-x=${x}][data-y=${y}]`).addClass(unit_class);
}
map_creation_unites

25/09/2016

Yann Vangampelaere - nouslesdevs -

NOUS LES DEVS

Vous aimez ce que je fais ? Vous voulez que j'en fasse plus ? dans le développement du blog.