JeuWeb - Crée ton jeu par navigateur
Algorithme d'ombre - 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 : Algorithme d'ombre (/showthread.php?tid=3119)

Pages : 1 2


Algorithme d'ombre - phenix - 01-10-2008

Bonjour à tous,

Sous ce nom obscure ce cache une chose que tout bon "tape case" doit avoir: la possibilité de "noircir" les cases que le personnage ne voie pas, en gros celle qui sont derrière un décors.

J'arrive absolument pas a le faire, mais alors pas du tout, je ne sais pas par ou commencer.

Mes outils:

1 cartes ou chaque case est une cellule de tableau.
C'est une carte X/Y commencent en 0/0 et finissant en 23/16.
Le personnage ce déplace sur la carte, qui elle est fixe.

Quelques screens:

[Image: screen_6.jpg]
[Image: screen_2.jpg]

Comment gériez-vous cela ? Comment detecter si le personnage vois la case ou s'il y a un obstacle dans le champs de vision ?

Merci d'avance,

Phenix, perdu...


RE: Algorithme d'ombre - Ekilio - 01-10-2008

Salut,

Un algo tout bête, imaginé sur le pouce :

Code PHP :
<?php 
$carte_ombre
= array_fill(0, 23, array_fill(0, 16, true));

// Donc ici on a $carte_ombre qui est un tableau contenant uniquement des true.
// On va passer la case où est le joueur à false : il peut toujours se voir lui-même.
$carte_ombre[$x_joueur][$y_joueur] = false;

// Ensuite le joueur a droit de se voir sur une certaine portée
$portee = 3;

// On va donc déjà déterminer horizontalement et verticalement, ce qu'il voit
// Pour ça très simple : on va utiliser deux boucles for pour chaque direction.
// Le principe est : on part du joueur et on va dans la direction voulue.
// Si un obstacle bloque le joueur, il ne voit pas plus loin.
$bloque = false;
for(
$i = $x_joueur; $i <= $x_joueur + $portee; $i++)
{
if(
obstacle($i, $y_joueur) || $bloque)
{
$bloque = true;
} else {
$carte_ombre[$i][$y_joueur] = false;
}
}
$bloque = false;
for(
$i = $x_joueur; $i >= $x_joueur - $portee; $i--)
{
if(
obstacle($i, $y_joueur) || $bloque)
{
$bloque = true;
} else {
$carte_ombre[$i][$y_joueur] = false;
}
}
$bloque = false;
for(
$i = $y_joueur; $i <= $y_joueur + $portee; $i++)
{
if(
obstacle($x_joueur, $i) || $bloque)
{
$bloque = true;
} else {
$carte_ombre[$x_joueur][$i] = false;
}
}
$bloque = false;
for(
$i = $y_joueur; $i >= $y_joueur + $portee; $i--)
{
if(
obstacle($x_joueur, $i) || $bloque)
{
$bloque = true;
} else {
$carte_ombre[$x_joueur][$i] = false;
}
}

Voila pour les directions "simples". Maintenant, il va falloir complexifier un petit peu les choses pour les directions en diagonal. Là, ça dépends vraiment de comment tu veux gérer ton ombre. La manière la plus simple, pour moi, est de gérer ça en décalant le joueur, ce qui implique de coder le tout en fonction récursive :

Code PHP :
<?php 
$carte_ombre
= array_fill(0, 23, array_fill(0, 16, true));

function
decouvre_carte($carte_ombre, $x_joueur, $y_joueur, $portee)
{
// On calcule comme expliqué précédement les directions "de base"
$bloque = false;
for(
$i = $x_joueur; $i <= $x_joueur + $portee; $i++)
{
if(
obstacle($i, $y_joueur) || $bloque)
{
$bloque = true;
} else {
$carte_ombre[$i][$y_joueur] = false;
}
}
$bloque = false;
for(
$i = $x_joueur; $i >= $x_joueur - $portee; $i--)
{
if(
obstacle($i, $y_joueur) || $bloque)
{
$bloque = true;
} else {
$carte_ombre[$i][$y_joueur] = false;
}
}
$bloque = false;
for(
$i = $y_joueur; $i <= $y_joueur + $portee; $i++)
{
if(
obstacle($x_joueur, $i) || $bloque)
{
$bloque = true;
} else {
$carte_ombre[$x_joueur][$i] = false;
}
}
$bloque = false;
for(
$i = $y_joueur; $i >= $y_joueur + $portee; $i--)
{
if(
obstacle($x_joueur, $i) || $bloque)
{
$bloque = true;
} else {
$carte_ombre[$x_joueur][$i] = false;
}
}

// On regarde pour chaque diagonale si on peut y voir
if($portee > 1)
{
if(!
obstacle($x_joueur - 1, $y_joueur - 1))
{
$carte_ombre = decouvre_carte($carte_ombre, $x_joueur - 1, $y_joueur - 1, $portee - 1);
}
if(!
obstacle($x_joueur - 1, $y_joueur + 1))
{
$carte_ombre = decouvre_carte($carte_ombre, $x_joueur - 1, $y_joueur + 1, $portee - 1);
}
if(!
obstacle($x_joueur + 1, $y_joueur + 1))
{
$carte_ombre = decouvre_carte($carte_ombre, $x_joueur + 1, $y_joueur + 1, $portee - 1);
}
if(!
obstacle($x_joueur + 1, $y_joueur - 1))
{
$carte_ombre = decouvre_carte($carte_ombre, $x_joueur + 1, $y_joueur - 1, $portee - 1);
}
}

return
$carte_ombre;
}

$carte_ombre = decouvre_carte($carte_ombre, $x_joueur, $y_joueur, 3); // Pour une portée de 3, bien sûr

Et ensuite, il suffit de n'afficher le masque que sur les cases pour lesquelles $carte_ombre[$x][$y] est à true. Je suis pas sûr que ça marche, j'ai pas testé, mais l'idée me semble correcte.

Mes deux centimes du soir :-)


RE: Algorithme d'ombre - phenix - 02-10-2008

C'est pas mal fichu, mais une chose me gène un peu, a moins que j'ai mal vu, il va blocker la vue, seulement en ligne droite, pas en format "angle".

exemple:


P: joueur
A: arbre,
O: ombre,
C: case vide

COOOOOC
CCOOOCC
CCCACCC
CCCPCCC
CCCCCCC

Je sais pas si c'est suffisamment clair comme schéma...
L'idée reste pas mal en tout cas.


RE: Algorithme d'ombre - Ekilio - 02-10-2008

Hum... J'ai pas le courage de coder ce soir, mais je pense que tu peux arranger ça en utilisant, au lieu de true et false, des entiers.

Dans ce cas, l'idée serait la suivante : tu as un tableau avec que des 0. Si un objet passe, on interromps le cycle récursif (comme dans mon exemple), mais en plus on remplit les cases autour de l'objet avec des 1. Et dans les tests, on remplace :

if(obstacle($i, $y_joueur) || $bloque)

par

if(obstacle($i, $y_joueur) || $carte_ombre[$i][$y_joueur] == 1 || $bloque)

Si le test est faux, on passe la case à 2.

Et dans ce cas :

- Une case avec 0 est une case qui est hors de portée de la vue du joueur
- Une case avec 1 est une case qui est à portée du joueur, mais masquée par un obstacle
- Une case avec 2 est visible par le joueur

Ca permet de nuancer un peu et de faire varier la forme des ombres.

Je sais pas si je suis très clair...


RE: Algorithme d'ombre - lemouix - 03-10-2008

Bonjour,

En gros, tu as ton tableau qui contient une carte et le deuxième avec des 1 ou 0 pour la visibilité :p

Est ce que vous masquez la carte à l'affichage ? Pour le masquage du contour, il suffit de balayer les éléments du tableau qui sont:
x-1,y
x+1,y
x,y+1
x,y-1
x+1,y+1
x-1,y-1
x-1,y+1
x+1,y-1

Ensuite, selon ta portée tu réalises ton cycle récurcif sur les cases autour Smile:
Boucle 1:
on découvre à case + 1 en mettant un 1 dans ton tableau Visible[][]
Boucle 2:
On découvre à case + 2 en mettant un 1 dans ton tableau Visible[][]
(etc pour tes n cases visibles)

Après la question est: est ce que ton personnage ou la vision part du centre (dans ce cas là mon algo marche) ou bien est ce que ça part de n'importe où et dans ce cas là, tu devras faire:

on stocke les cases initiales autour du joueur à découvrir
Boucle 1:
on découvre à case + 1 en mettant un 1 dans ton tableau Visible[][]
on stocke dans un tableau secondaire les prochaines cases à découvrir
Boucle n:
On découvre les cases autour de la case du tableau si par exemple:
il n'y a pas de mur, le joueur peut voir la prochaine case
Il y a un mur, la case derriere le mur ne sera pas découverte,....

Ce principe est exactement celui du démineur :p

A bientôt !


RE: Algorithme d'ombre - keke - 03-10-2008

Hum ...

On a tendance à se limiter à la notion de cases permissive à la vue et la case obstruante ... mais si sur tes cases tu as un grillage, ou de 2 piliers cote à cote ... La personne pourra théoriquement voir à travers le grillage ou entre les piliers.

Par ailleurs, un truc qui m'a toujours épaté, c'est que dans le cas d'un mur, on sache à priori constamment son épaisseur (le graphisme étant une simple ligne ...)

Je ne donne pas de solution technique, celle proposée au dessus me semblant tout à fait correcte, mais je me dis qu'il pourrait y avoir mieux à proposer à nos joueurs...

Kéké qui n'a pas de gestion des obstacles sur les vues de Magdales


RE: Algorithme d'ombre - lemouix - 03-10-2008

Yop kéké,

Perso, à part ce genre de solution et donner à chaque objet une valeur de transparence :p du genre:
grillage 1: transparence = 2 cases plus loin
grillage 2: transparence = 1 case

...


RE: Algorithme d'ombre - lemouix - 03-10-2008

Pour info, pour le dev du moteur de jeux qui nous permettra de créer X types de jeux, nous n'avons pas encore de représentation graphique... y aura t-il une map... que du texte... perso, j'aime bien les reprensentations, mais après ma question est la suivante:

Là, c'est bien beau, on donne du code, mais comment réalisez vous l'affichage ? un cadrillage de gif bien moche ? du flash ? ...


RE: Algorithme d'ombre - jo_link_noir - 14-10-2008

Perso j'aime bien les cartes iso, alors moi j'afficherais une carte isométrique. X-ZoD avais fait des tutos ici (un exemple à la fin)

Sinon, tu as réussi à faire l'algo ? j'ai tenter pour le fun et j'obptient ce resultat :

[Image: capture9ej5.png]

P -> personnage
O -> obstacle
C -> case visible
A -> case cacher

et le code (pas optimiser du tout)
Code PHP :
<?php
//calcule l'angle en degres (sur 360°)
function angle($x,$y)
{
$module = sqrt($x*$x + $y*$y);
$angleCos = acos($x / $module);
$angleSin = asin($y / $module);

if(
$angleSin < 0)
if(
$angleCos > pi()/2)
$angleCos = pi()*2 - $angleCos;
else
$angleCos = pi()*2 + $angleSin;

return
180*$angleCos/pi();
}

//array( ["x,y"] => type de terrain (pour la transparence par exemple (pas gerer ici))
$c_obstacle = array(
'5,6'=>3,
'3,2'=>3,
'5,3'=>3,
'8,8'=>3,
'8,9'=>3,
'13,14'=>3,
'20,5'=>3,
'18,10'=>3,
'16,15'=>3,
'12,14'=>3
);

$angle_obstacle = array();

$jx = 13;
$jy = 13;

//cherche les obstacles, la fourchette de l'angle et leur distance par rapport au joueur
foreach($c_obstacle as $coord=>$type){
list(
$x,$y) = explode(",", $coord);
$x -= $jx;
$y -= $jy;
/*definit l'anglez de chaque coin de la case
(0.5 => l'obstacle prend toute la case
0.45=> l'obstacle prend 90% de la case)*/
$angle_coin = array(
angle($x+0.45, $y+0.45),
angle($x+0.45, $y-0.45),
angle($x-0.45, $y-0.45),
angle($x-0.45, $y+0.45)
);


$angle_obstacle[] = array(
min($angle_coin), //angle min
max($angle_coin), //angle max
sqrt($x*$x + $y*$y) //distance
);
}

for(
$y=25;$y>=1;$y--){
for(
$x=1;$x<=25;$x++){
$cacher = false;

//regarde si la case est cacher par un obstacle
foreach($angle_obstacle as $values){
//zone d'ombre à porter (pas obligatoireent sur la case traiter)
if($values[2] < sqrt(pow($x-$jx, 2) + pow($y-$jy, 2))) {
$angle = angle($x-$jx,$y-$jy);
//verifie si la zone d'ombre est bien sur la case en cours de traitement
if($values[0] <= $angle AND $angle <= $values[1]){
$cacher = true;
//case cacher, pas la peine de continuer à chercher
break;
}
}
}

if(isset(
$c_obstacle["$x,$y"]))
echo
'<span style="color:red">O </span>';
elseif(
true === $cacher)
echo
'<span style="color:black">A </span>';
elseif(
$jx == $x AND $jy == $y)
echo
'<span style="color:green">P </span>';
else
echo
'<span style="color:gray">C </span>';
}
echo
'<br />';
}

echo
'<pre>';
print_r($angle_obstacle);
?>

bonne nuit


RE: Algorithme d'ombre - phenix - 16-10-2008

J'avoue n'avoir strictement rien comprit a ce code, je dois pas avoir le niveau en trigo :respect:

En tout cas, sa marche, je suis en train d'analyser et de modifier ce que je peux pour l'implanter dans la vallée des larmes.

Quand j'aurais fini (si je fini) je posterais la fonction, sa pourra servir a d'autre Wink. Grand merci à toi.

(Vont faire une drôle de tête mes joueurs :goodSmile

[edit]

Il doit y avoir un problème quelques part, parce qu'il arrive a me trouver des cases qui n'existe pas...

[Edit 2]: c'est en bidouillant qu'on reproduit les bug. Ton code semble foirer en largeur, en déplacent le personnage on a un joli bug:

Code PHP :
<?php
//calcule l'angle en degres (sur 360°)
function angle($x,$y)
{
$module = sqrt($x*$x + $y*$y);
$angleCos = acos($x / $module);
$angleSin = asin($y / $module);

if(
$angleSin < 0)
if(
$angleCos > pi()/2)
$angleCos = pi()*2 - $angleCos;
else
$angleCos = pi()*2 + $angleSin;

return
180*$angleCos/pi();
}

//array( ["x,y"] => type de terrain (pour la transparence par exemple (pas gerer ici))
$c_obstacle = array(
'5,6'=>3,
'3,2'=>3,
'5,3'=>3,
'8,8'=>3,
'8,9'=>3,
'13,14'=>3,
'20,5'=>3,
'18,10'=>3,
'16,15'=>3,
'12,14'=>3
);

$angle_obstacle = array();

$jx = 15;
$jy = 15;

//cherche les obstacles, la fourchette de l'angle et leur distance par rapport au joueur
foreach($c_obstacle as $coord=>$type){
list(
$x,$y) = explode(",", $coord);
$x -= $jx;
$y -= $jy;
/*definit l'anglez de chaque coin de la case
(0.5 => l'obstacle prend toute la case
0.45=> l'obstacle prend 90% de la case)*/
$angle_coin = array(
angle($x+0.45, $y+0.45),
angle($x+0.45, $y-0.45),
angle($x-0.45, $y-0.45),
angle($x-0.45, $y+0.45)
);


$angle_obstacle[] = array(
min($angle_coin), //angle min
max($angle_coin), //angle max
sqrt($x*$x + $y*$y) //distance
);
}

for(
$y=25;$y>=1;$y--){
for(
$x=1;$x<=25;$x++){
$cacher = false;

//regarde si la case est cacher par un obstacle
foreach($angle_obstacle as $values){
//zone d'ombre à porter (pas obligatoireent sur la case traiter)
if($values[2] < sqrt(pow($x-$jx, 2) + pow($y-$jy, 2))) {
$angle = angle($x-$jx,$y-$jy);
//verifie si la zone d'ombre est bien sur la case en cours de traitement
if($values[0] <= $angle AND $angle <= $values[1]){
$cacher = true;
//case cacher, pas la peine de continuer à chercher
break;
}
}
}

if(isset(
$c_obstacle["$x,$y"]))
echo
'<span style="color:red">O </span>';
elseif(
true === $cacher)
echo
'<span style="color:black">A </span>';
elseif(
$jx == $x AND $jy == $y)
echo
'<span style="color:green">P </span>';
else
echo
'<span style="color:gray">C </span>';
}
echo
'<br />';
}

echo
'<pre>';
print_r($angle_obstacle);
?>

C'est dommage car sa semblais fonctionner correctement :'(