JeuWeb - Crée ton jeu par navigateur
déplacement de plusieurs case - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : déplacement de plusieurs case (/showthread.php?tid=2237)

Pages : 1 2


RE: déplacement de plusieurs case - phenix - 03-01-2008

Citation :Ton tableau est donc à deux dimensions (tableau de tableaux de terrains), et pour accéder à une case on prend $tableau[$y][$x] (tableau de lignes) ou $tableau[$x][$y] (tableau de colonnes) ?

On prend $tableau[$numéro de la case] :heuuu:

Voila le code:
Code PHP :
<?php 
echo '
<table class="table_map" cellspacing="0" cellpadding="0">'
;
$x=0;
while (
$x<$hauteur)
{
echo
'<tr>';
$y = 0;
while (
$y<$largeur)
{
$modul = $x*$largeur+$y;
echo
recup_td($r[$modul],$modul,$tab_pos,$tab_objet,$position,$lieu,$quartier,$largeur,$guilde,$groupe,$tab_bat,$tab_case_dep,$tab_decor,$tab_pave,$tab_herbe);
$y++;
}
echo
'</tr>';
$x++;
}
echo
'</table>';

La fonction recup_td, qui choisi la case à mettre:
Code PHP :
<?php 
function recup_td($s,$case,$tab_poss,$tab_objet,$pos_joueur,$lieu,$quartier,$largeur,$guilde,$groupe,$tab_bat,$tab_case_dep,$tab_decor,$tab_pave,$tab_herbe)
{
if (
in_array($case,$tab_poss))
{
//selection de l'éventuel joueur sur la case
$sql = mysql_query('SELECT id,pseudo, orientation, groupe, guilde FROM users WHERE position=\''.$case.'\' and quartier=\''.$quartier.'\' and lieu=\''.$lieu.'\' and statut=\'1\'') or die(mysql_error());
$a = mysql_fetch_assoc($sql);
//s'il n'y a pas de joueur, il y a peut être un monstre
if (empty($a['pseudo']))
{
$sql = mysql_query('SELECT id,nom FROM monstre WHERE position=\''.$case.'\' and quartier=\''.$quartier.'\' and lieu=\''.$lieu.'\'') or die(mysql_error());
$i = mysql_fetch_assoc($sql);
$id_monstre = $i['id'];}
}

//contruction des actions possible si le joueur est proche de la case
$action = '';
$titre = '';
if (
is_proche($pos_joueur, $case, $largeur,1))
{
//Case sur lesquel il est possible de ce déplacer
if (in_array($s,$tab_case_dep) and $pos_joueur !== $case)
{
$action = '<ul><li><a href=vue.php?dep='.$case.'>Se déplacer (1 PM)</a></li></ul>';
$titre = 'Case de déplacement';}
//BATIMENT s'il est proche d'un batiment
elseif (in_array($s,$tab_bat))
{
$action = '<ul><li><a href=batiment/'.clear_bat($s).'.php>Entrer dans le batiment</a></li></ul>'; }

//Changeur de quartier s'il est proche du point de changement de quartier
elseif ($s === 'quartier_herbe' or $s === 'quartier_pave')
{
$action = 'Selectionnez un quartier:<ul>';
$sql = mysql_query('SELECT quartier FROM liste_quartier WHERE lieu=\''.$lieu.'\'') or die(mysql_error());
while (
$t = mysql_fetch_assoc($sql))
{
$action .= '<li><a href=vue.php?quartier='.str_replace(' ','%20',$t['quartier']).'>'.$t['quartier'].'</a> (2 PA/6 PM)</li>';}
$action .= '</ul>';
}
//Accès a la zone résidentiel s'il est proche du point d'accès au résidence
elseif ($s === 'residence_herbe' or $s === 'residence_pave')
{
$action = '<a href=vueresidence.php>Accès à la zone résidentiel.</a>';

}
elseif (
$s === 'coffre')
{
$action = '<ul><li><a href=residence.php?res='.$quartier.'>Gestion du batiment</a></li></ul>';
}
}

//Action sur un joueur
if (!empty($a['pseudo']) and $pos_joueur !== $case and $s !== 'quartier_herbe' and $s !== 'quartier_pave')
{
$action = '<ul><li><a onclick=popup_profil('.$a['id'].');>Profil</a></li><li><a href=attaque.php?cible='.$a['pseudo'].'>Attaquer</a></li><li><a href=sort.php?cible='.$a['pseudo'].'&type=joueur>Sort/compétence</a></li></ul>';
$titre = create_profil($a['pseudo']);
}

//Action sur un monstre
if (!empty($i['nom']) and $pos_joueur !== $case and $s !== 'quartier_herbe' and $s !== 'quartier_pave')
{
$action = '<ul><li><a href=attaquemonstre.php?cible='.$i['id'].'>Attaquer</a></li><li><a href=sort.php?cible='.$i['id'].'&type=monstre>Sort/compétence</a></li></ul>';
$titre = create_profil_monstre($i['id']);
}

//Action sur soi !
if ($pos_joueur === $case)
{
$action = '<ul><li><a onclick=popup_profil('.$a['id'].');>Profil</a></li><li><a href=sort.php?cible='.$a['pseudo'].'&type=joueur>Sort/compétence sur soi</a></li></ul>';
$titre = 'Votre personnage.';

if (
in_array($case,$tab_objet))
{
$sql = mysql_query('SELECT id,objet FROM objet_sol WHERE position = \''.$case.'\' and lieu=\''.$lieu.'\' and quartier=\''.$quartier.'\'') or die(mysql_error());
$action .= '<br /><br /><hr><br />Objet sur la case:<br />';
while (
$q = mysql_fetch_assoc($sql))
{
$action .= '<br />'.$q['objet'].' <a href=vue.php?objet='.$q['id'].'> <img src=image_carte/ramasser.gif /> (1 PA)</a>';}
}
//si le joueur est sur un changeur de quartier.
elseif ($s === 'quartier_pave' or $s === 'quartier_herbe')
{
$action .= '<hr><br />Selectionnez un quartier:<ul>';
$sql = mysql_query('SELECT quartier FROM liste_quartier WHERE lieu=\''.$lieu.'\'') or die(mysql_error());
while (
$t = mysql_fetch_assoc($sql))
{
$action .= '<li><a href=vue.php?quartier='.str_replace(' ','%20',$t['quartier']).'>'.$t['quartier'].'</a> (2 PA/6 PM)</li>';}
$action .= '</ul>';
}
}
//on regarde s'il y a des objets sur la case
if (in_array($case,$tab_objet))
{
$titre .= '<br /><hr><br />Objet sur la case:<br />';
$sql = mysql_query('SELECT objet FROM objet_sol WHERE position = \''.$case.'\' and lieu=\''.$lieu.'\' and quartier=\''.$quartier.'\'') or die(mysql_error());
while (
$q = mysql_fetch_assoc($sql))
{
$titre .= '<br />'.$q['objet'];}
if (empty(
$a['pseudo']) and empty($i['nom']))
{
$bat = '<img src="image_carte/objet_sol.gif" />'; }
}

//BATIMENT s'il y a un batiment on crée l'image
if (in_array($s,$tab_bat))
{
$bat = '<img src="image_carte/bat/'.$s.'.gif" />'; $titre = no_num($s);
}

//Si c'est un changeur de quartier, on crée l'image et on crée les quartier disponible
elseif (($s === 'quartier_pave' or $s === 'quartier_herbe') and $case !== $pos_joueur)
{
$bat = '<img src="image_carte/quartier.gif" />';
$titre = 'Changer de quartier';

$sql = mysql_query('SELECT pseudo FROM users WHERE lieu=\''.$lieu.'\' AND quartier=\''.$quartier.'\' AND position=\''.$case.'\'') or die(mysql_error());
$titre .= '<br /><br /><u>Joueur sur la case</u>:';
while (
$a = mysql_fetch_assoc($sql))
{
$titre .= '<br />'.$a['pseudo'];
}

}
//si c'est un point de résurection ob le crée et on affiche les autre joueur qui sont dessus
elseif (($s === 'resu_pave' or $s === 'resu_herbe') and $case !== $pos_joueur)
{
$bat = '<img src="image_carte/resu.gif" />';
$titre = 'Portail de résurection';

$sql = mysql_query('SELECT pseudo FROM users WHERE lieu=\''.$lieu.'\' AND quartier=\''.$quartier.'\' AND position=\''.$case.'\'') or die(mysql_error());
$titre .= '<br /><br /><u>Joueur sur la case</u>:';
while (
$a = mysql_fetch_assoc($sql))
{
$titre .= '<br />'.$a['pseudo'];
}
}
//Si c'est l'accès a la zone résidentiel, on la crée
elseif ($s === 'residence_herbe' or $s === 'residence_pave')
{
$bat = '<img src="image_carte/residence.gif" />';
$titre = 'Accès à la zone résidentiel.';
}
elseif (
$s === 'coffre')
{
$titre = 'Gestion du batiment'; }
//initialistion de la variable qui contiendra le TD final
$td = '<td ';

//On selectionne le type de fond de la case


//Déclaration
if (in_array($s,$tab_herbe))
{
$td .= 'style="background: url(\'image_carte/herbe.gif\');"'; }
elseif (
in_array($s,$tab_pave))
{
$td .= 'style="background: url(\'image_carte/pave.gif\');"'; }

//font eau pour le batiment du port
elseif ($s === 'port2' or $s === 'port4')
{
$td .= 'style="background: url(image_carte/eau.gif);"'; }
//font bord de l'eau pour le batiment du port
elseif ($s === 'port1' or $s === 'port3')
{
$td .= 'style="background: url(image_carte/bord_eau.gif);"'; }

//On afiche la case de déplacement
elseif (in_array($s,$tab_case_dep))
{
$td .= 'style="background: url(\'image_carte/'.$s.'\');"';
}
//mise des actions, on crée le javascript
$td .= ' onMouseOver="return overlib(\''.$titre.'\', SNAPX, 50, SNAPY, 30, CAPTION, \'Position: Y: '.y($case,$largeur).'/X: '.x($case,$largeur).' | distance: '.calcule_distance($pos_joueur,$case,$largeur).'\');" onClick="return overlib(\''.$action.'\', STICKY, CAPTION, \'Action: \');" onMouseOut="return nd();"';

//Ajout de la class
$td .= ' class="map">';

//Selection de l'image au centre
//JOUEUR
//déclaration de la bodure si besoin
//Joueur de la même guilde
if (!empty($a['guilde']) and $a['guilde'] === $guilde)
{
$bordure = 'style="border: 1px solid #00FF00;"';}
//Joueur du même groupe de chasse
if (!empty($a['groupe']) and $a['groupe'] === $groupe)
{
$bordure = 'style="border: 1px solid #4400ff;"';}
//Joueur en lui même :D
if ($pos_joueur === $case)
{
$bordure = 'style="border: 1px solid #FF0000;"';}

//Récupération de l'avatar en fonction de la classe du personnage
switch ($a['orientation']) {
//Guerrier
case 'Guerrier':
$td .= '<img src="image_carte/perso/guerrier.gif" '.$bordure.' />';
//Chevalier
break; case 'Chevalier':
$td .= '<img src="image_carte/perso/chevalier.gif" '.$bordure.' />';
//Paladin
break; case 'Paladin':
$td .= '<img src="image_carte/perso/paladin.gif" '.$bordure.' />';
//Barbare
break; case 'Barbare':
$td .= '<img src="image_carte/perso/barbare.gif" '.$bordure.' />';
//Trompe la mort
break; case 'Trompe la mort':
$td .= '<img src="image_carte/perso/trompelamort.gif" '.$bordure.' />';
//Berserk
break; case 'Berserk':
$td .= '<img src="image_carte/perso/berserk.gif" '.$bordure.' />';
//Assassin
break; case 'Assassin':
$td .= '<img src="image_carte/perso/assassin.gif" '.$bordure.' />';
//Tueur
break; case 'Tueur':
$td .= '<img src="image_carte/perso/tueur.gif" '.$bordure.' />';
//Ombre
break; case 'Ombre':
$td .= '<img src="image_carte/perso/ombre.gif" '.$bordure.' />';
//Rôdeur
break; case 'Rôdeur':
$td .= '<img src="image_carte/perso/rodeur.gif" '.$bordure.' />';
//Pisteur
break; case 'Pisteur':
$td .= '<img src="image_carte/perso/pisteur.gif" '.$bordure.' />';
//Traqueur
break; case 'Traqueur':
$td .= '<img src="image_carte/perso/traqueur.gif" '.$bordure.' />';
//Mage
break; case 'Mage':
$td .= '<img src="image_carte/perso/mage.gif" '.$bordure.' />';
//Nécromant
break; case 'Nécromant':
$td .= '<img src="image_carte/perso/necromant.gif" '.$bordure.' />';
//Elemeantaliste
break; case 'Elémentaliste':
$td .= '<img src="image_carte/perso/elementaliste.gif" '.$bordure.' />';
//Prêtre
break; case 'Prêtre':
$td .= '<img src="image_carte/perso/pretre.gif" '.$bordure.' />';
//Prêtre guerrier
break; case 'Prêtre guerrier':
$td .= '<img src="image_carte/perso/pretreguerrier.gif" '.$bordure.' />';
//Ermite
break; case 'Ermite':
$td .= '<img src="image_carte/perso/ermite.gif" '.$bordure.' />';
}

//Les joueurs qui on réserver un monstre le voie entouré d'un carré bleu
if (!empty($groupe))
{
$sql = mysql_query('SELECT id_monstre FROM groupe WHERE nom=\''.$groupe.'\'') or die(mysql_error());
$zer = mysql_fetch_assoc($sql);
if (
$id_monstre === $zer['id_monstre'])
{
$bordure = 'style="border: 1px solid #4400ff;"';}
}

//MONSTRE, on affiche le monstre
if (!empty($i['nom']) and $s !== 'quartier_pave' and $s !== 'quartier_herbe')
{
$td .= '<img src="image_carte/monstre/'.clear_bat($i['nom']).'.gif" '.$bordure.' />'; }

//DECOR, on affiche l'éventuel d"cor, (rocher, arbre, tombe...)
elseif (in_array($s,$tab_decor))
{
$td .= '<img src="image_carte/'.$s.'.gif" />';
}
$td .= $bat;

//On referme la TD
$td .= '</td>';

return
$td;
}

Comme sa vous voyer comment je construis la map Cool

Amicalement,

Phenix


RE: déplacement de plusieurs case - naholyr - 03-01-2008

zOMG, Warning, requête dans une boucle, système de stockage étrange, aucun système de coordonnées directe.

Je ne peux rien pour toi tant que tu n'auras pas réglé ces problèmes, implémenter un A* en ayant une requête à chaque accès à une case, c'est du suicide. La réalisabilité de cet algorithme tient à la simplicité d'accès aux données de chaque case (calcul du coût, c'est le coût principal, si cette fonction n'est pas en O(1), laisse tomber).


Dans le doute, je te fournis quand-même ce que j'avais réalisé, il ne manquait qu'à remplir les trous, mais sans requête. C'est bourré de commentaires donc je te laisse à de saines lectures :
Code PHP :
<?php 
/**
* Liste des cases adjacentes à [$x,$y] pour le tableau donné
*
* @param Array $tableau La liste des cases
* @param int $x
* @param int $y
* @return Array Liste des cases adjacentes (tableau de couples [$x,$y])
*/
function cases_adjacentes(&$tableau, $x, $y)
{
$cases_adjacentes = array(
array(
$x-1, $y-1), array($x-1, $y), array($x-1, $y+1),
array(
$x, $y-1), array($x, $y), array($x, $y+1),
array(
$x+1, $y-1), array($x+1, $y), array($x+1, $y+1));
// Exclure les murs et les cases "hors-tableau"
return $cases_adjacentes;
}

/**
* Coût d'accès à la case [$x,$y] (selon le type de terrain)
*
* @param Array $tableau La liste des cases
* @param int $x
* @param int $y
* @return int coût d'accès
*/
function cout_case(&$tableau, $x, $y)
{
switch (
$tableau[$y][$x]) {
// Adapter en fonction des types de terrain existant
case 'mur':
return
9999;
default:
return
1;
}
}

/**
* Chemin et coût total pour aller de ($x1,$y1) à ($x2,$y2)
* Implémentation de A* avec une heuristique nulle
* http://www.policyalmanac.org/games/aStarTutorial.htm
* http://fr.wikipedia.org/wiki/Pathfinding
* http://fr.wikipedia.org/wiki/Algorithme_A%2A
*
* @param Array $tableau La liste des cases
* @param int $x1 X départ
* @param int $y1 Y départ
* @param int $x2 X arrivée
* @param int $y2 Y arrivée
* @param int $coutMax Cout maximum du déplacement autorisé
* @param string $fonctionCout Nom de la fonction calculant le cout d'accès à une case,
* fonction acceptant 3 paramètres : le plateau, X, et Y
* @param string $fonctionHeuristique Nom de la fonction "d'heuristique" (détermine l'"intérêt" d'une
* position intermédiaire par rapport à une autre, on prend typiquement la distance pour indiquer une
* volonté d'utiliser le chemin le plus direct, quite parfois à ne pas utiliser le moins coûteux),
* fonction acceptant 3 paramètres : le plateau, X, et Y
* @return Array [ Array $chemin (liste des couples [x,y]), int $cout ]
*/
function chemin(&$tableau, $x1, $y1, $x2, $y2, $coutMax = null)
{
// Note : pour simplifier les traitements et pouvoir utiliser des cases comme index de tableau,
// on utilisera une représentation sous forme de chaine dans le déroulement de l'algo
$open = array(); // liste ouverte (cases à traiter)
$closed = array(); // liste fermée (cases traitées)
$G = array(); // coût total cumulé d'accès par case
$parent = array(); // case parente d'une autre (si $parent[$v] = $c,
// alors on passe par $c pour atteindre $v)
// Valeurs initiales
$case1 = "$x1,$y1"; // case de départ
$case2 = "$x2,$y2"; // case d'arrivée
$open[] = $case1; // la case de départ est déjà traitée d'office
$G[$case1] = 0; // coût de la case de départ = 0
$c = null; // case courante
while (true) {
// si la liste ouverte est vide, alors échec dans la recherche du chemin
if (count($open) == 0) {
return
false;
}
// trouver la case courante : celle qui a le coût le plus faible dans la liste ouverte
$Gmin = null;
$x = $y = $i = null;
foreach (
$open as $ic => $c) {
list(
$xc,$yc) = explode(",", $c);
if (
$Gmin === null || $G[$c] < $Gmin) {
$Gmin = $G[$c];
$x = $xc; $y = $yc; $i = $ic;
}
}
$c = "$x,$y"; // Nouvelle case courante
if ($c == "$x2,$y2") {
// Arrivée atteinte !
break;
}
// déplacer dans la liste fermée
$closed[] = "$x,$y"; // ajout dans la liste fermée
array_splice($open, $i, 1); // suppression dans la liste ouverte
// pour chaque case adjacente...
$voisines = cases_adjacentes($tableau, $x, $y);
foreach (
$voisines as $voisine) {
list(
$xx,$yy) = $voisine;
$v = "$xx,$yy";
if (
in_array($v,$closed)) {
// case déjà traitée, on l'ignore
continue;
}
$cout = cout_case($tableau, $xx, $yy);
if (
$coutMax !== null && $G[$c] + $cout > $coutMax) {
// coût total de déplacement jusqu'à cette case > coût maximum, on l'exclut
continue;
}
// la case en cours d'analyse fait partie d'une des cases possibles pour le calcul
// du chemin :
if (!in_array($v, $open)) {
// si elle est absente de la liste ouverte, on l'y ajoute
$open[] = $v; // Ajout à la liste ouverte
$G[$v] = $G[$c] + $cout; // Calcul de son coût total cumulé
$parent[$v] = $c; // On note son parent (en l'occurrence la case courante)
}
else {
// sinon, on récupère son coût précédemment calculée, et on le compare au nouveau
$ancienG = $G[$v];
$nouveauG = $G[$c] + $cout;
if (
$nouveauG < $ancienG) {
// meilleur coût pour ce chemin : on a trouvé un meilleur parent pour cette case
$parent[$v] = $c;
}
}
}
}
// on a atteint la case d'arrivée, on remonte le chemin à l'envers
$chemin = array(array($x2,$y2));
$p = null; // parent de la case courante pendant le parcours du chemin inversé
$coutTotal = 0; // cout total cumulé du chemin
while ($p != $case1) { // tant que la prochaine case n'est pas la case de départ...
list($x,$y) = explode(",", $c);
$coutTotal += cout_case($tableau, $x, $y);
$p = $parent[$c];
array_unshift($chemin, explode(",",$p)); // ajout en tête (parcours inversé)
$c = $p;
}
// on retourne le chemin et son coût total
return array($chemin, $coutTotal);
}

// exemple (calcul du chemin de [2,3] à [7,5]) : list($chemin, $cout) = chemin($tableau, 2, 3, 7, 5);



RE: déplacement de plusieurs case - mdcarter - 07-01-2009

Je me permet de déterrer ce sujet,
j'ai tenté l'implémentation de ton algo naholyr, le soucis étant que l'algo s'amuse à passer en plein milieu d'une case qui à un "cout" de 9999 O_o
J'ai beau lire et relire le code, j'ai du mal à tout saisir, même après plusieurs articles sur A*
[Image: 10bgn-b7b425e48c887dc41ccf2fe0726645e7.4...iginal.png]
[Image: 10bgn.png]
Oui c'est un test, les graph ne sont pas de moi évidemment Wink


RE: déplacement de plusieurs case - wild-D - 07-01-2009

ben en théorie; son algo passera automatiquement par une case 9999 si il n'y pas de chemin pour se rendre de la case de départ à la case d'arrivée.:p

d'après moi y manque un détail genre vérifier que le poids du chemin n'est pas supériieur à 9999) donc à priori c'est une vérif à faire en post traitement de la boucle (après avoir déplier la solution : ou pendant que tu déplie la solution t'assurer que y a pas de case à 9999) pour que t'ai un résultat cohérent (et tu retourne genre chemin le plus court = rester sur place ou alors un code d'erreur FALSE).

p.s.
note que si il te retourne le passage par une case 9999 alors qu'il y a des chemins plus court, là c'est que y a un beug

re-edit; en fait non, t'es censé pas dépasser coutmax; sauf que faut pas le laisser à NULL, ou éviter de lui donner une valeur astoomique ^^ c'est là que les ennui peuvent apparaitre


RE: déplacement de plusieurs case - mdcarter - 07-01-2009

J'ai essayer plusieurs valeur du coutmax, cela ne change jamais le chemin pris par le script Confused
Et oui je confirme (voir ci-dessus) il y à des chemins plus court sans montagne (case à 9999)


RE: déplacement de plusieurs case - wild-D - 07-01-2009

moi y en avait pas regardé les images :p
j'ai été voir 10bgn.png
y a un petit truc:
si je regarde l'image elle est apparemment avec un système à cases hexagonales; or a priori l'algo fourni est pour un système traditionnel (cases carrées).
Mon avis ça doit poser problème; faut suremment faire des adaptation sur la définitions des cases adjacentes, etc... Wink


RE: déplacement de plusieurs case - Ruz - 07-01-2009

un algo pour cases hexagonales???
hum, là, ca m'intéresse tout de suite plus ^^...

Me pencherai sur ca dès que j'ai du temps...