Bonjour,

Je suis bloqué sur une partie dans mon code je vous explique, je suis en train de faire un jeux en html avec la balise canvas , j'arrive à générer une map qui se place dans un canvas avec comme id canvasMap et mon joueur se trouve sur un autre canvas par dessus afin d'éviter de regénérer la map en permanance dans ma boucle de jeux étant donné qu'elle contient plus de 1800 cellules c'est gourmant à refaire jusque la tout va bien. La ou je suis bloqué c'est sur la partie joueur, j'ai initialisé la position de mon joueur au chargement et quand je clique ça donne la position final du joueur. Afin de le déplacer j'ai crée une fonction de pathfinding qui stock les coordonnées x et y du chemin que le joueur doit parcourir dans un tableau et j'aimerais déplacer le joueur sauf que ma boucle de jeux est basée sur un setInterval avec une attente de 1000/FPS

et je ne sais pas comment mouvoir le personnage , en espérant avoir été claire, je vous remercie d'avance.

63 réponses


voici la boucle principale

function boucle()
{
    timer = setInterval(function()
    {
      //on efface le perso
      contextJoueur.clearRect(0, 0, canvasJoueur.width, canvasJoueur.height);  

      //redessine le perso  
      dessinerPersonnage(initPosXplayer,initPosYplayer,contextJoueur,largeurJoueur,hauteurJoueur);
    },1000/fps);
}

le tableau qui contient les couples de coordonnées

var parcours = [];
 //acceder au position  : parcours[indice].x  et parcours[indice].y

le souci c'est que si je parcours le tableau dans la boucle ca va trop vite et du coup on ne voit pas le déplacement du joueur au clique il se retrouve directement dans la destination final.

mouai :)
englober ton setinterval dans une function je trouve ça louche...
ftps c une variable globale ?
t'utilise un object contextJoueur pour effacer ton sprite mais pas pour le redessinner : étrange...
ton parcours est lourd de sens ;)

bref, je capte pas grand chose...
donc je ne fais qu'imaginer : tu veux qu'un sprite suive un chemin (peut-être un labyrinthe ?) mais pas trop vite, pour qu'on puisse le voir ?
c'est ça ? :)

il est englobé dans une fonction car au debut j'initialise tout (charger les images etc) et une fois chargé j'apelle cette fonction pour lancer le jeux et si je le redessine avec dessinerPersonnage

var canvasJoueur = document.getElementById('canvasJoueur');
var contextJoueur = canvasJoueur.getContext('2d');
var parcours = [];
hauteurCellule = 106;
largeurCellule = 106;
hauteurJoueur = 159;
largeurJoueur = 106;
var initPosXplayer = 5;
var initPosYplayer = 4;
var fps = 35;
function initialisation()
{
  //charge les tuiles de la map
  chargerImageCarte(terrain,contextMap,largeurCellule,hauteurCellule);

  //charge la tuile du joueur
  chargementPersonnage(initPosXplayer,initPosYplayer,contextJoueur,largeurJoueur,hauteurJoueur);
}
function boucle()
{
   timer = setInterval(function()
    {
      //on efface le perso
      contextJoueur.clearRect(0, 0, canvasJoueur.width, canvasJoueur.height); 
      dessinerPersonnage(initPosXplayer,initPosYplayer,contextJoueur,largeurJoueur,hauteurJoueur);
    },1000/fps);
}
function dessinerPersonnage(x,y,context,largeurCellule,hauteurCellule)
{
    var posX,posY;
    var coefAjustement = hauteurCellule-((hauteurCellule*106)/hauteurCellule);
    for(var i = 0 ; i < listCase.length ; i++)
    {
        if(listCase[i].id==y+"-"+x)
        {
            posX = listCase[i].x;
            posY = listCase[i].y-hauteurCellule+coefAjustement;
            break;
        }
    }
    context.drawImage(personnage,posX,posY,largeurCellule,hauteurCellule);
}
function start()
{
  initialisation();
  boucle();
}

donc si ce que je fait à du sens tu n'as juste pas tout le code tout simplement et oui je voudrais qu'il suive un chemin qui est contenu dans le tableau parcours et pas trop vite pour qu'on puisse le voir

j'ai retiré la fonction boucle pour le moment étant donné qu'elle sert uniquement pour afficher l'état du joueur et je l'ai mise dans l'évènement clique en changeant la tempo

hello,
tout d'abord il y a quelques erreurs dans ton code (liste non exaustive) :

  • .getContext n'est pas une méthode d'un élément dom (utilise la console)
  • et si je ne me trompe ((x*106)/x) = 106, bref ton coefAjustement sera toujours = à 0 et je vois pas trop à quoi il sert....

cela, dit, avant de suivre un parcours as-tu déjà déplacé une div de manière fluide et visible ?
car il est inutile de l'effacer tu modifies juste son left et top

var canvasJoueur = document.getElementById('canvasJoueur'); // qui a position absolute
canvasJoueur.style.left = 300 + 'px';
canvasJoueur.style.top = 200 + 'px';

j'ai mis une valeur fixe de 106 tout simplement, ca va être modifié plus tard il servait à placer le perso il y a un produit en croix à faire enfin bref c'est du test en gros, deplacer une div j'y est pensé sauf que la map depasse de la taille du canvas mais est quand même généré du coup si je deplace la div ca ne deplacera pas la map on aura le même visuel simplement deplacé

Et je débute je n'avais jamais joué avec la balise canvas, donc quitte à avoir une liste complète de mes erreurs je suis prenneur ;)

en revanche j'ai lu quelque chose tout à l'heure context.translate(x,y); qui pour le coup pourrait peut-être servir

en effet translate peut t'être utile.
je ne sais pas trop ce que tu appelles canvas et context...
je vais me répéter : commence par un truc simple, genre :
html

<div class="map">
    <div id="sprite"></div>
</div>

css :

.map{position:relative;width:600px;height:600px;margin:auto;border:1px solid #000;}
#sprite{position:absolute;width:50px;height:50px;background:tomato;left:0:top:0;}

et enfin un ti js

var sprite = document.querySelector('#sprite');
var posInitial = {x:0, y:0};
var pos = posInitial;
var posFinal = {x:200, y:100};

// je sais po si c top mais bon
var dx = (posFinal.x - posInitial.x) / (posFinal.y - posInitial.y);
var dy = (posFinal.y - posInitial.y) / (posFinal.x - posInitial.x);

var interval = setInterval(function(){
    if(pos.x > posFinal.x || pos.y > posFinal.y)clearInterval(interval);
    pos.x+=dx; pos.y+=dy;
    sprite.style.left = pos.x +'px';
    sprite.style.top = pos.y + 'px';
}, 5);

Canvas fait référence à la balise du HTML5 canvas :

<canvas id="canvasJoueur" width="500" height="500">
            Message pour les navigateurs ne supportant pas encore canvas.
</canvas>

une balise pour dessiner comme svg par exemple

et le contexte est pour savoir dans quel contexte de dessin (2D ou 3D) le script va pouvoir agir qui en fonction de 2D ou 3D va nous donner accès à certaines fonction de dessin.

ok, j'en apprends tous les jours ;)
donc laisse tomber tout ce que g écrit précédemment, j'avais pas compris ton pb, sorry ;)

Pas de souci ;), faut dire aussi que je n'ai pas été très explicite non plus sur les techno employés etc..

mais du coup j'ai réfléchi(oui ca m'arrive) et je pense avoir réussi donc en gros dans la boucle de jeux principal j'efface le dessin et je redessine le joueur , et dans la fonction de clique je parcours le tableau du chemin avec un setInterval de 1s et je modifie les coordonnées du joueur étant donné que la boucle principal est plus rapide que celle du parcours le personnage ce déplace la ou je ne comprends pas c'est pour arrêter l'interval

var timer2;
  i=0;
 timer2 = setInterval(function(){
            initPosXplayer=path[i].x;
            initPosYplayer=path[i].y;
            if(i==path.length){clearInterval(timer2);i=0;}
            i++;
 },vitesseDeplacement);

Mais le souci est que je rentre bien dans la condition

 if(i==path.length){clearInterval(timer2);i=0;}

mais ca n'arrête pas le timer il continu et comme le tableau est vide il génère des erreurs dans la console , l'arrêt fonctionne bien à l'exterieur de l'interval mais pas dans l'interval lui même

essaie :

if(i>path.length){clearInterval(timer2);i=0;}

j'imagine que tu as lu ça
c génial !!

Oui en effet j'ai pas mal potassé ce site ou la documentation est très bien fourni ! effectivement le souci venait de la merci

Bonjour je viens vers vous car je suis de nouveau confronté à un problème tout bête.......

function Terrain(map)
{
        //tableau à deux dimensions
        this.map = map;
}

Terrain.prototype.Charger = function()
{
    //afficher une fois tuile chargé
}

Terrain.prototype.Afficher = function()
{
}

Comment fait-on pour appeler Afficher dans Charger ?

hello, je te conseille ce tuto.
mais en gros, tu as créé une "class" Terrain avec ta function, donc il faut tout d'abord l'instancier :

var myTerrain = new Terrain(map);

et ensuite tu pourras appelé tes méthodes :

myTerrain.Charger();

Très bien, merci je découvre la poo qui faut le dire est très pratique pour ce que je fait ^^

pour ce qui est de créer une classe j'y arrive mais c'est surtout comment appeler une méthod d'une classe dans une autre méthod de celle-ci. Dans mon cas j'aimerais appeler la méthod Afficher dans la méthod Charger

function Terrain(map)
{
        //tableau à deux dimensions
        this.map = map;
}

Terrain.prototype.Charger = function()
{
    //appeler méthod Afficher
}

Terrain.prototype.Afficher = function()
{
}

regarde vraiment ce tuto, tu apprendras plein de trucs...
tu l'as déjà fait dans ta création ;)

Terrain.prototype.Charger = function()
{
    //appeler méthod Afficher
    this.Afficher();
}

Je l'ai regardé très intéressant, merci je suis confronté à un problème

Terrain.prototype.ChargerTuileTerrain = function()
{
    var tuileChargee = 0,listTuiles=[];
    //var nb = this.tuiles.length;
    for (var i = 0; i < this.tuiles.length; i++) 
    {
        listTuiles[i] = new Image();
        listTuiles[i].src = this.tuiles[i];
        listTuiles[i].onload = function() 
        {
        tuileChargee++;
        if(tuileChargee==this.tuiles.length){console.log("tuiles chargee")}
        //afficher la carte
        }
    }
};

quand je l'appele ca bloque dans la condition

if(tuileChargee==this.tuiles.length){console.log("tuiles chargee")}

il connait

this.tuiles.length

dans la condition du for mais dans le if : Uncaught TypeError: Cannot read property 'length' of undefined

hello,
c peut-être un pb de scope... essaie ça :

Terrain.prototype.ChargerTuileTerrain = function()
{
    var self = this; // pour augmenter la portée
    var tuileChargee = 0,listTuiles=[];
    //var nb = this.tuiles.length;
    for (var i = 0; i < self.tuiles.length; i++) 
    {
        listTuiles[i] = new Image();
        listTuiles[i].src = self.tuiles[i];
        listTuiles[i].onload = function() 
        {
        tuileChargee++;
        if(tuileChargee==self.tuiles.length){console.log("tuiles chargees")}
        //afficher la carte
        }
    }
};

Bonsoir effectivement c'était bien un problème de scope...Merci.

La création de la map est fonctionnelle , la gestion des cliques dessus également , déterminer le chemin à prendre d'un point A et B aussi, créer un joueur et le déplacer aussi la ou je bloque c'est sur le déplacement de la map pour comprendre mieux imaginons que notre canvas fasse 600px 600px mais que la map généré fasse 900px 900px , la map est généré mais se qui sors du canvas n'est pas affiché du coup quand le joueur ce déplace sur la map il bouge mais on ne le vois pas car il est en dehors de la zone d'affichage et je cherche une solution à ca je sais faire bouger la map mais je ne sais pas comment m'y prendre en fonction du joueur , si vous avez une idée je suis prenneur

hum, je ne pense pas avoir toutes les billes :
tu n'arrives pas à faire glisser l'ensemble map-sprite quand le sprite arrive en bord du canvas ?
y'a la veille méthode consistant à faire bouger la map 'sous' un sprite fixe ;))
tu génères entièrement la map ou tu la 'clip' sur ton canvas ?

bref, peut-être qu'avec un peu de code, on peut aider...

le principe dessin sur canvas enfaite c'est quand tu veux déplacer quelque chose tu efface et tu redessine ce que tu veux déplacer donc enfaite si tu veux à l'initialisation de la map je donne un point d'origine pour la première tuile et tout le reste ce dessine en fonction de ca et du coup quand je deplace mon joueur j'incremente ou décrémente le point d'origine ce qui fait bouger la map mais le souci c'est que du coup la map bouge en permanance vu qu'elle dépend du joueur et ca fait des sacades ....

oui pour le moment c'est ce que j'ai réussi à faire : faire bouger la map 'sous' un sprite fixe mais ca fait bisard honnêtement ^^

hello, c déjà bien ce que tu as fait !!
si g bien capté le soucis, c quand tu déplaces le terrain avec les flèches tu peux faire sortir le joueur de l'affichage....
ce que tu peux faire :

  • ds main.js, quand le chemin est trouvé, avant d'envoyer ton timer, tu vérifies que le joueur est visible sinon tu recentres le terrain dessus
  • ds joueur.js, au lieu de déplacer le terrain, tu déplaces le joueur et dès qu'il arrive à une certaine distance du bord, alors tu déplaces le terrain

Coucou alors le déplacement ce fera au clique bien entendu les touches flêchés m'ont servi à tester le déplacement de la map ;) , je vais tester ca dessuite merci bien :p

un truc : pourquoi tu as fait un canvasJoueur aussi grand que canvasTerrain et que tu observes les click dessus : ce serait pas plus logique d'observer les clicks sur le terrain ?

car en réalité il y aura 3 canvas je veux tout séparer enfaite un canvas joueur , terrain et un pour les décords étant superposé si je faitpas la détection du clique sur le canvas avec le z-index le plus élevé le clique n'est pas détecté

ca rend 1000x mieux tu as eu une idée de génie je bougais la map toujours en fonction du joueur mais pour chaque déplacement et le fait de bouger par rapport au bord du canvas ca change tout et avec quelques timer pour que ca soit plus fluid c'est pas mal du tout !!!

super !
tu as gardé ton canvasJoueur aussi grand ?
je me disais que si tu avais 2 joueurs, ton système ne fonctionnerait plus....
j'ai fait un test rapide pour recarder ton canvas à la taille du sprite et observer les clics sur le terrain : ça fonctionne tout aussi bien (bon, mes calculs de positionnement sont pas au top, mais....), y'a pas grand chose à modifier :) et tu aurais un object joueur ultra simple et léger....

ca sera un mode multi joueur donc un seul joueur par pc et plus tard du node.js et les objets joueur seront stocker dans un tableau j'ai fait quelques essais dessus c'est fonctionnel donc mis de coté pour le moment je me concentre sur un seul joueur et optimiser tout à fond avant de gérer le multi ^^, je suis prenneur si ta soluce est plus opti ^^

mais si je passe en multi joueur ca risque de faire lourd je pense car un canvas de plus généré par joueur du coup non ?

yop, je ne sais pas où tu en es mais je commencerais par un grand nettoyage ;)
une nomenclature plus simple :
par exemple joueur.proto.deplacerLeJoueur c, comment dire... ;) il est préférable d'utiliser joueur.proto.deplacer ou joueur.proto.move
tu aurais un appel de methode plus intuitif et ergonomique : joueur.move(x, y, map)
pareil pour les params : joueur.canvasJoueur -> joueur.canvas, tout simplement
ça c pour l'esthétique... moi ça me pique un peu...

pour le code, je ne saisis pas toute ta logique :
tu dupliques des params dans tous tes obj (exemples 'cases', qui pour moi n'appartiennent qu'à map)
il y a des params inutiles (comme 'tampon') qui alourdissent le code...
beaucoup de répétitions de routines à différents endroits (comme la def de tes canvas dans main et dans tes objs...)
tes obj pourrait gérer eux mêmes leurs listeners... au lieu de les placer en 'sur-couche'...
et des petits trucs.....

tout ceci rend la lecture de ton code, et donc sa maintenance difficile

attention, je ne dit pas que tu dois tout refaire !!!
mais si tes ambitions sont telles que j'ai lu, il va falloir être plus organisé, sinon tu risques d'avoir très vite une usine à gaz !

je veux bien te donner un coup de main dans la mesure de mes capacités (j'aime bien ton projet :) ça me rappelle ma jeunesse ;) )
comme tu veux....

coucou c'est vrai que c'est pas très organisé car disons que cette idée m'a pris d'un coup et que je me suis jeté dessus comme un malade au lieu de passer par le classique papier!! ...

Je suis prenneur ;)

cool...
reprenons dans l'ordre :
ton jeux : c un perso sur une map qui peut faire des trucs, c ça ?

pour le moment un simple joueur qui navigue sur une map en effet

;) c à ce moment que tu dois bien définir ton contexte, tes obj joueur et obj map...

pour le rendu (normalement on commence pas par là, mais tu as déjà fait plein de trucs ; donc....) :
ta map peut être définit en 2d en 3d, en isomorphisme ( c ton cas ...) ou autre
je pense qu'il te faut une méthode pour convertir les x,y,z object en x,y,z visu...
dans ton cas c relativement simple....

en gros, on réfléchit en 2d (la composante z sous le coude) et le moteur de rendu dessine le tableau....

qu'en penses-tu ?

en faite mes tuiles de mon jeux sont déjà faite en vu isométrique je me contente de les placer les unes à coté des autres avec ma boucle dans Créer le terrain

oui, g vu...
c pq je t'ai dit que ct "relativement" simple....
la question que je te pose c :
est-ce que ton joueur peut se ballader sur cette map en coordonnées x,y ?

Oui il peut en gros dans ma boucle des que je dessine une tuile je la stock dans un objet case qui stock les coordonné dans le tableau genre (0,0) etc... et ca position en x et y dans le canvas

c'est pour ca que j'utilise case dans mon joueur pour detecter le clique car il existe pas d'evenement qui permette d'interagir avec le dessin du coup quand je clique je regarde dans les cases si c'est une cellule de la map et quand je deplace la map (du coup les coordonnes des tuiles change ) je vide le tableau et je le re rempli avec les nouvelles pos

autrement-dit :
en imaginant que le sprite du joueur soit un canvas qui n'englobe que lui...
si je dis à joueur : joueur.move(x,y,map) ira t-il au bon endroit sur map ?
ben non...
si je ne m'abuse ton map est en 2d (donc x,y), ton easystart te génère un path x,y :
ton joueur doit donc comprendre le x,y ; et c pas le cas....
je répète y'a po grand chose à faire....

j'ai donc une autre question, qu'on parte sur la même idée...
tu veux un seul canvas, ou plusieurs (map et joueur) ?
parce que tu raisonnes comme si tu n'en avais qu'un alors que t'en as définit 2.....

le joueur comprend le x,y grace au tableau de case quand je clique sur la map il compare la position de la souris dans le canvas avec le tableau de case si il y a correspondance il prend le x et le y tableau pour deplacer le joueur si c'est pas claire je vais esseyer de faire mieux pour te l'expliquer mais pas évident ^^

je pense que je vais partir sur un seul canvas se sera plus simple à traiter ...

aïe !! mauvaise idée....
tu vas avoir plein de soucis de rafrachissement...
2 canvas c mieux....
à mon avis

bon, c bientôt le week end....
je t'envoie une soluce de base si g le temps... et on en rediscute...

c'est ce que je me suis dit c'est pour ca qu'à la base j'ai fait sur plusieur canvas mais bon j'ai encore à réfléchir sur toute le logique/structure de tout ca je pense que le papier ne sera pas de refus

entendu merci beaucoup de mon coté je vais y réfléchir et revoir tout par rapport à ce que tu m'as dis ! :)

Coucou j'ai bien avancé j'ai refait toute la partie Terrain simplification de nom de méthod, virer variable superflu, commenter tout ca bref la total la ou je bloque maintenant c'est sur la class Joueur avant le clique était détecté dans le main et utilisait derrière la méthod pour déplacer le joueur mais j'aimerais intégrer l'évènement clique directement dans ma class de sorte que quand j'instancie mon objet les évènements clique et mousemoove soit prit en compte je sais pas si je suis claire ^^

hello, si tout à fait...
il suffit qu'une fois ton dom fait tu crées les listeners. et ça tu peux le faire où tu veux, comme dans tes class par exemple. la doc ici
sinon, moi g pas eu le temps ce week end... si tu as bien nettoyé ton code, as tu remarqué des redondances entre tes différentes class, si oui as tu pensé à l'héritage ?
par exemple, pour tes events : tu crées une class qui gère ça et tu en fais hériter tes class joueur et terrain, pareil pour le rendu.... enfin c une idée :) perso c comme ça que je procède...

Coucou, pas de souci tkt pas , je connais très bien l'héritage en java , c++ etc mais pas en js je vais me documenter sur ca voir si ca fonctionne parreil etc ..

tiens un ti exemple pour tes events :
tu te fais une class qui gère les view :

var CView = function(params){
    // ton constructor
};

// la fonction qui ajoutera les listeners
CView.prototype.addEvent = function(elt, event){
    if(typeof(event)==='undefined') return false;
    var self = this;
    els = [];
    els = el.querySelectorAll('[data-events]'); // je récupère tous les éléments qui ont un data-events

    for(var i = 0; i < els.length; i++){
        if(els[i].dataset.events[0]=='('){ // plusieurs events c po terrible, y'a bcp mieux à faire.... à bon entendeur
            events = els[i].dataset.events;
            events = events.substr(1, events.length-2);
            events = events.split(',');

            for(var j in events){
                els[i].addEventListener(events[j], self[event].bind(self), true); // je bind pour récupérer mon context
            }
        }
        else els[i].addEventListener(els[i].dataset.events, self[event].bind(self), true); // un seul event
    }
};

ensuite ton object qui hérite de CView

var myObj = new CView(); // avec params ou po

// ses méthodes propres
myObj.prototype.makeDom = function(params){
    var self = this;
    el = document.createElement('div');
      btn1 = document.createElement('a');
      btn1.className = 'btn bouton1';
      btn1.dataset.events = 'click'; // ici je lui dit qu'il doit réagir sur le click
        // btn1.dataset.events = '(click,mouseover)'; // si je veux qu'il réagisse sur plusieurs events
      btn2 = document.createElement('a');
      btn2.className = 'btn bouton2';
      btn2.dataset.events = 'click'; // ici je lui dit qu'il doit réagir sur le click

    el.appendChild(btn1);
    el.appendChild(btn2);

    document.body.appendChild(el);

    // ici j'appelle la méthode parent de CView
    self.addEvent(el, 'eventDom'); // eventDom sera la méthode qui va gérer les events

};

// donc la gestion des events
myObj.prototype.eventDom = function(e){
    if(!e.target) return;
    btn = e.target;

    // et c parti
   if(btn.classList.contains('bouton1')){
        console.log(e.type + 'sur bouton 1');
    }

    else if(btn.classList.contains('bouton2')){
        console.log(e.type + 'sur bouton 2');
    }

};

vala en gros... code à étudier
je te cache pas qu'un ti MCV serait pas mal du tout, avec un CController qui engloberait le CView et un CModel éventuellement...
ainsi tes objects hériteraient du CController et donc de toutes les routines de base qui vont bien :)

Coucou entendu, merci beaucoup !

hello, c week-end, je viens aux news...
++

just for up

Coucou je suis vraiment désolé de ne pas avoir eu le temps de te répondre .... j'ai mis le projet en pause car j'ai quelques soucis personnel dès que j'aurais un peu de temps je reviendrais avec plaisir

hello,
po de soucis,
prends soin de toi et des tiens,c le le plus important.