Author Topic: need help with collision function  (Read 3758 times)

0 Members and 1 Guest are viewing this topic.

Offline Merick

  • Atari ST
  • ***
  • Posts: 113
  • Karma: 7
    • View Profile
need help with collision function
« on: November 10, 2007 »
I've started working on a 2d game engine using opengl for the graphics, and the soil library (http://www.lonesock.net/soil.html) to load the images.

In the constructor for my sprite type it scans the image buffer for transparent pixels and uses the info to create a collision map. The collision map is an byte ptr which I'm filling with 0's for transparent pixels, and 1's for non-transparent pixels. After this is done I free the image buffer because once it's been uploaded to vram as an ogl texture the original buffer isn't needed anymore.

I've tested the c_map data and it looks like it's being input correctly, however my collision function has an error in the calculations somewhere thats making it so that a collision isn't registered until one object is halfway inside the other, and I just can't figure out why, anyone got any ideas? I tried asking about this on the FB board but haven't gotten any replies...

here's the code (you'll need to download soil to test it), and i'm also attaching the images I'm using to test it:

Code: [Select]
#include once "fbgfx.bi"
#include once "gl/gl.bi"
#include once "gl/glu.bi"
#include once "soil.bi"

#define FALSE 0
#define TRUE (Not FALSE)

type sprite
declare constructor(byval filename as string, byval make_c_map as integer = false)
declare destructor()

img as uinteger
width as integer
height as integer
depth as integer
x as single = 0
y as single = 0

c_map as byte ptr

declare sub blit(byval scale as single = 1.0, byval rot as single = 0)
end type

constructor sprite (byval filename as string, byval make_c_map as integer = false)
dim as ubyte ptr img_data = SOIL_load_image(@filename, @width, @height, @depth, SOIL_LOAD_RGBA)
img = SOIL_create_OGL_texture(img_data, width, height, depth,  SOIL_CREATE_NEW_ID, SOIL_FLAG_POWER_OF_TWO)

if make_c_map = true then
c_map  = allocate(height * width * sizeof(integer ptr))
dim as integer i, j, k = 0, offset = -1
for i = 0 to (height -1)*4 step 4
for j = 0 to (width -1)*4 step 4

offset = offset +4

if peek(img_data + offset) = 0 then
c_map[k] = 0
else
c_map[k] = 1
endif
k = k +1
next
next
endif
SOIL_free_image_data(img_data)

end constructor

destructor sprite ()
deallocate(c_map)
glDeleteTextures( 1, @img)
end destructor

Sub sprite.blit(byval scale as single = 1.0, byval rot as single = 0)

  glBindTexture (GL_TEXTURE_2D, img)
  glPushMatrix()
  glTranslated (x + ((width * scale)/2), y + ((height * scale)/2),-1)
 
  If rot <> 0 Then
    glRotatef(rot, 0, 0, -1)
  End If
 
  glBegin (GL_QUADS)
  glTexCoord2f (0, 0):glVertex2f (-(width * scale)/2, -(height * scale)/2)
  glTexCoord2f (1, 0):glVertex2f ( (width * scale)/2, -(height * scale)/2)
  glTexCoord2f (1, 1):glVertex2f ( (width * scale)/2,  (height * scale)/2)
  glTexCoord2f (0, 1):glVertex2f (-(width * scale)/2,  (height * scale)/2)
  glEnd
 
  glPopMatrix()
 
End Sub

function box_col (byref obj1 as sprite, byref obj2 as sprite) as integer
if ((obj1.x + obj1.width > obj2.x) and (obj1.x < obj2.x + obj2.width) and (obj1.y + obj1.height > obj2.y) and (obj1.y < obj2.y + obj2.height)) then
return true
else
return false
endif
end function

function pix_col (byref obj1 as sprite, byref obj2 as sprite) as integer
if box_col(obj1, obj2) = false then return false
dim as integer x_offset1 = 0,_
   x_offset2 = 0,_
   y_offset1 = 0,_
   y_offset2 = 0

dim as integer c_x1, c_x2, c_y1, c_y2

' calculate the coords and size of the overlapping area of the images
if obj1.x > obj2.x then
c_x1 = obj1.x
x_offset1 = 0
x_offset2 = obj1.x-obj2.x
else
c_x1 = obj2.x
x_offset1 = obj2.x-obj1.x
x_offset2 = 0
endif

if obj1.y > obj2.y then
c_y1 = obj1.y
y_offset1 = 0
y_offset2 = obj1.y-obj2.y
else
c_y1 = obj2.y
y_offset1 = obj2.y-obj1.y
y_offset2 = 0
endif

if (obj1.x + obj1.width) > (obj2.x + obj2.width) then
c_x2 = (obj2.x + obj2.width)
else
c_x2 = (obj1.x + obj1.width)
endif
if (obj1.y + obj1.height) > (obj2.y + obj2.height) then
c_y2 = (obj2.y + obj2.height)
else
c_y2 = (obj1.y + obj1.height)
endif

dim as integer x,y, c_w = c_x2-c_x1, c_h = c_y2-c_y1

' use the coords of the overlapping areas to scan the collision maps and use for pixel-perfect collision test
for y = 0 to c_h-1
for x = 0 to c_w-1
if (obj1.c_map[ (y+y_offset1) * (x+x_offset1) ]) + (obj2.c_map[ (y+y_offset2) * (x+x_offset2) ]) = 2 then return true
next
next
return false

end function

const s_x = 640
const s_y = 480
const s_c = 32

screenres s_x, s_y, s_c,,  FB.GFX_OPENGL or FB.GFX_MULTISAMPLE

open cons for output as #1


dim as sprite _
ball1 = sprite("testimg.png", true),_
ball2 = sprite("ball.png", true)


ball2.x = 200
ball2.y = 200
ball1.x = s_x/2 - ball1.width/2
ball1.y = s_y/2 - ball1.width/2

dim as single bgmove = -.05

glEnable (GL_TEXTURE_2D)
glEnable (GL_BLEND)
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glMatrixMode (GL_PROJECTION)
glLoadIdentity ()
glViewport(0, 0, s_x, s_y)
gluOrtho2D(0.0, s_x, s_y, 0.0)
glMatrixMode (GL_MODELVIEW)
glLoadIdentity ()

dim angle as single = 0


while not multikey(FB.SC_ESCAPE)
glClear(GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)

ball1.blit
ball2.blit

if multikey(FB.SC_UP) then ball2.y = ball2.y - .05
if multikey(FB.SC_DOWN) then ball2.y = ball2.y + .05
if multikey(FB.SC_LEFT) then ball2.x = ball2.x - .05
if multikey(FB.SC_RIGHT) then ball2.x = ball2.x + .05

if pix_col (ball1, ball2) then print #1, "true" else print #1, "false"

flip
wend

close #1


Offline rain_storm

  • Here comes the Rain
  • DBF Aficionado
  • ******
  • Posts: 3088
  • Karma: 182
  • Rain never hurt nobody
    • View Profile
    • org_100h
Re: need help with collision function
« Reply #1 on: November 10, 2007 »
I'm getting syntax errors related to declare constructor what version of FB are you using?
I took a look at the code it may be because of this

for y = 0 to c_h-1
for x = 0 to c_w-1

perhaps sometimes it is required to use step -1

Challenge Trophies Won:

Offline Shockwave

  • good/evil
  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 17409
  • Karma: 498
  • evil/good
    • View Profile
    • My Homepage
Re: need help with collision function
« Reply #2 on: November 10, 2007 »
I haven't run your code as I don't have the soil library, I have written collision routines though so perhaps I can be of a little help..

If my post doesn't help, someone else will be along in a while to do so I am sure :)

Firstly in the beginning, precalculate your masks as you have been doing as 1's and 0's.

So transparrent pixels are 0 and filled pixels are 1.

To optimise your collision detection routine, use bounding box method to eliminate a lot of pixel checking that would not be needed.

Then the way I would do it is to have a collision buffer... Or two of them actually (will become clear).

The collision buffers need only be one dimensional as you can just check one line at a time and exit the process if a collision is detected (and you get a speed gain too this way as you'll probably not need to check all lines of a sprite ;) )

Anyway, if the sprites in your game are all the same size it's easier, if not it's still straight forward.

Lets say all your sprites are 20*20

define the collision buffer as two seperate arrays of 40 elements.
Now you need to load the collision buffers with the correct lines of your masks.

So let's say sprite 1 is at screen x pos 100 and sprite 2 is at screen x pos of 110

Work out which one is leftmost.

In this case it's sprite 1

So the first 20 elements of buffer one are going to be the mask pixels of sprite 1
Then the next sprite is positioned 10 pixels to the right.
So the second collision buffer will have elements 10 - 30 loaded with the sprite mask of sprite 2.

Bear in mind here that I am not specifying which lines of the collision mask to load in, you will need to calculate this offset too as the sprites will likely be on different heights in the screen. (You only want to check the parts of them that overlap).

Now you should have two arrays loaded with bits from a mask.

Convert the elements in each array into 2 seperate binary numbers and perform an and operation on them.

If the result is one then there has been a collision.

It's a little bit sketchy because it's off the top of my head but I hope it helps you along.
Shockwave ^ Codigos
Challenge Trophies Won:

Offline Merick

  • Atari ST
  • ***
  • Posts: 113
  • Karma: 7
    • View Profile
Re: need help with collision function
« Reply #3 on: November 10, 2007 »
lol Shockwave, I think I'm actually already doing that bounding box thing (I just didn't know what it was called) in my function, that's what all the c_x1, c_x2, etc.. variables are for. The problem is that I want the collision buffers to be part of the sprite type, but because FB doesn't yet support dynamic arrays inside types I've been having to use memory pointers instead or regular arrays and I'm having trouble figuring out the correct formula to get to the specific points inside the buffers that I need to check.

rain_storm, I'm using the latest version - about once a week I download the daily build from the windows svn page: http://ecowles.dyndns.org/fbdu/

the for y = 0 to c_h-1 and for x = 0 to c_w-1 is to set up loops for the height and width of the bounding box, which should never have negative numbers so why would I need to step backwards?

Offline Stonemonkey

  • Pentium
  • *****
  • Posts: 1315
  • Karma: 96
    • View Profile
Re: need help with collision function
« Reply #4 on: November 10, 2007 »
Hi Merick,

first thing, you're allocating space for integer pointers when it should be bytes so:
Code: [Select]
c_map  = allocate(height * width * sizeof(byte))

you could also use 'New' instead as i found out recently

Code: [Select]
c_map = new byte[ height * width ]
'instead of using deallocate to clean up you'd use delete

and to access the memory from your pointer either way you use:

Code: [Select]
obj1.c_map[ (y+y_offset1) * width + (x+x_offset1) ])

afaics anyway.

Cheers, Fryer.

Offline Merick

  • Atari ST
  • ***
  • Posts: 113
  • Karma: 7
    • View Profile
Re: need help with collision function
« Reply #5 on: November 10, 2007 »
Thanks, I forgot to change my allocation statement when I changed the ptr type. As for accessing, I tried changing my check statement to this:

Code: [Select]
if ( (obj1.c_map[ (y+y_offset1) * width + (x+x_offset1) ]) + (obj2.c_map[ (y+y_offset2) * width + (x+x_offset2) ]) = 2 ) then return true
But it just makes the program crash

*edit* oopss of course it'll crash, i keep forgetting that "width" is a function in FB

« Last Edit: November 10, 2007 by Merick »

Offline Stonemonkey

  • Pentium
  • *****
  • Posts: 1315
  • Karma: 96
    • View Profile
Re: need help with collision function
« Reply #6 on: November 10, 2007 »
and the width may be different for both sprites.

Offline Merick

  • Atari ST
  • ***
  • Posts: 113
  • Karma: 7
    • View Profile
Re: need help with collision function
« Reply #7 on: November 10, 2007 »
got it:

Code: [Select]
if ( (obj1.c_map[ (y+y_offset1) *obj1.width + (x+x_offset1) ]) + (obj2.c_map[ (y+y_offset2) * obj2.width + (x+x_offset2) ]) = 2 ) then return true

works perfect now, thanks!