Building a 3D MMO using Websockets
来源:互联网 发布:此何遽不为福乎的意思 编辑:程序博客网 时间:2024/06/05 09:45
Author's note: This article does not aim to build huge MMO's like World of Warcraft or similar. These games have millions of players and gigantic budgets, and employ many skilled engineers to keep servers from falling apart. Do not start one of these on your own. The techniques described in this article however allow you to build moderately-sized MMO's that can potentially hold hundreds of players on a single server, depending on your hardware and packet size.
Hi there! My name is Nick Janssen, creator of Ironbane, a 3D MMO that uses WebGL and WebSockets. With this article I would like to give you a better insight in MMO’s and make you less afraid of the complexities involved in building one. From my experience I have found that people consider them very hard, while they are in fact quite easy to make using the web technologies of today.
MMO's are cool. Yet they are considered one of the hardest things to make when it comes to developing software. I believe MMO’s are mainly intimidating to people because of historical reasons.
In the old days, network programming used to be very hard. Complex socket calls were everywhere, multithreading was necessary and JSON was still unknown. A lot has changed since then with the coming of Node.js, its event loop and easy to use socket libraries.
In addition, writing a 3D game was a challenge on its own. You had to include the right libraries, install dependencies on the client, and write complicated engine calls to do trivial things such as creating a texture. Getting a triangle to show on the screen was already quite an accomplishment.
Creating a texture with DirectX10
Creating a texture with Three.JS
For our MMO Ironbane I took things one at a time, and it worked out very well. Remember, Rome wasn’t built in a day. But with today’s technology you can achieve things at a much faster pace than what was ever possible before.
I started from a three.js terrain demo and modified it step by step. Within a few days, I had a plane running around with a texture that looked like the pixelated back of a guy.
The next step was to make the player connect to a centralized server. Using Socket.IO I set up a very simple Node.js backend that responds to player connections, and puts them in a globalunitList managed by a service called worldHandler:
To let players know which other players are nearby, the server has to know at any time which players can see other players. To do so, every player instance on the server makes use of anotherUnits array. This array is simply filled with instances of other entities which are currently in the vicinity.
When a new player is added to the worldHandler, their otherUnits list gets updated depending on where they are in the world. Later, when they move around, this list is evaluated again, and any changes to this list are sent to the client in the form of addUnit and removeUnit socket events.
Now, I would like to point out that the first letter of MMO stands forMassive. For massive games, every player should not know about every other player because it will melt your server.
To remedy this, you need spatial partioning. In a nutshell, this means that you divide your world into a grid. To visualize it, think of it as the server making use of a Snap To Grid option, to “snap” the position of the players to an imaginary grid. The positions of the players are not altered, rather the server just calculates what the new snapped position of the player would be.
Image courtesy of Gameprogrammingpatterns.com
With many players spanning over many different positions, some will have the same “snapped” position. A player then, should only know about all players that are snapped in the same position and all players that are just one cell away from them. You can easily convert between grid and world positions using these functions:
When a new player is created on the server, they automatically add themselves to a multidimensional array of units on theworldHandler, using the grid position. In Ironbane we even use an additional zone index, since most MMO’s have multiple areas where players can reside.
Once they are added to the list of units on the server, the next step is calculate which other players are nearby.
Here, addOtherUnit() adds that player to their otherUnits array, and sends a packet to the client informing a new player has entered in their vicinity. This packet will contain the initial position, velocity, name and other metadata which only needs to be sent once. removeOtherUnit() simply removes the player from their array, and tells the client to destroy that player.
Now, we have the beating heart of an MMO. The final step is to inform the players on a regular basis the positions of the other players in their vicinity. We do this step only twice per second, because we do not want to overload the server.
That’s really all there is to building an MMO. The only things left to do now are building the features that are unique to your game, fine-tuning and security.
I hope I have given you fresh insights into MMO programming, and above all the courage to start working on them. At Ironbane we are surely looking for collaborators! You can find the full source code of Ironbane straight onGitHub, and you should be able to install it on your machine with ease.
Hi there! My name is Nick Janssen, creator of Ironbane, a 3D MMO that uses WebGL and WebSockets. With this article I would like to give you a better insight in MMO’s and make you less afraid of the complexities involved in building one. From my experience I have found that people consider them very hard, while they are in fact quite easy to make using the web technologies of today.
An MMO? You can’t do that!
MMO's are cool. Yet they are considered one of the hardest things to make when it comes to developing software. I believe MMO’s are mainly intimidating to people because of historical reasons.
In the old days, network programming used to be very hard. Complex socket calls were everywhere, multithreading was necessary and JSON was still unknown. A lot has changed since then with the coming of Node.js, its event loop and easy to use socket libraries.
In addition, writing a 3D game was a challenge on its own. You had to include the right libraries, install dependencies on the client, and write complicated engine calls to do trivial things such as creating a texture. Getting a triangle to show on the screen was already quite an accomplishment.
Creating a texture with DirectX10
D3DX10_IMAGE_LOAD_INFO loadInfo;ZeroMemory( &loadInfo, sizeof(D3DX10_IMAGE_LOAD_INFO) );loadInfo.BindFlags = D3D10_BIND_SHADER_RESOURCE;ID3D10Resource *pTexture = NULL;D3DX10CreateTextureFromFile( pDevice, "crate.gif", &loadInfo, NULL, &pTexture, NULL );
Creating a texture with Three.JS
var texture = THREE.ImageUtils.loadTexture('crate.gif'),
The beginnings
For our MMO Ironbane I took things one at a time, and it worked out very well. Remember, Rome wasn’t built in a day. But with today’s technology you can achieve things at a much faster pace than what was ever possible before.
I started from a three.js terrain demo and modified it step by step. Within a few days, I had a plane running around with a texture that looked like the pixelated back of a guy.
The next step was to make the player connect to a centralized server. Using Socket.IO I set up a very simple Node.js backend that responds to player connections, and puts them in a globalunitList managed by a service called worldHandler:
io.sockets.on("connection", function (socket) { socket.unit = null; socket.on("connectServer", function (data, reply) { var unit = new IB.Player(data); worldHandler.addUnit(unit); }});
Telling players about other players nearby
To let players know which other players are nearby, the server has to know at any time which players can see other players. To do so, every player instance on the server makes use of anotherUnits array. This array is simply filled with instances of other entities which are currently in the vicinity.
When a new player is added to the worldHandler, their otherUnits list gets updated depending on where they are in the world. Later, when they move around, this list is evaluated again, and any changes to this list are sent to the client in the form of addUnit and removeUnit socket events.
Now, I would like to point out that the first letter of MMO stands forMassive. For massive games, every player should not know about every other player because it will melt your server.
Spatial partioning
To remedy this, you need spatial partioning. In a nutshell, this means that you divide your world into a grid. To visualize it, think of it as the server making use of a Snap To Grid option, to “snap” the position of the players to an imaginary grid. The positions of the players are not altered, rather the server just calculates what the new snapped position of the player would be.
Image courtesy of Gameprogrammingpatterns.com
With many players spanning over many different positions, some will have the same “snapped” position. A player then, should only know about all players that are snapped in the same position and all players that are just one cell away from them. You can easily convert between grid and world positions using these functions:
function worldToGridCoordinates(x, y, gridsize) { if ( gridsize % 2 != 0 ) console.error("gridsize not dividable by 2!"); var gridHalf = gridsize / 2; x = Math.floor((x + gridHalf)/gridsize); y = Math.floor((y + gridHalf)/gridsize); return { x: x, y: y };}function gridToWorldCoordinates(x, y, gridsize) { if ( gridsize % 2 != 0 ) console.error("gridsize not dividable by 2!"); x = (x * gridsize); y = (y * gridsize); return { x: x, y: y };}
When a new player is created on the server, they automatically add themselves to a multidimensional array of units on theworldHandler, using the grid position. In Ironbane we even use an additional zone index, since most MMO’s have multiple areas where players can reside.
worldHandler.world[this.zone][this.cellX][this.cellY].units.push(this);
Updating the list of players nearby
Once they are added to the list of units on the server, the next step is calculate which other players are nearby.
// We have two lists// There is a list of units we currently have, and a list that we will have once we recalculate// If an item is in the first list, but no longer in the second list, do removeOtherUnit// If an item is in the first & second list, don't do anything// If an item is only in the last list, do addOtherUnitvar firstList = this.otherUnits;var secondList = [];// Check for all players that are nearby and add them to secondListvar gridPosition = worldToGridPosition(this.x, this.y, 50)var cx = gridPosition.x;var cy = gridPosition.y;for (var x = cx - 1; x <= cx + 1; x++) { for (var y = cy - 1; y <= cy + 1; y++) { _.each(worldHandler.units[this.zone][x][y], function(unit) { if (unit !== this) { secondList.push(unit); } }, this); }}for (var i = 0; i < firstList.length; i++) { if (secondList.indexOf(firstList[i]) === -1) { // Not found in the second list, so remove it this.removeOtherUnit(firstList[i]); }}for (var i = 0; i < secondList.length; i++) { if (firstList.indexOf(secondList[i]) === -1) { // Not found in the first list, so add it this.addOtherUnit(secondList[i]); }}
Here, addOtherUnit() adds that player to their otherUnits array, and sends a packet to the client informing a new player has entered in their vicinity. This packet will contain the initial position, velocity, name and other metadata which only needs to be sent once. removeOtherUnit() simply removes the player from their array, and tells the client to destroy that player.
var packet = { id: id, position: unit.position, name: unit.name, isGameMaster: true};this.socket.emit("addUnit", packet);
Sending packets to the players
Now, we have the beating heart of an MMO. The final step is to inform the players on a regular basis the positions of the other players in their vicinity. We do this step only twice per second, because we do not want to overload the server.
_.each(this.world, function(zone) { _.each(zone, function(cellX) { _.each(cellX, function(cellY) { _.each(cellY.units, function(unit) { var snapshot = []; _.each(unit.otherUnits, function(otherUnit) { var packet = { id:otherUnit.id, x:otherUnit.x, y:otherUnit.y }; snapshot.push(packet); )); if ( snapshot.length ) { unit.socket.emit("snapshot", snapshot); } )); }); });});
Conclusion
That’s really all there is to building an MMO. The only things left to do now are building the features that are unique to your game, fine-tuning and security.
I hope I have given you fresh insights into MMO programming, and above all the courage to start working on them. At Ironbane we are surely looking for collaborators! You can find the full source code of Ironbane straight onGitHub, and you should be able to install it on your machine with ease.
0 0
- Building a 3D MMO using Websockets
- Sponsored Feature: Building a Highly Scalable 3D Particle System
- Building a Java servlet framework using reflection
- Building a JavaFX Application Using NetBeans IDE
- Building a fat jar using maven
- Building a Non-blocking TCP server using OTP principles
- Building a 3D Game in XNA From Scratch - Free Video Tutorial Series Now Available!
- 使用Silverlight构建一个简单的3D引擎 【译改 Building a Simple 3D Engine with Silverlight】
- WebSockets
- webSockets
- WebSockets
- Building PBRT using VS2008
- Basic 3D using libGDX
- KinectFusion: Real-time 3D Reconstruction and Interaction Using a Moving Depth Camera
- 《3D Point Cloud Registration for Localization using a Deep Neural Network Auto-Encoder》读书笔记
- Head Pose Estimation From a 2D Face Image Using 3D Face Morphing With Depth Parameters [2015]
- Tutorial for building J2EE Applications using JBOSS and ECLIPSE -3
- Face detection using HTML5, javascript, webrtc, websockets, Jetty and OpenCV
- ssh服务器的建立
- ASP.NET 中文COOKIE乱码
- discuzX 回话保存机制
- Linux环境之1_临时文件
- 实时投票系统:数据类型上redis和memcache的差距
- Building a 3D MMO using Websockets
- poj3080
- 使用 CSS3 实现超炫的 Loading(加载)动画效果
- 3个著名加密算法(MD5、RSA、DES)的解析
- 最小二乘法及其c++实现
- 如何在Windows2008 r2 防火墙下设置QL Server 2005、2008 的1433端口
- protobuf中 repeated[Ptr]Field的序列化
- linux下mysql的卸载、安装全过程
- 自己动手写操作系统——bochs启动问题