Camera following cursor?

Started by FrankT, Tue 14/08/2012 11:04:04

Previous topic - Next topic

FrankT

Would there happen to be some sort of code that makes the viewport follow the cursor, rather than the player character? As in, the screen scrolls to follow its position?

Andail

You can easily make one.
Under repeatedly execute you set the viewport to follow mouse.x and mouse.y. Easy as pie.
Or, if you're using the parallax module (which I believe disables viewport) you create a dummy character instead, and update its x and y position repeatedly.

Khris

In case you're unsure about what the code has to look like exactly:

Code: ags
  SetViewport(mouse.x - System.ViewportWidth/2, mouse.y - System.ViewportHeight/2);


(You might have to include checks but I believe SetViewport ist robust when it comes to values outside the bounds.)

FrankT

Excellent! Thank you for the answers! ^^

FrankT

Hm... having a bit of trouble; I can't seem to scroll all the way to the right edge of my panel; how can I correct this?

Khris

Forget about the code I posted, it won't work.

The thing is, "the viewport follows the cursor" sounds pretty straightforward, but it actually isn't. Moving the mouse means that its position changes with regard to the room as well as the screen. Basically, when I move my mouse to the right, the viewpoint is also supposed to scroll to the right, but this system will only work if the mouse is moved back to the screen's center at the same time, so that its position in room coordinates doesn't change.
Since changing the mouse position, although possible, isn't recommend because a) the user might have to lift the mouse off of the table if the physical mouse can't be moved any further and b) the movement will get jittery due to the user interfering, direct movement of the viewport isn't going to work.

There are two ways I can think of:
1) The mouse position in relation to the screen's center determines viewport movement. So for instance if the mouse is at the right edge of the screen, the viewport will move quickly to right, and if it's just below the center, the viewport will scroll down slowly.
2) The mouse position is translated directly, i.e. mouse at 0,0 -> viewport is in top left corner; mouse at bottom right, viewport is all the way down and to the right.

Both of those are relatively easy to implement, I just don't have time right now to post some code.

Yeppoh

#6
Another method from these steps :

1. Save mouse x and y position into variables. This will be the origin.
2. Save screen x,y position.
Begin Loop {
3. Check difference between the saved origin and the new mouse position (as it moves).
4. Increment the saved screen x,y with the resulting difference.
5a. Check if the x value is greater than the room width minus viewport width. If true, return the room width minus viewport width as screen x.
5b. Check if the y value is greater than the room height minus viewport height. If true, return the room height minus viewport height as screen y.
6a. Check if the x is less than 0. If true, return 0.
6b. Check if the y is less than 0. If true, return 0.
7. Set the screen position with the resulting x,y.
} End Loop

I don't have AGS on hand at the moment to code and test this, but this is the main idea. It has just one problems when the player clicks as the mouse is positionned practically at the borders of the screen.

Ryan Timothy B

#7
Quote from: Khris on Tue 14/08/2012 17:02:28
Code: ags
  SetViewport(mouse.x - System.ViewportWidth/2, mouse.y - System.ViewportHeight/2);

Khris, I know you said the code won't work. But yeah, you're dead right. lol It wouldn't be able to move the viewport beyond the half of the system viewport width/height (assuming the viewport started 0,0). You forgot the Viewport x/y position, but even that is a pretty wacky camera.

Quote from: Khris on Tue 16/10/2012 23:13:53
Since changing the mouse position, although possible, isn't recommend because a) the user might have to lift the mouse off of the table if the physical mouse can't be moved any further and b) the movement will get jittery due to the user interfering, direct movement of the viewport isn't going to work.
Your A argument is what everyone needs to do while playing a first person 3D game while looking around. And I don't understand B. The camera being shaky because you're lifting your mouse and shifting it over?


FrankT, there are two types of screen movement you may be asking for (scrolling the viewport around a room larger than game resolution).
A) One where it follows the mouse like a first person shooter.
B) One where if your mouse moves in one direction and stops, the camera will constantly be moving around. Kind of like holding down on a joystick that controls the view (where it would keep spinning and spinning). To stop it you have to bring the cursor back to the center region of the screen. There's also just the edge boundaries like Starcraft where it will only move the viewport if you're near the edge.

I'm assuming you want the viewport to be centered on the cursor like an FPS game (unless the viewport is touching the edge of the room background).


This is what I would do, when you're in the mode where the center of the viewport is supposed to follow the cursor:

When this segment of gameplay starts, I would do three things:

1) In a windowed game I would set a cursor boundary of:
Code: ags
if (System.Windowed) mouse.SetBounds(0, 0, System.ViewportWidth - 1, System.ViewportHeight - 1);

This boundary prevents the cursor from leaving the window. ALWAYS remember to turn off this boundary whenever the menu is open, someone presses esc, or when the viewport isn't following the cursor anymore - that way you aren't angering any players because they can't leave the cursor from your game window without Alt-tabbing or quitting the game

2) I would then center the cursor in the center of the screen. You want the viewport to stay still until the mouse moves.

3) Turn off the cursor visiblity
Code: ags
mouse.Visible = false;

If you're going to have a cursor visible, you'll have to make a crosshair in the center of the screen with a GUI.

Next is the part where you move the viewport. It's pretty easy stuff:
Code: ags
int thisPositionX = mouse.x - System.ViewportWidth / 2;
int thisPositionY = mouse.y - System.ViewportHeight / 2;
SetViewport(GetViewportX() + (thisPositionX - lastPositionX), GetViewportY() + (thisPositionY - lastPositionY));
lastPositionX = thisPositionX;
lastPositionY = thisPositionY;


Lastly I would center the cursor to the center off the screen whenever it passes a certain boundary, like so (if this were a 320x240 game, I'd probably use a boundary like this):
Code: ags
if (mouse.x < 80 || mouse.x > 240 || mouse.y < 80 || mouse.y > 160) {
  mouse.SetPosition(System.ViewportWidth / 2, System.ViewportHeight / 2);
  lastPositionX = 0;
  lastPositionY = 0;
}

You do NOT want to set the position of the mouse every gameloop. Since we do not have access to the float position of the cursor, the viewport would only move if the mouse actually moved 1 pixel from the center point between the last game loop. Which would mean the cursor would be stuck in the center if you're moving the mouse slowly. The player would have to quickly slide the mouse to have it move at all. So don't do that!


Basically the whole code:
Code: ags

int lastPositionX, lastPositionY;

function repeatedly_execute()  {
  int thisPositionX = mouse.x - System.ViewportWidth / 2;
  int thisPositionY = mouse.y - System.ViewportHeight / 2;
  // of course - AGS crashes if you don't check if the viewport is being changed beyond the boundaries
  int viewX = GetViewportX() + (thisPositionX - lastPositionX);
  int viewY = GetViewportY() + (thisPositionY - lastPositionY);
  if (viewX < 0) viewX = 0;
  else if (viewX >= Room.Width - System.ViewportWidth) viewX = Room.Width - System.ViewportWidth; 
  if (viewY < 0) viewY = 0;
  else if (viewY >= Room.Height - System.ViewportHeight) viewY = Room.Height - System.ViewportHeight;
  SetViewport(viewX, viewY);
  lastPositionX = thisPositionX;
  lastPositionY = thisPositionY;

  if (mouse.x < 80 || mouse.x > 240 || mouse.y < 80 || mouse.y > 160) {
    mouse.SetPosition(System.ViewportWidth / 2, System.ViewportHeight / 2);
    lastPositionX = 0;
    lastPositionY = 0;
  }
}

(I did not test anything)

Now there's numerous ways to do this. You didn't exactly give us much to work with on your question.
You could also add a smooth transition to where the cursor is actually pointing, instead of a crosshair design. But for this it requires a bit more work. You'd need a fake cursor by using a GUI. It's very possible, but it would be more like a drunken camera if you were moving the cursor around quite fast or doing zigzags. Of course the faster the cursor is moving, the faster the viewport should move, but it may look strange and delayed. I won't be posting code for this design.

Edit: I'm an idiot.. just a fixing a few copy/past issues.

FrankT

I'm afraid you've all lost me there. :-\ Hm.. I think the edge boundary thing should work better. How's that work?

Ryan Timothy B

In what way are you lost? Have you even tried the code I provided at the bottom? Basically it's like the crosshair on an fps game, move the invisible cursor 10 pixels to the right, the viewport moves 10 pixels to the right. That's it. (assuming I didn't screw up on the code - I typed it all within the browser)

FrankT

Quote from: Ryan Timothy on Wed 17/10/2012 17:32:11
In what way are you lost? Have you even tried the code I provided at the bottom?

Yes I did, but it wasn't the one my panel demands. Since the cursor always centres itself on the screen, I can't get it to reach any of the buttons. Can we have a look at the Starcraft style?

Stee

So you want an strategy style camera, where the map/background scrolls along when it reaches the end of the screen?
<Babar> do me, do me, do me! :D
<ProgZMax> I got an idea - I reached in my pocket and pulled out my Galen. <timofonic2> Maybe I'm a bit gay, enough for do multitask and being romantical

Khris

Try this:
Code: ags
// in repeatedly_execute
  int x = mouse.x, int y = mouse.y;
  vw = System.ViewportWidth, vh = System.ViewportHeight;
  vp_x_max = Room.Width - vw;
  vp_y_max = Room.Height - vh;
  x = (x*vp_x_max)/vw;
  y = (y*vp_y_max)/vh;
  SetViewport(x, y);

FrankT

It works! Thanks! You've saved my bacon this time!  :-D

Keen

Sorry for my scripting knowledge, i'm bad at that.
I tried to paste code in different places such as:
room script file
GlobalScript.ash
GlobalScript.asc

but everywhere i got this problem:
Expected integer valye after "="

looks like AGS somehaw cant undertand mouse.x and mouse.y values

Please help, thx

Khris

As is mentioned in the first line of the code, it needs to go inside the repeatedly_execute function.
If you want to use this in every room, open GlobalScript.asc, then find the function (you can use the dropdown menu at the top):
Code: ags
function repeatedly_execute()
{
  ...
}


There's probably already stuff in there along with comments that explain where to put your own code.
Paste the code in there.

If you want to use this only in a certain room, add the room's repeatedly_execute event in the room editor, then open the room script and find the function called "room_RepExec()". Then paste the code in there.


(The reason why pasting the code outside a function didn't work is that AGS would have no idea when to call the code. AGS is event based, so every instruction (with a few exceptions) has to be inside a function. Basically, what you're supposed to do is tell AGS "when X happens, do Y". In what you tried, the "when X happens" part was missing.)

jamesreg

I know this is an old post I just posted a request to help with something similar to this but then found this post and the code seems to do some of what I need.

i used this code from this post

Code: ags
 int viewX = GetViewportX() + (thisPositionX - lastPositionX);
  int viewY = GetViewportY() + (thisPositionY - lastPositionY);
  if (viewX < 0) viewX = 0;
  else if (viewX >= Room.Width - System.ViewportWidth) viewX = Room.Width - System.ViewportWidth; 
  if (viewY < 0) viewY = 0;
  else if (viewY >= Room.Height - System.ViewportHeight) viewY = Room.Height - System.ViewportHeight;
  SetViewport(viewX, viewY);
  lastPositionX = thisPositionX;
  lastPositionY = thisPositionY;
 
  if (mouse.x < 80 || mouse.x > 240 || mouse.y < 80 || mouse.y > 160) {
    mouse.SetPosition(System.ViewportWidth / 2, System.ViewportHeight / 2);
    lastPositionX = 0;
    lastPositionY = 0;
  }
}
 


the above code was for 320x240 room but mine is default game 480x270 with a 2800x1400 scrolling background
so I changed the 80,240,80,160   numbers to 210, 270, 210, 420 which i think is correct change for my screen size.
is there away to scroll a 2800x1400 screen without picking up the mouse and putting it back down. I also need it to scroll and roll over and keep scrolling when it get to edges.

Also how do I take the gui target cursor and now let it act like a mouse when clicking on characters and objects on screen
I also need to be able to hit tab and break out of gui target mode and go back to mouse full screen mode to be able to access guis and such

SMF spam blocked by CleanTalk