Multiplayer WebGL Pong

So this post is is a few days late but that is mostly because I don’t have that much to say this week either. Most of the week I have been working on the final written report which can now be found in MS3. I have also done some setup and cleanup on the server running the game so if it crashes it is restarted. Then I did the presentation and the post-mortem which are also in MS3. Then on friday we had the presentation where everything went well and after which we voted on who should present their projects on the Gradshow on March 28:th. I was chosen as one of the two programmers, which is exiting and somewhat scary.

Next week I don’t think I will do any programming, only improve the presentation and prepare for the next course.

I have been quite busy with other things than this project this week so I have not gotten much work done, except putting the finishing touches on the game like an audiomanager which plays music as well as several different sound effects as well as some settings for the music and fx volumes. Other than that I have mainly fixed all the major bugs that showed themselfs during playtesting. So next week I will just work on the report and fixing the final bugs like AI getting stuck in one end after a powerup has run out.

Like I stated in last weeks blog post I moved the AI to this week so I spent Monday and Tuesday on that and then during Wednesday and Thursday I implemented the powerup/down and today I have made the gameover work when the time is up. For more info on the AI read the post “Dumb AI“.

For the powerup(s) I though that I had only planned on making a ‘faster ball’ powerup but when I checked the project plan I saw that I should instead make two variants, one to make the paddle wider and on to make it smaller. So the way this works is that a powerup will spawn in a random position on the playing field. If it is the wide-paddle one the player who ‘directs’ the ball to it will receive the award and if it is a small-paddle the next player to deflect the ball will receive it. When a powerup has been taken another one will spawn in the next 10-20 seconds.

I also change the walls of the playing field to be angled since I noticed that the ball would often get stuck bouncing between two walls.

And finally today I added a game clock to the server which starts when the first player joins and counts down from 5 minutes. When the time has run out a gameover message is sent to all clients and they all disconnect and the game gets deleted from the server. When a client receives the gameover message a sorted scoreboard is displayed with an option to return to the main menu.

The following video shows the angled walls, the new ai and the powerups

When I first created the local game I made all players that were not controlled by people just go back and forth just to add something dynamic to the game. However these were not especially fun to play against. The game is still designed to be a multiplayer game for four people, but since I want anyone to be able to just click play and get started immediately I still need the other players to move which is where the AI comes in.

I started working on the AI yesterday and made it move in the direction the ball(s) were relative to the paddle. This was easily done by just taking the dot product between the paddle’s forward vector and the vector between the paddle and the balls position + a vector in the direction of the balls velocity, making it look like its predicting the path of the ball. This way the paddle moves left or right to always be able to deflect the ball. And this is exactly what it did as long as there was only one ball. With two balls it did miss the ball sometimes since it prioritized the ball closest to the paddle and could only move with the same speed as the player.

However it is not very fun to play against an unbeatable opponent, so what I have done today is tried to make the AI more dumb, while still appearing intelligent. The way I did this was by only tracking balls that were moving in the direction of the paddle and are within a certain distance from the paddle since this is how many real players think (and moving towards the middle when the balls are moving away). The result is fairly good and is not impossible to score against, albeit quite difficult. Since I am making a multiplayer game I have decided to keep the AI in this relatively good state and not spend more time on perfecting something not central to the game but instead move on to the powerup(s) and other things concerning the logic of the game.

I decided to change the planning a little once again and push the AI work to next week and deal with GUI and the game-session this week. So I have spent most of it on making a pretty GUI and in the process I had to refine much of the code regarding all the managers and game-session. The code I had when posting the GUI post was completely rewritten yesterday to be more modular and simpler to extend and change. But it looks almost exactly the same. Except I added a working fullscreen button to the options menu :).

When I worked on being able to join and leave I ran in to some problems with resources, both socket.io and three.js, not being garbage collected for various reasons which I hope are all resolved now. I have come to the conclusion that spending more time learning the ins and outs of JavaScript would have been time well spent, since the garbage collected environment is quite difficult to work with when you’ve mostly worked in a manually memory managed language like C++. Another problem I have noticed is that most literature covering the subject is written by web-developers who often don’t take memory management into consideration, although the makers of the JavaScript game engine Construct 2 have posted some very good blog post on how to deal with some of the innate problems with using JavaScript in a real-time context.

I decided to change the plan a little bit by starting the gui work this week instead of next mostly since I discovered that it was not easy to leave and start a new game. So in order to debug easier I created a simple gui which I then just continued to work on. Right now I have a main menu where the player input their name which makes the play button appear. There is also an options and a help button but these are not implemented yet. At the bottom I put a link to the blog. I have also made an in-game menu from which the player can leave the game and return to the main menu. The options and help buttons will also be implemented in this menu. And after some work it is now possible to join a game, play a bit and then leave, without reloading the page or anything crashing. This process is perfectly in line with the goal of being able to start a match immediately and play for the duration you want, instead of waiting to find other players.

I think I will finish the menus tomorrow or Friday morning and will then start work on smarter AI’s.

This week I have been doing mainly two things. The first one is network-related and the second one is graphics so I have a fancy new screenshot.

During the first half of the week I implemented entity interpolation which is an essential feature in all network games. More details on how I did this can be found in the post “Smoothing Stuff“. It is not perfect but works well enough that I can continue working on other features. Perhaps I will have to work on it a bit more towards the end when I can playtest for real.

The other half I have been doing some cosmetic work combined with some game-logic things. The first plan was to have a screen/display, similar to ones in sports arenas, where I would render the game with different cinematic cameras. So I implemented this but then I started thinking of how the score and name of the players should be (the previous solution seen in earlier screenshots did not look good and was difficult to setup and update) when I stumbled upon this image, depicting the arena in Tron Legacy and that has been the main inspiration for the current version. Unfortunately using this scoreboard I could not keep the “jumbotron” so I decided to scrap that.

Jumbotrone

My Jumbotrone

Tron

When I had the cylinder with textured names and score the next thing I wanted was the light seen in the Tron image. I have not worked that much with shaders before (just implemented phong shading and shadow maps) but after some research I found out that it can be done using volumetric light approximation. And the only resource I found on this topic was a tutorial on how to implement it in javascript with Three.js. The tutorial was a bit outdated (2 years old) and was no longer working but with some modification both to use newer version of Three.js and some modification for my game I got it working. I will write a more detailed post on the VLA later with screenshots.

I then got an idea from a friend that I could use the VLA to create an effect where the ball would look like a hologram projected by something in the middle of the scoreboard. While I think that could be perfect for the environment of the game I can’t figure out a way to do it which would look as good as I want it to so I put that on the backburner for now. Instead I decided to make the entire scoreboard glow. I recon this adds a lot the visuals.

I apologize for the terrible image placement in this post but the wordpress editor is abysmal when it comes to working with images.
Also I finally updated the stable site so now that should work. The server application might crash in which case the game will not work but if you really want to test it you can just send me a message or leave a comment and I will fix it. You can find my mail on my website viktorhansson.net.

So far this week I have not made huge strides in the development. Most of the time has been spent implementing interpolation for the balls and paddles so their positions don’t have to sent each and every frame. Right now I send ball position every ~50ms and paddle position every 80ms. The reason I don’t send the paddle positions as often as the ball position is because the client predicts its own movement which is correct in almost every case (the exception is when the paddle stops ). So the players position is extremely close to that on the server and must therefore not be synced as often. Regarding the other players they don’t directly interact with the player and so they don’t have to be that precise. The ball however has currently no extrapolation so the ball on the client is rendered in the place it was ~50ms ago. I think this is sufficiently precise 80ms however is not.

I also added a precision control for the player using shift. This enables more control of the bounce direction by making the movement speed slower..

Since the game looks almost the exact same i have not included any screenshots this time.

Another week has passed and its time for a blog post! As planned this has been a week of networking with a lot of frustrations. But more on that towards the end. First a little roundup of what I have been doing.

Monday was milestone 2 so that entire day was spent preparing and doing that presentation. Tuesday and Wednesday was spent getting socket.io working, read more about this in the post Networking where I explain some implementation details. And lastly Thursday and Friday has been devoted to making the player movement work.

So have I encountered any problems? Short answer: YES. First off I required more time than planned on porting the client code to work on the server. But the biggest problem came forth when trying to make the player paddle move smoothly while maintaining server authority and not clogging up the network by sending the position each frame. After a lot of testing the solution I went with was a similar solution that is explained in Fast Paced Multiplayer by Gabriel Gambetta. I allocate an array with some 10 vectors when the player is created This buffer is then used to store the position each frame. When a message is received with a new position (about every 100-150 ms) the buffer is checked to determine if the position sent from the server is one that the player has visited in which case no correction is required. Otherwise the position is corrected and in both cases the pointer to the buffer is reset to 0. This works quite well but might need some modification since the last position calculated and received before the paddle stops are not exactly the same due to network latency when sending the keyup message.

For now the other connected players have no inter/extrapolation so they only move when the server sends the new position. Fixing this is what I’m planning on doing next week. And per tradition here are some screenshots.

Its difficult to illustrate multiplayer in just images but an indication visible in the above images is that they don’t see the same planet in the background since they are on opposite sides of the playing field.
I plan on updating the stable site this weekend but first I have to install node and all the plugins used.

After the first milestone I started implementing the network using socket.io. The plan was to just copy most of the client side code and remove the unnecessary parts like graphics and input but this did not turn out as I was hoping.

I quickly realized that I used stuff like Three.js vectors and 3d matrices for the paddle movement. So most of yesterday I spent on changing all that to use the PhysicsJS vectors instead and to use 2d transformations which is faster and which I then moved back to the client.

I created two networkmanagers for both the client and the server: LobbyManager and NetworkSession. Even though the game will not feature a traditional lobby, but instead just place you in the first game available and fill the non-player spots with AI opponents, I still need something that manages all the games, how many spots are available in each game and what players are in what game. This is the responsibility of the LobbyManager on the server side. On the client it just creates a socket and connects to the server. Then when it receives an id for which game it was assigned to it starts the GameManager.

The NetworkSession classes are in charge (to some extent) of the communication between the server and the client in-game. They have functions for sending data to all players in a game or just a specific socket. In other network games I have worked on I usually have a networkmanager which is in charge of sending and receiving all network traffic in which other parts of the game adds data to a queue which will be sent. However socket.io is more event-based: you register for a callback on a certain message and execution of that function is then left to the framework and will be done asynchronously. This has led me to do some stuff that at first look quite bad but as the game takes shape seems like a good solution. For example on client disconnect I have registered two callbacks: one in LobbyManager which removes the socket from all lists and one in Player which resets the player to an AI entity. Another example is player movement for which I register and send only in Player. I have not implemented enough code yet to decide if this will work though.

The stable site will probably stay the same for a few days until I have gotten something working without crashing or freezing.