While the award winning game Contre Jour is amazing on many fronts, its stunning visuals are probably its most unique asset. As detailed in a colleague’s blog post the game creates its graphics through the OpenGL drawing framework and several elements are dynamically rendered in a more complex manner than by simply drawing images. One of these is the Snot (also known as the Tentacle). They are the most common tools used to help guide Petit through the perils of his epic journey. So sit back and relax as we take a deep dive into how they are constructed as well as how we overcame the pitfalls of translating one into HTML5
The Physics Magic
In order to understand how drawing snots works, it is important to understand the basic structure of one. Please note that Contre Jour uses the 2d physics engine Box2d for its physics. We aren’t going to go over much of the physics aspect of the snot in this blog, but if you’ve never dealt with a physics engine before a quick overview of the documentation may help you understand the next sentence. The talented programmers at Mokus Games designed the snot out of multiple Box2d bodies which are sequentially connected to each other with joints (with the type of joint depending on the type of snot). You can think of each body as being a hinge that flexes when forces are applied to it. Since each body is connected to one another, forces applied to one body will cause the others to react as well. What is most important for us, however, is that the current location of each body is accessible. These control points will serve as the basis of all the upcoming drawing techniques.
Drawing a Snot
Now that we have a very basic understanding of the inner workings of a snot, we can take off our Box2d hats and think about how we are going to draw one. As shown in the picture below, there are two distinct sections of the snot that will require us to draw them using a different technique.
Drawing a Gradient Snot
While drawing a solid colored snot in HTML5 isn’t a big challenge compared to the native application, drawing the snots for the “Night” world (chapter 2) is a somewhat thornier issue. To modify the code to shade the snot with a gradient, the original version simply gives OpenGL a color array that it calculates based on the number of calculated curve points. When OpenGL draws the snot vertices, it uses the colors stored in the array to color the resulting shapes appropriately. Unfortunately, this is an area where canvas does not have the same flexibility. What canvas does have, however, is an API for creating linear gradients, which is what the snot body looks like. It isn’t as simple as creating a gradient and filling the snot body in one shot as we did with the solid black snot though. As the name implies, a linear gradient is defined as being, well, linear. The API expects a start point and an end point, which it takes and creates a gradient that goes straight between the two points. So if we simply create a gradient that goes from the snot head to its tail, the gradient will not fit correctly on the snot body unless it is in a straight line between the two points. Since a snot is very elastic, this will not always be the case. Luckily for us, simply segmenting how the snot is drawn solves this problem for us. Instead of drawing a path for the whole snot body at once, we break the control points up into groups that roughly correspond to the segments between the Box2d bodies and draw each segment individually and with a gradient that maps to the top and bottom of said segment.
While not 100% accurate, the behavior of the bodies connected by joints guarantees us that each segment will be fairly linear most of the time (except when a snot is bent at a hard angle). Though using phrases such as “not 100% accurate” and “fairly linear” are hardly confidence inspiring, the results of this technique are very close to the original.
Texturing the Rope
Despite the daunting task of porting all the OpenGL code to HTML5, so far we’ve done all right. The solutions we’ve come up with are fairly easy to understand and implement and stay pretty true to the original game. Sadly, there is always that one unruly individual who likes to rain on everyone’s parade and steal all the cookies. In our case this came to us in the form of the rope snot. In the iOS game, the striped pattern on the rope body is created by using a single texture:
The texture is then mapped to the snot body shape by OpenGL using texture mapping in a repeat pattern along the length of the body. This technique ensures that the texture fits perfectly with no overlap or breaks between sections.
Now the million dollar question is how to emulate the results of texture mapping in HTML5 (which has no concept of true texture coordinates) without slowing the browser to a crawl in the process. We tried several techniques to get this to work, including linear gradients, canvas pattern fills and even experimented with trying to create true texture mapping in the canvas. None of these created an effect that looked at all like the original. Luckily, after all these options were exhausted there was one method left to be tried, a rough approximation of texture mapping. It’s really an extension of the gradient technique from the last section. First, the snot body was segmented in the same fashion as the night snot. These sections were then cut into two halves. For each half, we calculated a transform to apply to the texture that mapped as close as possible to the half size. The center of the half was used as the texture’s position, while the rotation was found through calculating the angle of the vector from the top of the segment to the bottom. The x and y scale values were both individually computed by calculating a proportion of the average length and width of the segment with the length and width of the texture. Then, the canvas clip method was used to ensure that any excess texture outside of the segment wouldn’t be drawn.
The big flaw with this method is that without anything else it produces a noticeable disjoint between the individual segments of the rope and the outer curve loses some of its smoothness.
However, this can be minimized by drawing the entire body in with a solid color (like the normal snots) that matches the start and end of the texture pattern before drawing the texture segments. While it effectively hides the seams, it does create a small border on the outside of the snot that becomes more noticeable when the snot does a lot of twisting and turning. It actually creates a sort of 3D effect because of the color of the texture border, so we can chalk this one up as a “feature” and call it a day.
As you can see, it took some creativity to replace OpenGL drawing with the canvas, but with a little elbow grease and the processing power provided by hardware-accelerated browsers such as IE10 we managed to create a pretty consistent look with the native application that managed not to slow it down to a crawl. So I encourage you to go out and experiment with HTML5 yourself. Maybe you’ll accidentally make the next big browser game.99 comments , permalink