[SOLVED] I don't want the GUI to move with the camera ( just like objects don't)

Started by Rik Vargard, Tue 17/01/2023 20:16:43

Previous topic - Next topic

Rik Vargard

Hello,

I'm working on a simple fight script.
At some moments the camera moves to the right and back to the left.

For what I understand:
Unlike the objects whose positons are based on the background, the GUI's positions are based on the camera.
So when I move the camera to the right, the objects stay in place but the GUI's move along with the camera movements.

I need those GUI's to stay at the same place, like the objects.

The reason is that I need to show the player's and enemy's health (in numbers) and for what I know I can only do this with a GUI using a global variable.

I tried to use an invisible character to "Say" the health points in room_RepExec() but there's that flicker at every "update".

Any ideas (if it's possible) to kind of "lock" the GUI's so that they don't follow the camera movements?

Thanks! :)





Snarky

Yes, as the camera moves, you just move the GUIs in the opposite direction.

Code: ags
int oldCameraX;
int oldCameraY;

function late_repeatedly_execute_always()
{
  int deltaX = Game.Camera.X - oldCameraX;
  int deltaY = Game.Camera.Y - oldCameraY;

  // Nudge all the room-relative GUIs
  myGui1.X = myGui1.X - deltaX;
  myGui1.Y = myGui1.Y - deltaY;
  // etc.

  oldCameraX = Game.Camera.X;
  oldCameraY = Game.Camera.Y;
}

Note that GUIs don't like to be positioned outside of the screen. You may need to hide them if they go outside the screen edge.

Khris

Afaik you should also be able to use Overlay.CreateRoomTextual() instead of GUIs. They use room coordinates so should stay in place relative to the room when the camera moves.
To make them appear on top of everything else (except GUIs), set their ZOrder to the bottom Y coordinate after creating them.

Rik Vargard

Hey Snarky thank you so much for your reply. :)

Thing is, I don't understand a thing about the code you shared and I'm sorry because I know you took that time to reply.

BUT (!) that one sentence of yours "move the GUIs in the opposite direction" is what I understood.

So I came up with this:
Code: ags
while (Game.Camera.X < 900)
    {
      Game.Camera.X += 100;
      gLIFE_ENM.X -= 100;
      gLIFE.X -=100;
      Wait(1);
    }

And it's working, so thank you for that. :p

Rik Vargard

Quote from: Khris on Tue 17/01/2023 20:53:53Afaik you should also be able to use Overlay.CreateRoomTextual() instead of GUIs. They use room coordinates so should stay in place relative to the room when the camera moves.
To make them appear on top of everything else (except GUIs), set their ZOrder to the bottom Y coordinate after creating them.

I was replying when you where replying so yeah, I found a solution (above post).
And like I said to Snarky, I'm sorry but I don't understand what you're saying here. :P
(I'm still that noob, even if I moved from LVL 1 to LVL 2/1000)  :-D
But really, thank you so much for your reply... I might one day understand this  :-[

Snarky

Code: ags
int oldCameraX;
int oldCameraY;

function late_repeatedly_execute_always()
{
  int deltaX = Game.Camera.X - oldCameraX;
  int deltaY = Game.Camera.Y - oldCameraY;

  // Nudge all the room-relative GUIs
  myGui1.X = myGui1.X - deltaX;
  myGui1.Y = myGui1.Y - deltaY;
  // etc.

  oldCameraX = Game.Camera.X;
  oldCameraY = Game.Camera.Y;
}

Quote from: Rik Vargard on Tue 17/01/2023 21:10:56Thing is, I don't understand a thing about the code you shared and I'm sorry because I know you took that time to reply.

It's not all that complex. The short explanation is that any time the camera moves, it moves the GUI or GUIs equally in the opposite direction, so that they stay in the same position relative to the room. (Or, to put it a different way, it applies the on-screen movement of the room to the GUIs.)

To understand how it works, you just have to know that repeatedly_execute_always() is a function that runs each time the game updates, after all the other functions. And also that if you want a variable to keep its value even after a function ends, you need to declare it outside of the function. So in this case, what the function does is:

  • First (lines 6–7) it compares the current camera position to the last one (from the previous game update), subtracting the (X,Y) values to give the camera movement since the last frame, (deltaX,deltaY). ("Delta" is math jargon for "change", BTW)
  • Then (lines 9-12) it applies the opposite movement to the GUIs that you want to follow the room movement, just as in your code.
  • Finally (lines 14-15), it updates the "old" camera position to the current position. Because we declared these variables outside of the function (lines 1-2), these values will be used (in lines 6-7) when the function runs again on the next game update.

This method of storing some value from the last game update, comparing it to the current value to see if/how it has changed (and doing something that depends on the result), and finally updating the stored value with the current value so that you can do it all again on the next game loop, is a very common and useful pattern for detecting "events": code that needs to run when something changes/updates.

Crimson Wizard

I'd like to question whether the above method of adding coordinates is the best in this scenario. As Rik Vargard mentioned, he wants to have the GUI display player's and enemy's health. Does this mean that the GUIs should be located in certain position relative to these characters?

If you want the GUI to be displayed above particular spot in the room, then instead you may use a more simple method, where you base your GUI coordinates on wanted room coordinates.
In generic example you can do this:
Code: ags
function late_repeatedly_execute_always()
{
    myGui1.X = ROOM_X - Game.Camera.X;
    myGui1.Y = ROOM_Y - Game.Camera.Y;
}

Or, let's say, you want to place a GUI above player's head:
Code: ags
function late_repeatedly_execute_always()
{
    ViewFrame* vf = Game.GetViewFrame(player.View, player.Loop, player.Frame);
    int sprite_height = Game.SpriteHeight[vf.Graphic];
    myGui1.X = player.x - Game.Camera.X - (myGUI.Width / 2);
    myGui1.Y = player.y - sprite_height - Game.Camera.Y - myGUI.Height;
}

Snarky

I considered that, but in the first instance I thought it is better to keep the logic separate, so that the update function doesn't need to consider anything about how the GUIs are positioned and why, except that they are meant to be room-relative. With your method you'll either need another set of position coordinate variables for each GUI (and although that doesn't look so bad when it's just one of them, like in your example, I was imagining that there could be quite a few), or you'll have to copy all the original positioning logic to the update function.

As for the second case, if the characters/objects may move as well, and the GUIs need to be repositioned relative to them, then sure, you can't get away from the need to rerun the positioning logic. (But this was not the original question.) In that case, if you have a lot of GUIs, I would suggest pulling that out into its own function.

Rik Vargard

Oh wow, well that's an unexpected bonus. :)

So yes, I will read this like a dozen times at least and find out about the "late_repeatedly_execute_always" and other "GetViewFrame" and... al the rest.

But yes, I just needed two similar GUI's to stay at the same place so CW's script does inspire me.
(It is indeed a simple one VS one fight).

And so does Snarky's script because now I know I can imagine a battle with more characters in it.
Like: it can exist.

Thank you so much Crimson Wizard and Snarky! (nod)




SMF spam blocked by CleanTalk