Archived

This topic is now archived and is closed to further replies.

Hedos

2d game and collision detection

Recommended Posts

Hedos    674
Hey I'm about to start a new 2d game and I've been thinking about a way to handle collision between objects/world.. And I don't want to have a tile based world So here is what I have been thinking.. Each moving objects ( player, monsters, npc and maybe also some -small- static objects ) would have a bouncing box.. Do you think a bouncing box is the faster way to handle that kind of collisions? And for the static world, I would draw lines, in my Map editor, to specify where the collision is.. So I would just calculate the distance between the line and the objects.. Here is a little schema I made of what I was thinking ( if image doesn't show up, copy/paste this link: geocities.com/darkhedos/gameCollision.gif ) Are my idea good? bad? or even horrible? What do you suggest? Thanks [edited by - Hedos on March 22, 2004 6:42:48 PM]

Share this post


Link to post
Share on other sites
oliii    2196
boxes are fine, but spheres are even simpler

Ball and segment collision

that's a simple algorithm to get you going. Then you can move on prety quickly to boxes, swept spheres, and swept boxes if you need to. If you decide to go ahead, you can email me in case of troubles, coz it's untested code (but the algo is sound).

[edited by - oliii on March 22, 2004 7:23:17 PM]

Share this post


Link to post
Share on other sites
Hedos    674
Thanks for your reply I'm currently looking at your code
What would be the faster, boxes or circles?

And what about the lines to detect the static objects, is it a good idea?

Thanks again


edit: just finished reading your post, that looks like some pretty interesting code. I'm gonna try this and tell you if it works

[edited by - Hedos on March 22, 2004 7:33:16 PM]

Share this post


Link to post
Share on other sites
jack_1313    536
Here you can find code to handle circle-segment sweeps (ie a moving circle that may have collided with an obstacle between frames).
Circles (from my limited experience) are generally good when dealing with collision because they can be applied well to a variety of objects.

Share this post


Link to post
Share on other sites
Hedos    674
Wow awesome Jack, thanks

I''m currently working on both of your suggestions and I guess the result will be good.. but I''m still wondering if my whole concept is good or if something else would be better? ( Mainly in term of speed, as I want my game to be accessible even on low machines )

Share this post


Link to post
Share on other sites
eFoDay    300
here is a really simple bounding box collision detection function


bool Collision(CONST RECT *lprcSrc1, CONST RECT *lprcSrc2)
{
RECT rcDst;

// Check if the rects intersect

if( IntersectRect(&rcDst, lprcSrc1, lprcSrc2) )
return true;
else
return false;
}



if( Collision(sprite1.getBBox(), sprite2.getBBox()) )
{
// sprite1 and sprite 2 are colliding
}

Share this post


Link to post
Share on other sites
jack_1313    536
Your collision technique looks good to me - line segments to define the game world, spheres to handle the individual objects. That is how I might do it - just make sure that you reject any lines from the test that are not candidates of collision (or you could use a quadtree to speed things up, but then this goes beyond my experience) so that you are only testing against those you actually need to. Bounding boxes are often used to check if a collision is possible before performing pixel-overlap tests, so I would therefore suggest the use of circles as I believe they produce a better result.

[edited by - jack_1313 on March 23, 2004 3:58:17 AM]

Share this post


Link to post
Share on other sites
oliii    2196
as far as speed is concerned, it''s not a problem. either box or spheres, it''s very fast. Especially if you have a way to reduce the tests, by finding the segments close to the object very quickly, like with a quadtree or bsp tree, or any other methods.

The swept test is good if objects move fast, the simple test is a good start to see how things are going, it can be implemented in an hour and already allows you to have some gameplay. Also, to do character vs character, it''s just a matter of applying the same algo, but with sphere-sphere, so straight forward again.

With swept tests, there are a few gotchas, and some complications can arise when dealing with lots of moving objects at the same time.

Generally, the first solution can be applied straight away for slow-paced games (like in a rpg, or rts), while the other if better suited for fast arcade games, like Soldat, or a breakout/pong.

Share this post


Link to post
Share on other sites
Hedos    674
Jack_1313: I tried your code, but I'm stuck at the point of using the velocity


Vector2D op = Ball.p;
Ball.p + = Ball.v;
Vector2D i;
Vector2D r;
float t;
if(CircleSegmentSweep(Ball.r,op,Ball.p,linep1,linep2,i,t,r))
{
Ball.p = i;
Ball.v = r;
Ball.p += Ball.v; //Move the ball away from the collision

}

I can see that 'v' is the velocity of the ball, but I don't know what are velocity vectors and how to use that?

You said: "I recommend you alter your struct so as that it uses a velocity vector to represent speed and direction."

I guess that's what I would have to do, but I don't know how velocity vectors work... ( I haven't done really advanced math courses yet, haven't learned anything about vectors and that kind of stuff )

Thanks

[edited by - Hedos on March 23, 2004 5:59:35 PM]

Share this post


Link to post
Share on other sites
oliii    2196
as far as game programming, and mostly physics and rendering, Vectors are probably the best thing you can learn. also basic trig. It''s not very complicated.

head towards the article & resources section, here you''ll find a couple of good introductions to basic maths.

try this one
Vectors and Matrices: A Primer (Phil Dadd)

or this one (this site is quite useful as a reference)

Maths : Vectors (MArtin Baker)

you don''t have to start on matrices, but I kind of gave up explaining collision detection and basic physics without the use of the simplest of vector maths. It''s too much work. Besides, they are quite intuitive and not difficult to learn.

Share this post


Link to post
Share on other sites
jack_1313    536
You do not *need* to use velocities in order to use the code. I merely suggested it because in Legoboy's situation it would be the logical thing. Legoboy had a ball struct with the properties "speed" and "direction" - combine this into a velocity and things will get simpler. Although in your situation it may not be best to use this, I'll give a brief example of velocities anyhow:

Objects position is 10,20
Objects velocity is 2,1
Each time the game updates, add the objects velocity (modified by whatever frame-independent movement method you are using) onto the objects position in order to move the Object:
Objects position += objects velocity
Objects position is now 12,21. In this way the velocity represents the speed and direction that a thing will travel. For instance, a velocity of 4,2 would indicated the same direction as 2,1, but the object would move twice as fast. A velocity of -2,-1 would cause the object to travel in the opposite direction (to a velocity of 2,1).

Hope that helps a little. I wouldn't worry about not having done any advanced maths classes - neither have I. In fact, I have learnt more maths through game programming that I think I ever have at secondary school.

All that said, whatever method you are using to handle movement is probably fine. If you are merely moving the player left, right and forward at a constant speed whenever he/she is pressing Left, Right and Up, then you may not need to use velocities (although I seriously suggest that you get a good feel of vectors as they are used in may ways), although you will still need to obviously represent the direction the player is facing. The CircleSegmentSweep function takes the original position of the circle and the update position of the circle. It will not matter how you determine where the updated position will be. The function also gives the reflection vector. This is important because you will need to add it to the objects position after a collision in order to move the object away from the segment (or you could just move the object in the opposite direction it is traveling).
One thing that this code does not do particularly well is handle sliding (ie an object slides alone a surface it has collided with). Perhaps I'll get around to adding that functionality some time .
Another thing - it may be necessary to go through all possible collisions and determine which one is closest, then actually responding to that one. This, I believe (although I have not tried) will help prevent glitches when several collisions are detected.

It is also worth noting that is may be easier to go with the code oliii provided because, although I have not had a good look, it may be easier to get a grip on. That and the fact that you can guarantee that oliii knows what he's talking about (more than I do).

Anyhow, hope all this helped.

[edited by - jack_1313 on March 23, 2004 7:06:36 PM]

Share this post


Link to post
Share on other sites
Hedos    674
Thanks for your replies

oliii: I tried your code for collision between a circle and lines, it works!
It isn't slicing correctly, it can only slices in one direction and it seems that the more the line is horizontal and the faster the slicing will be ( it becomes extremly fast when close to horizontal )...
Also, it's kinda bugged when touching the end of a line
And when there is a segment of two lines, the sphare will go trhought the segment..

Oh, but maybe this is just because I don't understand what is the use of the 'NumSegments' parameter in the collision function?

Well.. I guess I'm gonna try to learn some vector stuff and create a better collision detection based on your code

jack_1313: Thanks for your explanations

Edit: oliii, I just realised what's really happening, the sphere is not slicing on -any- lines when going in left or right direction, but it's slicing when going up or down.. ( and slicing at exagerated speeds)

[edited by - Hedos on March 23, 2004 7:34:04 PM]

Share this post


Link to post
Share on other sites
oliii    2196
ok, it''s the closest point calculations that''s buggy. I''ll have a look The best way to debug it is to draw the actual point, but I''ll check it out.

Share this post


Link to post
Share on other sites
oliii    2196
I''ve had a look at it, and it works straight up. here are the files (ouch...).



//=========================================================

//---------------------------------------------------------

//

// Gamecode.h

//

//---------------------------------------------------------

//=========================================================


#ifndef OLI_GAMECODE_H
#define OLI_GAMECODE_H


/*
#include "GameCode.h"
*/


#include <windows.h>
#include <math.h>
#include <stdio.h>
#include <GL/glut.h>

extern void GameInit ();
extern void GameUpdate (float dt);
extern void GameRender (void);
extern void GameOnKeyCallback (char keypressed);
extern void GameMouse (float x, float y, int buttons);
extern void GameRenderStats ();
extern void Printf (int x, int y, unsigned int rgba, const char* str, ...);
static float dbg_world_size = 100.0f;


#endif OLI_GAMECODE_H



//=========================================================

//---------------------------------------------------------

//

// main.cpp

//

//---------------------------------------------------------

//=========================================================


#include "GameCode.h"

//--------------------------------------------------------------------------

// mouse coords

//--------------------------------------------------------------------------

float mouse_x = 0.0f;
float mouse_y = 0.0f;
int mouse_b = 0;

//--------------------------------------------------------------------------

// window size

//--------------------------------------------------------------------------

float width = 640;
float height = 480;


void Printf(int x, int y, unsigned int rgba, const char* str, ...)
{
static char buffer[512];

va_list params;
va_start(params, str);
vsprintf(buffer, str, params);
va_end(params);

glPushMatrix();

glColor4ubv((unsigned char*) &rgba);

float fx = x * ( 8 * dbg_world_size / width);
float fy = y * (13 * dbg_world_size / height);

glRasterPos2f(fx, 102 - fy);

char* c = buffer;
while(*c)
{
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, *c);
c++;
}

glPopMatrix();
}

//-----------------------------------------------------

// resets the world with random data

//-----------------------------------------------------

void Init()
{
GameInit();
}

//-----------------------------------------------------

// Update the objects

//-----------------------------------------------------

void Update(float dt)
{
GameUpdate(dt);
}

//-----------------------------------------------------

// displays the objects

//-----------------------------------------------------

void Display()
{
//--------------------------------------------------------------------------

// render stuff

//--------------------------------------------------------------------------

glClearColor(0.2f, 0.2f, 0.2f, 0.2f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

float aspect = width / (float) height;


glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, dbg_world_size, 0, dbg_world_size, -100, 100);

//-----------------------------------------------------------------

// Setup the model view matrix

//-----------------------------------------------------------------

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

float fx = ( (mouse_x / (float) width )) * dbg_world_size;
float fy = (1.0f - (mouse_y / (float) height)) * dbg_world_size;

GameMouse(fx, fy, mouse_b);

GameUpdate(1.0f / 60.0f);

GameRender();

glutSwapBuffers();
}


void Mouse(int buttons, int state, int x, int y)
{
mouse_x = x;
mouse_y = y;

if (buttons == GLUT_LEFT_BUTTON)
{
if (state == GLUT_DOWN)
mouse_b |= 1;
else
mouse_b &= ~1;
}

if (buttons == GLUT_RIGHT_BUTTON)
{
if (state == GLUT_DOWN)
mouse_b |= 2;
else
mouse_b &= ~2;
}
}
void PassiveMotion(int x, int y)
{
mouse_x = x;
mouse_y = y;
}

void Motion(int x, int y)
{
mouse_x = x;
mouse_y = y;
}

void Idle()
{
// Update(1.0f / 60.0f);


Display();
}

void Timer(int t)
{
Idle();

glutTimerFunc(t, Timer, (int) 500.0f / 60.0f);
}

void Reshape(int w, int h)
{
width = w;
height = h;
glViewport( 0, 0, w, h);
}

void Keyboard(unsigned char k, int x, int y)
{
if (k == 27)
exit(0);

GameOnKeyCallback(k);
}

void main(int argc, char** argv)
{
//--------------------------------------------------------------------------

// OpenGL / GLUT init

//--------------------------------------------------------------------------

glutInit( &argc, argv );
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);

glutInitWindowSize (width, height);
glutInitWindowPosition (0, 0);
glutCreateWindow ("ping pong");

glPointSize(3.0f);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable (GL_DEPTH_TEST);
glDisable (GL_LIGHTING);

glutDisplayFunc (Display);
glutReshapeFunc (Reshape);
// glutIdleFunc (Idle);

glutTimerFunc (0, Timer, (int) 100.0f / 60.0f);
glutPassiveMotionFunc (PassiveMotion);
glutMouseFunc (Mouse);
glutMotionFunc (Motion);
glutKeyboardFunc (Keyboard);


printf("---------------------------------\n");
printf("- colliding spehre-segment demo -\n");
printf("---------------------------------\n");
printf("olivierrenault@hotmail.com\n");
printf("\n");
printf("- press esc to shutdown\n");

Init ();
glutMainLoop ();
}



//=========================================================

//---------------------------------------------------------

//

// Gamecode.cpp

//

//---------------------------------------------------------

//=========================================================

#include "GameCode.h"


static bool dbg_Pause = false;
static int ikeypressedtimer = 0;
static int itimer = 0;
static float mousex = 0;
static float mousey = 0;

#define for if (0) {} else for

float frand(float x) { return (rand() / (float) RAND_MAX) * x; }

float TwoPi() { static float two_pi = atan(1) * 8.0f; return two_pi; }

struct Vector
{
float x, y;
};

enum { eNumSegments = 50 };
Vector Seg[eNumSegments][2];
int m_iNumSegments = eNumSegments;


Vector Pointer;
float radius = dbg_world_size * 0.02f;

Vector GetClosestPointOnSegment(Vector S0, Vector S1, Vector P)
{
Vector E; // edge direction

E.x = S1.x - S0.x; // E = S1 - S0

E.y = S1.y - S0.y; //


Vector D; // relative position of point to the edge

D.x = P.x - S0.x; // D = P - S0

D.y = P.y - S0.y; //


float de; // project point on edge (dot product)

de = D.x*E.x + D.y*E.y; // de = D dot E


float e2; // length of the edge, squared

e2 = E.x*E.x + E.y*E.y; // e^2 = E dot E


if (de < 0.0f) // if projection is before the edge start point

return S0; // return edge start point


if (de > e2) // if projection after edge end point

return S1; // return edge end point


float t; // percentage on how far the point projects on the edge

t = de / e2; // t = projection / edge length^2


Vector Q; // vector projection of point on edge

Q.x = S0.x + E.x * t;
Q.y = S0.y + E.y * t;

return Q;
}


bool PointInCircle(Vector C, float r, Vector P)
{
Vector D; // relative position of point to circle centre

D.x = P.x - C.x; // D = P - C

D.y = P.y - C.y; //


float d2; // distance squared of the point to the circle centre

d2 = D.x*D.x + D.y*D.y; // d^2 = D dot D


float r2; // radius squared of the circle

r2 = r*r; // r^2 = r * r


if (d2 > r2) // distance squared greater than radius squared

return false; // point not inside the circle


return true; // yes, point is inside

}


// returns the new circle position so the point is right on the

// surface of the circle

Vector ConstrainCircleToPoint(Vector C, float r, Vector P, float smoothness)
{
Vector D; // relative position of circle centre to point

D.x = C.x - P.x; // D = C - P

D.y = C.y - P.y;

float d2; // distance squared between points

d2 = D.x*D.x + D.y*D.y; // d^2 = D dot D


float d; // distance between points

d = (float) sqrt(d2); // d = dqrt(d^2)


float l = r - d; // how far is the point to the circle surface


D.x *= l / d * smoothness; // the movement vector required to push the circle

D.y *= l / d * smoothness; // centre away from the point

// D *= l / d


Vector NewC; // the new circle centre position,

NewC.x = C.x + D.x; // so that the point is on the circle surface

NewC.y = C.y + D.y; // NewC = C + D;


return NewC;
}



void GameInit(void)
{
for(int i = 0; i < m_iNumSegments; i ++)
{
Seg[i][0].x = frand(dbg_world_size);
Seg[i][0].y = frand(dbg_world_size);
Seg[i][1] = Seg[i][0];

Seg[i][1].x += frand(dbg_world_size * 0.3f) - dbg_world_size * 0.15f;
Seg[i][1].y += frand(dbg_world_size * 0.3f) - dbg_world_size * 0.15f;
}
}


void GameUpdate(float dt)
{
if (dbg_Pause)
return;

Vector D;
D.x = mousex - Pointer.x;
D.y = mousey - Pointer.y;

float d = sqrt(D.x*D.x + D.y*D.y);
D.x /= d;
D.y /= d;


float speed = d;
if (speed > dbg_world_size * 0.1f)
speed = dbg_world_size * 0.1f;

Pointer.x += D.x * speed * 0.1f;
Pointer.y += D.y * speed * 0.1f;

int iter = 5;
float smoothness = 5.0f / (float) iter;

if (smoothness > 1.0f) smoothness = 1.0f;

for(int j = 0; j < iter; j ++)
{

for(int i = 0; i < m_iNumSegments; i ++)
{
Vector Q = GetClosestPointOnSegment(Seg[i][0], Seg[i][1], Pointer);

glPointSize(3.0f);
glEnable(GL_POINT_SMOOTH);
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
glBegin(GL_POINTS);
glVertex2fv(&Q.x);
glEnd();


if (PointInCircle(Pointer, radius, Q))
{
Pointer = ConstrainCircleToPoint(Pointer, radius, Q, smoothness);
}
}
}
}

void GameRender(void)
{
GameRenderStats();


glPointSize(3.0f);
glEnable(GL_POINT_SMOOTH);
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
glBegin(GL_POINTS);
glVertex2fv(&Pointer.x);
glEnd();

glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
glLineWidth(1.0f);
glBegin(GL_LINES);

for(int i = 0; i < 10; i ++)
{
Vector P;
Vector Q;
P.x = Pointer.x + cos(i * TwoPi() / 10.0f) * radius;
P.y = Pointer.y + sin(i * TwoPi() / 10.0f) * radius;

Q.x = Pointer.x + cos((i+1) * TwoPi() / 10.0f) * radius;
Q.y = Pointer.y + sin((i+1) * TwoPi() / 10.0f) * radius;

glVertex2fv(&P.x);
glVertex2fv(&Q.x);

}
glEnd();

glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glLineWidth(1.0f);
glBegin(GL_LINES);
for(int i = 0; i < m_iNumSegments; i ++)
{
glVertex2fv(&Seg[i][0].x);
glVertex2fv(&Seg[i][1].x);
}
glEnd();
}

float fCoF = 0.3f;
float fCoR = 0.7f;
float fSep = 0.5f;

float Clamp(float a, float min, float max)
{
return (a < min)? min : (a > max)? max : a;
}

void GameOnKeyCallback(char keypressed)
{
switch(keypressed)
{
case ''p'':
case ''P'':
dbg_Pause = !dbg_Pause;
break;
case '' '':
GameInit();
break;
case ''h'':
case ''H'':
ikeypressedtimer = 0;
default:
printf("\n");
printf("--------------------------------\n");
printf("- Keys -\n");
printf("--------------------------------\n");
printf("- Pause On/Off : ''p''\n");
printf("- help : ''h''\n");
printf("--------------------------------\n");
printf("\n");
break;
}
}


void GameMouse(float x, float y, int buttons)
{
mousex = x;
mousey = y;
}

void GameRenderStats()
{
if (dbg_Pause)
ikeypressedtimer = 0;

if (ikeypressedtimer < 256)
{
unsigned int a = (255 - ikeypressedtimer);
unsigned int r = 0xFF;
unsigned int g = 0xFF;
unsigned int b = 0xFF;
unsigned int c = ((a << 24) | (r << 16) | (g << 8) | b);

int x = 2;
int y = 3;
Printf(2, y++, c, "- Mouse [%d, %d]", (int) mousex, (int) mousey);

x = 47;
y = 3;
Printf(x, y++, c, "--------------------------------");
Printf(x, y++, c, "- Pause On/Off : ''p''");
Printf(x, y++, c, "- help : ''h''");
Printf(x, y++, c, "--------------------------------");

ikeypressedtimer+=8;
}
}

Share this post


Link to post
Share on other sites
Hedos    674
Wow, it works great Thanks a lot
Now I''m gonna try to make it work with collision with other circles... ( I have no idea if I''ll be able to do that )

Share this post


Link to post
Share on other sites
oliii    2196
it''s even simpler.

took me 15 minutes here is the full code, since you have to change the main collision loop slightly to take care of player/player collisions.


don''t kid yourself, this algo is simplistic, it''s just a start for your game. you''ll need to do things a lot more complicated so the collsion detection becomes bullet proof. For example, if the sphere colides with two parallel walls, you''ll have problems. to get rid of those cases, you can either fudge it (detect two collisions walls that are parallel => set player movement to the player before collision, but for that, you need to detect collisions ahead of the frame, so a bit more code), or do it properly (swept spheres, and the problem of sorting collisions, grouping objects in potentially colliding clusters, ect...).


#include "GameCode.h"


static bool dbg_Pause = false;
static int ikeypressedtimer = 0;
static int itimer = 0;
static float mousex = 0;
static float mousey = 0;

#define for if (0) {} else for

float frand(float x) { return (rand() / (float) RAND_MAX) * x; }

float clamp(float a, float min, float max) { return (a < min)? min : (a > max)? max : a; }

float TwoPi() { static float two_pi = atan(1) * 8.0f; return two_pi; }

struct Vector
{
float x, y;
};

struct CPlayer
{
Vector P;
float r;

CPlayer()
{
P.x = frand(dbg_world_size);
P.y = frand(dbg_world_size);
r = frand(dbg_world_size * 0.04f) + 0.04f;
}
};

enum { eNumSegments = 50 };
Vector Seg[eNumSegments][2];
int m_iNumSegments = eNumSegments;

enum { eMaxPlayers = 20 };
CPlayer Player[eMaxPlayers];
int iNumPlayers = 10;


Vector GetClosestPointOnSegment(Vector S0, Vector S1, Vector P)
{
Vector E; // edge direction

E.x = S1.x - S0.x; // E = S1 - S0

E.y = S1.y - S0.y; //


Vector D; // relative position of point to the edge

D.x = P.x - S0.x; // D = P - S0

D.y = P.y - S0.y; //


float de; // project point on edge (dot product)

de = D.x*E.x + D.y*E.y; // de = D dot E


float e2; // length of the edge, squared

e2 = E.x*E.x + E.y*E.y; // e^2 = E dot E


if (de < 0.0f) // if projection is before the edge start point

return S0; // return edge start point


if (de > e2) // if projection after edge end point

return S1; // return edge end point


float t; // percentage on how far the point projects on the edge

t = de / e2; // t = projection / edge length^2


Vector Q; // vector projection of point on edge

Q.x = S0.x + E.x * t;
Q.y = S0.y + E.y * t;

return Q;
}


bool PointInCircle(Vector C, float r, Vector P)
{
Vector D; // relative position of point to circle centre

D.x = P.x - C.x; // D = P - C

D.y = P.y - C.y; //


float d2; // distance squared of the point to the circle centre

d2 = D.x*D.x + D.y*D.y; // d^2 = D dot D


float r2; // radius squared of the circle

r2 = r*r; // r^2 = r * r


if (d2 > r2) // distance squared greater than radius squared

return false; // point not inside the circle


return true; // yes, point is inside

}


// returns the new circle position so the point is right on the

// surface of the circle

Vector ConstrainCircleToPoint(Vector C, float r, Vector P, float smoothness)
{
Vector D; // relative position of circle centre to point

D.x = C.x - P.x; // D = C - P

D.y = C.y - P.y;

float d2; // distance squared between points

d2 = D.x*D.x + D.y*D.y; // d^2 = D dot D


float d; // distance between points

d = (float) sqrt(d2); // d = dqrt(d^2)


float l = r - d; // how far is the point to the circle surface


D.x *= l / d * smoothness; // the movement vector required to push the circle

D.y *= l / d * smoothness; // centre away from the point

// D *= l / d


Vector NewC; // the new circle centre position,

NewC.x = C.x + D.x; // so that the point is on the circle surface

NewC.y = C.y + D.y; // NewC = C + D;


return NewC;
}


// check if distance squared between the centre are more than the sqaure of the sum of the radius

// if not, compute the penetration vector (depth and direction in one single vector)

// the separation (penetration vector), if the vector required to push the spheres apart so

// they stop intersecting.

bool CircleCircleCollide(Vector C0, float r0, Vector C1, float r1, Vector* pSeparation)
{
Vector D;
D.x = C0.x - C1.x;
D.y = C0.y - C1.y;
float d2 = (D.x * D.x + D.y * D.y);

float r = r0 + r1;
float r2 = r * r;

if (d2 > r2)
return false;

if (!pSeparation) // this call was just a query, so return that the spheres do collide

return true;

float d = (float) sqrt(d2);
float l = r - d;

D.x *= l / d;
D.y *= l / d;

*pSeparation = D;

return true;
}


void GameInit(void)
{
for(int i = 0; i < m_iNumSegments; i ++)
{
Seg[i][0].x = frand(dbg_world_size);
Seg[i][0].y = frand(dbg_world_size);
Seg[i][1] = Seg[i][0];

Seg[i][1].x += frand(dbg_world_size * 0.3f) - dbg_world_size * 0.15f;
Seg[i][1].y += frand(dbg_world_size * 0.3f) - dbg_world_size * 0.15f;
}

for(int i = 0; i < iNumPlayers; i ++)
{
Player[i] = CPlayer();
}
}


void CheckPlayerSegmentsCollisions(int iPlayer, float smoothness)
{
Vector& Pointer = Player[iPlayer].P;
float radius = Player[iPlayer].r;

for(int i = 0; i < m_iNumSegments; i ++)
{
Vector Q = GetClosestPointOnSegment(Seg[i][0], Seg[i][1], Pointer);

if (PointInCircle(Pointer, radius, Q))
{
Pointer = ConstrainCircleToPoint(Pointer, radius, Q, smoothness);
}
}
}

void CheckPlayerPlayersCollisions(int iPlayer, float smoothness)
{
int i = iPlayer;
for(int j = i+1; j < iNumPlayers; j ++)
{
Vector Separation;

if (CircleCircleCollide(Player[i].P, Player[i].r, Player[j].P, Player[j].r, &Separation))
{
Player[i].P.x += Separation.x * 0.5f * smoothness;
Player[i].P.y += Separation.y * 0.5f * smoothness;

Player[j].P.x -= Separation.x * 0.5f * smoothness;
Player[j].P.y -= Separation.y * 0.5f * smoothness;
}
}
}


void GameUpdate(float dt)
{
if (dbg_Pause)
return;

Vector& Pointer = Player[0].P;

Vector D;
D.x = mousex - Pointer.x;
D.y = mousey - Pointer.y;

float d = sqrt(D.x*D.x + D.y*D.y);
D.x /= d;
D.y /= d;

float speed = d;
if (speed > dbg_world_size * 0.1f)
speed = dbg_world_size * 0.1f;

Pointer.x += D.x * speed * 0.1f;
Pointer.y += D.y * speed * 0.1f;

int iter = 2;
float smoothness = 5.0f / (float) iter;

if (smoothness > 1.0f) smoothness = 1.0f;

// core collision loop

for(int j = 0; j < iter; j ++)
{
for(int i = 0; i < iNumPlayers; i ++)
{
CheckPlayerSegmentsCollisions(i, smoothness); // player vs world collision

CheckPlayerPlayersCollisions (i, smoothness); // player vs other players collision

}
}
}

void GameRender(void)
{
GameRenderStats();

glPointSize(3.0f);
glEnable(GL_POINT_SMOOTH);
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
glBegin(GL_POINTS);
for(int i = 0; i < iNumPlayers; i ++)
{
glVertex2fv(&Player[i].P.x);
}
glEnd();

glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
glLineWidth(1.0f);
glBegin(GL_LINES);

for(int p = 0; p < iNumPlayers; p ++)
{
Vector Pointer = Player[p].P;
float radius = Player[p].r;

for(int i = 0; i < 10; i ++)
{
Vector P;
Vector Q;
P.x = Pointer.x + cos(i * TwoPi() / 10.0f) * radius;
P.y = Pointer.y + sin(i * TwoPi() / 10.0f) * radius;

Q.x = Pointer.x + cos((i+1) * TwoPi() / 10.0f) * radius;
Q.y = Pointer.y + sin((i+1) * TwoPi() / 10.0f) * radius;

glVertex2fv(&P.x);
glVertex2fv(&Q.x);
}
}
glEnd();

glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glLineWidth(1.0f);
glBegin(GL_LINES);
for(int i = 0; i < m_iNumSegments; i ++)
{
glVertex2fv(&Seg[i][0].x);
glVertex2fv(&Seg[i][1].x);
}
glEnd();
}

void GameOnKeyCallback(char keypressed)
{
switch(keypressed)
{
case ''p'':
case ''P'':
dbg_Pause = !dbg_Pause;
break;
case '' '':
GameInit();
break;
case ''h'':
case ''H'':
ikeypressedtimer = 0;
default:
printf("\n");
printf("--------------------------------\n");
printf("- Keys -\n");
printf("--------------------------------\n");
printf("- Pause On/Off : ''p''\n");
printf("- help : ''h''\n");
printf("--------------------------------\n");
printf("\n");
break;
}
}


void GameMouse(float x, float y, int buttons)
{
mousex = x;
mousey = y;
}

void GameRenderStats()
{
if (dbg_Pause)
ikeypressedtimer = 0;

if (ikeypressedtimer < 256)
{
unsigned int a = (255 - ikeypressedtimer);
unsigned int r = 0xFF;
unsigned int g = 0xFF;
unsigned int b = 0xFF;
unsigned int c = ((a << 24) | (r << 16) | (g << 8) | b);

int x = 2;
int y = 3;
Printf(2, y++, c, "- Mouse [%d, %d]", (int) mousex, (int) mousey);

x = 47;
y = 3;
Printf(x, y++, c, "--------------------------------");
Printf(x, y++, c, "- Pause On/Off : ''p''");
Printf(x, y++, c, "- help : ''h''");
Printf(x, y++, c, "--------------------------------");

ikeypressedtimer+=8;
}
}

Share this post


Link to post
Share on other sites
Hedos    674
...
damn you.. I wish I was as much good as you in maths!

This code is just awesome! and you did it in 15min!

Well, I''m gonna try to understand that.. maybe it''s not so complicated after all

Thanks again

Share this post


Link to post
Share on other sites