Monday, December 19, 2016

Box2D ninja rope


I recently played a game called BEEP which had a grappling hook mechanic. The game used Box2D, so I wanted to figure out how they did it. After figuring out a combination of revolute and prismatic joints is the best solution, I also wanted to make a ninja rope like in Worms games, where you can move up/down the rope, stand on the rope like it is a pole and the rope wraps around the terrain. I didn't manage to make it perfect, but it kind of works.


Swinging

1. Rope joint method

The easiest method is to just create a new rope joint connecting the player and terrain body and Box2D will handle the rest. There are two problems with this method:

a) Since it is a rope nothing is stopping the player to move closer to the point where rope connects to the terrain. The rope just stops the player from moving further away than what is defined with joint's maximum length (SetMaxLength()) variable. This makes it impossible to stand on the rope.
b) The player can't move up or down along the rope. The joint's maximum length can be changed after it is created, but that will either; try to move the player "by force" upwards if it is changed to lower value even if something is blocking the way(might even just teleport it instantly), or the player will just fall down a bit if the rope is changed to be longer.

2. Distance joint method

Alternatively a distance joint can be used instead of a rope joint. The difference is that the player body will not be able to move closer to where rope connects to the terrain or fall down, since the joint always tries to maintain the same distance. This is actually closer to the ninja rope behavior in Worms than the rope joint option and you can stand on the rope using this method. SetLength() can be used to "move" the player along the rope, but similar to the rope joint it tries to push the player to new position even if there is something in the way.

3. Chain method

This is also a simple method, but requires a lot more work. Simply create many small bodies between the player and the terrain body and connect everything with revolute joints. The problems:

a) The rope is a bit hard to control, moves around too much and can stretch. In games like Worms or Bionic Commando the rope isn't behaving very realistically, more important was giving the player intuitive control over it.
b) You can't move up/down the rope or stand on it.

The positive thing about this method is that the rope can wrap around terrain without any additional coding.



Tips:
1) For some reason joints are more stable if the bodies connected have higher density value, but the downside is that heavier rope is harder to control.
2) Enable joint motors and set torque to very small value and set the speed to zero, so the chain stops swinging sooner.
3) Connect the terrain and the player with a rope joint to prevent the chain from stretching too much. The max length should be same as the length of the entire chain or just a tiny a bit longer, depending how much do you want to allow it to stretch.
4) Since all the chain bodies would by default have zero linear and angular velocity when created, it would slow down the moving player when initially created. I don't know how to calculate the initial values of each link based on player velocity and stuff, but simply giving all the bodies the same velocity as the player at the moment of creation works good enough.

4. Prismatic and revolute joint method

The best way to make Worms like ninja rope using Box2D is using a prismatic joint on a rotating body:
a) With a prismatic joint the body is limited to moving/sliding along an axis and that is exactly what we need.
b) Using the joint's motor we can make the attached body move up/down without breaking the physics simulation and if some object is in the way and blocking the movement it will behave normally (unless we set the motor torque to an insanely huge value).
c) Using a motor with a high torque we can fix the players position on the rope, so the player can "stand" on the rope, just like in Worms games.

The rope will have three parts:

1. Rope/hook (long thin triangle) - a body which is connected to terrain using a revolute joint, so the whole thing can swing.
2. Slider (rectangle) - a body connected to the hook using a prismatic joint, so the player can move up/down the rope using the joint's motor.
3. Rotor (yellow circle) - Usually in Box2D games the player body has a fixed rotation, so it always stays upwards. We can't connect it directly to the slider, because with fixed rotation it wont allow connected bodies to rotate freely. There needs to be a body between the player and slider connected to both with a revolute joint, so the player can maintain the fixed rotation, but the rope can still freely rotate. If you have a player that can rotate freely, this isn't needed and can be attached directly.



Wrap around terrain

It is important to note that only one segment of ninja rope is fully active at any given moment. Basically the swinging part will work as described above in the method 4, and the rest of the rope are just static points with a line rendered between them.




a) How to detect when a corner is hit and split the rope ?

One method is to use the rope/hook part from method 4. It needs to be very long and thin, and needs to be a bullet body. When bullet bodies collide in Box2D you get the exact point of impact in PreSolve() callback function which you can use to determine where the rope needs to be split.

- The rope needs to collide with the terrain, but shouldn't bounce of the terrain, we just need the collision point. So in the PreSolve() callback the contact needs to be disabled with contact->SetEnabled(false).
- The rope should not be connected directly on the collision point, because we don't want the tip of the rope/hook to constantly collide around that point while rotating and cause problems. The connection point needs to be a bit outside of the terrain body (see image above).

When existing rope collides all the rope objects are deleted and the rope is recreated in that collision point. Like mentioned previously; only the last part of the rope is active. The previous connection points are still needed to render the entire rope and to know when to reconnect the parts.

The downside of using this method is that when the player is moved all the way up the rope, you have all this extra mass bellow him (the player in not the center of mass) that can cause unwanted movement.
The solution is to make the rope/hook body very small, so the center of mass is always the player, and use some other method to find out where the rope hits the terrain.

b) How to reconnect it all again ?

Simple; if you swing clockwise and hit something and split the rope, you will have to reconnect only when you swing in the opposite direction and past that point. Use the revolute joint's speed to check the direction(negative is clockwise, positive is counter-clockwise), and you can check if you past that point using a cross product formula:

http://stackoverflow.com/questions/1560492/how-to-tell-whether-a-point-is-to-the-right-or-left-side-of-a-line

Two last rope points is the line you need to cross, and the point you check is the current player position. If you crossed the line you remove the last rope point and recreate the rope in the last of the remaining collision points. While doing all this splitting and reconnecting you need to keep in mind the total length of the rope, and change the rope/hook body length accordingly.

 
You can try it out for yourself using the link above. I also uploaded the source code for the rope handling. Not sure how useful it will be, since I just copied it from my game/engine, so don't expect to just copy/paste it into your code and think it will work. Think of it more as a sign of goodwill. Don't forget to read the included README.

Sunday, October 23, 2016

Random #7 - attack of the view bots

This blog seems to be under "attack" by bots or something. For the last month I'm getting ten times more daily views than the average was. It started on September 30th. Every few hours the statistics shows a spike of 30 new views, and every day the same thing. The views are coming from USA, operating system is Macintosh and the browser is Chrome, but it shows no referring site or anything to pinpoint the source. It has made the statistics data completely useless.

Monday, October 3, 2016

Particle effects

Here is a video showing the updated particle system and editor, and some stuff that can be done with them.

It was all inspired by Unreal Engine particle system. Particle effects can contain several emitters and emitter and particle values are not just variables they are "modules" with "distributions" and stuff to control the values over time, etc. There are around 30 of these modules; emitter and particle lifetime, particle width, height, speed, direction, color, angle, number of particles on init/on update... Each effect, and even particles, can have dynamic light attached to them. Also, particles can be attached to a Box2D body, but then some of the functions wont work (particle position will be updated by Box2D and not by emitter functions). I also implemented ribbon and beam/line emitters.




Here is what you can see in the video:
- Fireworks - shows "nested" effects. Each particle can carry its own effect(rocket trail) and also can create a new one at the end of its life(explosion).
- Text - various line effects. The lightning effect uses a tilemap.
- Circles - nothing special, just a bunch of circular effects.
- Sparks - shows Box2D enabled particles. Particles change rotation and size based on velocity.
- Ribbons - ribbon effect can be attached to other particles or objects.
- Flamethrower - shows effect and individual particle lights, and also collision response (creating ground fire on particle-ground contact).
- Colored lights - shows particles with colored dynamic light.
- Paint explosions - shows creation of decals on contact on static and moving objects.
- Particle effect editor - shows how one effect can be made of several particle emitters.
- Leafs - shows direction change using a sin wave.

***

Updating, saving, parsing, then the editor code itself.... it's well over 5000 lines of code. It all became over complicated and confusing and I don't know does it even make any sense anymore, I just lack the knowledge to design such a complicated system. Quake 2D didn't even have a particle system, it was all hand made and hardcoded, and it still worked. I have been working on the particle effects until March and then completely stopped. Then fooled around with Abuse SDL and haven't coded since. I wanted to post this way back in March, but just didn't want to bother anymore.

Monday, May 9, 2016

Abuse 1996 - SDL port 0.9a



When I released my Quake 2D demo back in 2012 many people compared it to Abuse, a game by Crack dot Com released in 1996. While I was waiting for my new PC to get fixed(it took months!) I was stuck with a PC bought in 2005. I already played everything I could on it and my gaming options were limited. I found out Abuse was available for free, so I wanted to check it out. It ran in DOSBox at 320x200 resolution and felt like playing a FPS game with very low FOV. Aiming with the mouse was very difficult, because even in fullscreen the mouse was still behaving like it was 320x200 resolution and was too sensitive.

Reading the included readme file I saw there was a high resolution option, but it seemed to be only available in the shareware version or in editor mode, and the game would automatically turn off the in-game lights, because it would be too demanding for the PCs in 1996 on high resolutions. Not to mention a bug would cause the entire screen to go black the second time a level was loaded. While looking for a fix I found out the original source code was released and there were several source ports released during the years, mostly for Linux. Finally Xenoveritas SDL2 port from 2014 showed up in the search results and was exactly what I needed; a working Windows port, plus it had instructions how to build it.

After I enabled custom resolutions and fixed the light, I also wanted to extract the textures, so I can use them in my own engine. Then I saw Xenoveritas never finished adding Xbox controller support, so I did it instead... it didn't stop there, and here is the final result in motion:


As you can see the game gets pretty intense. It's a classic design where you need to find switches to open new areas, and you need to cleverly use security turrets and destructible walls to fight the enemies. It takes 2-3 hours to beat the game.

 Here is the list of updates in Abuse SDL 0.9a compared to Xenoveritas version from 2014:

- Enabled custom resolutions and enabled lights on high resolutions
- Re-enabled OpenGL rendering to enable vsync
- Game screen scaling in window and fullscreen mode using F11 and F12
- Enabled some high resolution images from the 1997 Mac OS release
- Fixed level music not being played correctly, added "victory" music in the end game screen
- Fixed the health power image, fixed mouse image when choosing initial gamma
- Added or re-enabled various settings in the config file (borderless window, grab input, editor mode, high resolution images...)
- Local save game files and configuration files
- Quick load using F9, quick save using F5 on save consoles
- Added cheats via chat console: bullettime, god, giveall, flypower, sneakypower, fastpower, healthpower, nopower
- XBox360 controller support with rebindable buttons
- Updated abuse-tool so it can extract the images in Abuse SPEC files to modern image formats as individual images, tilemaps or a texture atlas with information about image, tile and animation frame sizes and positions


I only tested the game on my old 2005 PC and the new one, running Windows 32bit and 64bit. I would really appreciate some feedback, so I know it works or not. I would also like to know how playing with the Xbox controller feels, since I plan to use the same controls in my own engine. Please read the included README file for more information, controls and links to previous port releases and other stuff. (NOTE: Since changing sound volume doesn't work, if you don't want music just delete the music folder). You can download the game from this link or on ModDB:

md5: 16E116B753A2142E4AE2BB63C2A4A351


I already mentioned I wanted to extract the images, so here is an archive that contains all the extracted Abuse images in PNG format, together with information about image name, size and positions of individual frames of animation: 
 Aliens addon

md5: 5A2D1E65A2D76C6C0D340F600E2F7B5B

I also used a HMI to MIDI converter to convert the music to MIDI. I didn't convert them to wav or mp3, because different soundfonts give different results when playing MIDI music. You can download the MIDI files here:
md5: 5FC937F71DB047DA9E46961F83016CDA

The source code for Abuse SDL port 0.9a is available on my GitHub page. As I said the game is not fully tested and there are few issues which are described in the README file. The game physics are locked at 15 FPS, and the rendering is a bit slow, those are two major things I would like to eventually fix. Multiplayer doesn't work, but that is way out of my league:


Thank you for playing Abuse!

Saturday, April 9, 2016

Texture packing algorithm


I was exporting some textures from an old game, and needed an algorithm to pack many small textures into a texture atlas. I found a very nicely explained algorithm on this page and wanted to share, if someone might need something like this:

http://www.blackpawn.com/texts/lightmaps/default.html

There are some other interesting articles from the same guy here:

http://www.blackpawn.com/texts

The packing algorithm is so simple, I didn't think it would work. You just create a node with the output texture size and loop trough your images and call Insert() with individual texture's width and height. The node recursively splits itself if its size doesn't match the input, and calls Insert() of the child nodes. The node that gets returned holds the final [x,y] position, and if it returns NULL it means the image couldn't fit.

The grey lines mark individual textures and animations that I grouped together in code, so it doesn't all get mixed, but that is another story.



Tip 1.

In the example above I sorted my images according to their size(width*height) before I started to call Insert().

Tip 2.

If you look at the examples from the link, you will see the images are stored in an upside-down L pattern starting from top left, so if your output image is too big the input images will be positioned in the L pattern and the rest of the space(bottom right) is left unused. To avoid that;
- I sum the size of all the textures and set node width and height to sqrt(sum), so I have a minimum square output.
- Then I check if some image has width higher than that, if so I take that value for the final node width, so it can fit.
- The final height is some arbitrary huge value. While calling Insert() I keep track of where the bottom corners of the images end up and take the maximum value when creating the actual output image, which, after all this mess, should be more or less a square.

C++

Here is my implementation in C++ to spare you 2 minutes of converting the pseudocode. Unfortunately Blogger doesn't have code tags or something to format it properly.

#include <vector>

class AR_Node
{
public:
    std::vector<AR_Node> child;    //child nodes
    int x, y, w, h;                //position and node size
    bool image;                    //image stored in the node

    AR_Node* Insert(int img_w, int img_h);

    AR_Node()
    {
        this->x = y = w = h = 0;
        this->image = false;
    }
};

AR_Node* AR_Node::Insert(int img_w, int img_h)
{
    //if we are not a leaf then
    if(!this->child.empty())
    {
        //try inserting into first child
        AR_Node *newNode = this->child[0].Insert(img_w,img_h);
        if(newNode!=NULL) return newNode;

        //no room in first child, insert into second
        return this->child[1].Insert(img_w,img_h);
    }
    else
    {
        //if there is already a image here, return
        if(this->image) return NULL;

        //if image doesn't fit, return
        if(this->w<img_w || this->h<img_h) return NULL;

        //if we're just right, accept
        if(this->w==img_w && this->h==img_h)
        {
            this->image = true;
            return this;
        }

        //otherwise split this node and create some children
        this->child.push_back(AR_Node());
        this->child.push_back(AR_Node());

        //decide which way to split
        int dw = this->w - img_w;
        int dh = this->h - img_h;

        if(dw>dh)
        {
            //vertical split           
            //left

            this->child[0].x = this->x;
            this->child[0].y = this->y;
            this->child[0].w = img_w;
            this->child[0].h = this->h;
            //right
            this->child[1].x = this->x + img_w;
            this->child[1].y = this->y;
            this->child[1].w = this->w - img_w;
            this->child[1].h = this->h;
        }
        else
        {
            //horizontal split
            //up

            this->child[0].x = this->x;
            this->child[0].y = this->y;
            this->child[0].w = this->w;
            this->child[0].h = img_h;
            //down
            this->child[1].x = this->x;
            this->child[1].y = this->y + img_h;
            this->child[1].w = this->w;
            this->child[1].h = this->h - img_h;
        }

        //insert into first child we created
        return this->child[0].Insert(img_w,img_h);
    }
}

Tuesday, April 5, 2016

Random #6 - Youtube channel statistics

This January my YouTube channel exploded ! Well, at least one video did. My "The Bloodcrafter - Minecraft 2D shooter" video is getting in average almost 1000 views DAILY, and is rapidly approaching 400 000 views, with the channel surpassing 500 000 total views. If I were monetizing this I might have made enough money to buy the actual game :/


Other videos are not that successful, and the total view count of all the videos I uploaded in the last two years is only around 15 000, and that number would be much, much lower if Bloodcrafter wasn't attracting so much attention to the channel.

Since the most popular videos are based on Minecraft, Serious Sam and Quake, and with so many views in the period of over 5 years, the channel statistics are worth looking at to see some trends. I think it would make sense to say that the viewer demographics represent the actual player base.

The most significant change in the last 5 years is how Minecraft has become more popular among female players. In the first year(2011-2012) only 5 percent of players were female, and the data from last year (2015-2016) shows it is currently at almost 40%.


The percentage of Serious Sam female viewers/players jumped from 5% to around 16%. Quake 2 continues to be a "man's" game, probably because 90s girls weren't interested in it, and new generations don't even know about it, since the series is dead since 2005. But, looking at Serious Sam, which is a similar game, the new entries don't help much. Serious Sam continues to be most popular in Eastern Europe.


Other videos share the same faith like Quake, it's such a sausage festival that YouTube doesn't even want to display the statistics and it shows an error. Since most of them are about video game development, and are shared on some game development and gaming forums, my conclusion would be that we wont see a rapid increase in the number of female game developers any time soon... unless it is forced.

Another sad statistic is Linux. I only have the data starting from January 2013, and they show only 1.3% of PC viewers are on Linux, and 1.8% use Mac. The stats from this blog are a bit more positive, where 5% of readers are on Linux, and 5% on Mac (but these are overall numbers, not just for PC).