Checking Multiple Objects with IsColliding

Started by Snake, Tue 19/05/2009 02:20:37

Previous topic - Next topic

Snake

Sorry for posting again on the same day - hell, I hate posting at all.

But there's just GOT to be an easier way to check whether an object is colliding with ten others - other than writing them all out in one big-arse "if and OR" statement... like I've been sitting here doing for the past ten minutes.

I'm hoping there's something like this;
Code: ags

if (object[1].IsCollidingWithObject(object[1,2,3,4,5,6,7,8,9,10])==1){
  //do stuff here
}


I also have to do this code for the ten other objects.

What I'm doing is seeing if any of the ten random objects are touching eachother. If they are, then push them over a bit.

The code I've got works, but it's just so damn tedious and time consuming, not to mention hideous. I'm not even through testing the first object and have 19 more to go (in all).

Any insight is much appreciated,

--Snake
Grim: "You're making me want to quit smoking... stop it!;)"
miguel: "I second Grim, stop this nonsense! I love my cigarettes!"

GarageGothic

#1
If all the objects are numbered sequentially, you can use a "while" loop (or rather, two of them):

Code: ags

int objectcount = 1; //because 1 is the first object in your example, but maybe you meant 0?
while (objectcount < 10) { //doesn't include object 10 since that will be the final second object we check against
  int secondobjectcount = objectcount+1;
  while (secondobjectcount <= 10) {
     if (object[objectcount].IsCollidingWithObject(object[secondobjectcount]) == true) {
       //DO STUFF
       }
     secondobjectcount++;
     }
  objectcount++;
  }


Of course this doesn't take into account that pushing an object away could make it collide with an already checked object, so they may overlap until next game loop (I take it this code is in your repeatedly_execute?). There's really no way around that without the code getting very complicated.

Snake

So far, that has seemed to work quite well. I honestly don't have a clue what you have done, but with further inspection I'm sure I'll understand it enough ;)

Thank you
Grim: "You're making me want to quit smoking... stop it!;)"
miguel: "I second Grim, stop this nonsense! I love my cigarettes!"

GarageGothic

Glad it works. I realize it looks a bit confusing at first, but it's actually quite simple once you understand the concept of "while" loops. Take your time to look it over and let me know if you have any questions. I can also explain it step-by-step to you if you want.

Trent R

#4
Not quite sure if it would work, but another option (though possibly too laggy for your use) may be to use SSH's Pixel Perfect Collision Detection.

[Edit]:I believe it has a IsCollidingWithAnything.
Quote
//  PPColliding.OWithAnything(Object *a);
//    Check if a is colliding with any object or character. Returns
//    the value of GetLocationType for the first collision found, but there
//    may be more than one collision.

~Trent
To give back to the AGS community, I can get you free, full versions of commercial software. Recently, Paint Shop Pro X, and eXPert PDF Pro 6. Please PM me for details.


Current Project: The Wanderer
On Hold: Hero of the Rune

GarageGothic

#5
Unless you truly need pixel perfect detection I wouldn't really recommend it. I wrote the original code that the PixelPerfect module is based on, and while it does work fine, it is a bit of a hack and can be quite slow depending on the size of the objects and how many of them are likely to collide at the same time.
Also, since you would have to run it once for each individual object, you would miss out on the "secondobjectcount = objectcount+1" optimization that I included in the script above (i.e. object 0 will check for collisions with objects 1-10, so object 1 doesn't need to check collisions with object 0 and so on, until object 9 only checks for collisions with object 10 since everything else has been taken care of earlier).

Edit: I just realized you could probably optimize it a bit further by only allowing one collision per game loop for each object (so once it has collided and gets pushed away it will not move again until the next loop). I'm not sure if this is desirable, but as it currently doesn't recheck collisions after being pushed away* it shouldn't make much of a difference. At the moment I'm too tired to figure out how best to implement this though.

* The reason it doesn't do this is that it would require quite a bit of extra code to avoid endless loop situations where an object gets stuck between two or more other objects, freezing and eventually crashing the game.

Trent R

Ahah. I wasn't even thinking of what was going on internally.... But I already knew that a while loop is the best and tried (and failed) to suggest an alternative. Oh well ;D

~Trent
To give back to the AGS community, I can get you free, full versions of commercial software. Recently, Paint Shop Pro X, and eXPert PDF Pro 6. Please PM me for details.


Current Project: The Wanderer
On Hold: Hero of the Rune

Snake

#7
I was going to use that module anyway for the actual gameplay, since, IsColliding checks the entire rectangle, ignoring the transparency. That is, of course, if this has been updated since then (I remember making the suggestion for a Pixel Perfect collision module years ago ('04/'05 maybe)... heh, for this very same game too).

I'll further explain what is happening so you have a better understanding on the effects of code or modules. Sorry for the lack of further explaination earlier on:

The resolution is 800x600 (I know I've religiously bashed this resolution in the past, but I feel it is well needed for this project).  In the beginning of each "stage" (before fade in), ten objects are randomly placed on the "field". The check to see whether these objects are too close to each other will only happen once, which is, in the very start of the room.
I see what you are saying now, GG, since the code you have created is in the repeatedly execute section, the module would slow it down being continuously checked throughout, even when it doesn't need to be.

Hmm... couldn't I make a variable to see whether they are all done? If so, I could stop the code from running. If any slow-down, this should end rather quickly, in theory.

//--EDIT--//
Come to think of it, the module would be used for something else entirely anyway. For the initial check of the objects being too close, I'd much rather it be checking the rectangle.
But anyway, terminating the code when it doesn't need to be run any more, since the objects won't be moving, would be the way to go anyway, wouldn't it?
Grim: "You're making me want to quit smoking... stop it!;)"
miguel: "I second Grim, stop this nonsense! I love my cigarettes!"

GarageGothic

#8
QuoteThe check to see whether these objects are too close to each other will only happen once, which is, in the very start of the room.

Oh, this is a different matter entirely. I thought this was some sort of physics thing where objects could bump into eachother. I wish you had mentioned it from the start since this is a much easier thing to do. You can still reuse part of the code, just move it to the EnterRoomBeforeFadeIn event and instead of bumping the object a few pixels away just keep randomizing its position until it no longer overlaps anything else.

Code: ags

//first a couple of utility functions

bool checkprevcollision(int objectid) {
  int secondobjectcount;
  while (secondobjectcount < objectid) {
    if (object[objectid].IsCollidingWithObject(object[secondobjectcount]) == true) return true;
    secondobjectcount++;
    }
  }

function randomizeposition(int objectid, int x, int y, int width, int height) {
  object[objectid].X = x + Random(width);
  object[objectid].Y = y + Random(height);
  }

function PositionObjects(int range) {
  int objectcount = 0;
  while (objectcount <= range) {
    randomizeposition(objectcount, 20, 20, 760, 560); //Just some random numbers for the bounding box
    while (checkprevcollision(objectcount) == true) {
      randomizeposition(objectcount, 20, 20, 760, 560);
      }
    objectcount++;
    }
  }


And just call PositionObjects(10) from the EntersRoomBeforeFadein event.

Edit: Doh, you posted while I was writing. Well, as long as what you've got is working, everything is fine  :).

Edit 2: Fixed some stupid mistakes in the code in case anybody wants to use it.

Snake

#9
Sorry, GG, I have this aweful habit and "fear" of letting in on too much of what I'm working on. It's just as silly as someone watermarking their screenshots in the GiP forum.

That is what I've been doing lately, I've just been randomizing their placement again if they are touching (instead of just pushing them over by 20 or so pixels), except from within the REP_EXEC.

I didn't realize that from within the BEFORE_FADEIN it would keep checking if they were touching or not before actually loading the room. Thanks, I've moved the code and it still works perfectly.

//--EDIT--//
While we're on the subject of randomizing objects on screen;
How can I randomize the other ten objects on the BOTTOM HALF of the screen?
Random(XX) allows only one number, and that is the maximum. How do I tell AGS I want them to be randomized within 350 and 550?
Grim: "You're making me want to quit smoking... stop it!;)"
miguel: "I second Grim, stop this nonsense! I love my cigarettes!"

NsMn

The second part is easy. Just write

Random(200)+350;

GarageGothic

See the randomizeposition utility function in the above code, it uses a bounding box that the coordinates are randomized within.

Snake

My God, this is almost histerical. That's what I've been trying to do, but foolishly been writing it like this:
Code: ags

object[11].Y=Random((200)+350);


Thanks, guys, once again.
Grim: "You're making me want to quit smoking... stop it!;)"
miguel: "I second Grim, stop this nonsense! I love my cigarettes!"

Snake

Sorry for the double post.

I'm just now realizing and remembering that IsColliding only checks the baselines.

GG, I've been messing with your code to add in AreThingsOverlapping, but nothing seems to be working.
Here's what I tried last:
Code: ags

     if (AreThingsOverlapping(objectcount, secondobjectcount) == true) {


It says: AreThingsOverlapping: Invalid parameter. I'm not sure I understand what I should be doing...
Grim: "You're making me want to quit smoking... stop it!;)"
miguel: "I second Grim, stop this nonsense! I love my cigarettes!"

Matti

I think you have to add 1000 to the object number (for whatever reason). This is what the manual says:

"THING1 and THING2 can either be a CHARID, or can be an object number PLUS 1000. So for example, passing EGO as THING1, and 1004 as THING2, will compare the character EGO with Object 4 in the current room."


Snake

That's strange. I just double checked the manual with IsColliding and it says it ONLY checks in rectangular form, which is what I want. But if that's the case, why are some objects still overlapping?
Grim: "You're making me want to quit smoking... stop it!;)"
miguel: "I second Grim, stop this nonsense! I love my cigarettes!"

GarageGothic

#16
I think the baseline issue is only with characters, in the AreThingsOverlapping section in the help file it even says:

QuoteCalling this function with both the parameters as objects is the same as calling Object.IsCollidingWithObject.

Which one of my scripts are you using, still the first one? If so the optimization* may be causing problems (as it checks for collision with items that haven't yet been positioned - depending of course on where you've put your randomization code), take a look at the new script that I posted and see how it only does collision checks with items already in place. But it would be even easier to troubleshoot if you posted your own code.

*The optimization is still there in the new code, so it's the same number of iterations, but it works backwards instead of forwards. I should really have done it like this from the start.

Snake

GG:
Yes, I was still using your first code. It seemed to be working fine but I sat down this morning and restarted the room a bundle of times and there were certainly times where the objects would over lap. I tried messing around with it but to no avail.

So what I've done now is deleted your code entirely (copied and pasted into notepad for backup of course) and am trying to use the new code that you had substituted.

I'm having a bit of a problem. Now, I'm using the new AGS so I'm still trying to get used to it - used 2.72 for too long.

For some reason I'm finding it very hard, frustratingly hard, to implement it into the game.

I'm assuming your entire code goes into the global script. I pasted everything at the bottom just like I would have with 2.72, but nothing is being recognized.

What in the hell am I doing wrong here?
Grim: "You're making me want to quit smoking... stop it!;)"
miguel: "I second Grim, stop this nonsense! I love my cigarettes!"

GarageGothic

#18
What exactly do you mean that "nothing is being recognized"?

The code doesn't need to be in the global script. If you only use it in a single room you can put it in the room script and call it from the room's EnterRoomBeforeFadein event (which needs to be below my code to be able to call the functions). If you use it in multiple rooms, put it in the global script and remember to add:

Code: ags
import function PositionObjects(int range);


to the global header. Again, call it from the EnterRoomBeforeFadein event in the room script.

Snake

Sorry - what I mean by "nothing's being recognized" is that secondobjectcount is an undefined token.

I tried again and remembered this time to include the import aspect of it (heh) and got the same thing.
Grim: "You're making me want to quit smoking... stop it!;)"
miguel: "I second Grim, stop this nonsense! I love my cigarettes!"

SMF spam blocked by CleanTalk