Original Post
EDIT: WHoops! I forgot to tell you. This game is made using DarkGDK, MS Visual C++, and DirectX Hey everyone, I've been working on trying to get this pixel perfect collision function to work for days and I can't seem to get it. I can only get pixel perfect when I move a sprite into the top left quadrant of another sprite, everywhere else is kinda weird. NOTE: All sprite coordinates are at top left corner of sprite. So when I say dbSprite(i, 1); this will move sprite with ID "i" to the right 1 pixel Here is the beta version of "Pixel Perfect Collisions" Headers/Prototypes Main Sprite Mask Pixel Collision [Edited by - dhardin on April 4, 2010 1:06:18 PM]
#include "DarkGDK.h"
#include <vector>
#if _MSC_VER > 1020 // if VC++ version is > 4.2
using namespace std; // std c++ libs implemented in std
#endif
bool BuildSpriteMask(int s1, vector<int> &v);
bool PixelCollision( int s1, int s2 );
void DarkGDK ( void )
{
// when starting a Dark GDK program it is useful to set global
// application properties, we begin by turning the sync rate on,
// this means we control when the screen is updated, we also set
// the maximum rate to 60 which means the maximum frame rate will
// be set at 60 frames per second
dbSyncOn ( );
dbSyncRate ( 60 );
// a call is made to this function so we can stop the GDK from
// responding to the escape key, we can then add in some code in our
// main loop so we can control what happens when the escape key is pressed
dbDisableEscapeKey ( );
dbLoadImage ( "land.bmp", 1 );
dbSprite ( 1, 0, 0, 1 );
dbLoadImage("collisionTXT.bmp", 3);
dbLoadImage ("circle1.bmp", 4);
dbLoadImage("circlePlayer.bmp",2);
dbSprite (4, 300, 100, 4);
dbSprite(3,0,0,3);
dbSprite ( 2,0, 0, 2 );
// now we come to our main loop, we call LoopGDK so some internal
// work can be carried out by the GDK
while ( LoopGDK ( ) )
{
if(dbRightKey() == 1)
{
dbRotateSprite(2,90);
dbMoveSprite(2,1);
dbRotateSprite(2,0);
}
if(dbLeftKey() == 1)
{
dbRotateSprite(2,270);
dbMoveSprite(2,1);
dbRotateSprite(2,0);
}
if(dbUpKey())
{
dbMoveSprite(2,1);
}
if(dbDownKey())
{
dbRotateSprite(2,180);
dbMoveSprite(2,1);
dbRotateSprite(2,0);
}
if(PixelCollision(2,4))
{
dbShowSprite(3);
}
else
{
dbHideSprite(3);
}
if ( dbEscapeKey ( ) )
break;
dbSync();
// here we make a call to update the contents of the screen
}
// when the user presses escape the code will break out to this location
// and we can free up any previously allocated resources
// delete all the sprites
for(int i = 1; i < 5; i++)
dbDeleteSprite ( i );
// delete the backdrop image
dbDeleteImage ( 1 );
// and now everything is ready to return back to Windows
return;
}
bool BuildSpriteMask(int s1, vector<int> &v)
{
bool result = FALSE;
LPDIRECT3DTEXTURE9 pTexture = dbGetImagePointer(s1);
// Lock the texture:
D3DLOCKED_RECT rect;
HRESULT hResult = pTexture->LockRect(0, ▭, NULL, D3DLOCK_READONLY);
if(FAILED(hResult))
{
// Failed to lock the texture, error
return 0;
}
// Get the bits as a BYTE*
const BYTE* pBits = (BYTE*)rect.pBits;
// Seek to the correct pixel and read it
DWORD nBytesPerPixel = 4;
const DWORD* pPixel = 0; // (DWORD*)(pBits + y * rect.Pitch + x*nBytesPerPixel);
//DWORD dwPixel = *pPixel;
int sh = dbSpriteHeight(s1);
int sw = dbSpriteWidth(s1);
for(int y = 0; y < sh; y++)
{
for(int x = 0; x < sw; x++)
{
/* NOTE: I'm a little rusty on my math, but it should just be the same as before */
pPixel = (DWORD*)(pBits + y * rect.Pitch + x*nBytesPerPixel);
if( *pPixel == D3DCOLOR_XRGB(255,0,255))
{
v.push_back(0);
}
else
{
v.push_back(1);
}
}
}
// Unlock the texture
pTexture->UnlockRect(0);
return result;
}
bool PixelCollision( int s1, int s2 )
{
/*
Load the Sprite1/Sprite2 variables into local memory so that we
can avoid the extra overhead required for multiple calls to the
related GDK calls. Functions are slower than memory reads, so
we want to call them as few times as possible...
*/
int s1x=dbSpriteX(s1);
int s1y=dbSpriteY(s1);
int s1w=dbSpriteWidth(s1);
int s1h=dbSpriteHeight(s1);
int s2x=dbSpriteX(s2);
int s2y=dbSpriteY(s2);
int s2w=dbSpriteWidth(s2);
int s2h=dbSpriteHeight(s2);
int s1x2 = s1x+s1w;
int s1y2 = s1y+s1h;
int s2x2 = s2x+s2w;
int s2y2 = s2y+s2h;
/* Calculates the intersection rectangle between the two sprites */
int minX = max(s1x, s2x);
int maxX = min(s1x2, s2x2);
int minY = max(s1y, s2y);
int maxY = min(s1y2, s2y2);
/*
if our instersection rect's Width is greater than the combined
widths of both sprites, then no collision could have taken
place since there would be at least one pixel between both
sprites
*/
if (s1x2 < s2x || s2x2 < s1x)
return false;
/*
Similarly, if our instersection rect's height is greater than
the combined heights of both sprites, then no collision could
have taken place since there would be at least one pixel between
both sprites
*/
if (s1y2 < s2y || s2y2 < s1y)
return false;
/*
Now we know that there is still a potential for collision. In
order to find out for sure, we have to test every pixel between
both sprites that fall within the intersection rect - we can
ignore all of the others.
*/
/*
First, we have to build the sprite masks of the images. To
gain a performance boost, we wait until this point to actually
build them because there was possibilities of no collisions
that could be eliminated before wasting time reading video
memory. Alas, we can't put it off any longer...
Note: Ideally, the sprite masks would be built just once and
stored in memory so that repeated calls to BuildSpriteMask()
wouldn't be necessary...
*/
vector<int> spriteOneVect;
vector<int> spriteTwoVect;
/*
We place a single call to BuildSpriteMask() for each sprite
so that we only have to lock the video memory once instead of
once for each pixel...
*/
BuildSpriteMask( s1, spriteOneVect);
BuildSpriteMask( s2, spriteTwoVect);
/* Now that the sprite masks are built, let's test them */
/*FIRST: check to see what direction we are coming from
Think of this in quadrants (We'll start in the top left,
then to top right, then bottom left, then bottom right*/
int s1XAP;
int s1YAP;
int s2XAP;
int s2YAP;
for(int currentY = minY; currentY < maxY; currentY++)
{
s1YAP = currentY - s1y;
s2YAP = currentY - s2y;
for(int currentX = minX; currentX < maxX; currentX++)
{
s1XAP = currentX - s1x;
s2XAP = currentX - s2x;
if(spriteOneVect[s1XAP*s1YAP] == spriteTwoVect[s2XAP*s2YAP] && spriteOneVect[s1XAP*s1YAP] == 1)
return true;
}
}
return false;
}