J'ai décidé de sortir une nouvelle version de mon ancien jeu par navigateur, qui depuis quelques années a été une application réussie sur les réseaux sociaux. Cette fois, j'ai décidé de le concevoir également comme une application pour Windows (7-8-10) et de le placer dans divers magasins. Bien sûr, à l'avenir, vous pourrez créer des assemblages pour MacOS et Linux.
Le code du jeu est entièrement écrit en javascript pur. Pour afficher des graphiques 3D, la bibliothèque three.js est utilisée comme lien entre le script et WebGL. Cependant, c'était le cas dans l'ancienne version du navigateur. La chose la plus importante dans ce projet pour moi était la raison, en parallèle avec le jeu, d'ajouter ma propre bibliothèque, conçue pour compléter three.js avec les outils pour un travail pratique avec les objets de la scène, leur animation et de nombreuses autres fonctionnalités. Je l'ai ensuite abandonné pendant longtemps. Il est temps de revenir vers elle.
Ma bibliothèque contient des outils pratiques pour ajouter et supprimer des objets de la scène, changer les propriétés de parties individuelles d'objets (maillages), une animation indépendante du cadre du mouvement des objets 3D, un nuanceur de ciel avec la texture du ciel étoilé la nuit et bien plus encore. Je vais vous parler de certains d'entre eux. En ce qui concerne le ciel, j'ai implémenté sa création avec une seule fonction qui prend un certain nombre de paramètres d'entrée, initialise le shader, charge la texture du nuage (si nécessaire) et commence à mettre à jour le ciel avec une itération donnée.
Cependant, tout y est un peu plus compliqué - pour les fonctions périodiques, mais rarement appelées, une autre construction fonctionne réellement, en utilisant setInterval (), dans laquelle les événements peuvent être lancés à différents intervalles, et cela réduira tout cela à un dénominateur commun et le calculera à droite événements nécessaires sur la liste. Là, vous pouvez également lancer l'intervalle de mise à jour du ciel. Mais le mouvement des objets de jeu 3D pour plus de fluidité a déjà été implémenté via requestAnimationFrame () ...
Donc, puisque nous parlons du ciel, nous allons commencer par lui.
Le ciel
Ajouter le firmament à la scène est le suivant.
Vous devez d'abord ajouter la lumière standard three.js à la scène avec ses valeurs de luminosité maximales (initiales). La scène entière avec ses objets, sa lumière et d'autres attributs, afin de ne pas encombrer l'espace global, sera stockée dans l'espace de noms apscene.
Après cela, vous pouvez déjà exécuter l'animation du ciel avec des shaders, des textures (blackjack et ... enfin, d'accord) à travers l'une de mes fonctions:
m3d.graph.skydom.initWorld(
En conséquence, un ciel dynamique apparaît sur la scène avec un changement en douceur de la hauteur du soleil et, par conséquent, un changement de l'heure de la journée.
Il n'est pas nécessaire d'utiliser tous les types d'éclairage sur scène. Et il n'est pas nécessaire de modifier tous les paramètres en fonction de l'heure de la journée. Mais, en jouant avec leur luminosité, vous pouvez néanmoins créer une image assez réaliste du changement de jour et de nuit. Vous pouvez nommer les paramètres comme vous le souhaitez, l'essentiel est d'observer les clés des objets à l'intérieur comme ils sont spécifiés dans three.js.
À quoi cela ressemble, vous pouvez voir la vidéo de la scène de démonstration:
C'est un jeu différent. C'est juste que l'horizon n'est pas encombré de divers objets, et donc le travail de ce script est le plus clairement visible. Mais dans le jeu dont l'histoire est discutée, exactement la même approche est utilisée. La vitesse élevée du passage du temps ici n'est réglée qu'à des fins de démonstration, et donc le temps s'écoule, bien sûr, plus lentement, avec la même étape de l'itération mettant à jour le firmament. Dans cette démo, au fait, un shader d'eau est impliqué, également avec des paramètres variables, en fonction de la hauteur du soleil ... Mais je ne l'ai pas encore finalisé.
Performances
Tout cela demande très peu de fer. Fonctionnant dans le navigateur Chrome, il charge le Xeon E5440 sous LGA775 (et avec 4 Go de RAM) de 20%, et le cœur de la carte graphique GT730 de 45%. Mais cela est uniquement dû à l'animation de l'eau. Si nous parlons d'un jeu où il n'y a pas d'eau, mais il y a une ville, celle-ci:
puis au moment où la voiture se déplace dans la ville - 45% pour cent, 50% pour la carte vidéo. En principe, avec un certain fps (jusqu'à environ 30 images par seconde), cela fonctionne assez bien même sur Pentium4 3GHz (1 Go de RAM) et sur une tablette sur Intel Atom 1,3 GHz (2 Go de RAM).
Tout ce matériel est extrêmement faible et d'autres jeux similaires sur WebGL et sur HTML5, même certains 2D, ça me ralentit impie, au point qu'il devient impossible de les jouer. Comme on dit, écrivez le jeu vous-même, selon vos besoins, et jouez.
Scène
La scène 3D dans three.js est un objet de scène et son tableau d'enfants est, en fait, tous les modèles 3D chargés dans la scène. Afin de ne pas enregistrer un appel de chargeur de démarrage pour chaque modèle, j'ai décidé que la scène de jeu entière serait définie sous la forme d'une certaine configuration, avec un grand tableau associatif locd: {} (comme les données de localisation), qui contiendrait tous les paramètres - lumières, chemins des textures préchargées et des images pour l'interface, des chemins vers tous les modèles qui devraient être chargés sur la scène, et plus encore. En général, c'est la configuration complète de la scène. Il est défini une fois dans le fichier js du jeu et envoyé à mon chargeur de scène.
Et cet objet locd: {}, en particulier, contient les chemins d'accès aux modèles 3D individuels qui doivent être chargés. Le chemin zéro est le chemin commun, puis les chemins relatifs pour chaque objet, tels que:
['path/myObj', scale, y, x,z, r*Math.PI, 1, '', '', '', 1, ['','','',''], 'scene']
Il est entendu que tous les modèles sont exportés de l'éditeur 3D au format json, c'est-à-dire qu'ils ont des chemins comme path / myObj.json. Viennent ensuite l'échelle (puisque l'éditeur peut être enregistré avec une échelle qui ne convient pas au jeu), la position de l'objet en hauteur (y), le long des axes (x) et (z), puis l'angle de rotation ® du modèle en (y), un certain nombre de paramètres optionnels et le nom de la scène où charger le modèle - sur la scène principale (scène) ou sur l'arrière-plan (scèneb).
Oui, il fallait l'implémenter non pas sous la forme d'un simple, mais sous la forme d'un tableau associatif. Donc, l'ordre des paramètres est incompréhensible même sans documentation, ou du moins sans le type de fonction qui prend ces paramètres, vous ne comprendrez pas. Je pense qu'à l'avenir je vais refaire ces lignes dans des tableaux associatifs. En attendant, cela ressemble à ceci:
landobj: [ ['gamePath/'], [ ['landscape/ground', 9.455, 0, 0,0, 0*Math.PI, 1, '', '', '', 1, ['','','',''], 'scene'], ['landscape/plants', 9.455, 0, 0,0, 0*Math.PI,1, '', '', '', 1, ['','','',''], 'scene'], ['landscape/buildings/house01', 2, 0, -420,420, -0.75*Math.PI, 1, '', '', '', 1, ['','','',''], 'scene'], ... ] ],
Ces modèles sont chargés sur la scène et placés dans les coordonnées données ici dans l'espace. En principe, tous les modèles peuvent être chargés en tant qu'objet unique, c'est-à-dire exportés de l'éditeur sous forme de scène de jeu entière et chargés en coordonnées (0; 0; 0). Ensuite, il n'y aura qu'une seule ligne: paysage / sol - j'ai ground.json - c'est la partie principale du monde du jeu. Mais dans ce cas, il sera difficile de manipuler des objets individuels de la scène, car vous devrez d'abord regarder dans la console du navigateur et vous rappeler lequel des enfants de cet immense terrain est. Et puis contactez-les par numéros. Par conséquent, les modèles de jeux itinérants sont mieux chargés avec des objets séparés. Ensuite, ils sont accessibles par nom à partir du tableau associatif, qui sera automatiquement créé spécialement à cet effet.
La configuration complète du jeu peut ressembler, par exemple, à ceci:
locd:{
Oui, il vaut mieux refaire tous ces sous-réseaux en tableaux associatifs, sinon l'ordre des paramètres n'est pas clair ...
Modèles 3D

Une autre chose intéressante. Chargement des modèles. Ma bibliothèque accepte les modèles 3D avec des textures et définit automatiquement certains paramètres sur leurs éléments individuels (mailles), en fonction des noms. Le fait est que si, par exemple, le modèle est configuré pour projeter une ombre, il sera projeté par chaque maillage inclus dans sa composition. Il n'est pas toujours nécessaire que l'ensemble du modèle projette une ombre complète ou acquière d'autres propriétés qui affectent fortement les performances. Par conséquent, si vous activez un certain indicateur signalant qu'il est nécessaire de considérer chaque maillage séparément, alors lors du chargement, il est possible de déterminer quel maillage aura telle ou telle propriété et laquelle ne le sera pas. Eh bien, par exemple, il n'est absolument pas nécessaire que l'ombre soit projetée par le toit horizontal plat de la maison ou par de nombreux détails mineurs non pertinents du modèle sur un grand arrière-plan. Néanmoins, le lecteur ne pourra pas voir ces ombres et la puissance du processeur vidéo sera utilisée pour les traiter.
Pour ce faire, dans l'éditeur graphique (Blender, Max, etc.), vous pouvez immédiatement spécifier les noms des mailles (dans le champ du nom de l'objet) selon une certaine règle. Il doit y avoir un trait de soulignement (_). Les caractères de contrôle conditionnel doivent aller sur le côté gauche, par exemple: d - double face (le maillage est bidirectionnel, sinon - unidirectionnel), c (ombre portée) - projette une ombre, r (reçoit l'ombre) - prend des ombres. Autrement dit, le nom du maillage de tuyau dans la maison peut être cr_tube. De nombreuses autres lettres sont également utilisées. Par exemple, "l" est un collisionneur, c'est-à-dire que le mur de la maison, ayant le nom crl_wall01, ne permettra pas au joueur de passer à travers lui-même, et projette également et prend une ombre. Il n'est pas nécessaire de fabriquer des collisionneurs, comme un toit ou une poignée de porte, et donc de dégrader les performances. Comme vous l'avez déjà compris, lors du chargement d'un modèle, ma bibliothèque analyse les noms des maillages et leur donne les propriétés correspondantes sur la scène. Mais pour cela il faut nommer correctement toutes les mailles avant d'exporter le modèle depuis l'éditeur 3D. Cela réduira considérablement les performances.
Tous les indicateurs de contrôle pour les maillages à l'intérieur d'un objet:
col_ ... est le collisionneur. Un tel maillage sera affiché simplement comme un collisionneur transparent et invisible. Dans l'éditeur, il peut ressembler à tout, seule sa forme est importante. Par exemple, il peut s'agir d'un parallélépipède autour de l'ensemble du modèle s'il est nécessaire que le joueur ne puisse pas passer par ce modèle (bâtiment, grosse pierre, etc.).
l_ ... est un objet collidable. Donner à n'importe quel maillage une propriété de collision.
i_ ... - intersections. Le maillage sera ajouté à la liste des intersections, qui peut être utilisé, par exemple, pour cliquer dessus, c'est-à-dire pour donner de l'interactivité au jeu.
j_ ... aussi des intersections. Le même que ci-dessus, seulement une version plus récente - avec un algorithme amélioré pour trouver des intersections dans le jeu et moins de consommation de ressources.
e_ ... - intersections pour les portes des maisons (entrée / sortie). Exclut l'intersection sur les autres maillages d'objets. Il est utilisé s'il est nécessaire dans les maisons à un moment donné de ne rendre interactives que les portes, à l'exclusion de tous les autres éléments interactifs. Avec l'imagination, vous pouvez trouver cela et une tonne d'autres utilisations.
c_ ... - projette des ombres. Le maillage projette une ombre.
r_ ... - recevoir des ombres. Un maillage accepte les ombres de toutes les autres mailles qui les ont projetées.
d_ ... - bilatéral (double face). Visible des deux côtés, la texture se superpose des deux côtés.
t_ ... - transparent (transparent), si l'objet entier est défini sur alphatest dans three.js.
u_ ... - transparent (transparent), avec une densité fixe (0.4), si l'objet entier ne spécifie pas alphatest dans three.js.
g_ ... - verre. La transparence fixe est définie (0,2).
h_ ... - invisible (caché). Pour les parties de l'objet (maillages) qui doivent être masquées lors de l'ajout de l'objet à la scène. Entré dans la liste des cachés.
v_ ... visible. Tous les objets à l'exception de ceux marqués avec "h" sont déjà visibles, mais avec le drapeau "v", ils sont entrés dans une liste séparée visible pour une dissimulation ultérieure ou d'autres manipulations.
Par conséquent, le nom du maillage pourrait bien être quelque chose comme ceci: crltj_box1 (jette, accepte une ombre, collisionneur, transparent, interactif). Et un autre maillage faisant partie du même modèle: cr_box2 (ne transforme et ne prend que des ombres), Naturellement, les caractères de contrôle peuvent être définis dans n'importe quel ordre. Ainsi, depuis l'éditeur, vous pouvez contrôler l'affichage futur de parties de l'objet dans le jeu, ou plutôt, certaines de leurs propriétés, tout en économisant de la puissance de calcul.
L'essence du jeu
En fait, le sens du jeu dont parle l'histoire est de se déplacer dans le périmètre du terrain carré et d'acheter des entreprises. Le champ est réalisé sous forme de rues 3D. Le modèle économique du jeu est sensiblement différent de ceux du genre. Dans mon jeu, lorsque quelqu'un démarre une entreprise, votre bénéfice attendu baisse. Et vice versa, lorsque vous découvrez quelque chose, cela augmente. Tous les calculs de profits et pertes sont effectués dans l'inspection fiscale du champ Début. Vous pouvez également contracter un emprunt auprès d'une banque, négocier des titres et faire un certain nombre d'autres choses. J'ai amélioré le comportement de l'IA par rapport à l'ancienne version. Refonte de presque tous les modèles et textures 3D du jeu et performances optimisées. Faites plus de réglages et bien plus encore.
L'animation
Pour animer le mouvement des objets du jeu, le moteur le plus simple est utilisé, qui avec une fréquence d'images donnée (il est limité au 60e) modifie tous les paramètres numériques pour un intervalle de temps donné, transmettant des valeurs intermédiaires au gestionnaire. Et dans le gestionnaire, par exemple, le modèle s'affiche.
Par exemple. Nous devons déplacer l'objet obj dans l'espace de la position (10; 10; 50) au point (100; 300; 60). Nous définissons 3 paramètres en indiquant leurs valeurs initiales et finales. La coordonnée x variera de 10 à 100, y - de 10 à 300 et z - de 50 à 60. Et tout cela devrait arriver, disons, en 4 secondes.
m3d.lib.anim.add( 'moveobj', 1, 4000, 'and', {userpar1:111, obj:my3DObject}, [ {lim1:10, lim2:100, sstart:10, sfin:100, t:0}, {lim1:10, lim2:300, sstart:10, sfin:300, t:0}, {lim1:50, lim2:60, sstart:50, sfin:60, t:0} ], function(){ myPeriodicFun(this); }, function(){ myFinishFun(this); } ); m3d.lib.anim.play();
La première ligne de 5 paramètres: moveobj - nom de l'animation (n'importe laquelle), 1 - numéro de flux (vous pouvez animer des objets en parallèle dans un nombre illimité de flux), 4000 - temps d'animation de 4 secondes, et - tandis qu'un paramètre inutilisé, qui sera à l'avenir responsable de la logique de transition entre les animations dans le même flux, userpar est tout tableau associatif qui sera transmis au gestionnaire en tant que paramètre, par exemple, avec un rayon calculé, des sinus, des cosinus et généralement toutes les valeurs calculées pour cette animation, afin de ne pas être calculées dans sur le temps de chaque itération. Ou en référence à un objet 3D qui, en fait, s'animera.
Vient ensuite un tableau avec des paramètres modifiables. Nous constatons que le premier paramètre est le changement de la coordonnée x, le second est y, le troisième est z. Nous écrivons pour chacun en lim1 et lim2, de quoi et à quelle taille cela va changer. Dans sstart et sfin, nous spécifions les mêmes valeurs. Ici, vous pouvez spécifier un début, par exemple, à partir d'une autre valeur, puis le paramètre «défilera» dans un cercle de celui-ci, en contournant lim2 et en commençant une nouvelle «révolution» avec lim1. Eh bien, par exemple, cela est nécessaire si notre animation est bouclée entre certaines valeurs (lim1 et lim2), mais nous devons la démarrer non pas depuis le début (c'est-à-dire pas depuis lim1), mais à partir d'une valeur intermédiaire.
t: 0 définit simplement que l'animation de ce paramètre est exécutée 1 fois, en fonction du temps total (4000), comme si elle y était étirée. Si nous fixons un autre nombre, inférieur à l'heure principale, alors ce paramètre sera mis en boucle et sera répété jusqu'à l'heure de l'animation principale (4000). C'est pratique, par exemple, pour définir la rotation de l'objet lorsque l'angle doit traverser la ligne à 360 degrés de manière répétée et être remis à 0.
Viennent ensuite 2 rappels - celui qui sera exécuté à chaque itération et celui qui sera exécuté une fois à la fin de toute l'animation (point de sortie).
Le premier rappel myPeriodicFun (this), par exemple, peut être comme ceci:
myPeriodicFun:function(self) { var state=self.par[0].state, state_old=self.par[0].state_old; var state2=self.par[1].state, state_old2=self.par[1].state_old; var state3=self.par[2].state, state_old3=self.par[2].state_old; if ((state!=state_old)||(state2!=state_old2)||(state3!=state_old3)) { var obj=self.userpar.obj; obj.position.x=state; obj.position.y=state2; obj.position.z=state3; ap.cameraFollowObj(obj); }; },
C'est-à-dire qu'à chaque itération du mouvement, un paramètre (self) est lancé dans cette fonction contenant les valeurs intermédiaires calculées pour tous les paramètres d'animation donnés: self.par [0] .state, self.par [1] .state, self.par [2] .état. Ce sont nos x, y et z à l'heure actuelle. Par exemple, si l'animation dure 4 secondes, après 2 secondes, x sera égal à (100-10) / 2 = 45. Et donc pour toutes les coordonnées. En conséquence, dans myPeriodicFun, nous affichons simplement notre objet dans ces coordonnées.
Si le navigateur commence à prendre du retard ou s’exécute lentement sur ce matériel, cela ne fait pas peur: la durée totale de l’animation ne changera pas, seule la fréquence d’images diminuera et l’image se transformera en diaporama.Pourquoi vérifier f ((state! = State_old) ..., c'est-à-dire si la nouvelle valeur calculée est égale à l'ancienne (state_old se souvient du calculé dans l'itération précédente), eh bien, par exemple, de sorte que si certains paramètres changent moins d'un, alors ne redessinez pas l'objet entier et ne dépensez pas la puissance du système dessus, et le moteur d'animation produit des entiers dans state et state_old, qui, par exemple, peuvent être interprétés comme un pas égal à un pixel. Et si l'objet ne bouge pas par rapport à la position précédente même par 1 pixel, il n'est pas nécessaire de le redessiner, car e la position sur l'écran ne change pas.En général, l'animation est comprise comme un simple changement d'un nombre quelconque de paramètres dans un certain temps avec la livraison de leurs valeurs intermédiaires à une fonction de rappel. Par exemple, vous pouvez ajouter un autre 4ème paramètre, qui sera responsable de l'angle de rotation de l'objet. Et vous pouvez généralement entasser les paramètres de nombreux objets en une seule animation s'ils se déplacent de manière uniforme. Vous pouvez mettre des animations dans différents flux, puis elles seront traitées en parallèle. Vous pouvez ajouter (m3d.lib.anim.add ()) à un seul thread toute une séquence d'animations, et elles seront exécutées l'une après l'autre. De plus, chaque thread aura sa propre séquence indépendante. L'essentiel est que le système ait suffisamment de puissance et que tout ne se transforme pas en diaporama.PS. Les flux ici sont réalisés simplement par énumération séquentielle à chaque itération de toutes les animations parallèles et calcul pour chacune d'elles de valeurs intermédiaires de tous leurs paramètres. Autrement dit, il n'y a pas de véritable multithreading en javascript.Le même «moteur» peut également être utilisé pour animer des éléments d'interface en les réglant pour changer 2 coordonnées sur le plan de l'écran et en affichant ces éléments dans des fonctions de rappel basées sur les valeurs intermédiaires obtenues. Ce qui, en fait, se fait dans mon jeu.Ombres dynamiques
L'affichage des ombres sur toute la scène s'est avéré être un gaspillage si important que lors de leur activation, les images par seconde ont chuté plusieurs fois. Ce n'est pas bon. Nous afficherons les ombres des objets dans un certain petit carré autour du joueur. Je dirai tout de suite qu'une telle technique augmente considérablement la fréquence d'images.Rien de compliqué ici. Les ombres de three.js sont projetées à l'intérieur d'une certaine caméra d'ombre spécifiée par la boîte (shadowCameraLeft, shadowCameraRight, shadowCameraTop, shadowCameraBottom). Il peut être étendu à l'ensemble de la scène ou créé de manière à ce qu'il suive la caméra principale et que les ombres soient projetées uniquement autour d'elle. La seule chose que j'ai ajoutée à ce système est l'étape par laquelle les ombres seront mises à jour. Il n'est absolument pas nécessaire de faire cette mise à jour à chaque fois que le joueur se secoue, car cela charge le système avec des calculs. Laissez-le dépasser une certaine distance minimale le long de l'un des trois axes afin que les ombres soient mises à jour.Dans ma bibliothèque, lors de l'initialisation du monde 3D, un objet contr est créé, qui contient à tout moment les coordonnées de la caméra. Vous pouvez également y définir le paramètre utilisateur contr.cameraForShadowStep, qui contient le pas de la position de la caméra à laquelle la position de la caméra fantôme change.Si, par exemple, le parallélépipède de la caméra fantôme a des dimensions 700x700x700, alors contr.cameraForShadowStep peut être défini, par exemple, sur 20. Et lorsque le joueur bouge de 20 sur l'un des axes, la position d'origine sera mémorisée et les ombres autour du joueur seront mises à jour. L'échelle du monde 3D peut être quelconque, selon l'échelle à laquelle tous les modèles ont été créés dans l'éditeur 3D. Et il est probable qu'au lieu de 700x700x700 et 20, vous devrez utiliser 7000x7000x7000 et 200. Mais cela ne change en rien l'essence.Soit dit en passant, lorsque le soleil se déplace dans le ciel, les ombres sont mises à jour indépendamment de ce système, car la direction des ombres devrait y changer. Autrement dit, ils seront mis à jour même si le joueur est debout sans mouvement. Là, la fonction de mise à jour des ombres en fonction de la période de mise à jour du ciel est appelée sans ambages.Système d'éclairage ponctuel
La présence de plus d'une douzaine de sources lumineuses ponctuelles sur la scène est aussi forte en fps que les ombres dynamiques. Et rend même impossible de jouer l'ancien "Hemp". De plus, peu importe que ces sources soient dans le champ de vision du joueur (la plage de dessin du monde peut être définie) ou à une distance considérable. S'ils sont stupidement présents sur scène, alors tout fonctionne lentement. Par conséquent, j'ai fourni dans le menu des paramètres du jeu, qui peut être appelé avant de charger le monde 3D, des options pour le nombre de ces sources (2, 4 ou 8) appelées "Light of Lanterns".
Ainsi, la nuit, allumer toutes les lumières placées sur la scène en même temps ne fonctionnera pas. Ci-dessus, dans la description du schéma d'initialisation du monde, j'ai présenté des tableaux de userPointLights et lightsPDynamicAr. Dans userPointLights, les coordonnées de toutes les lampes de la lampe sur la scène sont définies dans un tableau. Et lightsPDynamicAr contient les paramètres d'éclairage pour les 8 instances. Selon le réglage du nombre de lumières, la bibliothèque prendra la première d'entre elles et ajoutera à la scène dans le champ de vision du joueur.En fait, pendant que le joueur se déplace, une recherche est effectuée pour 2-8 lanternes les plus proches du joueur par le tableau de coordonnées des lanternes userPointLights. Et les sources lumineuses ponctuelles se déplacent sous elles. En d'autres termes, 2-8 lampes d'éclairage suivent le joueur, l'entourant. De plus, cela ne se fait pas non plus avec chaque trame de fps, mais avec une étape donnée. Il n'est absolument pas nécessaire de démarrer la fonction de recherche 60 fois par seconde, surtout si le joueur ne bouge pas - puis laissez brûler les lumières déjà trouvées autour de lui.Voici à quoi cela ressemble en mouvement (Xeon E5440, GeForce GT730):Construction de distribution
Comme je n'utilise aucun environnement de développement sophistiqué (à l'exception du bloc-notes avancé), j'ai écrit un fichier bat dans lequel le compilateur Google Closure est appelé pour masquer le code de chaque fichier * .js. Et puis nwjc.exe du bundle nw.js y est appelé pour compiler js en binaires (* .bin). Je vais donner un exemple pour l'un des fichiers:java -jar D: \ webservers \ Closure \ compiler.jar --js D: \ webservers \ proj \ m3d \ www \ game \ bus \ bus.js --js_output_file D: \ webservers \ proj \ nwProjects \ bus \ game \ bus \ bus.js
cd D: \ "Program Files" \ Web2Exe \ down \ nwjs-sdk-v0.35.5-win-ia32
D: \ "Program Files" \ Web2Exe \ down \ nwjs-sdk-v0.35.5-win -ia32 \ nwjc.exe D: \ webservers \ proj \ nwProjects \ bus \ game \ bus \ bus.js D: \ webservers \ proj \ nwProjects \ bus \ game \ bus \ bus.bin
del D: \ webservers \ proj \ nwProjects \ bus \ game \ bus \ bus.js
Ensuite, j'utilise le simple utilitaire Web2Executable pour créer un fichier exe avec assemblage sous Windows. J'ai choisi la version 0.35.5 de nw.js, bien que des versions plus récentes soient également disponibles. Je n'ai remarqué aucun effet de leur part, sauf pour augmenter la taille de l'assemblage.L'utilitaire est capable de télécharger la version sélectionnée de nw.js dans le dossier spécifié lui-même. La sortie est un assemblage. Le fichier exécutable de 35 mégaoctets contient, en fait, le jeu lui-même. Tout le reste est node-webkit. Le dossier locales contient évidemment des fichiers avec des ressources dans différentes langues. Je les ai supprimés et n'ai laissé que ceux liés à l'anglais. Soit dit en passant, cela n'a pas empêché le lancement de la version russe du jeu (dans le jeu, la langue bascule entre le russe et l'anglais). Pourquoi tous ces fichiers alors, je ne sais pas. Mais sans l'anglais, rien ne commence.L'assemblée entière a finalement pris 167 Mo.Ensuite, j'ai tout emballé dans un fichier de distribution exécutable à l'aide de l'un des utilitaires gratuits conçus à cet effet, et la sortie était de 70,2 Mo Businessman3DSetup.exe.Publication
J'ai envoyé l'assemblage à différents magasins d'applications. La plupart d'entre eux sont encore en train de modérer mon jeu. Pour le moment, seule la démangeaison l'a publié jusqu'à présent. Je vous préviens tout de suite, le jeu est payé, le prix est de 3 $. Il n'y a pas encore d'achats, mais je n'ai pas encore été engagé dans sa promotion. GOG a refusé de publier, citant le fait que le jeu est assez simple et de niche. Je suis en principe d'accord. Epic Store, je pense, fera de même.La version publiée du jeu est mono-utilisateur, avec des bots d'un montant de 1 à 5. Langue - russe et anglais. J'ai l'intention de terminer la version réseau. Mais en pensant - pour le publier sous la même application ou sous la forme d'une version de navigateur Web disponible immédiatement sous Windows, Linux, iOs et MacOs, et généralement partout où le navigateur prend en charge WebGL. En effet, en fait, le webkit est un navigateur et le jeu y fonctionne très bien, dans Firefox, dans Edge et même dans IE11 sous Windows, bien que dans ce dernier il soit très lent.Conclusions
Je suppose que je ne suis pas encore prêt à disposer mon moteur pour une utilisation générale, car il n'est pas encore terminé. Je vais d'abord écrire un autre jeu dessus, juste celui de la vidéo de démonstration sur les vaisseaux, car sur ce jeu vous pouvez "parcourir" le travail avec le shader d'eau. De plus, je prévois d'y implanter un moteur physique simple. Oui, et vous devez toujours terminer toutes les autres fonctionnalités, corriger tous les défauts. Et il vaut mieux le faire sur deux jeux que sur un, car certaines nuances sont possibles. En attendant, mon moteur, à mon avis, est encore trop affûté pour un match.De plus, je ne suis pas du tout sûr que quelqu'un ait besoin de tout cela. Si vous regardez sobrement, personne n'écrit de jeux en pur javascript. Mais je l'aime parce que les jeux sont assez faciles et rapides pour le navigateur. Ils se chargent rapidement, ne nécessitent pas beaucoup de RAM et fonctionnent assez rapidement par rapport aux concurrents en html5, même par rapport à la 2D. Je pense que je publierai plus d'un jeu de navigateur (et pas seulement) sur tous ces développements.