SOLVED: Ignoring transparent pixels on GUI Buttons for Click / Mouseover label

Started by Privateer Puddin', Tue 17/04/2018 05:43:14

Previous topic - Next topic

Privateer Puddin'

Hello,

I have a custom inventory GUI where I am using buttons for each item. On each button is an image, such as:



The issue being when I click

Code: ags
GUI.ProcessClick(mouse.x, mouse.y, eModeInteract);


or mouse over

Code: ags
(GUIControl.GetAtScreenXY(mouse.x, mouse.y);)


it takes into account the whole button, including the grey transparent area (unless I have another button on top z order wise)

Is there a way to ignore the transparent pixels?

I did search the forums and came across a couple of suggestions and wanted to know if these would be the route to go down, or if there's something better?

http://www.adventuregamestudio.co.uk/forums/index.php?topic=28882.msg367578#msg367578

http://www.adventuregamestudio.co.uk/forums/index.php?topic=44095.msg587336#msg587336 - I guess this one only works if the graphic is a certain shape

Thanks!

Snarky

Yeah, AFAIK there's no built-in way. If you're using magic pink for transparency you could probably do a getpixel() on the sprite, but with alpha that gets more complicated.

The dummy character and pixel-perfect click detection hack is probably your best bet.

Privateer Puddin'

I did take a quick look at the GetPixel stuff. Am I right in thinking it would return the pixel colour of any buttons behind it on the transparent section? Edit: No, it's picking up the colours from the room background behind the GUI

In theory, if my inv items was made up of three (unique? does it matter if I'm doing the first btn check? and I know the button behind is 99% not going to match) colours, could I do something like...

Code: ags

if (theControl == btnHose) && ((surface.GetPixel(mouse.X, mouse.Y)) == COLOURA / COLOURB / COLOURC)   //mouse is over button, and not a transparent pixel
 lblHotspot.Text = "Hose";
} else {
// do nothing, since it's not over button / transparent pixel
}

(I know the operator bit isn't right)


Snarky

I meant GetPixel() on the sprite itself, or rather, on its DrawingSurface. Copy the sprite to a DynamicSprite, get a  DrawingSurface for it, and call GetPixel().

Privateer Puddin'

Right! I'm not very good with DrawingSurface etc. so I've given this a go, and it seems to work..

Code: ags

if (theControl == btnLunchbox) { //mouseover lunchbox inv item
    DynamicSprite* sprite = DynamicSprite.CreateFromExistingSprite(1183, false); //store sprite of inv item with pink bg no transparancy
    DrawingSurface *surface = Room.GetDrawingSurfaceForBackground(); //draw surface
    surface.DrawImage(btnLunchbox.X, btnLunchbox.Y, sprite.Graphic); //draw inv item on surface
      if (surface.GetPixel(mouse.x, mouse.y) == 63519) { //check if mouse is over pink transparency colour
        btnLunchbox.NormalGraphic = 1125; // set normal version of inv item graphic
        lblHotspot.Text = ""; // clear label
        surface.Release(); 
        sprite.Delete();
      } else {
        btnLunchbox.NormalGraphic = 1126; //mouse is over inv item, so set mouseover graphic
        lblHotspot.Text = "Lunchbox"; //mouse is over inv iem, so set label
      }


I'm also putting the  btnLunchbox.NormalGraphic = 1125; when mouse is over no buttons

Snarky

You got it. I'd personally refactor it as a standalone function taking the sprites you want to detect and the button versions as parameters,but you have the logic of it.

Privateer Puddin'

Broke something, will update!

Snarky

Another thing you'll need to do is to calculate the x,y coordinates relative to the spite, not just use mouse.x/y. In practice this means you subtract (Button.X/Y + Button.OwningGUI.X/Y) from mouse.x/y.

Privateer Puddin'

Hm, how come I need to do that?

This is basically on the same code I posted earlier (in terms of anything mentioning mouse.x)




Snarky

Oh, looking more closely at your code, you did it a little differently than I meant (by getting a drawingsurface for the background, which I think is a bad idea: here it looks like you're actually drawing the button sprite to the background, surely not the intention). Still, even with your approach it should only work at all if the GUI is full-screen (or at least, located at 0,0.

Privateer Puddin'

Yeah, I came across that earlier and 'fixed' it but I'm sure is bad but it's all I could get to work :p

I didn't realise what was happening at first, because I was testing on the Lunchbox inventory item, which is sat squarely on the bag gui button and hadn't turned the inventory off. Only when I started tested on the rope did I realise I was drawing on to the background. So, here's what I have at the moment. I tried a function as well (I'm not very good with either functions or drawingsurface, a lot of the manual stuff goes over my head).

Code: ags

function InvUpdate(Button *btnName, String description,  int sprite_number_nontrans, int sprite_number_normal, int sprite_number_mouseover) {
  DrawingSurface *backgroundCopy2;
  DrawingSurface *surface = Room.GetDrawingSurfaceForBackground();  
  if (backgroundCopy2 == null) backgroundCopy2 = surface.CreateCopy(); // first time, create back-up copy of background
  else surface.DrawSurface(backgroundCopy2); // restore previous background
  DynamicSprite* sprite = DynamicSprite.CreateFromExistingSprite(sprite_number_nontrans, false); //store sprite of inv item with pink bg no transparancy
  //DrawingSurface *surface = sprite.GetDrawingSurface();  // I tried using this at one point?
  surface.DrawImage(btnName.X, btnName.Y, sprite.Graphic); //draw inv item on surface
    if (surface.GetPixel(mouse.x, mouse.y) == 63519) { //check if mouse is over pink transparency colour
    OverInv = false;
      btnName.NormalGraphic = sprite_number_normal; // set normal version of inv item graphic
      lblHotspot.Text = ""; // clear label
      surface.Clear();
      surface.DrawSurface(backgroundCopy2);
      surface.Release(); 
      sprite.Delete();
    } else {
      surface.Clear();
      surface.DrawSurface(backgroundCopy2);
      surface.Release(); 
       
      sprite.Delete();
      btnName.NormalGraphic = sprite_number_mouseover; //mouse is over inv item, so set mouseover graphic
      lblHotspot.Text = description; //mouse is over inv iem, so set label
      OverInv = true;
    }
}


Which I call with

Code: ags
InvUpdate(btnRope, "Rope", 1184, 1129, 1130);


and yeah, my GUI is full screen.

Snarky

Try this, man:

Code: ags
bool MouseOverPixelPerfect(this Button*, int mouseOverGraphic, String label)
{
  // Calculate mouse coordinates relative to button sprite canvas 
  int x = mouse.x - this.X - this.OwningGUI.X;
  int y = mouse.y - this.Y - this.OwningGUI.Y;

  // Get the color on button sprite canvas at mouse cursor pixel
  DynamicSprite* buttonMask = DynamicSprite.CreateFromExistingSprite(this.NormalGraphic, false);
  DrawingSurface* maskSurface = buttonMask.GetDrawingSurface();
  int pixelColor = maskSurface.GetPixel(x,y);
  maskSurface.Release();
  buttonMask.Delete();

  if(pixelColor == COLOR_TRANSPARENT) // if this doesn't work then try your harcoded number, 63519
  {
    // Transparent, so don't highlight
    this.MouseOverGraphic = this.NormalGraphic;
    lblHotspot.Text = "";
    return false;
  }
  // Otherwise, highlight
  lblHotspot.Text = label;
  this.MouseOverGraphic = mouseOverGraphic;
  return true;
}


Call it like:

Code: ags
if (theControl == btnRope)
  btnRope.MouseOverPixelPerfect(1130, "Rope");

Privateer Puddin'

Hey! That works just as well as mine ;)

Thanks a lot for your help Snarky - Solved!

SMF spam blocked by CleanTalk