iDevGames Forums

Full Version: Image Collison for Unknown Curves
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I just started working on the 'terrain' section of my 2d-sidescrolling platformer; and have run into a bit of a problem.
I'm using collison detection between a character's image and the terrain image, which has an unkown curve. I can get the character to land and walk around on a flat surfece, but I have NO idea how I would do//test this with unkown curved terrain objects. Any Suggestions?

Thanks
If you have an array of points, you could test the y coordinate of the terrain for the x coordinate of the player to the y coordinate of the player. If the y coordinate of the player is <= the y coordinate of the terrain, you've collided. If you don't have such an array, it could be useful to create one.
Wow that could be.... very complex. Is there any easier way?
What exactly do you mean by an "unknown" curve? Surely you know the shape of the terrain, how else would you draw it?

The two best ways to handle 2D terrain collisions in general is the way that akb825 said, or split your terrain into short line segments and collide your objects against those.
So if I understand you correctly, you have arbitrary images of the terrain that you need to walk around on? In that case, you need to analyze the alpha mask of those images and decide that you can only walk on pixels that have an alpha of 100% or something like it.

However, this is not practical. I'd go with Skorche's suggestion of line segments. If you check out El Ballo in my signature, I used separate polylines for collision detection, and drew arbitrary images in the viewport.
I happen to have just the code for you. Unfortunately it's in Java, but you should be able to get something working.

Code:
class sprite
{
  boolean[][] bitMask;
  PImage[] img=new PImage[16];
  float x,y;
  int w,h,ix,iy,bcs;
  float fps;
  int frames;
  float currentFramef=0;
  int currentFramei=0;
  boolean loop=true;
  boolean visable=true;
  int upper=0;
  int lower=0;
  
  sprite(String _img)
  {
    newSprite(_img,0,0,30);
  }
  sprite(String _img, float _x, float _y)
  {
    newSprite(_img,_x,_y,30);
  }
  sprite(String _img, float _x, float _y,float _fps)
  {
    newSprite(_img,_x,_y,_fps);
  }
  void newSprite(String _img, float _x, float _y, float _fps)
  {
    x=_x;
    y=_y;
    img[0]=loadImage(_img);
    bcs=int(sqrt(sq(img[0].width)+sq(img[0].height))/2);
    w=img[0].width;
    h=img[0].height;
    ix=int(x);
    iy=int(y);
    fps=_fps;
    frames=1;
    upper=frames;
    
    bitMask=new boolean[16][w*h];
    img[0].loadPixels();
    for(int j=0;j<h;j++){
      for(int i=0;i<w;i++){
          if(alpha(img[0].pixels[j*w+i])==255) bitMask[0][j*w+i]=true; }}
  }
  void addFrame(String _img)
  {
    if(frames==16){ println("Max Frames Exceeded"); return; }
    img[frames]=loadImage(_img);
    img[frames].loadPixels();
    for(int j=0;j<h;j++){
      for(int i=0;i<w;i++){
          if(alpha(img[frames].pixels[j*w+i])==255) bitMask[frames][j*w+i]=true; }}
    frames++;
    upper=frames;
  }
  void set(float _x, float _y)
  {
    x=_x;
    y=_y;
    ix=int(x);
    iy=int(y);
  }
  void move(float _x, float _y)
  {
    x+=_x;
    y+=_y;
    ix=int(x);
    iy=int(y);
  }
  boolean collides(sprite that)
  {
    if(sq(x-that.x)+sq(y-that.y)>sq(bcs+that.bcs)) return false;
    if (iy > that.iy+that.h) return false;
    if (iy+h < that.iy) return false;
    if (ix+w < that.ix) return false;
    if (ix > that.ix+that.w) return false;
    
    // Ok, compute the rectangle of overlap:
    int over_bottom,over_top,over_right,over_left;
    if (iy < that.iy) over_bottom = that.iy;
    else over_bottom = iy;

    if (iy+h > that.iy+that.h) over_top = that.iy+that.h;
    else over_top = iy+h;

    if (ix+w > that.ix+that.w) over_right = that.ix+that.w;
    else over_right = ix+w;

    if (ix < that.ix) over_left = that.ix;
    else over_left = ix;
    over_right-=over_left;
    over_top-=over_bottom;
    
    //fill(0,255,0,128);
    //rect(over_left,over_bottom,over_right,over_top);
    
    int thix, thiy, thax, thay;
    thix=over_left-ix;
    thiy=over_bottom-iy;
    
    thax=over_left-that.ix;
    thay=over_bottom-that.iy;
    
    /*for(int j=0;j<over_top;j++)
      for(int i=0;i<over_right;i++)
        if(bitMask[(j+thiy)*w+i+thix]) point(thix+ix+i,thiy+iy+j);*/
    for(int j=0;j<over_top;j++)
      for(int i=0;i<over_right;i++)
        if(bitMask[currentFramei][(j+thiy)*w+i+thix]&bitMask[that.currentFramei][(j+thay)*w+i+thax]) return true;
    
    return false;
  }
  void reset()
  {
    visable=true;
    currentFramef=0;
    currentFramei=0;
  }
  void cap(int _min,int _max)
  {
    lower=_min;
    upper=_max+1;
  }
  void setFrame(int _frm)
  {
    currentFramef=_frm;
    currentFramei=_frm;
  }
  void onlyFrame(int which)
  {
    currentFramef=which;
    currentFramei=which;
    lower=which;
    upper=which+1;
  }
  void draw()
  {
    int mn=max(0,lower);
    int mx=min(upper,frames);
    if(!visable) return;
    image(img[currentFramei], x, y);
    currentFramef+=(float)fps/(float)framerate;
    if(loop==true) currentFramef=mn+(currentFramef-mn)%(mx-mn);
    else if(currentFramef%frames>mx) visable=false;
    currentFramei=int(floor(currentFramef));
    
    //stroke(255,0,0);
    //noFill();
    //ellipse(x+img[currentFramei].width/2,y+img[currentFramei].height/2,bcs*2,bcs*2);
    //rect(ix,iy,w,h);
    /*for(int j=0;j<h;j++)
      for(int i=0;i<w;i++)
          if(bitMask[j*w+i]) point(ix+i,iy+j);*/
  }
}

The bit you're interested in is the function "boolean collides(sprite that)"
(ix,iy) is the position of the sprite (w,h) are the dimensions bcs is the bounding circle radius. bitMask[currentFrame][pixels] is a boolean collision map of the sprite created in newSprite from the alpha mask.

Ask if you have any questions.
Grin
Thanks Guys

Quote:What exactly do you mean by an "unknown" curve? Surely you know the shape of the terrain, how else would you draw it?

I actually don't know the curve, since I'm just drawing an picture to the screen and working with that picture.

I think splitting the terrain up might look a little bit too wierd for the stuff I'm trying to do, but it might just have to do if nothing else works.

Joseph I'll give your code a shot, it looks promising.

Thanks for the help guys! Wink
Shunter,
Take a look at what I'm doing in El Ballo.
http://www.elballo.com/postmortem/el_ballo_igda.pdf (Not sure about the page number, it's #2 of the Bad Stuff. There's an image of the editor where you can see the polylines overlaid on the graphics.)

I've been trying to find a better screenshot that shows off what I'm getting at, but I can't find them. :-/ The basic technique is, anyway, that you can have any graphic you want, and then just create arbitrary line strips for collision. Keep asking if you want more of a clarification. Smile
Yeah, OK, I get that. Its a good idea, but...

I'd like to be able to do it without having to manually work on each picture. I think I found out how to do it with shadow collisons steps. (ie testing for collison on either side of the characters and real-time configuring for the slope of the curves)

I'll keep you posted, thanks for the help.
Haha I found a better way. Grin

Its almost like your method Ivan, but instead of configuring each image manually, I wrote a function to simply peek through the alpha mask and find the first vertical appearance of a "ground spot", while loading the game. It then takes those coordinates and stores them in the object's array. Since I only have to take a vertical test every hundered pixels or so, it doesn't hinder the loading time much. Although I only have a few images now, nevermind a few hundered :/
Ok, so I got it up to where I saw in your editor, Ivan.
I have another question though, what's the best way to check for "collision" between the character and the line?
You don't want to make to big a gap, or he'll stay too far above it, and if it's too small, he might go through it.
Reference URL's