JeuWeb - Crée ton jeu par navigateur
Gestion des combat en tour par tour synchronisés - 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 : Gestion des combat en tour par tour synchronisés (/showthread.php?tid=1539)



Gestion des combat en tour par tour synchronisés - Ruz - 20-04-2008

Le deuxième point noir de mon projet: les combats ^^

1. Description des règles à utiliser:

Les combats se déroulent par assauts successifs, amenant à la défaite (ou fuite) d'un des deux groupes opposés. Chaque assaut dure 6 secondes (pour vous donner une idée)

Les persos/monstres jouent par ordre d'initiative décroissante, et peuvent réaliser une action + déplacement court par assaut.
Actions possibles:
* rien
* frapper corps a corps avec arme
* frapper/immobiliser corps a corps à mains nues
* tirer à distance
* lancer/utiliser un sortilège/rune
* utiliser un objet (potion, parchemin...)
* changer l'inventaire (lacher l'arc pour prendre l'épée, au hasard)
* fuir
* déplacement long

Ce n'est pas un jeu où l'on a 12PA par 12heures, ou un truc du genre... on récupère 10PA/minutes. 1 action: 1PA ou plus.

2. Comment gérer ca?

Alors, c'est là qu'intervient la notion de compagnie (des joueurs qui forment un petit groupe pour aller a l'aventure). le groupe arrive sur la sous-carte, ensemble... un perso va engager un monstre et initier un combat.
phase1: initiation du combat
les persos a coté peuvent rejoindre le combat pendant un délais max de 2 minutes que l'engageur peut finir a tout moment. Les membres de la compagnie de l'engageur sont automatiquement admis. D'autres joueurs peuvent demander à entrer. L'engageur peut accepter ou non.
Coté monstre, si le monstre est isolé, c fait. Sinon, le reste de la meute/groupe est automatiquement inclus.
phase 2: combat
deux possibilités:
A. chaque joueur joue a tour de role (en gros, on passe la main à chaque joueur un après l'autre... 5 joueurs... en gros, on se tourne les pouces pas mal, je crois, c'est long, c chiant. (enfin, c'est mon avis))
Je ne suis pas cette voix, mais si vous trouvez que ca peut le faire, ben, je veux bien des idées d'implémentations, de comment gérer le temps libre... etc...

B. les joueurs décident de leur action tous en meme temps.
En gros, y a un timer sur le serveur... chaque joueur à 1 minute pour envoyer un ordre. Dès que c'est fait, le tour est résolu (on attend pas forcément la minute), et le résultat est envoyé à chacun, et le tour suivant commence. SI la minute passe sans choix validé, le joueur perd son tour.

phase 3: fin du combat
calcul XP, fouille des cadavres, etc...
durée max à 10 minutes, on quitte quand on veut, et on se fait éjecter sinon.

3. Implémentation
Ajax arrive a point nommé...

nouveau tour... on crée un timer en JS, pour le décompte du temps restant a afficher au joueur. Je ne met rien à jour tant que le joueur ne valide pas son ordre (a part le chat, bien sur). Une fois validé, l'ordre part vers le serveur. Soit c'est le dernier ordre qu'on attendait... le serveur éxécute le script de combat, et renvoit les infos, et le tour suivant commence. Soit, un autre joueur a pas encore répondu (le plus courant)... Que va faire le serveur?
peut-il bloquer la réponse jusqu'à ce que le dernier ai validé?
dois-je envoyer un "ordre recu"... et attendre quoi pour demander la suite? renvoyer une requete en ajax toutes les x secondes pour voir l'état
du tour?
enfin, bref: quelle serait pour vous la meilleure méthode à appliquer?

grand merci d'avance pour vos idées!
PS: applet java qui recoit en UDP, j'y ai pensé, mais ca passerait pas sur OVH, a ce que j'ai cru lire.


RE: Gestion des combat en tour par tour synchronisés - Lanwin - 20-04-2008

Le système de tours successifs est pas mal, je pense qu'il y a de bonnes raisons que ce soit ce système qui soit utilisé dans pas mal de JDR papier...

Bon, après m'être restauré, voilà enfin mon opinion. Celle-çi se base principalement sur le JDR Papier Warhammer, je préviens tout de suite, sait-on jamais. Tongue

Bref, je suis plutôt partisan, comme je l'ai dit, du système de combat en tour par tour, avec, comme toi tu le dis, une grande partie du temps où on se roule les pouces.
Alors déjà, enlèvons cette idée préconçue, un joueur n'attend pas trois lustres pour jouer. Dans mon groupe de JDR, on est 7, la plupart du temps on rencontre 5 ou 6 gobelins sur la route, bon ok, il faut attendre 12 ou 13 tours pour rejouer de nouveau. Le fait est que les tours passent très vite. Pour mon tour, étant archer, je lance mes dés. S'ils sont en dessous de la CT, c'est ok, j'annonce que je tire ma flèche et qu'elle atterit dans un des ennemis. Je lance mon jet de dégats pendant que le MJ fait une description RP de la scène. J'annonce mes dégâts, le MJ enlève les PV correspondants, puis décrit la scène. Et hop mon tour est terminé.
Dans la mesure où toi, dans un jeu, ton lancer de dé, de dégâts, et ton calcul est fait très rapidement, un tour pour un joueur ne durerait grand-maximum que 10 secondes, le temps de choisir ce qu'il veut faire, et c'est tout, le jeu prend le relai. Du coup, à 10 ou 12 intervenants, le joueur intervient toutes les 2 minutes environ. Alors soit, ça fait pas beaucoup 10 secondes sur deux minutes, mais le tour par tour successif a un autre avantage : la stratégie.

Mon groupe est un peu (trop?) conditionné par WoW, on y a tous joué plus ou moins. On sait que ce n'est pas une référence, mais ça nous conditionne pour nos combats. On sait qui sont nos DPS (en l'occurence moi avec mon arc, et un pote avec un fléau, au CàC), on sait qui sont ceux qui ont besoin de plus de temps pour taper fort (notre mage de feu). On sait aussi ceux qui nous ont rejoint en cours de route (donc bas lvl, donc ils tapent moins forts), et ceux pour qui un combat serait fatal (on a une contrebandière pas douée au combat, et pas destinée pour, mais qui nous fait gagner un max à chaque fois qu'on vent quelque chose ^^). Notre idée, c'est pas de taper et de tout tabasser au plus vite, mais plutôt de permettre à tous, et surtout aux petits levels, de gagner de l'XP. Comment on fait? Ben nos DPS attaquons à chaque tour une cible différente. On fait un max de dégâts (ou pas), mais on n'achève pas les ennemis. Ce sont les petits lvl qui ont cette joie, ça leur rajoute de l'XP et ils levelent plus vite. Le mage de feu, qui attend généralement un tour pour pouvoir taper plus fort encore, prend pour cibles les ennemis que les DPS ont touché mais moins que les autres. Généralement il les achèvent aussi. Au final, tout le monde s'est bien battu, tout le monde il est content, on partage les gains, ou on les fout dans notre carriole, et on continue.

Et le problème dans le second système que tu proposes, c'est que les joueurs risquent de ne pas avoir la possibilité de taper stratégiquement. Quand on doit écrire pour discuter, une minute ça passe très très vite. Au final, il reste 5 secondes, pas de stratégie déployée, les joueurs tapent tous sur le premier ennemi qui va être tué, re-tué, sur-tué à la vue de tous les dégâts qu'il se prend dans la tronche et pendant ce temps là les autres sont toujours vivants, en pleine santé, et prêts à venger la mort de leur coupaing. Alors du coup, je suis pas sûr que les joueurs y prennent beaucoup de plaisir, car on peut facilement imaginer le même souci dans l'autre sens, à savoir un joueur qui crève bêtement parce que l'équipe d'en face a tout tapé sur ce mec, au lieu de blesser judicieusement tout le groupe.
Ce système (à savoir le second, hein, je parle toujours du même ^^) peut être viable si on imagine que le maximum d'outils est mis en place pour aider à la communication : indication en temps réel de la cible de chaque joueur, en précisant la technique utilisée (CàC, à distance, etc, à main nues), ou en tout cas indiquer à chaque fois l'action choisie par chacun des joueurs du groupe, histoire que l'un se mette pas à taper un monstre pendant que tous les autres joueurs décident de fuire...

Pour la technique, vu que PHP ne peut pas gérer l'envoi de données en dehors du moment où il est appelé par le client (contrairement aux jeux "à part" utilisant UDP si j'ai bien compris ^^), la seule méthode que je vois est le rafraichissement de la page par Ajax à un certain intervalle. Dans le second cas, si la limite est une minute, à quoi servirait de rafraichir avant, comme tu le supposes, vu que dans une minute tout au plus, le tour se termine. Et dans le cas de déconnexions intempestives, les autres joueurs attendront forcément une minute pour jouer vu qu'un joueur ne pourra pas donner son ordre... Big Grin

Voilà, un peu long, mais bon Tongue

Amicalement,

PS : Dans mon projet qui se déroule en quasi temps réel aussi, j'ai choisi le système que je préconise, à savoir le tour par tour non simultané. De toute façon, le combat n'est qu'une phase annexe de mon projet qui concerne surtout tout le reste d'un RPG : crafting, quête, et surtout RP ^^


RE: Gestion des combat en tour par tour synchronisés - Ruz - 20-04-2008

oui, tu ressors tout ce qui me pose problème, au niveau stratégique, le kill unique, la frappe sur le mort, le soin pas lancé pendant le tour, etc...

prenons ce que tu préconises: le tour par tour classique.
equipe: 7 joueurs, c souvent la taille de mes groupes sur les jeux. et 6 monstres en face.

Bon, ils jouent par initiative décroissante, ca, c'est facile à gérer...

au niveau de la gestion des tours, comment vas-tu gérer ca (pour économiser au max la bande passante, évidemment)?
le premier joue... il valide... envoit en ajax. Le serveur éxécute l'ordre, et passe au 2e (un monstre, ok, c interne... automatique), puis au 3e: pouf Un joueur.
le joueur en question, c son premier tour... le serveur ne peut pâs spontanément lui dire 'Oh, c a toi'. faut donc une première requete du perso... générée par JS? auto générée à l'initiation du combat? la requete (en ajax, on va dire) arrive au serveur: "je veux jouer".. c pas son tour. Le serveur peut-il mettre en attente la demande? (sans bloquer de ressources) et la reprendre et lui envoyer "oui, maintenant, c'est a toi", par après?

Je précise qu'ajax, j'ai les notions, mais suis loin d'être un expert... toute cette partie presque client-serveur me prend vraiment la tête^^

enfin, bref: quelle est la logique à suivre pour un truc du genre?
ca fait du bien de se reprendre un peu la tete sur des trucs comme ca ^^


RE: Gestion des combat en tour par tour synchronisés - Lanwin - 20-04-2008

En effet le système est assez dur à imaginer... Enfin le plus compliqué à prendre en compte est la différence de connexion des joueurs. Si tous avaient la même connexion, c'est-à-dire qu'ils accédaient à la même page en même temps, ça faciliterait les choses, mais on va faire avec... (moi aussi j'aime me prendre la tête sur des trucs comme ça, après je suis bien fier de ce que j'ai fait ^^).

Cela dit l'avantage premier de l'Ajax, c'est de recharger des morceaux de pages. Donc à ce moment là, autant faire charger un morceau le plus adapté possible à la situation. L'idée c'est d'utiliser PHP et Ajax en parfaite symbiose. (ouh c'est beau ça xD). Bref, partons dans la grande théorie. Big Grin

Partons d'un cas simple : 3 joueurs (A B C), 2 monstres (M1, M2). Bon y'a plus simple, mais bon comme ça on généralise. Ordre d'initiative : A M1 B M2 C. Pour chaque joueur, un tour : 30 secondes.

/me est en train de se rendre compte qu'il est train de faire aussi sa propre réflexion sur son système de combat pour son RPG xD

Ce que j'ai : une table MySQL indiquant les combats, avec notamment le timestamp du début du combat, et un champ indiquant combien de joueurs sont impliqués, et combien ont "accepté". Chaque joueur possède un rafraichissement toutes les 5 secondes de son état dans le jeu.

Le groupe ABC croise donc les monstres M1 et M2 sur la route. Le joueur A décide d'activer le "mode combat".

Ce mode appelle donc une nouvelle page (par ajax ou non), qui notamment ajoute le combat dans la table SQL, en indiquant que le joueur A accepte le combat et attend les autres (en faisant un rafraichissement toutes les 15 secondes cette fois). Le joueur B, qui a un décalage "de base" de 2 secondes avec A, va donc voir, deux secondes après A, "l'invitation" au combat, qu'il accepte (cela mettant à jour la table SQL). Enfin le joueur C, au décalage de 4 secondes, a l'invitation le dernier, et l'accepte. En gros, 5 secondes après la validation du A, tout le monde a accepté (B ayant accepté entre temps).

10 secondes après l'acceptation de C, le joueur A rafraichit automatiquement. Le serveur lui envoie que tout le monde est prêt, le lancer d'initiative se fait, l'ordre est défini, le timestamp du début de combat est inscrit dans la table SQL : 1000 (pour faire simple).

On dispose d'un outil sur JS, qui permet lui aussi de connaitre le Timestamp au moment où JS le demande. On va s'en servir pour connaître les décalages. L'idée est que, techniquement, malgré les décalages de chaque personnes, on les cale une fois pour toute sur la même fréquence d'actualisation, c'est-à-dire faire en sorte qu'ils rafraichissent tous au même timestamp (à quelque petites choses près ^^). Et ce tout au long du combat.

A commence son tour au timestamp 1000, il le terminera donc à 1030. La résolution du montre M1 étant faite par le serveur, donc en un minimum de temps, on peut estimer qu'elle sera instantanée. B commencera donc son tour à 1030, pour le finir à 1060. M2 : IA donc instantané aussi. C commencera son tour à 1060 pour le finir à 1090.

A partir de là tout est quasi-joué. Big Grin


A joue donc sont tour à 1000. Pendant ce temps là, B et C sont encore sur l'ancien écran (leur 15 secondes de rafraichissement ne se sont pas encore pleinement écoulé).
A 1015, B et C rafraichissent. Ils voient que A est en train de jouer son tour. Côté "caché", JS se rend compte que mince, on est déjà au Timestamp 1015, donc le prochain tour est à 1030. Le prochain rafraichissement aura donc lieu dans 15 secondes.
A 1030, le tour de A est joué, il est résolu. Par la même occasion, M1 joue son coup, la résolution est aussi faite. A a sa page qui rafraichit, et du coup, B et C également. C'est au tour de B. Pendant ce temps-là, chez A et C, Javascript sait qu'il devra rafraichir à 1060, donc dans 30 secondes. Pas besoin de le faire avant.
A 1060, rafraichissement de A, B et C. Les trois joueurs rafraichissent alors ensemble, tout est synchronisé. A partir de là, tout est vraiment joué... Il suffit de continuer sur la lancée des rafraichissement toutes les 30 secondes.


En gros, voilà comment je ferai. Ca a plusieurs avantages, plusieurs inconvénients.
Avantages :
- C'est JS qui prend l'initiative de recharger, il ne recharge pas dans le vide entre les tours, juste pour demander à PHP si c'est à son tour de jouer. Du coup ça relâche la charge du serveur.
- Dans le cas où, comme je le dis au début, on recharge vraiment que ce qu'on l'a besoin (c'est-à-dire les données brutes, les messages pouvant être gérés par JS), la technique est, je pense assez fiable. Il y a peu de chance que le décalage entre chaque joueur soit énorme, sauf si le serveur peine un peu...
Inconvénients :
- Cette technique, comme tu l'auras deviné, fait que les joueurs rechargent en même temps, pour faire la même requête "Est-ce à moi de Jouer?". Cette page étant demandée en même temps par 3 personnes, il va falloir faire en sorte qu'elle soit hyper-optimisée... Sait-on jamais, peut-être auras-tu à gérer des combats avec plus de 40 joueurs... A ce moment là la même page sera demandée 40 fois en même temps... Ca devra assurer autant côté PHP que MySQL...
- La limite de temps est constante, c'est-à-dire que même si un joueur envoie son ordre dans les 10 premières secondes, tous les joueurs n'auront le résultat que 20 secondes plus tard... Bah, pourquoi ne pas en profiter pour faire un peu de RP? (genre jeter une phrase du genre "MOUAHAHAHA TU VAS MOURIR SALE GOBELIN!" sur le chat ^^). Ca remplira parfaitement les 20 secondes pour le joueur, et dans tous les cas, les deux autres attendront 30 secondes... Après il faut trouver le bon timing au niveau des tours, pour qu'ils ne soient ni trop long, ni trop courts...

Bref, voilà comment je ferai moi, et je le ferai sans doute pour mon projet, car c'est pour moi l'une des meilleures solutions sur le rapport charge serveur / "impression" de temps réel / plaisir de jouer.

Amicalement, Big Grin

Lanwin, content de sa tirade... Tongue


RE: Gestion des combat en tour par tour synchronisés - Harparine - 21-04-2008

Perso, je ferais un truc très simple, basé sur Ajax :

__Au début du combat, on calcule le rang de chaque joueur en fonction de son initiative.
__Dans la BDD ou dans un fichier, on stocke le premier rang => 1
__De façon régulière, chaque joueur fait des requêtes en Ajax pour récupérer le rang en cours de jeu (juste un chiffre, ça va vite).
__Si c'est le rang du joueur qui est dans le fichier, il joue. Le coup est résolu grâce à un script PHP qui renvoie les résultats au joueur et incrémente le rang dans le fichier.
__A la requête suivante, le joueur qui joue après aura son écran rafraichi et verra qu'il peut joueur.


RE: Gestion des combat en tour par tour synchronisés - Ruz - 21-04-2008

Merci a vous ^^

Alors, je vais mettre quelques idées en vrac: laisser JS gérer le temps d'attente enter deux mises à jour, j'ai testé... et ca m'a bien merdé. En gros, sur PC1, ca marche nickel. Je prends un autre PC, meme config, meme navigateur, etc... le JS déconne. :/

J'ai jamais trouvé la solution: y a-t-il des trucs auquel on doit faire attentiondnas l'utilisation des JS en tant que compte à rebours?

D'autre part... j'ai un chat, qui peux servir à passer son temps (ah oui, le chat est comment aux joueurs, équipes confondues... donc, deux équipes de joueurs s'affrontant et parlant dnas la meme langue pourront parler ensemble. (limité par l'usage des langues diffférentes, vive la stratégie ^^)) mais je vais, pour ce chat, faire des requetes souvent, pour mettre à jour la discussion... pourquoi ne pas utiliser cet envoi en ajax pour lui greffer (parfois) une 2e requete? (et remettre une liste succcinte des persos des groupes, en ordre de jeu, avec un barre de vie imprécise?)

Bref, je m'explique: (et je pars sur la dernière idée: le chiffre du perso en cours dans la table combat)
chat: le chat ^^
situation: perso en cours de jeu
Tableau: liste des persos, avec PV actuels

t0: requete => load Chat + situation + Tableau
réponse: chat: vide * situation: pas a toi (admettons je suis 5 et il affiche 1) * tableau: mis a jour

t2 (5s) Saddélais du chat... 3-5 secondes, je pense . je prends 5 en exemple) : load chat (logique) + pas situation (trop court... 4 à jouer avant moi (oui, meme si 2 monstres)) + tableau ? (bof, pas essentiel en si rapide)

t3 (10s) : chat + situation + tableau
r: chat: 8 lignes * situation: 3 * tableau: mise à jour

t4: (15s): chat + situation + tableau (oui, j'approche de mon tour, me faut des infos le plus proches possibles de la réalité.)
r: chat ok * a moi: je joue : mise a jour tableau

t5 (20s) chat + pas situation (j'ai pas encore envoyé mon action) - rien d'autre
r: chat

t5.5 (24s): envoit de l'action - retour du résultat + mise à jour tableau

t6(25s) chat * situation (inutile, je laisse un bon temps sans demander) - pas de mise a jour autres (viens de se faire faire)

etc...

Alors, en écrivant ca, une autre idée:
Sur mon écran de combat, j'ai plusieurs DIV: carte + chat + actions possibles + Evenements + liste des engagés

aller recharger la liste des engagés (pour la mise à jour des PVs (et d'une icone disant qui joue)) alors que l'on attend une action ne sert à rien...
Donc, je limiterais à deux trucs à vérifier max:
1. chat (oui, c la base)
2. (par moment) Situation du tour. Si identique à la dernière requete: => rien
Si différent:
1.1 on recharge les évènements et la liste des engagés (pour mise à jour des PV et du statut du joueur en cours) (d'office)
1.2 Si il y a eu déplacement (et uniquement) => rechargement de la carte
1.3 si c'est mon tour: activation du bouton "Valider" du panneau de choix d'action (le joueur peux ainsi déjà préparer sa prochaine action. MAIS si il y a un déplacement, remise à zéro du choix (pour éviter de frapper au corps à corps une personne qui a bougé entre temps (ou alors faire la vérification sur le serveur (d'office), et renvoyer message "try again") pitet moins lourd.)

Hop, allez, je m'en vais au boulot, un patient vient d'arriver ^^
je relirai ca plus tard.