OurProblems is a browser based math mmo rpg video game.
OurProblems features four avatars each of which have unique abilities that assist them in their rpg role when solving problems. The player must control their avatar's movement to aim at other players and to dodge other player abilities all the while simultaneously trying to solve arithmetic problems. We hope you have a NUMBERPAD on your keyboard!!!!
Game Lore
The denizens of the game Our Problems were accustomed to solving problems in their own myopic perspective until they were struck with the ability to see the problems of others. The characters are somewhat sterotypical personifications of various professions. Anyone can do math though regardless of profession. What problems can a scientist, businessman, artist or therapist solve for you that are related to Math??? Who knows, things made sense during initial development. Perhaps all characters are exagerated aspects of each us....so this introduction has been a labor of fitting lore to a game. This is what happens when lore is the last thing created in a video game. This is a simple 2d game, leave logic at the door and have a little fun. There is only one server running so you may not even be able to play at this time.
A lot of what you will read here is about how this game was made. Also some detail will be presented into how to build a near realtime multiplayer game. The advice and suggestions given here are NOT from professionals. This is simply an account of what it took to accomplish what has been done for this game.
The World
The world of Our Problems is idealistic fantasy. The world is a fictional utopia depicting the possibilities derived from solving problems. One where we have miraculously solved the problem of nuclear fusion for cheap endless energy. If they ever prove it to be impossible the art will be changed. All the denizens of the OP seek to solve problems in their world their own way.
The world is 8192x8192 pixels featuring at most anywhere from 50 - 100 characters concurrently at a time. The 100 player max was chosen to provide a decent amount of space for players when moving around the world between opposing players. The second factor for choosing this number of players is to reduce packet drops on the internet. The max player count is still being tuned as internet testing continues. The more players you have in a game the higher the bandwidth cost. Bandwidth costs money with most hosting providers. Also the higher the potential for network delays when attempting to send a large data payload to a player. During development testing the game was being tested at 500 plus characters on a local area network without compression. The moment though you get on the internet with a geographically distant server everything starts getting dicey.
The art for the world took many turns and is a 0 budget piece of work. The original direction for the art was going to be a world in ruins with broken or dilapidated machinery and buildings. Conceptually it would have been a world with problems that needed to be fixed or solved with hard work and cleverness. The new direction was simply to build an optimistic zaney inane world where we have managed to solve miraculous problems and pursue near unrealistic idealistic goals. We have an artist that seeks to inspire cleverness, we have a businessman that owns everything, we have a therapist that wants to guide individuals behavior towards an answer and a scientist that always has the answer. The result of the art is what happens when a world is developed part time and with 0 budget. The four corners of the world are embodiments of the origins of the four central characters.
Character Concept
The problems each denizen has is abstractly represented as a Math problem floating near them. How does this make sense??? Who knows! Maybe the denizens
are thinking of a problem or maybe they are being nettled by a problem. This is just art and is fully subjective, you decide. The bulging eyes
- Scientist: "The people need answers!"
- Executive: "The people need aide!"
- Artist: "The people need inspiration!"
- Therapist: "The people need guidance!"
Character Controls
- LEFT HAND
The left hand is used to control movement with the W,A,D,Q,E controls on the keyboard. This layout is typical of a lot of games.
- W - Move forward
- A - Turn Left
- D - Turn Right
- Q - Strafe left
- E - Strafe Right
- RIGHT HAND
The right hand is used to input answers into the answer box in the top middle of the screen via the number pad on the keyboard. Unfortunately for OP lots of people don't have a number pad.
- (RIGHT THUMB for clearing answer left, up, right, down)(RIGHT HAND Numpad '+')
During a battle you can reset the answer box by for example shooting your answer one time then retyping a new answer which will reset the answer box. This unfortunately means you'd be shooting a known bad answer which could possibly reflect off the enemy back to you. Thusly the ability to clear the answer box comes in handy. During actual gameplay the player has the ability to clear the answer box by hitting the back space key. This unfortunately is bad design since the player would need to lift their right hand off the number pad and over to the backspace. There is also the ability to clear the result box by using your right thumb and tapping any of the left,up,right,down keys. The reason for using all of the arrow keys is so you don't have to focus on a specific key to hit. The reason for not putting it an extra clear option on the number pad is so you don't accidentally clear a valid answer.
Math Combat
THE GOAL OF THE GAME
The goal of the game is simply survival. Do your best to collect joy solving problems lest you disappear from the world. When solving others problems you will be challenged with 3 problems ranging from addition, subtraction and multiplication. When you run out of joy you will be presented with a score board.
HOW TO CREATE A SINGLE PLAYER CANVAS GAME?
-
Core HTML5 Canvas by David Geary (Paid Link)
This book is strongly recommended and will be recommended multiple times. This book contains close to everything you need to know to create a single player game. The book covers how to put images on the canvas, provides a sample engine for animation, provides ways to do collision detection between different polygons, a sample single player engine...etc. Even if you are not creating an interactive video game this book contains many details for drawing and animating images to an html canvas. As such not much detail will be provided on creating a single player game. What will be provided in following sections are some minor performance enhancements that might be of use and an account of how the multiplayer game was built. The single player game available in the lower left corner of the main site, https://ourproblems.io, was created using introductory techniques from this book. This was the go to resource for canvas when creating the multiplayer game as well except for the fact it doesn't include networking.
HOW TO CREATE A MULTIPLYER CANVAS GAME?
The below is a list of the knowledge and resources used to create this simple 2D canvas game. The majority of the game logic for this game is completely original. As noted before this is not professional work. The game does have bugs and is still a work in progress. OP does NOT recommend building a custom game engine. Writing a single player 2d canvas game is easy but adding multiplayer on top of that is almost triple the effort. This is not an easy undertaking. If possible find an open source premade game engine.Knowledge
- HTML/CSS
- Javascript programming language
- C++ programming language
- Linux skills for the game server
- Nodejs Skills, also some Express.js
- Windows skills for the front end development
- Basic Arithmetic
- Trigonometry, lots of trig
- Basic Certificate knowledge to convert the sites to ssl
- Art Skills, OP is severely lacking great art
- Websockets, (OP is experimenting with using newer technologies, more to come in the future)
Tools
- JetBrains Webstorm IDE for development
- Gulp, customizeable javascript deployment system
- Makefiles, used when compiling c++ code
- Notepad++ or Textpad, general purpose editing and management
- Inkscape for artwork
- Blender for artwork and animations
Resources (Just Recommendations)
-
Valve Software Lag Compensation
and
Valve Software Multiplayer Networking
These valve articles have been an invaluable resource for developing this game. Our Problems only has a minimal networking implementation at this time which is why you will see odd gaps with projectile collisions. The game currently is not using a server side history buffer...more on that subject in the later. The majority of the input prediction, lag compensation and client side prediction concepts implemented in the game came from reading the valve articles. The code is authentic and is not the best. The primary recommendation for building networked games in general is to use UDP. At the time OP was initially conceived, tcp websockets were chosen because there were no great alternatives. Our Problems hopes to switch to a QUIC stack in the near future. Our Problems is using both interpolation and extrapolation to smooth target movement. If your latency is extremely bad you could be in the extraplation zone and jerky movement will be more noticeable. Also we only have a few nodes running and unless you are relatively close to the server you will have bad latency....more on smooth movement later. Our Problems has literally no geographic proximate coverage for players at this time. Servers cost money and hopefully we can start up a few more servers in the future.
-
Understanding ECMAScript 6 by Nichoals C. Zakas
Javascript goodness! The entire front end is nothing but javascript.
-
C++ Primer (Fifth Edition) by Stanley B. Lippman, Josee Lajoie, Barbara E. Moo (Paid Link)
This book is a great way to learn c++. It is not written for the latest c++ standard however but does cover C++11. The great thing about this book is its organization of topics and conciseness of explanation. C++ is a large language for anyone to memorize and while most books inundate you with pedantic information, this book allows you to get a general understanding fairly quickly. If you have had some experience with c++ in the past and want a refresher this book is a great pick for cramming the information quickly. There are alot of pejorative comments in the cybersphere to choose other languages than c++ as to avoid memory safety issues. Note though with C++11 standard and above you can more easily adapt your code to write in a style that allows you to avoid memory corruption akin to more modern languages. You can use pointers or allocate memory on the heap only if you feel it is absolutely necessary.
-
The C++ Programming Language, Fourth Edition, Bjarne Stroustrup (Paid Link)
This book served as a backup for more pedantic information and a secondary perspective from the original creator of c++.
-
The C Programming Language by Brian W. Kernighan and Denis M. Ritchie (Paid Link)
Learn C in a couple hundred pages. This book will be useful for you if you need to interact with a C library from your C++ code. Please don't take this to mean you should write a game engine completely in C. This isn't the type of language that boosts productivity for product creation but you may need it to interact with the most performant libraries available for your network stack. This is a great book and a common recommendation for to get acquainted to the language. The very first backend server for OP was written in nodejs before shifting over to c++. Albeit the shift to c++ was actually misguided at the time. You can certainly make a game without a c++ or c backend to start with and move over if you believe you have a need. Rewriting code from javascript to c/c++ for example was insanely easy with the auto declaration keyword.
-
Core HTML5 Canvas by David Geary (Paid Link)
The only book needed for canvas. This book will walk you through step by step with every single thing you need to do to build a single player game.
-
JavaScript Bitmap Graphics With Canvas by Ian Elliot (Paid Link)
Only 1 heart for this one. This book will cover some advanced topics you may want if you seek to improve the performance of your canvas game if you start animating hundreds of images.
-
Pro Node.js for Developers (Paid Link)
When creating a multiplayer game you will more or less need several more applications outside of your basic game engine. Nodejs shines when it comes to standing up utility applications both quickly and conveniently. Also during development testing against dev nodejs servers using express is extremely convenient.
-
O'REILLY Nginx Cookbook (Paid Link)
Nginx is useful for setting up statically hosted sites and load balancing to various applications. If your team ends up getting serious you will end up creating inventive ways to architect your solution. This would be a useful tool to start with in the beginning for a low budget enterprise.
MATH USED IN THE ENGINE
What math skills were needed for this 2d math game?
- Arithmetic
- Trigonometry
Most common Equations!!
Given this game is centered around solving problems it seems fitting to provide some of the most commonly used equations in the game engine. These few are perhaps the most frequently invoked functions. Only several are provided as not to inundate the content here with minor use cases.- Given a starting x,y coordinate, an angle and a distance calculate the next point?
This is trigonometry and is definitely the most abundantly used equation in the game. The player movements
are based on a starting point, an angle and a velocity. The same is true of the projectiles. This same formula
is being used for the lead arm that points in the direction the character is moving. The lead arm is just a straight
line of known distance with a starting pivot point in the center of the chest that extends outward based on the angle
in which the character is moving. One very important thing about the below is "time" is not be considered in the example below.
The amount of time past will be a factor in your calculations to smooth out the movement...more on that later. The prominent
note here is that pretty much everything you are doing on the screen is literally guided by this one equation.
player.x = player.x + Math.cos(angle) * velocityInPixels; player.y = player.y + Math.sin(angle) * velocityInPixels; - Calculate the distance between two points, just uses the pythagorean theorem. This is needed to tell for example
how close two players are to each or did they collide.
let dist = Math.sqrt(Math.pow(X2 - X1, 2) + Math.pow(Y2 - Y1, 2)); The Law of Cosines!! This is might be the only somewhat advanced formula in the game given this isn't right angle trig math. This is responsible for the janky looking back arm that sends out the player projectile..etc. Our Problems needed a way to rotate the arms at any angle and still have a way to connect the upper arm and lower arms to a central elbow. Given this is not a side scroller and the character moves in 360 degrees, something dynamic was needed as opposed to static animations of a side scroller. The arms are just two flat planks being connected at the elbow by this formula. The top shoulder position is empirically known by how far back the character is leaning or how forward the character is leaning. The hand position unfortunately was manually calculated and known in advance for each frame of the animation. The upper and lower arm distances are known ahead of time and determined based on the scale of the character in advance to be around 20 pixels. Knowing the shoulder coordinate, the hand coordinate, the lower arm size and the upper arm size you can use the law of cosines to figure out what the elbow coordinate would be for any given direction. Given the two points of the shoulder and the hand are known, the angle to the hypotenuse is first calculated. Then the solve angle is decremented from the hypoteneuse to get the angle needed for the lower arm. With this known you can calculate how to position the lower arm. Then move on to the upper arm now that the elbow point is known. The outcome doesn't look great though, it may be changed in the future.
solveAngle = (Math.pow(upperArmDist, 2) - Math.pow(distHandShoulder, 2) - Math.pow(lowerArmDist, 2)) / (-2 * distHandShoulder * lowerArmDist); solveAngle = Math.acos(solveAngle);- Check whether a circle and a rectangle overlap? Used in collision detection between missles and players.
function checkOverlap(R, Xc, Yc, X1, Y1, X2, Y2) { co_Xn = Math.max(X1, Math.min(Xc, X2)); co_Yn = Math.max(Y1, Math.min(Yc, Y2)); co_Dx = co_Xn - Xc; co_Dy = co_Yn - Yc; return (co_Dx * co_Dx + co_Dy * co_Dy) <= R * R; }
Javascript Tips
The below is a summary of some general javascript performance tips for computation speed or memory use. The below list does not take into account any performance optimizations the browser may be doing to your code.- Think of the simplest way you can write a line of code with no sophisticated operators and give it preference.
- Avoid javascript inheritance. Avoid prototype chaining. Try to keep most of your variables accessible with single dot notation. Initial performance testing showed any variable references beyond 2 dots started to suffer though only minorly. Not enough to be game breaking but noticeable when you raise the numbers in a metrics test for a specific use case. This was not directly tied to game impacts though, this was only observed while performing metrics tests before writing the real code.
-
Avoid creating new objects except during game initialization. The main function running your game is the animate loop.
Rewrite any code that uses the "new" keyword, "= []", or "= {}". This function gets called 60 times a second. Now
also consider if you have 100 players in a game this piece of code could be called
60 * 100 times a second. Now consider if each player can have 10 projectiles in flight and you have a line of code
like this for one of those projectiles you are now creating a new object 60 * 100 * 10 times a second. Javascript
will randomly choose a time to clean up all of the objects that are unused. Our Problems cannot attest that the heap clean up
has actually caused issues but during early coding this was of great concern. There were many issues during early
coding that it's hard to hold any one issue culpable. After cleaning the code though and monitoring the heap memory
there was a significantly slower rate at which the heap was being used.
- Things to avoid in animate loop
- avoid resetting objects, myObj = {},instead opt to reuse an existing object
- avoid "new" keyword
- avoid resetting arrays with "myArray = []" instead prefer "myArray.length = 0"
- avoid array methods that return a new array
- concat()
- slice()
- be mindful that shift,unshift,splice,sort can be expensive if done frequently on large arrays, try to avoid
- Use the Path2D object when you have a static svg asset, scalable vector graphics. OP is using svg assets for the boundaries of the objects the player collies against in the environment.
-
Our Problems created globals to store common computations. It seemed at the time that this gave the best results between the
three browsers explorer, chrome and firefox when opting for overall performance. However empirical results show that chrome
prefers the simple computation. Do your own testing to find out.
//EXAMPLE OF DOING A PERFORMANCE TEST DURING EARLY DAYS OF PREMATURE OPTIMIZATION AND FIXING ALL THE WRONG //THINGS BEFORE GETTING TO THE REAL FIXES. Ran on an i3 processor, with 16GB of RAM const piChart = { half_pi: Math.PI /2, }; let half_pi = piChart.half_pi; let x = 0; let velocity = 8; let beginTime = performance.now(); for(let i = 0; i < 100000; i++){ x = x + Math.cos(Math.PI/2) * velocity; x = x + Math.cos(Math.PI/2) * velocity; x = x + Math.cos(Math.PI/2) * velocity; } console.log('computation time: ' + (performance.now() - beginTime)); x = 0; beginTime = performance.now(); for(let i = 0; i < 100000; i++){ x = x + Math.cos(piChart.half_pi) * velocity; x = x + Math.cos(piChart.half_pi) * velocity; x = x + Math.cos(piChart.half_pi) * velocity; } console.log('single dot global time: ' + (performance.now() - beginTime)); x = 0; beginTime = performance.now(); for(let i = 0; i < 100000; i++){ x = x + Math.cos(half_pi) * velocity; x = x + Math.cos(half_pi) * velocity; x = x + Math.cos(half_pi) * velocity; } console.log('global time: ' + (performance.now() - beginTime));Google Chrome Firefox Microsoft Edge Computation Time 15ms 384ms 135ms Single Dot Variable 21ms 333ms 122ms Global Variable 12ms 328ms 113ms As you can see in this table results vary per browser. In the majority of the adhoc performance optimizations used for this game, Google Chrome would win. Do your own testing to figure out what works for you.
Timers
Obviously use the windows.requestAnimationFrame() to control your animations. Perhaps not so intuitive is try to use the time argument from requestAnimationFrame for anything and everything you might need to do dealing with time. The event loop will attempt to keep this render function call in sync with the browser refresh rate. This is literally the most reliable timer you have available.
-
Don't call "new Date()", "performance.now()" or Date.now() if you don't absolutely need too. See if you can use the requestAnimationFrame function time argument instead. The timer auto corrects drift and makes sure your animations occur on time as well as possible.
-
Don't count the frames that have passed to use as your timer. The requestAnimationFrame function matches the refresh rate of the browser. Thusly it can be more than 60Hz on better hardware. Also you may want to tune down to 30Hz. Make sure you are triggering any action or animation needed based on time passed and not frame count. Also remember sometimes the call to requestAnimationFrame can actually be a a little to early or a little to late. So you may want to put a margin of several milliseconds in the next frame if for some reason it occurs to quickly around the time your action should occur. For example if the call occurs 2 milliseconds to early it would be better to go ahead and perofrm the action or render the next image than to wait 14-16ms for the next animation frame. Also during testing watching tens of thousands of calls of this function every now and then it would vary wildy by 20+ms. So it would seem unequivocal perfection would always be out of reach relying on javascript. This hasn't been attributed to anything as yet. The game is a work in progress, as well as this documentation.
-
In your backend server game engine loop make sure to eliminate any shifting or drifting of the pulse rate of your game engine processing loop. Assuming you are using an event loop, calculate the time the next processing should occur, then when it does occur auto correct the difference by however soon or late the invocation happened when scheduling the next invocation. This is extremely critical to help smooth out player movement. If you don't do this jitter of enemy players will be exacerbated by the shifting server loop.
Smooth Animations
Unfortunately the majority of the animations in this game are not very smooth. This is because all of the animations are running at 20 fps. This may change in the future. The original thinking was to minimize the download time for the art spritesheets by reducing their download size. As mentioned before this is not professional work. This is the result of 0 budget and no time. There could have been 40 additional frames for the animated legs, arms, and head which given we have four players would definitely increase the size of the sheet by at least 480 images. If you want beautiful flowing and fluid animations then you'd definitely want to go higher than 20fps. Notice all the characters are using the same pants. The whole same pants thing drastically cut down on animation work. Changing the color of the pants per character at the very least would require all those frames to be rendered and added to the sheet. That would be around 80ish more images for multicolored legs. This might be done in the future....anything could change. This is by no means a limitation for browser games, there are plenty of browser games rendering far more resources than this one. Especially games using webgl. This game is literally a first work concept. If there were only one player the one avatar could have had prolific and fluid animations done in the same amount of production time. Things being what they are the first design is where the game landed and stuck. All animations are being done at 20fps but this should not be confused with movement. Actual movement is being interpolated over time for every frame. The ideal place to be with animations is above 33 fps to make choppy animations almost imperceptible. The nice thing about this being a browser game is updates can be easily made and redistributed.
Smooth Movement
Our Problems had many issues dealing with the smooth movement of players. So many in fact that this was one of the last few major issues addressed. The initial version only moved players based on action ticks from a fauilty understanding of the unreal valve article. The original solution amazingly worked as long as one specific scenario was avoided. The scenario that made the lack of smoothness extremely noticeable was when two players walked side by side parallel to each other in the same direction. This is when the makeshift raft of a custom game engine started coming apart. The primary player would move using client side prediction from time sampled inputs controlled by checks inside of requestAnimationFrame(). The secondary player however was controlled by network ping, minor backend server processing loop timer fluctuations and a bad history buffer implementation on the client. Needless to say the secondary player was jittering quite frequently. The primary player was moving normally. This is a tale of how these issues were addressed.
The initial troubleshooting process was targeted at player action input pulse timing and sever process loop timing. Regardless of actual sample rate lets suppose that a player is moving at 6 pixels per input sample. The player presses the up key to move forward then at the end of the sample period that action input is sent to the server and the player advances forward. Lets suppose that both the server and the client are on the same period. That the browser client is sending action inputs every 33.33 ms and the server process loop is processing those actions and sending a world update every 33.33 ms. With both client and server on the same period the ideal scenario would be that 1 client player input is recieved every server period. Networking though throws a wrench into the ideal scenario but before we get into networking you will see that timer fluctuations are also a culprit. Knowing that there are minor fluctuations in time on BOTH the browser client requestAnimationFrame and also the backend server process loop it may be possible for the server to receive two action inputs within the same pulse period. The initial thought process was to do as much as possible to make sure that one action input is being recieved on the server per server pulse. This concept is much easier to diagram when using a much smaller pulse range.
Lets suppose that we were cycling/pulsing at an INCREDIBLE (FAKE) RATE of 5ms on the server for demonstration purposes only. Also suppose the client is being intentionally pulsed even faster at an INCREDIBLE (FAKE) RATE of 4ms. Also posit that latency is near 0 for the sake of demonstration. This is so we understand what happens if a client timer is fluctuating slightly faster than the server. You will repeatedly end up in scenarios where the server gets two action inputs at once causing the enemy player to move at double the speed for a single pulse of the server. Effectively you will see random jitter where the enemy player moves at double the speed every so often then returns to normal speed.
This is only half the story. As mentioned before the timers on both the server and client are wobbly or fluctuating although they should both try to auto correct on the next iteration to stay on time. Lets explore what happens when the server is fluctuating faster and the client is fluctuating slower. You should end up in a scenario where the exact same world update with no change in enemy position is sent to the client since the browser player input didn't arrive within that period. Diagram:
Now as you can see if the server ever fluctuates to quickly you end up in scenarios where some of the server pulses get no action inputs from the client. This means that when the server sends out the server pulse at the end of the period there effectively will be no change in the enemy player position. This will present the visible effect to the player that the enemy was moving then suddenly stopped then moved again thusly causing jitter. Also note a wobly network ping can cause the action inputs to arrive at the server arbitrarily. All of these wobly fluctuating timers causes the enemy players to move normally, double in speed, possibly stop or return to normal repeatedly.
So how was enemy player jitter fixed?
-
The first leap was making sure the server attempted to say on time. It is not enough to simply schedule the next execution of the processLoop 50 ms out. You instead need track and calculate when the next cycle was supposed to run ,then compare that difference from when it actually ran and schedule the next execution based on that assesment. A sliding timer will only exacerbate the two client inputs or no client inputs per server pulse issue.
-
The second leap was ensuring the timer provided by requestAnimationFrame was used for everything to do with time in the game. Date.now and performance.now were initially being used for ping calculation attributing to jitter.
-
The third leap was accepting the wobbliness as a fact of life given the current implementation stack. The ideal scenario is zero wobble. The first version of the interpolation algorithm only interpolated between the last two world states sent by the server. Thusly we could for instance be recieving a world state where two action inputs had been processed for the enemy if the server had been running to slow relative to the client or instead a world state where no action inputs had been processed by the player if the server had been running to fast relative to the client. This is what visibly caused the jitteriness. Accepting that jitter may be likely to happen the algorithm was shifted to interpolate over three history buffer coordinates for the enemy player. The coordinates were also smoothed out over the three buffers only if jitter was detected, this way the enemy player would always be moving, no stops. It is easier for the eye to see the enemy player suddenly stop than it is for the eye to notice a minor slow down of the enemy player due to thier movement being smoothed over multiple buffers. Creating an extra set of coordinates smoothed out based on the three history buffer coordinates would mask the jitteriness.
-
Although the formula for interpolation was implemented to handle moving the enemy players between two points the primary player
was still moving based on action input samples. Once this was corrected there was not only extra smoothness when
watching the two players run in parallel but also smoothness with the world itself being moved past the player in smaller increments
though a only minor improvement. The player presses the up key, they don't immediately jump to the destination instead they interpolate to the destination.
Our Problems is using straight linear implementation for both the primary player and the target player. An illustration has been provided below.
Since this game is using a very basic implementation with a slow pulse period you can clearly see that the player is being interpolated from
a past position to a present position. Also it can be implied that it would be better to aim ahead of the targets current location to where you
think they will be in order for the server to count the hit as accurate. In this specific implementation the server has no history buffer. A history
buffer on the server might be implemented in the future. In games that have a tighter server pulse period, the margin at
which an enemy player's position is interpolated from a past position would be greatly reduced. Thus making aiming ahead almost negligble for
"real" major brand games.
//linear interpolation including a time ratio, separate ratios used depending whether this is the player or the enemy otherPlayer.x = beginX + ((timeNow - timeSinceLastUpdate)/(pulsePeriod)) * (endX - beginX); otherPlayer.y = beginY + ((timeNow - timeSinceLastUpdate)/(pulsePeriod)) * (endY - beginY);
- Lets not forget that all projectiles need to be interpolated as well..... What looks like a simple 2d game requires a ton of work.
Bandwidth and Data Usage
A critical concern with creating an online multiplayer game is your data payload byte size. Many server hosting providers for your game nodes will charge exorbitant fees for your data usage. When the server sends out the world update it needs to send that data payload to every single player in the game. Assuming for example that your server has 10 players and every player has a total size of 60 bytes. When sending the world state to the browser client of every player that would total to (10 * 60 * 10) per pulse from the servers perspective, 6000 bytes per pulse. The original version of Our Problems was played with 500+ uncompressed characters on a local area network. Now if in our 10 player game example we are pulsing world states at 20fps or 50ms you would then be required to calculate your monthly data usage to determine if your hosting provider might be able support your data load. In other words (6000bytes * 20 TimesAsecond * 60seconds * 60minutes * 24hours * 30days) or 311GB assuming maximum game load for that month. Given a large number of hosting providers provide their compute instance packages based on 1TB or 2TB per month before charging overages this might seem great at first. That is until you start thinking about how to support 1000 players or even 10000 players. Then thinking about architecting such a solution becomes a wholy separate skill that will need to be developed. Creating a simple 2d networked multiplayer game looks easy on the outside but requires a ton of work underneath the hood. Had all precepts and tenets associated with creating a multiplayer game been understood prior to the development of Our Problems, such a high player count would not have been chosen. In coming months the player count may be tuned down and extra game mechanics added. Also we only have a several nodes at this time.
-
If you have a game with a high player count you will need to spend a painstakingly large amount of time compressing your data so that only the world state differences are being sent to the client. Given context if you had a 2 player game you might not even care about compression...who knows.
-
Decreasing the payload size will increase the chance your packet makes it to its destination the first time. You want both player actions sent to the server and server payloads sent to the client browser to be as small as possible so that packets have a higher chance to succeed the first time.
-
Depending on how many nodes you plan on running buying a server with a good network card to handle the bandwidth of your data could also cost extra fees. Secondly they may also charge you for data used on top of your expenses for good hardware. For example a lot of companies are offering compute instances with a 1Gbps card and also 1TB of free data per month. You would need to calculate your max monthly data usage to ensure you fit under the 1TB without being charged overages. You would also need to calculate how many nodes you could possibly run on the server at once.
How is game ping measured?
Ping is what most people in gaming refer to as the round trip time it takes for a packet to travel from your computer to the server and back again. Latency is the time it takes for a packet to travel from your computer to the game server. The way ping works for this browser game is that it relies on the requestAnimationFrame timer to send the player action input to the server and waits for it to return to be processed on the next requestAnimationFrame. A detailed illustration has been provided for how the ping works:
Latency1 + Latency2 + Render Delay - Pulse Delay
Is a websocket fast enough for a multiplayer game?
Our Problems certainly hopes so....hahaha. OP is also researching newer technologies though. When testing on a local area network the ping is around 0-1ms. This is with no network congestion and an extremely basic echo response returned upon reciept of a message. Interestingly in production this game was validated on a server located 1063km away using a websocket connection and getting an initial ping of 28ms over a vpn. However when switching vpn endpoints even if the vpn servers were located in the same city, different results were produced yielding a range 20-50ms depending on vpn endpoint chosen. The only conclusion was that different vpn endpoints could have different hardware associated with them and different levels of network congestion. The network path taken, the hardware along that route and the congestion has an impact on the speed of your response. If you are a gamer with bad ping no matter what game you are playing and going across a vpn, try switching endpoints. Even if an alternate endpoint is in the same city. Let's not forget this game is using websockets which is based on tcp. Hopefully this will change very soon.
What hardware is needed for the server?
The Our Problems server is currently using an event loop. No particular research has been done on what hardware is needed. Given that this is an event loop the presumption was simply to get the fastest processor available so that all the instructions could be finished before the end of a server pulse. This thinking though IS somewhat naive. The initial metrics of simply checking the process time inside of the server processLoop shows that the computation does indeed finish well under the pulse period. The game is currently running on a AMD 3.4GHZ processor in production. However during the early days of testing OP did not have a linux server on which to validate in the development phase. OP started off using a Raspberry PIs for linux development, using the Raspberry Pi OS Lite operating system. The raspberry pis were overclocked from 1.5GHZ to 2GHZ based again on a presumption that it would be best without actual testing. Amazingly everything actually worked. This was literally a zero budget operation and still is currently. Later on OP started using a refurbished intel i3 computer for the linux OS development.
AI Bots and Scripts
In the current simple design without any bots there would be nothing for a player to do when initially joining a game. Currently when a player joins a game, two bots will appear if the player count is under 8, or some other configurable low threshold. Perhaps the most difficult algorithm built for this type of 2d game was for the bot pathing and that is the AStar algorithm. This algorithm finds the shortest path between any two points. Thusly the path between the bot and the player. Given that our node count is pretty low the pathing the bots take can get a little wacky.
Challenges and Concerns
-
Anyone that plays this game must have a number pad. The likely hood of sustaining any kind of player base over this simple hardware requirement is low.
-
Being able to provide coverage for players to play a game like this over a websocket definitely requires a large number of geographically proximate servers to a player. We currently only have one node so if you aren't close to it you won't even be able to play. Starting up more geographically disparate nodes requires financing.
-
Upgrading to newer technology beyond websockets is currently being researched. It is only an assumption at this time that moving over to webtransport would yield better results.
-
Game design in general is a critical issue. Attempting to make solving a math problems somewhat fun..... Many hours were spent just hitting other characters in the game and it is noticeably more fun to mindlessly hit something than it is to add an extra layer of exertion to solve a problem while hitting something. The majority of successful games don't have a player also trying to solve an equation at the same time as performing some complex movement like attempting to strafe around a player while aiming.
-
Better art work. Better everything.......in general.
Future Updates
-
Webtransport testing. Why would we want to switch to webtransport?
What is head of line blocking?
Websockets use the TCP protocol which guarantees your data packets are sent to the server in the order they were placed on the network. Likewise from the server to the client. It is possible however the first packet on sent to the server could have encountered some dilatory decision by a switch, router or network card along the way needing it to be buffered or resent. In which case subsequent packets will end up being queued behind the first waiting for the first packet to successfully arrive to the host.
If player input actions,(move forward,down,up,left,shoot) encounter a head of line blocking issue. There will be a delay with the that action making it to the server. As whatever network issue clears up all the packets will arrive to the server at once queued up behind the first failed action. So any input actions that were delayed during that time will arrive all at once. The main player would most likely notice no visual effect since their actions are being interpreted via client side prediction. The main player might notice a state change issue though depending on how much state management the game does during client side prediction. If the packets arrive to late then the player that you thought you should have hit during the client side prediction play thru may have actually cleared. From the main players visual perspective if the packet delay is nominal they will notice nothing. From the perspective of every other player in the game though they will notice that you sped up a little. This is highly context depended. If you had been moving in a straight line other players may not notice anything due to extrapolation of your movement on their end.
The alternate scenario is a head of line blocking issue from server to client. If server world state updates to your client suffer a head of line blocking issue. Then there will first be a delay in the arrival of world packets to your client in which case you will notice the enemies will continue to have their position extrapolated from their last known velocity. Once the world packets arrive to your client you will recieve a bunch at once. It is now that you may notice thier positions snapping back from the over extraploation into some newer postion. You will notice the entire world of players suddenly speed up.
Quite literally one of the only solutions OP can think of to mitigate head of line blocking while still using tcp is to try to have game servers all over the world close to every region a player could play from. A closer distance to the game server ideally should mitigate network issues. That doesn't mean though someone close to a game server can't have a bad network route though but intuitively it would be the best option to have close game servers. Unfortunately there is only 1 server on the east coast of the U.S. running at this time. If you aren't close you probably won't be able to play. Having tons of geographically disparate servers is also costly and inconvenient.
Webtransport sits on top of http3 and the quic udp based protocol. There "might" be a speed improvement due to the quic protocol. Even if there isn't a speed improvement being able to eliminate head of line blocking utilizing webtransports multiple streams should be possible making data transmission more efficient. The burden is on the programmer to reassemble the packets in whatever order is needed. Also knowing where a player will be even if the first packet is delayed you "might" be able to make use of the second packet. The first test though is to verify that the speed doesn't get any worse before contriving some crafty coding logic.
Why not webrtc? The only reason webrtc is not being researched at the moment is we are using a client server architecture game model. Webrtc has incredible speed due to two factors. The first is it uses udp as well as the aforementioned webtransport. The second is it is primarily known as a peer to peer data transfer tech stack option. This means no middle man game server. Being able to communicate from one browser to another is NEEDED for real time voice and audio communications. A turn server is an option though if you want to emulate a client server model using webrtc. Which means the second factor for which webrtc is known for its speed would not be an option for us since we need a middle man server following the common client/server game architecture model. Webrtc is definitely a more mature option though with more resources. This is not to say that webrtc is off the table, webtransport is simply getting more effort first due to the allure of the client server tech stack branding for game development. There are very few webtransport examples though so who knows, maybe op will dive into webrtc.
Server Side History Buffer Implementation and Documentation. Server side history buffer will help minimize collision gaps when projectiles hit players and what players see on the screen.
More bug fixes. More mitigating jitter where possible.