Mapping multiple area hotspots within sprites / objects

Started by Ryan Timothy B, Thu 24/01/2008 02:05:12

Previous topic - Next topic

Ryan Timothy B

Ok if this has already been asked, I apologize.  Nothing more frustrating then answering the same old questions over and over.

I'll start with an example, lets say I have a bookshelf, and this bookshelf is a sprite/object (because it isn't static).  Is it possible to have areas of that sprite that are lets say, a radio, a book, etc.  I want each area to interact differently as though they were an area hotspot on a BG.

I was hoping I wouldn't have to have a bookshelf as one object, and then place new objects of the radio, or book, etc over top (because these objects are only interactions, they will not be picked up).

Gilbert

This is not possible unless you do this via scripting checking which region of the sprite is clicked (checking coordinates, etc.).

The best (easiest) way is still to use multiple objects for that.

Khris

Yep, and if your game features some sort of action line ("look at book") or the cursor e.g. turns into a hand above the radio, using more than one object will prevent you from having to script the behavior manually, too.

And just in case you didn't know, place the baselines of the small objects slightly below the bookshelf's so they appear in front of it.

Baron

In my opinion, it would be easiest to just have one object and use Gilbot's co-ordinate coding for different interactions.  You say that the image won't be static, so it would be a pain to co-ordinate all the different objects in terms of simultaneous movement when the only benefit would be simpler coding.  Since the objects won't leave/change the coding approach seems best.

So in your room script under, say, interact with object (the whole bookcase) you'd have some conditional coding such as:

Code: ags
 if (mouse.Y <= oBookshelf.Y + 20)  Display ("The Radio can't be turned on.");  //the top shelf
if ((mouse.Y >= oBookshelf.Y +21) && (mouse.Y <= oBookshelf.Y +40)) { Display("That book is too boring to take."); //the next shelf down...


So the proper message will be returned depending on where your mouse was when the interaction click occurred, relative to the bookshelf's current location (remember object coordinates are measured from the upper corner, while character coordinates from the middle of the bottom  ::) ).  Of course you'd have to line the coordinates up so that the correct messages would be displayed, that's the finicky bit....

Baron

Ryan Timothy B

Hey Baron, if I used your method, how would I let the @overhotspot@ know it's on a 'book' or 'radio' etc? (i have it display in text on screen below mouse)

See I have on this shelf only a handful of items.  But when the mouse is over the shelf, I don't want it to just say "shelf", that makes the game boring. 
And to make objects for each interaction would also slow the game down (from what I've read on other threads), and the limit was 25 i think?  I know right now with this particular shelf and room, I won't be passing over that limit.
But, this shelf is (without giving away any parts of my game) lets just say, it's falling over backwards.  If I had all the items on the shelf as their own objects, I'd have to erase them, then replace the shelf with the image of the items on it, when the time comes for the animation for the shelf to fall over.

I'm not only asking for just this room of the game, If there is a way to do this, I'll be doing it throughout the game over and over.

Code: ags
if (mouse.Y <= oBookshelf.Y + 20)  Display ("The Radio can't be turned on.");  //the top shelf
if ((mouse.Y >= oBookshelf.Y +21) && (mouse.Y <= oBookshelf.Y +40)) { Display("That book is too boring to take."); //the next shelf down...


I haven't tried this yet, no point unless I find out if I can change @overhotspot@ to display anything other then the "shelf" text. -- So if I did this, I'd be inserting the @overhotspot@ value in there (imagining i can make a rectangle)?

Code: ags
if (mouse.Y>=oBookshelf.Y+10 && mouse.Y<=oBookshelf.Y+20 && mouse.X>=oBookshelf.X+5 && mouse.X<=oBookshelf.X+15 {
 display("coolest book in the world.. oh yeah!");
 //then insert whatever the code is to change the @overhotspot@ value?
}


I imagine I'd still have "shelf" and not "book".  Because the objects are probably ran after the script, more than likely replacing the "book" to "shelf".  But I have no clue, I'm new to this and haven't fully explored the order of operations.

Khris

See, that's exactly what I was talking about.
Of course you *can* workaround the @OVERHOTSPOT@ issue, but it's going to get more and more of a hassle.

If you still want to stick to Baron's method, do this:
First, create a global function to determine if the mouse is above the radio/book/etc or just the shelf:

Code: ags
// top of global script

function room3_shelf(int xo, int yo) {
  if (xo>10 && xo<15 && yo>3 && yo<20) return 1; // book
  if (xo>30 && xo<50 && yo>23 && yo<33) return 2; // radio
  return 0;  // none
}

Import this in the global header: "import function room3_shelf(int xo, int yo);" to be able to use it in room scripts.

In the room script, when e.g. interacting with the shelf:
Code: ags
function shelf_Interact() {
  int o = room3_shelf(mouse.x - shelf.X, mouse.y - shelf.Y); // get object
  if (o==0) player.Say("I can push it, but why would I?");
  if (o==1) player.Say("That book is too boring to take.");
  if (o==2) player.Say("The Radio can't be turned on.");
}


As for @OVERHOTSPOT@, that has to be adjusted in the global script's repeatedly_execute because it has to be updated constantly:
Code: ags
// global script
function repeatedly_execute() {

  String s = "@OVERHOTSPOT@";
  Object*o = Object.GetAtScreenXY(mouse.x, mouse.y);
  if (player.Room == 3 && o != null && o.ID == 2) {     // assuming shelf is obj nr. 2 in room 3
    int ob = room3_shelf(mouse.x - o.X, mouse.y - o.Y);  // mouse @ radio or book?
    if (ob == 1) s = "book";
    if (ob == 2) s = "radio";
  }
  hotspotlabel.Text = s;   // set the label's text
}


Now imagine adding such a function and those few lines in rep_ex for each "multiple object object" and compare it to adding the stuff as actual objects, like I suggested.
Making the bookcase fall over isn't any different because a) you can keep the radio/book in the shelf's sprite because those are covered by their objects anyway (this will help placing them correctly in the editor, too, btw) and b) you simply turn off the objects before animating the falling bookcase.

Regarding the situation, having multiple objects is clearly the way to go here.

PS:
Quote from: ryanb84 on Fri 25/01/2008 02:22:31And to make objects for each interaction would also slow the game down (from what I've read on other threads), and the limit was 25 i think?
The limit is 40 objects per room (AGS3.0). You won't notice any slowdown unless you have multiple transparent as-big-as-the-screen objects.
If you really ran out of objects in one room, you could use characters alternatively.

Baron

If the bookshelf just has to do one animation, and not be floating around, then KhrisMUC is right: It would be much easier just to have the elements on the shelf as separate graphics overlapping identical graphics drawn on the bookshelf object.  Then, when the time comes for the bookcase to fall over:

Code: ags

oRadio.Visible =false;
oDictionary.Visible =false;
oEverythingelse.Visible =false;  //etc.
oBookshelf.SetView (int view, optional int loop, optional int frame); //where view and loop refer to your falling over animation.


Now say later on, if you want to have objects animating on their shelves separately, you could just switch the bookcase object graphic at runtime to a copy of your bookcase that has been left empty, so that the duplicated object graphics wouldn't show behind the movements of the objects.  The trick to this is to draw frames around your bookshelf graphics so that when you import them they are exactly the same size.  Er... don't import the frames, just everything they enclose.  Then I think the command for switching the graphic is oBookshelf.Graphic = sprite slot;    None of this will be visible to the player since it happens instantly.  And then, when it is time for the falling over animation, you switch the graphic back (probably by setting its view, for the animation).

Baron

Ryan Timothy B

Thanks guys.

I never actually thought of having a shelf with items already on it before <just overlapping with objects>.  Probably due to the 320x240 or whatever the object grid is, while the image is 640x480.  Makes lining things up quite tedious.

For this application I think I'm going to use the objects instead of the programming route.  But I will more than likely use the other approach as well later on.

Thank you again.

Khris

In your paint program, turn on a grid of 2x2. Whenever you select one of the objects-to-be to import as a sprite, make sure the sides of the selection are all on grid lines. Then you shouldn't have any trouble aligning the objects in the editor (as long as you don't crop the sprite in AGS afterwards).

SMF spam blocked by CleanTalk