Maison > interface Web > js tutoriel > JS imitation jeu légendaire classique

JS imitation jeu légendaire classique

php中世界最好的语言
Libérer: 2018-03-17 14:45:44
original
3722 Les gens l'ont consulté

Cette fois, je vous présente le jeu d'imitation JS Legend of Legend. Quelles sont les précautions pour l'imitation JS du jeu Legend of Legend. Voici des cas réels, jetons un coup d'oeil.

Avant-propos

La première version du jeu a été développée en 2014. Le navigateur utilise html+css+js et 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 Implémenter un jeu côté PC relativement complexe

1.js est réalisable pour implémenter des jeux en ligne côté PC. 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 à ce stade, 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 avez-vous choisi un jeu légendaire de 2001 ?

La première raison est le ressenti de l'ancien jeu bien sûr, l'autre raison plus importante est que.. . Soit je ne peux pas jouer au jeu, soit je peux y jouer mais je n'ai pas le matériel (images, bruitages, 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.

Comme 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

Côté navigateur

Le rendu de l'écran utilise du 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 objets 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 pour surveiller les événements pour les animaux et les objets. Si vous utilisez la méthode dom, il y aura plusieurs problèmes difficiles à résoudre :

a. L'ordre de rendu est différent de l'ordre de traitement des événements (parfois le plus petit z. -index doit être traité en premier événement), nécessitant un traitement supplémentaire. 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 pour une gestion facile (de cette manière, le positionnement de plusieurs éléments peut être hérité de l'élément parent, sans avoir à 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 d'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 les différentes opérations de rendu du canevas (telles que drawImage, fillText, etc.) sont mises ensemble avec le code du projet, cela conduira inévitablement à l'incapacité 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 sera beaucoup plus facile. 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. Easycanvas est responsable du lien « les modifications des données entraînent des modifications de la vue ». 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, les styles d'un total de 40 éléments sur 5 lignes et 8 colonnes peuvent être transmis à Easycanvas sous la forme suivante (l'index est l'index de l'article, la distance entre les éléments dans la direction x est de 36 et la distance dans la direction y de 32). Et cette logique est immuable, quelle que soit la façon 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;
 }
}
Copier après la connexion

Rendu en couches de toile

Hypothèse : le jeu doit conserver 40 images, le navigateur fait 800 de large et 600 de haut, avec une superficie de 480 000 (ci-après dénommé 480 000 pour 1 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 n'importe où sur toute la toile : cela peut être le mouvement des joueurs et des animaux, cela peut être les effets spéciaux des boutons, cela peut être l'effet de un certain changement de 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 qu'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'intégralité 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'UI, du sol (carte) et des elfes (personnages, animaux, effets de compétences, etc.) :

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

Par exemple, la couche UI, 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. Étant donné que les changements dans les données de la couche d'interface utilisateur, telles que la force physique, sont difficiles à modifier plusieurs fois de manière continue sur une courte période de temps, et que le délai de 50 ms est difficile à percevoir pour les humains, un dessin fréquent n'est donc pas nécessaire. Si nous économisons 20 images par seconde, nous pouvons probablement enregistrer 10 zones de dessin à l’écran.

Comme 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. Puisqu'il est nécessaire d'assurer la fluidité des mouvements des joueurs, la fréquence d'images maximale au sol ne doit pas être trop faible. Si le cadre au sol est de 30 images, alors lorsque le joueur est à l'arrêt, 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.

Dans de cette façon, la zone dessinée par seconde, mouvement du joueur. Elle peut être de 80 à 100 zones d'écran lorsque le joueur ne bouge pas, mais elle 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 permet d'économiser considérablement les performances.

Côté serveur

Le but étant d'implémenter un jeu en ligne multijoueur en js, le côté serveur utilise Node et socket 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 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é Node 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. Implémentation 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.

Réflexion

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. À l'heure actuelle, il existe deux grandes solutions :

La superposition de cartes, "au sol" et "au-dessus du sol" séparées. 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 en prenant en sandwich le personnage au milieu :

Cela semble avoir résolu le problème, mais introduit en fait 2 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 être capable de bloquer des choses "au sol" (comme se tenir dessus En dessous de l'arbre, la tête obscurcira 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 rompt é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, par exemple comme indiqué ci-dessous :

Il n'y a qu'un seul inconvénient à faire 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 complètement 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.

Mise en œuvre

J'ai demandé à un ami de m'aider à exporter la carte de "Beach Province" dans le client Legend of Legend, qui est 33600 large et 33600 haut 22400, 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.

Nous avons fixé la taille de la toile à 800x600, de sorte que les joueurs n'aient qu'à charger 3x3, soit un total de 9 images, pour couvrir toute la toile. 800/480=1,67, alors pourquoi pas 2x2 ? Parce qu'il est possible que la position actuelle du joueur ne provoque l'affichage que d'une partie de certaines images. Comme indiqué ci-dessous :

Je pense que vous maîtrisez la méthode après avoir lu le cas présenté dans cet article. Pour des informations plus intéressantes. , veuillez faire attention aux autres sites Web chinois php Articles connexes !

Lecture recommandée :

Comment définir le mode distant de webpack-dev-server

Comment appeler le parent dans un composant enfant du composant ES6

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!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
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
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal