Heim  >  Artikel  >  Web-Frontend  >  Ausführliche Erläuterung eines Beispiels für die Implementierung eines einfachen Multiplayer-Flugzeugspiels in HTML5

Ausführliche Erläuterung eines Beispiels für die Implementierung eines einfachen Multiplayer-Flugzeugspiels in HTML5

零下一度
零下一度Original
2017-05-13 15:36:483195Durchsuche

Ich habe vor mehr als einem Jahr eine Multi-Personen-Chatroom-Anwendung gesehen. Es war ein Socket.io-Tutorial, das von Lehrer Zhang Dan geschrieben wurde (der ursprüngliche Link kann nicht geöffnet werden, daher habe ich einen Repost gepostet). magisch. Später nutzte ein Projekt während meiner Postgraduiertenzeit auch .Net-Sockets. Es war auch die Zeit, in der ich Konzepte wie Ereignisse, Delegation usw. vollständig verstand und anwendete. Kürzlich nahm ich mir, inspiriert von der Begeisterung für das Studium von Mapbox in der Gruppe und der Hilfe und Inspiration aller Mitglieder von @开 Onkel, @老宇 @F3earth, plötzlich etwas Zeit, um ein Flugzeugkriegsspiel zu entwickeln, das auf Mapbox GL Js und Socket basiert .io! , obwohl die funktionale Schnittstelle sehr einfach ist. Darüber hinaus ist der Code relativ primitiv und wurde nicht ordnungsgemäß umgestaltet. Aber im Grunde habe ich ein Framework, sodass ich es einfach aufschreiben und analysieren kann. Wenn Sie sich für den Quellcode interessieren oder an Entwicklungs- und Optimierungsarbeiten teilnehmen möchten, besuchen Sie bitte das Git-Repository: Jqmap2

Gesamtarchitektur

Einfach ausgedrückt, die Designidee von Das gesamte Spiel ist:

Der Server überwacht von Anfang an jede vom Client gesendete Websocket-Verbindungsanfrage. Nachdem die Verbindung (Verbindungsereignis) hergestellt wurde, werden der Benutzername, die Koordinaten und die Richtung des aktuellen Flugzeugs gesendet vom Client zum ersten Mal als Client zum Client-Datenpool hinzugefügt werden (derzeit werden sie einfach als ClientsArray behandelt). Anschließend werden die Daten eines Clients mit dem Server synchronisiert.

Zukünftig werden auch die Flugzeugkoordinaten, der Kurs und andere Informationen des Clients regelmäßig an den Server gesendet (socket.send), sodass der Server gleichzeitig an alle anderen Clients senden kann. Tatsächlich hat der Server klargestellt, dass er für die Weiterleitung von Nachrichten verantwortlich ist und der Zweck darin besteht, den Status des Spielerflugzeugs im Sichtfeld aller Clients konsistent zu halten.

Da es sich bei Websocket um eine bidirektionale Kommunikation handelt, muss der Client auch regelmäßig Nachrichten an den Server senden und, was noch wichtiger ist, verschiedene vom Server gesendete Nachrichten (Nachrichtenereignisse) verarbeiten, um zu unterscheiden, welche Nachrichten Benutzer online willkommen heißen. , gewöhnliche Spielerpositions-Synchronisationsnachrichten oder Nachrichten, dass A B besiegt hat.

Während dieses Vorgangs halten das Frontend und der Server eine Websocket-Verbindung aufrecht und kommunizieren ständig. Im Vergleich zu herkömmlichen Abfragen und langen Abfragen werden dadurch Datenverkehr und Leistung gespart. Im Allgemeinen basiert die Logik des Spiels auf verschiedenen Nachrichtenereignissen. Das Verbindungsereignis generiert eine Socket-Verbindung, und die Socket-Verbindung verfügt über Nachrichtenereignisse und so weiter.

Backend-Websocket

Nachdem wir so viel Unsinn gesagt haben, schauen wir uns kurz an, wie man einige Schlüsselcodes implementiert.

// Implementierung des Backend-Schlüsselprozesses

var app = express(),
    server = require('http').createServer(app),
    io = require('socket.io').listen(server); // 引入socket.io 库
io.on('connection', function(socket) { // 开始监听客户端的websocket连接请求,connection事件产生 socket 对象
    socket.emit('open'); // 向该客户端发送open事件.
    // init client drone obj for each connection !!
    var client = {
        name: false,
        color: getColor(),
        direction: 0,
        coordinates: [0, 0]
    }
    // message from client.
    socket.on('message', function(msg) {
        if (!client.name && msg.name) { // 如果是第一次连接,把用户的名字存储起来,并且广播给所有客户端。
          var obj  = { }; // 构建发送给其他客户端的消息
          obj = msg;
          clients.push(client); // 加入后台维持的客户端数组
          socket.broadcast.emit('message', obj); // 广播欢迎语给其他客户端
        } else if ( client.name == msg.name ) { // 客户端发来的飞机状态消息
            // 广播给其他客户端
          socket.broadcast.emit('message', obj);
        }
    }
}

Der Hintergrundverarbeitungsprozess ist relativ einfach, Sie müssen nur die von einem Client gesendete Nachricht akzeptieren und weiterleiten an andere Clients (Ich werde nicht über die zufälligen Standorte der feindlichen Flugzeuge sprechen. Natürlich wird es später geändert, sodass alle Clients eine Reihe von Informationen über feindliche Flugzeuge teilen, damit sie gemeinsam gegen denselben BOSS kämpfen können.) Allerdings muss das Front-End die Nachrichten vom Server entsprechend den Geschäftsanforderungen separat verarbeiten

Front-End-Sockel und FlugzeugModell

Das Front-End-Geschäft ist relativ komplex. Zusätzlich zu den Websocket-Nachrichten muss eine Reihe von Flugzeugdatenmodellen erstellt werden, einschließlich Position, Geschwindigkeit, Ausrichtung, Blutvolumen, Waffen und Ausrüstung usw. (es ist möglich). sehr komplex sein, deshalb werden wir uns vorerst einfach damit befassen).

var socket;
try {
    socket = io.connect("http://123.206.201.245:3002");
    socket.on('open', function(){  // 当服务端确认连接后,开始发送第一次数据。
        statusBar.innerText = "已经连上服务器..";
        var askName = prompt("来,取个名字", "");
    }
    socket.on("message", function(json) { // 其实收到的是js 对象,这一点很牛逼。因为双向通信过程中传递的是 Binary 形式的数据,不需要再次解析了。
      if (json.type === "welcome" && json.text.name) {
          // .. 显示其他用户登录消息
      } else if (json.type === "defeat") {
          // .. 在前端的敌机数据模型中移除空血槽的飞机
      } else if (drone && json.text.name != drone.name) {
          // .. 传来的其他客户端飞机消息
          featureCol.features.forEach(function(drone) {
          // featureCol 是所有敌机数据集合,根据用户名check是更新还是新增.
          }
      }
   }

Weitere Probleme sind:

Flugzeugdaten müssen jederzeit geändert, auf den Server hochgeladen und gerendert werden. Das Rendering verwendet das GeoJSON-Objekt als Quelldaten in der Mapbox-API. Ist es also notwendig, alle Flugzeugpositionen neu zu zeichnen, sobald die serverseitige Nachricht empfangen wird? Hier wird die setData()-Methode der Quelle regelmäßig über setInterval aufgerufen, um das Neuzeichnen zu implementieren.

Die Flugbahnberechnung des Flugzeuggeschosses berücksichtigt die Position und Ausrichtung des Flugzeugs in dem Moment, in dem der Benutzer die Leertaste drückt. Basierend auf der eingestellten Flugzeit des Geschosses wird eine Animation angezeigt

Die Kollision zwischen dem Geschoss und dem feindlichen Flugzeug Erkennung und vereinfachte Verarbeitung: Legen Sie eine Konstante als Volumen des Flugzeugs fest und berechnen Sie den Abstand zwischen dem Geschoss und dem feindlichen Flugzeug in Echtzeit während des Fluges des Geschosses. Wenn die Karte einen kleinen Maßstab hat, wird der Erkennungsradius vergrößert, und wenn die Karte einen großen Maßstab hat, wird der Erkennungsradius entsprechend verringert.

Derzeit ist es möglich, dass die Berechnungsmenge der Kollisionserkennung während des Geschossflugs zu groß ist, was zu Verzögerungsproblemen und hoher CPU-Auslastung führt und die gesamte Anwendung etwa 100–130 MB Speicher verbraucht... Es gibt viele kleine Probleme, die ich nicht erwähnen werde. . Es hat wirklich einiges an Hirn gekostet.

Wählen Sie einen oder zwei Punkte zur Analyse aus, einer ist der Flugprozess des Geschosses und der andere ist die zufällige Verhaltenssteuerung des feindlichen Roboterflugzeugs

// setPostion is to update Mydrone position.
function setPosition() {
    // direction in Rad. Generally, 1 Rad stands for 100km
    var current_rotate = map.getBearing(); 
    if (!manual && Math.random() > 0.95) { // 这边有意思,在每秒50帧的情况下,不是每一帧都会随机微调飞机的方向。而是5%的概率。
        direction += (Math.random() - 0.5) /5;
    }    
    // 根据飞机朝向和速度更新位置。
    point.coordinates[0] += speed * Math.sin(direction) / 100;
    point.coordinates[1] += speed * Math.cos(direction) / 100;
    // 校正飞机的朝向显示。因为默认情况下mapbox是根据你的视角随时调整图标方向。但实际上飞机图标的朝向必须和飞机运行方向一致,而不是简单的和标注一样。
    current_rotate = (-current_rotate) + direction * (180 / Math.PI);
}

Das Folgende ist die Berechnung Prozess des Geschossfluges.

// start: fire location, target: bullet destination, duration: total animation time
function renderBulvar(start, target, direction, duration) {
    // target is geojson POINT, add Temp point in layer.. 
    var interval = 20, ratio = interval/duration, real_point = start, range = 0.4, count = 0, hitted = false;
    if (target.coordinates) {
        var targetSource = map.getSource('drone-target');
        window.setInterval(function(){
            if (count > duration/interval) { // 到达终点,不计算了
            } else {
                // 子弹每一帧跑一定比例的路程,最终到达指定终点
                real_point.coordinates[0] += Math.sin(direction)*ratio*range;
                real_point.coordinates[1] += Math.cos(direction)*ratio*range;
                targetSource.setData(real_point);
                if (!hitted){
                    hitted = testCrash(real_point.coordinates); // 感觉这里的hitted 有问题.
                }
                count += 1;
            }
        }, interval);
    }

到这里其实基本介绍了这个游戏的制作过程,经历了一些不成熟的想法,总共花了近十个小时完成目前的开发。还没有严谨地考虑过代码结构和 函数复杂程序,特别是子弹飞行和碰撞等部分,碰撞到后就终结子弹飞行等等。各位如果感兴趣,愿意完成 To DO list中的事情或者有何建议,请访问git仓库: Jqmap2 。请各位大神多提修改意见!

喜欢的同学请关注、收藏相关专题,会不断更新文章。希望和大家分享!!

【相关推荐】

1. 特别推荐“php程序员工具箱”V0.1版本下载

2. 免费h5在线视频教程

3. php.cn原创html5视频教程

Das obige ist der detaillierte Inhalt vonAusführliche Erläuterung eines Beispiels für die Implementierung eines einfachen Multiplayer-Flugzeugspiels in HTML5. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn