Maison  >  Article  >  interface Web  >  Imitation JS du jeu légendaire (avec code)

Imitation JS du jeu légendaire (avec code)

php中世界最好的语言
php中世界最好的语言original
2018-04-12 16:40:393949parcourir

Cette fois, je vais vous apporter le jeu JS imitant Legend of Legend (avec code). Quelles sont les précautions pour le jeu JS imitant Legend of Legend Voici des cas réels, prenons. un regard.

La première version du jeu a été développée en 2014. Le navigateur utilise html+css+js, le serveur utilise asp+php, la communication utilise ajax et le stockage de données utilise access+mySql. Cependant, en raison de certains problèmes (je ne savais pas comment utiliser node à l'époque, écrire une logique complexe en asp était vraiment difficile à écrire ; il y avait peu d'écriture sur le canevas à cette époque, et le rendu dom pouvait facilement atteindre des goulots d'étranglement en termes de performances) , il a été abandonné. Plus tard, une version a été refaite à l’aide de toile. Cet article a été rédigé en 2018.

1. Préparation avant développement

Pourquoi utiliser Javascript pour implémenter un jeu PC relativement complexe

1. Il est possible d'implémenter des jeux en ligne côté PC avec js. Avec l’évolution des configurations matérielles des PC et téléphones mobiles, la mise à jour des navigateurs et le développement des diverses bibliothèques H5, il devient de plus en plus difficile d’implémenter un jeu en ligne en js. La difficulté ici réside principalement dans deux aspects : les performances du navigateur ; la question de savoir si le code js est suffisamment facile à étendre pour satisfaire l'itération d'un jeu à la logique extrêmement complexe.

2. Parmi les jeux js actuels, il y en a peu à grande échelle pour référence. La plupart (presque tous) les jeux impliquant des connexions multijoueurs, un stockage de données côté serveur et des interactions complexes sont développés à l'aide de Flash. Mais Flash est finalement en déclin, tandis que js se développe rapidement et peut fonctionner tant qu'il y a un navigateur.

Pourquoi avoir choisi un jeu légendaire de 2001

La première raison est mes sentiments pour les vieux jeux, bien sûr, l'autre raison plus importante est que soit je ne sais pas jouer à d'autres jeux, soit je sais comment y jouer mais je n'ai pas le matériel (images, sons). effets, etc). Je pense que c'est une perte de temps de consacrer beaucoup d'efforts à collecter la carte d'un jeu, les modèles de personnages et de monstres, les diagrammes d'objets et d'équipement, puis de les traiter et de les analyser avant de les utiliser pour le développement js.

Depuis que j'ai déjà collecté du matériel pour les jeux Legend et que, heureusement, j'ai trouvé un moyen d'extraire les fichiers de ressources du client Legend of Legend (adresse github), je peux commencer à écrire du code directement, économisant ainsi du temps de préparation.

Difficultés possibles

1. Performances du navigateur : cela devrait être le point le plus difficile. Si le jeu souhaite conserver 40 images, il ne reste alors à chaque image que 25 ms pour que js puisse le calculer. Et comme le rendu consomme généralement plus de performances que le calcul, le temps réel restant pour js n'est que d'environ 10 millisecondes.

2. Anti-triche : Comment empêcher les utilisateurs d'appeler directement les interfaces ou de falsifier les données des requêtes réseau ? Puisque l’objectif est d’utiliser js pour implémenter des jeux plus complexes et que tout jeu en ligne doit en tenir compte, il doit exister une solution relativement mature. Ce n’est pas l’objet de cet article.

2. Conception globale

Navigateur

Le rendu d'écran utilise le canevas.

Par rapport à dom(p)+css, canvas peut gérer un rendu de scène et une gestion d'événements plus complexes. Par exemple, la scène suivante implique quatre images : des joueurs, des animaux, des éléments au sol et l'image la plus basse de la carte. (Il y a en fait des ombres au sol, les noms correspondants qui apparaissent lorsque la souris pointe sur des personnages, des animaux et des objets, ainsi que des ombres au sol. Pour faciliter la lecture, nous ne considérerons pas autant de contenu.)

À ce stade, si vous souhaitez obtenir l'effet « cliquez sur l'animal, attaquez l'animal ; cliquez sur l'objet, ramassez l'objet », alors vous devez surveiller les événements pour les animaux et les objets. Si vous utilisez la méthode dom, il y aura plusieurs problèmes difficiles à gérer :

a. L'ordre de rendu est différent de l'ordre de traitement des événements (parfois l'événement doit être traité en premier si le z-index est petit), et un traitement supplémentaire est requis. Par exemple, dans l'exemple ci-dessus : lorsque vous cliquez sur des monstres ou des objets, il est facile de cliquer sur des personnages, vous devez donc effectuer un traitement de « pénétration d'événement de clic » pour les personnages. De plus, l'ordre de traitement des événements n'est pas fixe : si j'ai une compétence (comme un traitement dans le jeu) qui nécessite la libération d'un personnage, alors le personnage doit avoir une surveillance des événements à ce moment-là. Par conséquent, le fait qu'un élément doive gérer des événements et l'ordre dans lequel les événements sont gérés varient en fonction de l'état du jeu, et La liaison d'événements du DOM ne peut plus répondre aux besoins.

b. Les éléments associés sont difficiles à placer dans le même nœud dom : comme le modèle du joueur, le nom du joueur et les effets de compétence du joueur. Idéalement, ils devraient être placés dans un

ou

Dans le conteneur, c'est facile à gérer (de cette façon, le positionnement de plusieurs éléments peut être hérité de l'élément parent, et il n'est pas nécessaire de gérer la position séparément). Mais de cette façon, le z-index sera difficile à gérer. Par exemple, si le joueur A est au-dessus du joueur B, alors A sera masqué par B. Par conséquent, l'index z de A doit être plus petit, mais le nom du joueur A ne doit pas être masqué par le nom ou l'ombre de B, ce qui ne peut pas être obtenu. . Pour faire simple, La maintenabilité de la structure DOM sacrifiera l'effet de l'affichage à l'écran, et vice versa.

c. Problèmes de performances. Même si l'effet est sacrifié, l'utilisation de DOM pour le rendu conduira inévitablement à de nombreuses relations imbriquées, et les styles de tous les éléments changeront fréquemment, déclenchant continuellement la repeinture du navigateur ou même la redistribution.

Séparation de la logique de rendu du canevas et de la logique du projet

Si diverses opérations de rendu du canevas (telles que drawImage, fillText etc.) sont placés avec le code du projet, ce qui entraînera inévitablement l'impossibilité de maintenir le projet plus tard. Après avoir parcouru plusieurs bibliothèques de canevas existantes, combinées aux outils de débogage liaison de données+ de vue, j'ai créé une nouvelle bibliothèque de canevas Easycanvas (adresse github), et comme vue, elle prend en charge un plug-in pour déboguer les éléments dans le toile.

De cette façon, la partie rendu de l'ensemble du jeu est beaucoup plus simple. Il vous suffit de gérer l'état actuel du jeu et de mettre à jour les données en fonction des données renvoyées par le socket par le serveur. "Les modifications des données entraînent des modifications des vues" est de la responsabilité d'Easycanvas . Par exemple, dans l'implémentation du joueur enveloppant les éléments dans l'image ci-dessous, il nous suffit de donner l'emplacement du conteneur d'emballage et les règles de disposition de chaque élément dans le sac à dos, puis de lier chaque élément emballé à un tableau, puis gérer ce tableau Oui (Easycanvas est responsable du processus de mappage des données à l'écran).

Par exemple, le style d'un total de 40 éléments répartis sur 5 lignes et 8 colonnes peut être transmis à Easycanvas sous la forme suivante (l'index est l'index de l'élément, l'espacement dans la direction x de l'élément est de 36 et l'espacement dans la direction y est de 32). ). Et cette logique est immuable, quelle que soit la manière dont le tableau d’éléments change ou l’endroit où le package est déplacé, la position relative de chaque élément est fixe. Quant au rendu sur toile, il n'est pas nécessaire de considérer le projet lui-même, la maintenabilité est donc meilleure.

style: {
 tw: 30, th: 30,
 tx: function () {
 return 40 + index % 8 * 36;
 },
 ty: function () {
 return 31 + Math.floor(index / 8) * 32;
 }
}

Rendu en couches sur toile

Hypothèse : le jeu doit conserver 40 images, le navigateur a une largeur de 800 et une hauteur de 600, avec une superficie de 480 000 (ci-après dénommée 480 000 comme zone d'écran).

Si le même canevas est utilisé pour le rendu, alors le numéro d'image de ce canevas est 40 et au moins 40 zones d'écran doivent être dessinées par seconde. Cependant, il est probable que plusieurs éléments se chevauchent au même point de coordonnées. Par exemple, l'interface utilisateur, la barre de santé et les boutons en bas se chevauchent et bloquent conjointement la carte de la scène. Ainsi, en les additionnant, la quantité d'affichage par seconde du navigateur peut facilement atteindre plus de 100 zones d'écran.

Ce dessin est difficile à optimiser, car la vue est mise à jour partout sur toute la toile : il peut s'agir des mouvements des joueurs et des animaux, des effets spéciaux des boutons, des changements dans les effets d'une certaine compétence. Dans ce cas, même si le joueur ne bouge pas, la toile entière sera redessinée en raison de l'effet des vêtements "flottant au vent" (en fait l'animation du sprite passe à l'image suivante), ou d'une bouteille de potion apparaissant sur le sol. parce que Il est presque impossible qu'une certaine image du jeu ne se distingue pas de l'image précédente. Même une partie de l'écran de jeu est difficile à rester inchangée. L'ensemble de l'écran de jeu est toujours mis à jour.

Parce qu'il est presque impossible qu'une certaine image du jeu ne se distingue pas de l'image précédente, et l'écran est toujours mis à jour.

J’ai donc cette fois adopté la disposition superposée de trois toiles. Étant donné que le traitement des événements d'Easycanvas prend en charge la diffusion, même si l'utilisateur clique sur le canevas supérieur, si aucun élément ne termine un clic, le canevas suivant peut également recevoir l'événement. Les trois toiles sont responsables de l'interface utilisateur, du sol (carte), des elfes (personnages, animaux, effets de compétences, etc.) :

L'avantage de cette superposition est que le nombre maximum d'images par couche peut être ajusté selon les besoins :

Par exemple, la couche d'interface utilisateur, car de nombreuses interfaces utilisateur ne bougent généralement pas, et même si elles bougent, elles ne nécessitent pas de dessin trop précis, le nombre d'images peut donc être réduit de manière appropriée, par exemple à 20. De cette façon, si la force physique du joueur passe de 100 à 20, la vue peut être mise à jour dans les 50 ms et le changement de 50 ms ne peut pas être ressenti par le joueur. Parce que des choses comme la force physique Il est difficile pour les données de la couche d'interface utilisateur de changer plusieurs fois de manière continue sur une courte période de temps, et le délai de 50 ms est difficile à percevoir pour les humains, donc un dessin fréquent n'est pas nécessaire. . Si nous économisons 20 images par seconde, nous pouvons probablement enregistrer 10 zones de dessin à l’écran.

Un autre exemple est le sol. La carte ne changera que lorsque le joueur se déplacera. De cette façon, si le joueur ne bouge pas, 1 zone d'écran peut être sauvegardée par image. En raison de la nécessité d'assurer la fluidité des mouvements des joueurs, la fréquence d'images maximale au sol ne doit pas être trop faible. Si l'image au sol est de 30 images, alors lorsque le joueur ne bouge pas, 30 zones d'écran peuvent être enregistrées par seconde (dans ce projet, la carte est presque dessinée pour remplir l'écran). De plus, les mouvements des autres joueurs et animaux ne modifieront pas le sol, et il n’est pas nécessaire de redessiner la couche de sol.

La fréquence d'images maximale de la couche de sprite ne peut pas être réduite. Cette couche affichera les parties essentielles du jeu telles que les mouvements des personnages, la fréquence d'images maximale est donc fixée à 40.

De cette manière, la zone dessinée par seconde peut être de 80 à 100 zones d'écran lorsque le joueur est en mouvement, mais ne peut être que de 50 zones d'écran lorsque le joueur ne bouge pas. Dans le jeu, les joueurs s'arrêtent pour combattre des monstres, taper, organiser des objets et libérer des compétences tout en restant immobiles. Par conséquent, le dessin du sol ne sera pas déclenché pendant une longue période, ce qui économise considérablement les performances.

Côté serveur

Puisque l'objectif est d'implémenter un jeu en ligne multijoueur utilisant js, le serveur utilise Node et des sockets pour communiquer avec le navigateur. Un autre avantage est qu'une certaine logique commune peut être réutilisée aux deux extrémités, comme déterminer s'il y a un obstacle à un certain point de coordonnées sur la carte.

Les données liées au jeu telles que les joueurs et les scènes du côté Node sont toutes stockées en mémoire et synchronisées régulièrement avec des fichiers. Chaque fois que le service Node démarre, les données sont lues du fichier vers la mémoire. De cette façon, lorsqu'il y a plus de lecteurs, la fréquence de lecture et d'écriture des fichiers augmente de façon exponentielle, provoquant des problèmes de performances. (Plus tard, afin d'améliorer la stabilité, un tampon a été ajouté pour la lecture et l'écriture des fichiers, en utilisant la méthode "memory-file-backup" pour éviter les dommages aux fichiers causés par le redémarrage du serveur pendant le processus de lecture et d'écriture).

Le côté nœud est divisé en plusieurs couches telles que l'interface, les données et l'instance. L'"interface" est chargée d'interagir avec le navigateur. Les « données » sont des données statiques, telles que le nom et l'effet d'un certain médicament, la vitesse et la force physique d'un certain monstre, et font partie des règles du jeu. « Instance » est l'état actuel du jeu. Par exemple, un médicament sur un certain joueur est une instance de « données sur un médicament ». Pour un autre exemple, « l'instance de cerf » a l'attribut « volume sanguin actuel ». Le cerf A peut être de 10, le cerf B peut être de 14, et « le cerf » lui-même n'a qu'un « volume sanguin initial ».

3. Mise en place de la carte de scène

Scène de la carte

Commençons par la partie scène de la carte, qui s'appuie toujours sur Easycanvas pour le rendu.

Penser

Puisque le joueur est toujours fixé au centre de l'écran, le mouvement du joueur est en réalité le mouvement de la carte. Par exemple, si le joueur court vers la gauche, la carte se déplacera vers la droite. Comme mentionné tout à l'heure, le joueur se trouve dans la couche intermédiaire des trois toiles et la carte appartient à la couche inférieure, le joueur doit donc bloquer la carte.

Cela semble raisonnable, mais s'il y a un arbre sur la carte, alors « le niveau du joueur est toujours supérieur à celui de l'arbre » est faux. En ce moment, il y a 2 grandes solutions :

La carte est superposée et le « sol » est séparé du « au-dessus du sol ». Placez le joueur entre deux calques, par exemple, dans l'image ci-dessous, le côté gauche est au sol et le côté droit est au sol, puis superposez et dessinez pour prendre en sandwich le personnage au milieu :

Cela semble résoudre le problème, mais cela introduit en fait deux nouveaux problèmes : le premier est que les joueurs peuvent parfois être bloqués par des objets « au sol » (comme un arbre), et parfois ils doivent pouvoir bloquer des objets « au sol ». le sol" (Par exemple, si vous vous tenez sous cet arbre, votre tête bloquera l'arbre). Un autre problème est que le coût des performances du rendu va augmenter. Étant donné que les joueurs changent tout le temps, la couche « sol » doit être redessinée fréquemment. Cela brise également la conception originale - pour enregistrer autant que possible le rendu de la grande carte au sol, ce qui rend la superposition de la toile plus compliquée.

La carte n'est pas superposée, "au sol" et "au-dessus du sol" sont dessinés ensemble. Lorsque le joueur est derrière un arbre, réglez la transparence du joueur à 0,5, comme indiqué ci-dessous :

Il n'y a qu'un seul inconvénient à cela : le corps du joueur est soit opaque, soit translucide (les monstres marchant sur la carte auront également cet effet), ce qui ne sera pas tout à fait réaliste. Car l'effet idéal est d'avoir une scène où une partie du corps du joueur est obscurcie. Mais cela améliore les performances et le code est facile à maintenir. J'utilise actuellement cette solution.

Alors, comment déterminer quelles parties de l’image « carte » sont des arbres ? Les jeux ont généralement un gros fichier de description de carte (en fait un tableau), qui utilise des nombres tels que 0, 1 et 2 pour identifier quels endroits peuvent être traversés, où se trouvent des obstacles, quels endroits sont des points de transfert, etc. Le "fichier de description" dans Legend of Hot Blood est décrit en 48x32 comme la plus petite unité, donc les actions du joueur dans Legend auront une sensation "d'échiquier". Plus l'unité est petite, plus elle est fluide, mais plus le volume qu'elle occupe est important et plus le processus de génération de cette description prend du temps.

Passons aux choses sérieuses.

Réussite

J'ai demandé à un ami de m'aider à exporter la carte de la « Province de Beech » dans le client Legend of Legend. Elle mesure 33 600 de large et 22 400 de haut, soit des centaines de fois la taille de mon ordinateur. Pour éviter que l'ordinateur n'explose, il doit être divisé en plusieurs morceaux à charger. Puisque la plus petite unité d'une légende est 48x32, nous divisons la carte en 4900 (70x70) fichiers image à 480x320.

Je pense que vous maîtrisez la méthode après avoir lu le cas dans cet article. Pour des informations plus intéressantes, veuillez prêter attention aux autres articles connexes sur le site Web chinois de php !

Lecture recommandée :

FileReader implémente l'aperçu local avant de télécharger des images

Explication détaillée des étapes de vue-dplayer pour implémenter la lecture hls

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn