Original Post
I'm trying to get this collision thing working with minimal luck. Right now 1 ball, one wall. Reflections are off. Can someone run it and point me in the right direction. Im positive my dot product is correct, im using fake collision around the borders. I just need a hint. (left or right click to change wall or ball location) win32 project using vs 2005
// really basic GDI program
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
#include <math.h>
#include <cstdlib>
#include <ctime>
// ==== DEFINES ==========================================================
#define WINDOW_CLASS_NAME "WINCLASS1"
#define WIN_WID 640
#define WIN_HGT 480
#define X_OFFSET (WIN_WIDTH/2)
#define Y_OFFSET (WIN_HEIGHT/2)
#define WIN_LEFT 0
#define WIN_TOP 0
#define BALL_WIDTH 5
#define BALL_SPACING 2
#define BALL_SPEED 5
#define DEG2RAD(a) ((a) * 0.0174533f) // = deg x PI/180
#define X_START 100
#define Y_START 100
#define START_ANGLE_DEG 53.0f // launch angle in degrees
#define START_VELOCITY 8.0f // length of velocity vector
COLORREF BallColor = RGB(255, 0, 0);
COLORREF WallColor = RGB(0, 250, 100);
// ==== STRUCTURES ===============================================
typedef struct tVector2
{
float x;
float y;
} tPoint2;
struct tLineSeg
{
tPoint2 p0;
tPoint2 p1;
bool isVertical; // slope infinite, no b
float m;
float b;
tVector2 n;
};
// ==== GLOBALS ==========================================================
HWND g_hWnd; // main window handle
POINT gCursorPos;
COLORREF gBallColor = 0x00FFFFFF;
bool ticker = false;
float counter = 0;
float counter1 = 0;
float counter2 = 0;
float counter3 = 0;
float counter4 = 20;
#define NUM_WALLS 11
tLineSeg gWallArray[NUM_WALLS] =
{
// p0.x, p0.y, p1.x, p1.y, isVert,m, b, n.x, n.y
{20.0f, 20.0f, 20.0f, (WIN_HGT-55), false, 0, 0, 0, 0}, // left bounding edge
{20.0f, (WIN_HGT-55), (WIN_WID-50), (WIN_HGT-55), false, 0, 0, 0, 0}, // top bounding edge
{(WIN_WID-30), (WIN_HGT-55), (WIN_WID-50), 20.0f, false, 0, 0, 0, 0}, // right bounding edge
{(WIN_WID-30), 20.0f, 20.0f, 20.0f, false, 0, 0, 0, 0}, // bottom bounding edge
{400.0f, 350.0f, 500.0f, 230.0f, false, 0, 0, 0, 0}, // obstacle 1
{100.0f, 250.0f, 200.0f, 400.0f, false, 0, 0, 0, 0}, // obstacle 2
{600.0f, 150.0f, 500.0f, 50.0f, false, 0, 0, 0, 0}, // obstacle 3
{150.0f, 100.0f, 225.0f, 120.0f, false, 0, 0, 0, 0}, // obstacle 4
{225.0f, 300.0f, 350.0f, 250.0f, false, 0, 0, 0, 0}, // triangle side 1
{225.0f, 300.0f, 260.0f, 200.0f, false, 0, 0, 0, 0}, // triangle side 2
{260.0f, 200.0f, 350.0f, 250.0f, false, 0, 0, 0, 0}, // triangle side 3
};
tLineSeg OneLine = {20.0f, 250.0f, 400.0f, 150.0f, false, 0, 0, 0, 0};
tLineSeg gBall = { 20.0f, 400.0f, 450.0f, 200.0f, false, 0, 0, 0, 0};
HDC memDC; // handle to memory device context for back buffer
// ==== PROTOTYPES =======================================================
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void ClearScreen(HDC hDC);
void DrawWalls();
void DrawLine(HDC hDC, tPoint2 p0, tPoint2 p1, COLORREF color);
void ClearScreen();
void DrawCircle(HDC dc, int xOrg, int yOrg, int radius, int spacing, COLORREF color);
void MoveBall();
void FakeCollision();
//bool BorderCollision(); // fake collision
tVector2 Normalize(tVector2 A);
void InitializLine(tLineSeg *A);
bool Intersection(tLineSeg &ball, tLineSeg &wall, tVector2 *hitPT);
float hitTime(tLineSeg A, tVector2 *hitPT);
float Dott(tVector2 A, tVector2 B);
tVector2 Reflect(tVector2 B, tVector2 W);
float Vec2Mag(tVector2 v);
tVector2 hitPT = {0, 0};
tVector2 Vel = { 40, 30};
int tit = 0;
int nearest = 0;
bool nextWall = false;
int wall = 0;
// ==== FUNCTIONS ========================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc; // this will hold the window class
MSG msg; // message container
HDC hDC; // handle to device context
//HDC memDC; // handle to memory device context for back buffer
HBITMAP memBM; // bitmap to size the memory device context
// Fill the window class structure
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = WINDOW_CLASS_NAME;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// Register the window class
if (!RegisterClassEx(&wc))
return 0;
// Create the window
if (!(g_hWnd = CreateWindowEx(NULL,
WINDOW_CLASS_NAME,
"GDI Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
WIN_LEFT, WIN_TOP,
WIN_WID, WIN_HGT,
NULL,
NULL,
hInstance,
NULL)))
return 0;
// Show the window
ShowWindow(g_hWnd, SW_SHOWDEFAULT);
UpdateWindow(g_hWnd);
// create an offscreen drawing surface
hDC = GetDC(g_hWnd);
memDC = CreateCompatibleDC(hDC);
memBM = CreateCompatibleBitmap(hDC, WIN_WID, WIN_HGT);
SelectObject(memDC, memBM);
ReleaseDC(NULL, NULL);
// --------------------------------------
ShowCursor(false);
bool quit = false;
// Enter main loop
while (!quit)
{
// handle messages first
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
quit = true;
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else // no messages? draw stuff
{
ClearScreen(memDC);
//######## DRAWING CODE GOES HERE #############################################
InitializLine(&OneLine);
// numbers on the screen to help understand whats going on
char Stir[40];
sprintf(Stir,"X reflect: %f", counter);
TextOut(memDC, 20, 40, Stir, (int)strlen(Stir));
char Stirr[40];
sprintf(Stirr,"Y reflect: %f", counter1);
TextOut(memDC, 20, 60, Stirr, (int)strlen(Stirr));
char String[40];
sprintf(String,"Ball hit: %f", counter2);
TextOut(memDC, 20, 80, String, (int)strlen(String));
char Strring[40];
sprintf(Strring,"Wall hit: %f", counter3);
TextOut(memDC, 20, 100, Strring, (int)strlen(Strring));
char Strringg[40];
sprintf(Strringg,"R click to change walls");
TextOut(memDC, 20, 120, Strringg, (int)strlen(Strringg));
char iinng[40];
sprintf(iinng,"L click new B loc");
TextOut(memDC, 20, 140, iinng, (int)strlen(iinng));
char ring[40];
sprintf(ring,"Ball p0.x: %f", gBall.p0.x);
TextOut(memDC, 200, 40, ring, (int)strlen(ring));
char rring[40];
sprintf(rring,"Ball p0.y: %f", gBall.p0.y);
TextOut(memDC, 200, 60, rring, (int)strlen(rring));
char fring[40];
sprintf(fring,"Ball p1.x: %f", gBall.p1.x);
TextOut(memDC, 200, 80, fring, (int)strlen(fring));
char frring[40];
sprintf(frring,"Ball p1.y: %f", gBall.p1.y);
TextOut(memDC, 200, 100, frring, (int)strlen(frring));
char ing[40];
sprintf(ing,"hitPT.x: %f", hitPT.x);
TextOut(memDC, 200, 120, ing, (int)strlen(ing));
char iing[40];
sprintf(iing,"hitPT.y: %f", hitPT.y);
TextOut(memDC, 200, 140, iing, (int)strlen(iing));
////////////////////////////////////////////////////
MoveBall();
//ClearScreen();
FakeCollision();
DrawWalls();
if(nextWall){
OneLine = gWallArray[wall];
wall++;
nextWall = false;
if(wall >= NUM_WALLS){
wall = 0;}}
Sleep(20);
//#############################################################################
// copy back buffer to screen
BitBlt(hDC, 0, 0, WIN_WID, WIN_HGT, memDC, 0, 0, SRCCOPY);
// lock to 60 fps
}
}
ShowCursor(true);
return (int)msg.wParam;
}
//-------------------------------------------------------------------------------
// Window's message procedure
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps; // used in WM_PAINT
HDC hDC; // handle to a device context
int i = 4;
if(i >= NUM_WALLS)
i = 0;
// Handle messages
switch (msg)
{
case WM_CREATE:
return 0;
break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
case WM_RBUTTONDOWN:
//GetCursorPos(&gCursorPos);
nextWall = true;
break;
case WM_LBUTTONDOWN:
gBall.p1.x = 250;
gBall.p1.y = 250;
break;
}
// Process any messages that we didn't take care of
return DefWindowProc(hWnd, msg, wParam, lParam);
}
//---------------------------------------------------------------------------------
// Function to clear the screen
void ClearScreen(HDC hDC)
{
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = WIN_WID;
rect.bottom = WIN_HGT;
FillRect(hDC, ▭, (HBRUSH)GetStockObject(BLACK_BRUSH));
}
//----------------------------------------------------------------------------------
void MoveBall(){
Vel.x = gBall.p1.x - gBall.p0.x;
Vel.y = gBall.p1.y - gBall.p0.y;
Vel = Normalize(Vel);
gBall.p1.x += Vel.x * 10;
gBall.p1.y += Vel.y * 10;
//trail of ball
gBall.p0.x += Vel.x * 9;
gBall.p0.y += Vel.y * 9;
InitializLine(&gBall);
int lastTime = 999;
int timex = 999;
int timexx = 999;
bool reflect = false;
tVector2 tempHit = {0, 0};
tVector2 tempHit2 = {0, 0};
tit = Intersection(gBall, OneLine, &hitPT);
double BallHit = hitTime(gBall, &hitPT);
counter2 = BallHit;
double WallHit = hitTime(OneLine, &hitPT);
counter3 = WallHit;
if(BallHit >= 0 && BallHit <= 1){
if(WallHit >= 0 && WallHit <= 1){
tempHit = Reflect(gBall.p1, OneLine.n);
// show reflection
tempHit2 = hitPT;
tempHit2.x = hitPT.x + (tempHit.x * 5);
tempHit2.y = hitPT.y + (tempHit.y * 5);
tempHit.x = hitPT.x + (tempHit.x * 2);
tempHit.y = hitPT.y + (tempHit.y * 2);
gBall.p1 = tempHit2;
gBall.p0 = tempHit;
counter = tempHit2.x;
counter1 = tempHit2.y;
RECT r;
GetClientRect(g_hWnd, &r);
int xOff = 0;
int yOff = (r.bottom - r.top);
HDC hDC = GetDC(g_hWnd);
HPEN hPen = CreatePen(PS_SOLID, 5, RGB(250,250,250));
SelectObject(hDC, hPen);
MoveToEx(hDC, (int)(hitPT.x + xOff), (int)(yOff - hitPT.y), NULL);
LineTo(hDC, (int)(tempHit2.x + xOff*1), (int)(yOff - tempHit2.y*1));
DeleteObject(hPen);
// left to right ball goes through wall
// and bot to top same
}}
// draw main ball
DrawCircle(memDC, (int)gBall.p1.x , (int)(gBall.p1.y), BALL_WIDTH, BALL_SPACING, BallColor);
// hitpt where the lines would connect
DrawCircle(memDC, (int)hitPT.x , (int)(hitPT.y), 10, BALL_SPACING, RGB(00,200,250));
RECT r;
GetClientRect(g_hWnd, &r);
int xOff = 0;
int yOff = (r.bottom - r.top);
HDC hDC = GetDC(g_hWnd);
HPEN hPen = CreatePen(PS_SOLID, 2, RGB(50,200,100));
SelectObject(hDC, hPen);
MoveToEx(hDC, (int)(gBall.p0.x + xOff), (int)(yOff - gBall.p0.y), NULL);
LineTo(hDC, (int)(gBall.p1.x + xOff), (int)(yOff - gBall.p1.y));
DeleteObject(hPen);
}
void DrawWalls()
{
HDC hDC = GetDC(g_hWnd);
DrawLine(hDC, OneLine.p0, OneLine.p1, RGB(255,239,0));
ReleaseDC(g_hWnd, hDC);
}
void DrawLine(HDC hDC, tPoint2 p0, tPoint2 p1, COLORREF color)
{
RECT r;
GetClientRect(g_hWnd, &r);
int xOff = 0;
int yOff = (r.bottom - r.top);
HPEN hPen = CreatePen(PS_SOLID, 2, color);
SelectObject(hDC, hPen);
MoveToEx(hDC, (int)(p0.x + xOff), (int)(yOff - p0.y), NULL);
LineTo(hDC, (int)(p1.x + xOff), (int)(yOff - p1.y));
DeleteObject(hPen);
}
void ClearScreen()
{
HDC hDC = GetDC(g_hWnd);
RECT rScreen = {0,0,WIN_WID,WIN_HGT};
FillRect(hDC, &rScreen, (HBRUSH)GetStockObject(BLACK_BRUSH));
ReleaseDC(g_hWnd, hDC);
}
void DrawCircle(HDC dc, int xOrg, int yOrg, int radius, int spacing, COLORREF color)
{
float x,y;
float rad = 0;
RECT r;
GetClientRect(g_hWnd, &r);
int xOff = 0;
int yOff = (r.bottom - r.top);
for(int i = 0; i < 360; i+= spacing)
{
rad = (i * 3.14159f) / 180.0f;
x = xOrg + (float)radius * cos(rad);
y = yOrg + (float)radius * sin(rad);
SetPixel(dc, (int)(x + xOff), (int)(yOff- y), color);
}
}
tVector2 Normalize(tVector2 A){
float mag;
mag = sqrt(A.x * A.x + A.y * A.y);
A.x/=mag;
A.y/=mag;
return A;
}
bool Intersection(tLineSeg &ball, tLineSeg &wall, tVector2 *hitPT){
//// check if wall or ball is a dot you cant solve
if(ball.p0.x == ball.p1.y && ball.p1.x == ball.p0.x){
return false;}
if(wall.p0.x == wall.p1.y && wall.p1.x == wall.p0.x){
return false;}
// if lines parallel
if(ball.m == wall.m){
return false;
}
// - If 1 line vertical, xHit = x from
//vertical line, use m & b from non-vertical line to find yHit.
if(ball.isVertical){
hitPT->x = ball.p1.x;
hitPT->y = ( wall.m * hitPT->x) + wall.b;}
else if(wall.isVertical){
hitPT->x = wall.p0.x;
hitPT->y= (ball.m * hitPT->x) + ball.b;
}
//- If 1 line horizontal, yHit = y from horizontal line, solve for xHit(already solved)
else if(ball.p0.y == ball.p1.y){
hitPT->y = ball.p0.y;
hitPT->x = (ball.b - wall.b)/(wall.m - ball.m);
}
else if(wall.p0.y == wall.p1.y){
hitPT->y = wall.p1.y;
hitPT->x = (ball.b - wall.b)/(wall.m - ball.m);
}
else {
hitPT->x = (ball.b - wall.b)/(wall.m - ball.m);
hitPT->y = (ball.m * hitPT->x) + ball.b;
}
return true;
}
void InitializLine(tLineSeg *A){
if(A->p0.x == A->p1.x){
A->m = 0;
A->b = 0;
A->isVertical = true;}
else{
A->m = (A->p1.y - A->p0.y)/(A->p1.x - A->p0.x); // slope
A->b = -(A->m * A->p0.x) + A->p0.y;
A->isVertical = false;
}
//
A->n.x = A->p1.y - A->p0.y;
A->n.y = -(A->p1.x - A->p0.x);
//A->n = Normalize(A->n);
}
float hitTime(tLineSeg A, tVector2 *hitPT){
float time = 0;
if(A.isVertical){
time = (hitPT->y - A.p0.y)/(A.p1.y - A.p0.y);}
else
time = (hitPT->x - A.p0.x)/ (A.p1.x - A.p0.x);
//(time <= 0 && time <= 1) means it hit inside line
return time;
}
tVector2 Reflect(tVector2 B, tVector2 W){
tVector2 tmp, tmp1;
// w already normalized
tmp = Normalize(W);
tmp1 =Normalize(B);
tmp1.x *= -1.0f;
tmp1.y *= -1.0f;
tmp1.x = 2 * (tmp.x * Dott(tmp, B)) - B.x;
tmp1.y = 2 * (tmp.y * Dott(tmp, B)) - B.y;
return tmp = Normalize(tmp1);
}
float Dott(tVector2 A, tVector2 B){
float tmp;
return tmp =(A.x * B.x + A.y * B.y);
}
float Vec2Mag(tVector2 v)
{
return sqrt(v.x*v.x + v.y*v.y);
}
void FakeCollision(){
if(gBall.p1.x > ( WIN_WID)){
int tmp = 0;
tmp = gBall.p1.x - gBall.p0.x;
gBall.p0.x = tmp + gBall.p1.x;
}
if(gBall.p1.x < 0){
int tmp = 0;
tmp = gBall.p1.x - gBall.p0.x;
gBall.p0.x = tmp - gBall.p1.x;
}
if(gBall.p1.y > ( WIN_HGT)){
int tmp = 0;
tmp = gBall.p1.y - gBall.p0.y;
gBall.p0.y = tmp + gBall.p1.y;
}
if(gBall.p1.y < 0){
int tmp = 0;
tmp = gBall.p1.y - gBall.p0.y;
gBall.p0.y = tmp - gBall.p1.y;
}
}
[scource]