Transparent Blitting, DirectDraw, Part 5

All this while, we have been blitting the spaceship image:

onto a black surface:

giving an image which looks like:

Thus giving an illusion that only the spaceship has been Blt on to the screen. But if our background was anything but black, things would look a lot different. For e.g. if our background was blue:

the resulting Blt() would have looked like this:

which, as you can see, is not very appealing to the eyes. Enter the concept of transparent blitting!

 

With transparent blitting, you can specify any arbitrary color, on the source surface or the destination surface or both, as a transparent color. So for e.g. in the above case we would specify the color BLACK on the source surface (the spaceship) as the transparent color. Therefore, when the spaceship is being Blt onto the destination surface, the color Black will be treated as transparent and the background will be preserved. So in short whenever the color black is encountered while Blting, its not Blt onto the destination surface, instead the Blue background color is displayed.

 

Color keys are used to specify which color you want to be treated as transparent. You can specify a range of colors to be transparent but, color ranges are not supported by HEL (Hardware Emulation Layer). Color keys can be specified:



1) While creating the surface

2) Calling SetColorKey() function explicitely on a surface

3) While Blting the surface

Color Keys are specified using the DDCOLORKEY structure. This structure has two DWORD members, dwColorSpaceLowValue and dwColorSpaceHighValue. Unless you are specifying a range of colors, both these values must be the same. If a surface is in palettized format then the color is specified as an index or a range of indices. If the surface’s pixel format is specified using a code then the color keys have to be specified using appropriate means like RGB(r, g, b) etc.





Color Keys while creating a surface

Assign the appropriate color values to ddckCKSrcBlt or ddckCKDestBlt members of the DDSURFACEDESC structure. Also, the dwFlags member must include DDSD_CKSRCBLT or DDSD_CKDESTBLT or both depending on whther you want to specify source color key, destination color key or both.



Using SetColorKey()

Another way to specify color keys is by calling SetColorKey() function on a surface directly. This function takes a pointer to DDCOLORKEY structure where you specify your color keys. In the dwFlags parameter you set either DDCKEY_SRCBLT or DDCKEY_DESTBLT or both to indicate whether you are setting a source color key or a destination color key or both.



While Bltting the surface

If the color keys for the surfaces you are bltting was already specified using one of the methods above then you just need to set the dwFlags parameter to DDBLT_KEYSRC or DDBLT_KEYDEST or both. Alternatively, if you want to override the color keys while blitting then you can specify the source and destination color keys by setting the ddckDestColorKey and ddckSrcColorKey members of the DDBLTFX structure, you will also need to specify DBLT_KEYSRCOVERRIDE or DBLT_KEYDESTOVERRIDE flags in the dwFlags parameter of the DDBLTFX structure.

 

In our UpdateFrame() function specify the color keys as:



    ddbltfx.ddckSrcColorkey.dwColorSpaceLowValue = RGB(0, 0, 0);

    ddbltfx.ddckSrcColorkey.dwColorSpaceHighValue = RGB(0, 0, 0);

Note that both the values are same. Here, the color black is specified as the source color key.

And the blitting,


        hRet = g_pDDSBack->Blt(&dest, g_pDDSOne, NULL, DDBLT_ROP | DDBLT_KEYSRCOVERRIDE, &ddbltfx);


The DDBLT_KEYSRCOVERRIDE flag is specified to use the color key from the ddbltfx structure.

 

So now when I Blit:

onto the same Blue surface, the resultant image is:

BallOnBlue

For more information on Transparent Blitting, take a look at MSDN.

Fun with menus!

I received a comment on one of my previous posts, correcting me on what I had mentioned. The menu bar resource was defined as below:



IDR_MENU SHMENUBAR DISCARDABLE

BEGIN

    IDR_MENU,

    2,



    I_IMAGENONE, IDM_OK, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE,

    IDS_OK, 0, NOMENU,

   

    I_IMAGENONE, IDM_HELP, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,

    IDS_HELP, 0, 0,

END

And I had said, "Everything in between BEGIN and END defines the menu bar. The first line under BEGIN should be the same as the menu bar identifier, IDR_MENU". Well, I was wrong there. The first line under BEGIN could be same as the menu bar identifier but it need not be. Thanks Binary for noticing that! The first line under BEGIN is the identifier of the popup menu that appears when the user clicks on one of the popup menus.  If both the softkeys in your menu bar are buttons then the popup menu identifier could be specified as zero. So the correct way of doing it would be to define the menu bar as:

IDR_MENU SHMENUBAR DISCARDABLE

BEGIN

    IDR_MENU_POPUP,

    2,



    I_IMAGENONE, IDM_OK, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE,

    IDS_OK, 0, NOMENU,

   

    I_IMAGENONE, IDM_HELP, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,

    IDS_HELP, 0, 0,

END

and IDR_MENU_POPUP is defined as a popup menu,

IDR_MENU_POPUP MENU

BEGIN

    POPUP ""

    BEGIN

        MENUITEM "System Metric",               IDM_SYSTEM_METRIC

        MENUITEM "About",                              IDM_HELP_ABOUT

    END

END

In the above menu bar, the left softkey is an "OK" button and right softkey is a popup menu with "System Metric" and "About" as its menu items. Just remember that IDR_MENU is the identifier of the softkey bar which is created and IDR_MENU_POPUP is the identifier of the popup menu, which appears when the user clicks on the right softkey "Menu"

 

Binary also raised another interesting point. How do we get popup menus on both right softkey and left softkey? Of course, the first thing I did was to remove the NOMENU option from the part defining the left softkey. You can see the problem there can’t you? Both left softkey and right softkey become popup menus, but both the popup menus show the same menu items because we specified a single popup menu identifier, IDR_MENU_POPUP, and that is why the same popup menu shows up for both the softkeys.

I was looking into how to get different popup menus for the 2 softkeys when Mr (or Ms) Binary replied again. Binary had figured out how.

 

You define the popup menu as usual but with two popup entries,

IDR_MENU_POPUP MENU

BEGIN

    POPUP "Menu L"

    BEGIN

        MENUITEM "Menu Item1",                        0

        MENUITEM "Menu Item2",                        0

    END



    POPUP "Menu R"

    BEGIN

        MENUITEM "System Metric",               IDM_SYSTEM_METRIC

        MENUITEM "About",                              IDM_HELP_ABOUT

    END

END
 

and while creating the menubar in WM_CREATE message, specify the following options,

        case WM_CREATE:

            SHMENUBARINFO mbi;



            memset(&mbi, 0, sizeof(SHMENUBARINFO));

            mbi.cbSize     = sizeof(SHMENUBARINFO);

            mbi.hwndParent = hWnd;

            mbi.nToolBarId = IDR_MENU_POPUP;

            mbi.hInstRes   = g_hInst;

            mbi.dwFlags = SHCMBF_HMENU;



            if (!SHCreateMenuBar(&mbi))

            {

                g_hWndMenuBar = NULL;

            }

            else

            {

                g_hWndMenuBar = mbi.hwndMB;

            }

The only difference is we specify the popup menu identifier for nToolBarId member instead of the menu bar identifier and we also set the SHCMBF_HMENU flag. When SHCMBF_HMENU flag is not specified then the nToolBarId value is treated as a toolbar identifer which creates our usual softkey bar with left and right softkeys. If the flag is set then nToolBarId is treated as a menu identifier. Just a regular menu bar with menus and submenus. So now when you run the application you get,

Be sure to specify the message value if you want to handle these menu clicks. I have specified zero above because I am not really interested in handling these items.

 

So now a question pops up (pun intended), what if I have several popup menus and submenus in the popup identifier. Will it take only the first two and display them or will it do something strange? See for yourself, I used the following popup menu just for the sake of it,

IDR_MENU_POPUP MENU

BEGIN



    POPUP "File"

    BEGIN

        MENUITEM "&Open",               0

        MENUITEM "&Save",                0

        MENUITEM "E&xit",                  0

    END



    POPUP "Edit"

    BEGIN

        MENUITEM "&Copy",               0

        MENUITEM "C&ut",                 0

        MENUITEM "&Paste",              0

    END



    POPUP "View"

    BEGIN

        MENUITEM "&Full Screen",       0

        MENUITEM "&Options",            0

    END



    POPUP "Help"

    BEGIN

        MENUITEM "Abou&t",              0

        MENUITEM "He&lp",                0

    END



END



here’s what is does,

and,

I have never seen that on a ppc! I was surprised that it actually works and creates a full fledged menu bar with several menus. And notice the SIP button has moved to the right. The color of the menu bar has changed as well. A pretty interesting find, I would say.

Update:
You might also be interested in this post, Fun with menus – part II

Lets make the spaceship move! DirectDraw, Part 6

The title of this post might be a little misleading but frankly I haven’t had the motivation to take the Spaceship to the next level. I had a few ideas that I wanted to do but I got lost in nothing. I don’t know. Anyways, I won’t be pasting any code here this time around because there is not much that I have done in the first place (: Just played around a bit with the spaceship and came up with this in about half-hour. Nothing fancy.

 

 


As you can see, the balls pass through each other! I have been trying to get them to bounce off each other but trust me this time around it is really a bit complicated (; I landed up on a few things when I binged around, I am yet to check if those might be useful. Hopefully I can get it done soon enough and if you have any ideas you are more than welcome to comment and guide me.

Bouncing/Colliding marbles, DirectDraw, Part 7

Hi there! The marbles finally collide and collide they do! (: I came across this thread, where Professor Fu-Kwun Hwang has a nice applet on collisions. If you go through the entire thread, professor Hwang explains the equations and mathematics related to elastic two-dimensional collisions.

 

The calculations for a collision is a mathematically involved affair, as I had mentioned in my previous post. I can’t say I fully understand the computations yet, maybe I will look into the details later (: Anyways, the marbles collide and deflect off each other pretty nicely.

 

I’ll let the video do the talking:

 

I will explain the code in my next post, gotta get up early tomorrow (: Goodnight!

 

Bouncing/Colliding marbles, DirectDraw, Part 8

The code for colliding marbles is built on top of earlier changes to the DDEX3 sample. Look here, here and here for more information. There are a few changes I made in the code for making the marbles collide and I hope you have already gone through Prof. Fu-Kwun Hwang post on how to make stuff bounce off each other.



Create a structure to store the information about a marble:



/*

This structure stores the info related to a ball.

Its center (xPos, yPos)

Its velocity along both axes (dwVelX, dwVelY)

Its radius in pixels

*/

typedef struct _ballinfo

{

    int xPos;

    int yPos;

    int dwVelX;

    int dwVelY;

    int radius;

} BALLINFO;

And here are the globals we use in the source file:



#define MAX_BALLS 3



//we’ll be bouncing 3 balls around here.

BALLINFO g_BallInfo[MAX_BALLS];



//to store the rects of each ball

RECT g_Rect[MAX_BALLS];

To add more marbles to the program you just need to change the MAX_BALLS macro and recompile the code. Well, the terms balls and marbles are used interchangeably (:

 

We initialize our global data in InitApp() function, we call InitializeData() just before InitApp() returns:



void InitializeData()

{

    int radius = 22;



    for (int i=0; i < MAX_BALLS; i++)

    {

        g_BallInfo[i].xPos = RandInt(radius, 216);



        //switch on i to decide on yPos so that the balls dont overlap when they are created

        switch(i)

        {

            //dividing the screen into 3 horizontal sections with one ball in each

            case 0:

                g_BallInfo[i].yPos = RandInt(radius, 100-radius);

                break;

            case 1:

                g_BallInfo[i].yPos = RandInt(100+radius, 200-radius);

                break;

            case 2:

                g_BallInfo[i].yPos = RandInt(200+radius, 300-radius);

                break;

            default:

                g_BallInfo[i].yPos = RandInt(radius, 320-radius);

        }



        g_BallInfo[i].dwVelX = RandInt(-6, 6);

        g_BallInfo[i].dwVelY = RandInt(-6, 6);



        g_BallInfo[i].radius = radius;



        printf("Ball %d details:\n", i+1);

        printf("X=%d  Y=%d  VelX=%d  VelY=%d\r\n", g_BallInfo[i].xPos, g_BallInfo[i].yPos,

                    g_BallInfo[i].dwVelX, g_BallInfo[i].dwVelY);

    }

}

Here we initialize all the data related to each marble, its center, velocity and the radius. The switch case makes sure that the balls do not overlap initially, currently I am only checking for three marbles in the switch. So if there are more than 3, there is still some possibility of the balls overlapping. I’ll leave that part for later and lets focus on more important things.

 

The RandInt() function is simple, it returns a random value between the two ranges specified:



DWORD RandInt(DWORD low, DWORD high)

{

    DWORD range = high – low;

    DWORD num = Random() % range;



    return (num + low);

}





Next, we look at the UpdateFrame() function:



static void UpdateFrame(HWND hWnd)

{

    HRESULT                     hRet;

    DDBLTFX                     ddbltfx;

    int i = 0, j = 0;



    memset(&ddbltfx, 0, sizeof(ddbltfx));

    ddbltfx.dwSize = sizeof(ddbltfx);

    ddbltfx.dwFillColor = 0;

    ddbltfx.dwROP = SRCCOPY;



    //clear the back buffer (color fil with black)

    g_pDDSBack->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAITNOTBUSY, &ddbltfx);



    for (i = 0; i < MAX_BALLS; i++)

    {

        g_Rect[i].top = g_BallInfo[i].yPos – 24;

        g_Rect[i].left = g_BallInfo[i].xPos – 24;

        g_Rect[i].bottom = g_BallInfo[i].yPos + 24;

        g_Rect[i].right = g_BallInfo[i].xPos + 24;

    }



    ddbltfx.ddckSrcColorkey.dwColorSpaceLowValue = RGB(0, 0, 0);

    ddbltfx.ddckSrcColorkey.dwColorSpaceHighValue = RGB(0, 0, 0);



    //blt all the balls on the screen

    for (i = 0; i<MAX_BALLS; i++)

    {

        while (TRUE)

        {

            hRet = g_pDDSBack->Blt(&g_Rect[i], g_pDDSOne, NULL, DDBLT_ROP | DDBLT_KEYSRCOVERRIDE, &ddbltfx);

            if (hRet == DD_OK)

                break;

            if (hRet == DDERR_SURFACELOST)

            {

                hRet = RestoreAll();

                if (hRet != DD_OK)

                    break;

            }

            if (hRet != DDERR_WASSTILLDRAWING)

                break;

        }

    }



    //update the balls positions and check for bounds

    for (i = 0; i<MAX_BALLS; i++)

    {

        g_BallInfo[i].xPos = g_BallInfo[i].xPos + g_BallInfo[i].dwVelX;

        g_BallInfo[i].yPos = g_BallInfo[i].yPos + g_BallInfo[i].dwVelY;



        if(g_BallInfo[i].xPos <= 24)

        {

            g_BallInfo[i].xPos = 24;

            g_BallInfo[i].dwVelX = -g_BallInfo[i].dwVelX;

        }



        if(g_BallInfo[i].xPos >= (240 – 24))

        {

            g_BallInfo[i].xPos = 240 – 24;

            g_BallInfo[i].dwVelX = -g_BallInfo[i].dwVelX;

        }



        if(g_BallInfo[i].yPos <= 24)

        {

            g_BallInfo[i].yPos = 24;

            g_BallInfo[i].dwVelY = -g_BallInfo[i].dwVelY;

        }



        if (g_BallInfo[i].yPos >= (320 – 24))

        {

            g_BallInfo[i].yPos = 320 – 24;

            g_BallInfo[i].dwVelY = -g_BallInfo[i].dwVelY;

        }

    }



    //check for collisions between all the balls

    for (i = 0; i < MAX_BALLS-1; i++)

    {

        for (j = i+1; j < MAX_BALLS; j++)

        {

            if(Collision(&g_BallInfo[i], &g_BallInfo[j]))

            {

                printf("Balls HIT!\n");

                CalculateNewVelocities(&g_BallInfo[i], &g_BallInfo[j]);

                MessageBeep(MB_OK);

            }

        }

    }

}

This is more or less the same as before except that the same things are done for all the three marbles here. Calculate the RECTs for each marble in g_Rect[]. Blt the marbles, update the marbles’ position and check for collisions between marbles. This is the interesting part.

 

Let me digress a bit here. How do you find out if the marbles are colliding? The presence of RECTs tricks your mind into thinking that you can check if the two RECTs overlap, which is what I did initially and which was really dumb, really. I used IntersectRect() api to find out if the two RECTs overlap, and if they did I decided its a collision! The problem here is that the two RECTs may overlap but there might actually be no collision for example when only the corners of the two RECTs intersect. And that happens way too many times, and watching the program run like that leaves you baffled, things dont make sense! It didn’t take me long to figure out a better way, instead of checking for overlapping RECTs, you had to check if the two circles (marbles) overlapped. Let the radius of the two circles be R1 and R2, calculate the distance between the centers of the two marbles, lets call it D. The marbles collide when D <= R1 + R2. Well, they actually collide when D = R1 + R2, but because our program updates the frames by a certain delta everytime we need to check for D <= R1 + R2.

 

The Collision() function takes two BALLINFO structures as input and determines if they collide, returns TRUE if they do and FALSE otherwise:



BOOL Collision(const BALLINFO *ball1, const BALLINFO *ball2)

{

    int distance = 0, temp = 0;



    int MinDistance = ball1->radius + ball2->radius;



    //calculate the distance between the centers of the two balls

    int dx = ball2->xPos – ball1->xPos;

    int dy = ball2->yPos – ball1->yPos;

    temp = dx*dx + dy*dy;

    distance = sqrt(temp);



    //printf("COLLISION: distance=%d, 2*R=%d\n", distance, MinDistance);



    if(distance <= MinDistance)

    {

        return TRUE;

    }



    return FALSE;

}

We use the standard formula to calculate the distance between two points, Distance = SQRT((x2-x1)2 + (y2-y1)2). If the marbles do collide then we have to calculate the new velocities for the marbles, and this is how we do it:



void CalculateNewVelocities(BALLINFO *ball1, BALLINFO *ball2)

{

    int ed = 1, mass1 = 20, mass2 = 20;



    double velX1, velY1, velX2, velY2;



    double dx = ball2->xPos – ball1->xPos;

    double dy = ball2->yPos – ball1->yPos;



    //calculate the distance between their centers

    double distance = sqrt(dx*dx+dy*dy);



    double vp1 = ball1->dwVelX*dx/distance + ball1->dwVelY*dy/distance;

    double vp2 = ball2->dwVelX*dx/distance + ball2->dwVelY*dy/distance;



    double dt = (ball1->radius + ball2->radius – distance)/(vp1 – vp2);



    //printf("CENTERS BEFORE: X1=%d, Y1=%d     X2=%d, Y2=%d\n", xPos1, yPos1, xPos2, yPos2);



    //the centers of the ball when they actually collided



    ball1->xPos = ball1->xPos – floor(ball1->dwVelX*dt + 0.5);

    ball1->yPos = ball1->yPos – floor(ball1->dwVelY*dt + 0.5);



    ball2->xPos = ball2->xPos – floor(ball2->dwVelX*dt + 0.5);

    ball2->yPos = ball2->yPos – floor(ball2->dwVelY*dt + 0.5);



    //now calulate the distance between centers (this should be very close to the sum of their radii)

    dx = ball2->xPos – ball1->xPos;

    dy = ball2->yPos – ball1->yPos;

    distance = sqrt(dx*dx+dy*dy);



    // Unit vector in the direction of the collision

    double ax = dx/distance, ay=dy/distance;



    // Projection of the velocities in these axes

    double va1 = (ball1->dwVelX*ax + ball1->dwVelY*ay);

    double vb1 = (-ball1->dwVelX*ay + ball1->dwVelY*ax);



    double va2 = (ball2->dwVelX*ax + ball2->dwVelY*ay);

    double vb2 = (-ball2->dwVelX*ay + ball2->dwVelY*ax);



    // New velocities in these axes (after collision): ed<=1,  for elastic collision ed=1

    double vaP1 = va1 + (1+ed)*(va2-va1)/(1 + mass1/mass2);

    double vaP2 = va2 + (1+ed)*(va1-va2)/(1 + mass2/mass1);



    // Undo the projections

    velX1 = vaP1*ax-vb1*ay;  velY1 = vaP1*ay+vb1*ax;// new vx,vy for ball 1 after collision

    velX2 = vaP2*ax-vb2*ay;  velY2 = vaP2*ay+vb2*ax;// new vx,vy for ball 2 after collision



    //printf("CENTERS AFTER: X1=%d, Y1=%d     X2=%d, Y2=%d\n", xPos1, yPos1, xPos2, yPos2);



    //printf("Old Vel: velX1=%d, velY1=%d     velX2=%d, velY2=%d\n", dwVelX1, dwVelY1, dwVelX2, dwVelY2);



    //new velocities of the balls

    ball1->dwVelX = floor(velX1 + 0.5);

    ball1->dwVelY = floor(velY1 + 0.5);

    ball2->dwVelX = floor(velX2 + 0.5);

    ball2->dwVelY = floor(velY2 + 0.5);



    //undo the correction done before

    ball1->xPos = ball1->xPos + floor(ball1->dwVelX*dt + 0.5);

    ball1->yPos = ball1->yPos + floor(ball1->dwVelY*dt + 0.5);

    ball2->xPos = ball2->xPos + floor(ball2->dwVelX*dt + 0.5);

    ball2->yPos = ball2->yPos + floor(ball2->dwVelY*dt + 0.5);



    //printf("New Vel: velX1=%d, velY1=%d     velX2=%d, velY2=%d\n", dwVelX1, dwVelY1, dwVelX2, dwVelY2);



}

This function is taken directly from Professor Fu-Kwun Hwang’s explanation on 2D collisions. Changes made to the velocities and positions in CalculateNewVelocities() will be reflected back in the caller.

 

Note: I ran this program quite many times and sometimes it does fail. Maybe something like once in 30 runs. The marbles stick to each other and don’t let go. The problem is in the calculation of ‘dt‘, where it tries to calculate where the balls would have been when they were about to collide (i.e. D = R1 + R2), but since even this is an approximation, it fails sometimes. One fix for this problem is to check if  the balls collide after calculating the new position and velocities. If they do then adjust the position values such that new D > R1 + R2. The problem mentioned above happens when after calculating the new positions and velocities for both marbles they still overlap.

Draggin the marbles around, DirectDraw, Part 9

You could sure use some mouse input with the marbles. Remove the timer stuff from the DDEX3 program that we modified and add handling for WM_LBUTTONDOWN, WM_LBUTTONUP and WM_MOUSEMOVE messages in the WindowProc() function. You will need to add some code to find out if WM_LBUTTONDOWN was on the marble and then move the position of the marble relative to its last position whenever you get WM_MOUSEMOVE message.

 

Here is a video of it:

 

 

If you run this in debug mode you can notice considerable lag because it runs over the debugger. The video here shows the application running normally, i.e. I select the program to "Start Without Debugging" (ctrl + F5).

Combining the pieces, DirectDraw, Part 10

Hi there! What do get when you combine this and this? Well, maybe a game called Crash Marbles? ;)

 

Anyways, game is still pretty far off but here is a video of the result:

 

 

Well, the basic concept of the "would-be" game revolves around the player having to dodge the evil marbles and also collect the powers that show up randomly on the screen. I am still thinking about how to make the game more interesting and entertaining. Don’t bother about the score that shows in the top left, its still work in progress ;)

A glitch corrected, DirectDraw, Part 11

In part 8 of this series of posts on DirectDraw, I had mentioned that there still seems to be a minor problem with the program. Read the "Note" section at the end of the post. The problem now has been solved.



Recap

The problem was that even after calculating the new positions for the marbles, the marbles would still be overlapping because of the approximations that we use. And this would sometimes repeat many times over which creates the problem.



Solution

The fix that I’d said in the "Note" section works well. After calculating the new position for the marbles you check again if they collide. And if they do then adjust their positions so that they don’t overlap anymore. Every time I move the marbles 2 pixels away from each other and check the distance again. And repeat this until they are no more overlapping. Here is the code:

     MinDistance = ball1->radius + ball2->radius;

    dxx = ball1->xPos – ball2->xPos;

    dyy = ball1->yPos – ball2->yPos;



    //calculate new distance between marbles

    distance = sqrt(dxx*dxx + dyy*dyy);

    printf("COLLISION: New Distance:%d \r\n", (int)distance);



    //this part makes sure that the new positions of the marbles are not overlapping

    //sometimes the new position itself overlaps and that leads to weird behaviour

    //check if the marbles are overlapping

    if ((int)distance <= MinDistance)

    {

        //adjust their positions

        do

        {

            if (ball1->xPos > ball2->xPos)

            {

                ball1->xPos += 1;

                ball2->xPos -= 1;

            }

            else

            {

                ball1->xPos -= 1;

                ball2->xPos += 1;

            }



            if (ball1->yPos > ball2->yPos)

            {

                ball1->yPos += 1;

                ball2->yPos -= 1;

            }

            else

            {

                ball1->yPos -= 1;

                ball2->yPos += 1;

            }



            dxx = ball1->xPos – ball2->xPos;

            dyy = ball1->yPos – ball2->yPos;



            distance = sqrt(dxx*dxx + dyy*dyy);



        }while((int)distance <= MinDistance);

    }

 

So the same set of values for xPos and yPos which used to create the problem before, now work fine.