• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Legacy GM Refining an "RTS" style camera in GM:S

M

MANGOSENTINEL

Guest
Hi GMC, I've been working on a reverse tower defense game (you play the attacking force, I like to think of it as a strategy puzzle game almost) for awhile and I'm finally starting to see the light at the end of the tunnel where I worry about making my game look better as opposed to just, functioning. Which brings me to a concern my artist has been having: The camera for our game is..."jerky".

It's only jerky on some occasions, mostly the diagonals and I'm at a loss to what to do about it. Here's some background information if anyone can lend a hand:

Since I wanted to cage the mouse to the game window without doing a DLL, I developed a system with the help of a few friends that locks the mouse into the middle of the game window and feeds the mouse inputs into a "false" cursor, obj_cursor. I then use obj_cursor for all the purposes of a mouse, such as interacting with with menus or units on the field. In designing a camera for the game, my theory was to have a camera object that followed the cursor object at a set distance, and only moved the screen when the cursor got near the edge of the view. To that end, I've created a camera with this code:


Code:
STEP EVENT:

//CAMERA CONTROLS
if distance_to_point(obj_cursor.x,obj_cursor.y) >= 400
 {
 var xTo, yTo;
 move_towards_point(obj_cursor.x,obj_cursor.y,0)
 xTo = obj_cursor.x + lengthdir_x(min(800,distance_to_point(obj_cursor.x,obj_cursor.y)),direction)
 yTo = obj_cursor.y + lengthdir_y(min(450,distance_to_point(obj_cursor.x,obj_cursor.y)),direction)

 x += (xTo - x)/25
 y += (yTo - y)/25

}

//TWEEENING ??

view_xview[0]+=((x-(view_wview[0]/2))-view_xview[0])*.5
view_yview[0]+=((y-(view_hview[0]/2))-view_yview[0])*.5

//////CLAMP THE VIEW

view_xview[0]=clamp(view_xview[0],0,room_width - view_wview[0])
view_yview[0]=clamp(view_yview[0],0,room_height - view_hview[0])

Addittionally, I have the view set to follow my camera object with the following values:

Hborder = 800
Vborder = 450
Hspd = -1
Vspd = -1

I chose 800 and 450 since my port on screen for my game is 1600 x 900, despite my games view in room being 1920 x 1080. Not sure if that would cause issues or not.

While this is close to achieving the effect I want for a camera, I was wondering if anyone had advice or a better method for this, since I can't quite tweak the numbers to make them the way I want to.

Thanks in advance!!
 
That's a lot of questions... let's tackle them one at a time, because many of these things could be overlapping and causing problems.

First. Your mouse work around.

What's wrong with window_mouse_set()?

window_mouse_set(clamp(mouse_x,0,window_get_width()),clamp(mouse_y,0,window_get_height()));

That should lock the mouse to the window with no issues.
 
M

MANGOSENTINEL

Guest
When I was attempting to cage my mouse using that function a few months ago, I would have issues with the mouse being extremely jittery on the side of the screen or it would continue to fly out of the window onto a second monitor and then rubberband back in if you moved the mouse quickly. I can try to implement that again but the last time I attempted to do that it was fairly fruitless.

http://gmc.yoyogames.com/index.php?showtopic=687064

This was the thread I made on that subject
 
Ah, okay. Yeah, I was noticing that as well. Figured if you were using an "in game" cursor it wouldn't be such a big deal as long as you made your cursor smart about not following the mouse perfectly at the edge of the screen... still think it'd be a good idea to try and work around those issues.

Okay, so I feel like you are over-complicating this movement with your object. Doing both a "move towards" and updating your x and y is a bad idea (as that's exactly what move_towards() does).

In fact, since you are using object view following (from the views tab in the room editor) AND manually moving the view in your "tweening" section that is also doing the same (or slightly different) thing twice. So you need to choose.

Here's what I recommend. Stop using automatic object following. It's really not great. Instead, replace your tweening code with this:

Code:
view_xview=x-view_wiew/2
view_yview=y-view_hview/2
You don't need to include [0] if you are only ever using one view, btw.

This will make it follow the camera object perfectly... which you may not want. But worry about smooth acceleration and all of that fancy stuff once you have a basic camera functioning.

So get that working, and then we'll go from there.
 
M

MANGOSENTINEL

Guest
Ok I replaced my tweening code and removed the automatic following from the view, it's a functioning camera but it still seems, choppy. (Sorry it took so long I went to cook). How should I proceed from here?
 
So, the choppiness may be coming from this whole movement code you've got with trying to follow the camera object. Let's talk about how you want the camera to work.

Typically in an RTS, the cursor is locked to the window, and behaves normally near the center, but once it reaches the edge of the window, it starts scrolling. Is that how you want your camera to behave? Try and describe how you want it to work in general terms.
 
Last edited:
M

MANGOSENTINEL

Guest
Right so I'm trying to achieve an RTS/MOBA (i guess) style camera that when you reach the edges of the screen it starts panning until the "fake mouse" cursor is no longer inside those edge "hotzones" or whatever you want to call them. Right now this camera system achieves that, but while it's moving it's...quite choppy and not very visually pleasing I'm finding. Having this camera object following the fake mouse cursor is how I was attempting to implement this.
 
Right, so I think that may be part of your problem. So typically the camera will pan until it reaches the edge of the play area if the mouse hasn't moved. As long as the mouse is in those "margin" areas around the screen, the camera should pan.

So instead of following an object, you can just detect if the cursor object is in one of those margins, and if so, shift the view's position depending on which margins it is in. Don't follow an object or anything, just shift the view depending on where the mouse is. You'll need to play with it a bit so that you can move diagonally, but with this set up, you'll have a lot of control over how fast your view scrolls and can even scroll faster the "deeper" into the margin the mouse is if you want.
 
M

MANGOSENTINEL

Guest
Hmm, so how would I go about designing these margin areas?

Something like point in rectangle function? I would imagine I'd have to define where the margin area is and then check to see if my obj.cursor x,y, or both are meeting with this margin area and then pan the view accordingly yeah ?
 
Exactly. Make sure these "areas" are relative to the view. You can use point in rectangle, or a combination of point in rectangle and point_direction.

Say you have a rectangle in the center of the screen that covers the whole screen, but not all the way to the edges, then if the cursor is not in that box, you could get the direction from the center to the cursor and use lengthdir_x/y to move the view towards where the cursor is. This would let you move the view in a full 360 degrees.... honestly not sure if that is better or worse than limiting it to 8 directions or not. You'd have to play with it, but that would be the easiest check.

Code:
if(!point_in_rectangle(cursor.x,cursor.y,view_xview+margin,view_yview+margin, view_xview+view_wview-margin,view_yview+view_hview-margin))
{
    var dir = point_direction(view_xview+view_wview/2,view_yview+view_hview/2,cursor.x,cursor.y);
    view_xview+=lengthdir_x(scroll_speed,dir);
    view_yview+=lengthdir_y(scroll_speed,dir);
}
 
M

MANGOSENTINEL

Guest
Ok, here's what I got in my camera's step event so far:

Code:
 //CAMERA CONTROLS
if distance_to_point(obj_cursor.x,obj_cursor.y) >= 400
 {
 var xTo, yTo;
 move_towards_point(obj_cursor.x,obj_cursor.y,0)
 xTo = obj_cursor.x + lengthdir_x(min(800,distance_to_point(obj_cursor.x,obj_cursor.y)),direction)
 yTo = obj_cursor.y + lengthdir_y(min(450,distance_to_point(obj_cursor.x,obj_cursor.y)),direction)

 x += (xTo - x)/25
 y += (yTo - y)/25

}
 
if(!point_in_rectangle(obj_cursor.x,obj_cursor.y,view_xview+80,view_yview+60, view_xview+view_wview-80,view_yview+view_hview-60))
{
    var dir = point_direction(view_xview+view_wview/2,view_yview+view_hview/2,obj_cursor.x,obj_cursor.y);
    view_xview+=lengthdir_x(20,dir);
    view_yview+=lengthdir_y(20,dir);
}

//TWEEENING ??

view_xview= x-view_wview/2
view_yview= y-view_hview/2

//////CLAMP THE VIEW

view_xview[0]=clamp(view_xview[0],0,room_width - view_wview[0])
view_yview[0]=clamp(view_yview[0],0,room_height - view_hview[0])

From here I should just play with the numbers? Or did I implement this wrong

EDIT: I just realized I probably should have taken the first block out and replaced it with what you posted, but I'm not sure?
 
Yeah, get rid of pretty much everything but the point in rectangle and the clamp to the room (including the "tweening" block). That should give you a good baseline to work from.
 
M

MANGOSENTINEL

Guest
ok, seems to be working but it is rather slow, guess I'll have to mess around with the scroll speed (10 seems to be too low). Besides that, what other changes are there to make ?
 
Lots. You could make so the closer to the edge the cursor is the faster you scroll or the longer the cursor is near the edge the faster it goes so it sort of accelerates. You could get more complicated with the shape so its a bit easier to scroll diagonally. Totally up to how it feels to you and how you want it to work differently.
 
M

MANGOSENTINEL

Guest
I think the main thing I want to improve on is smoothing the motion: even with this improvement when i take the mouse near the edge of the screen and inch it into the border the camera "jumps" instead of just kind of sliding over, not really sure how to approach this but my gut tells me there's a function I'm not aware that will smooth out that jump on the camera or at least add like a "camera drift" so the motion is less violent.
 
M

MANGOSENTINEL

Guest
Code:
 //CAMERA CONTROLS
///define and set scrollspeed
var scrollspeed;
scrollspeed = 0


if(!point_in_rectangle(obj_cursor.x,obj_cursor.y,view_xview+80,view_yview+60, view_xview+view_wview-80,view_yview+view_hview-60))
{
    scrollspeed += 10
    var dir = point_direction(view_xview+view_wview/2,view_yview+view_hview/2,obj_cursor.x,obj_cursor.y);
    view_xview+=lengthdir_x(scrollspeed,dir);
    view_yview+=lengthdir_y(scrollspeed,dir);
    
}
This definitely takes it from 0 to 10, but it doesn't seem to increase the speed anymore than that regardless of how I manipulate it. Not really sure what i'm doing wrong (this code is all in step event)
 
M

MANGOSENTINEL

Guest
Code:
 //CAMERA CONTROLS
///define and set scrollspeed
var scrollspeed;

if(!point_in_rectangle(obj_cursor.x,obj_cursor.y,view_xview+80,view_yview+60, view_xview+view_wview-80,view_yview+view_hview-60))
{
    scrollspeed = clamp(10+2,10,50)
    var dir = point_direction(view_xview+view_wview/2,view_yview+view_hview/2,obj_cursor.x,obj_cursor.y);
    view_xview+=lengthdir_x(scrollspeed,dir);
    view_yview+=lengthdir_y(scrollspeed,dir);    
}
else
{
scrollspeed = 0
}

I feel like there's something very basic I lack in this: when i tried to use the line

Scrollspeed = clamp(scrollspeed+01,0,10)

It returned with scrollspeed not yet defined or something along those lines, so I tried putting a number into the scrollspeed and it works but doesn't accelerate. And obviously setting scrollspeed to a number right under where I define it also just sets it to that number repeatedly so I'm pretty lost, as dumb and basic as a mistake as this is.
 
Yeah. Really basic. Define scrollspeed as a variable in your create event. Get rid of the var definition. And, seriously, .01 added to scroll speed. POINT ZERO ONE and add it to scrollspeed variable as the first argument in clamp. Trust me on this. You may want to kick it up or down a tiny, tiny bit, but adding 10 isn't going to do anything for you.
 
M

MANGOSENTINEL

Guest
Yup, that was definitely the basic issue I was having.

Now I just need to tweak the numbers, it is super slow panning at first, but it does seem smoother to some extent. Would the size of the view affect the number portion of the code at all ?
 
D

Dramier

Guest
One trick I used some time ago for panning a screen smoothly was limiting the scroll speed based on the FPS. So if there were alot of things happening in your game causing slowdowns, you slow down the scroll speed so you get a smoother movement. Otherwise if cpu load is high and FPS is low you get stutter movement. That may not be the case as you may have nice smooth high FPS, but it's just a thought that might help.

Also, I have done the margin scrolling in the past by using objects. Then based on collisions with those objects it would do the same thing. Definitely not as good as the way you are already doing it, but thought you might appreciate hearing about an alternative way.
 
M

MANGOSENTINEL

Guest
hmm I see I see, I'm essentially just moving my obj.cursor into a margin to manage my camera movement right now, but I haven't even really considered FPS in any capacity yet, which might be an error on my part. Thanks for bringing all that up !
 
View size shouldn't matter unless you want the margin to be relative to the size of the view. I doubt you are running into fps issues this early, but make sure your room speed is set to 60. If you want to tune the speed you have a few options.

Increase how much is added every step you are scrolling from .01 to... something higher. .02 .1 maybe .5 or even 1.

Increase the max speed from 10 to something higher.

Increase the default value of scroll speed. Instead of resetting it to 0, try 1 or 2.

Mess around with all these options to tune it to your tastes.
 
M

MANGOSENTINEL

Guest
Ok cool, thanks for your help, I really appreciate it!
 
M

MANGOSENTINEL

Guest
So after tinkering around with a little bit this morning, I came across a problem that I might need your help with: my game uses the obj_cursor to interact with some menus, but since I unclamped the view some of my menus on are unusable when the cursor is at the bottom of the view because the menus that are draw to the view are outside of the room technically i suppose? When the cursor isn't at the absolute bottom of the screen it's fully functional but when it is they cursor simply cannot reach them, which is why I think i had the view clamped in the first place? It's been so long since I've designed that portion of the game I've forgotten honestly.


EDIT: I'm also trying to add a deceleration to the camera when the cursor comes back into view, but so far the only code I've been able to come up with is this

Code:
 //CAMERA CONTROLS
///define and set scrollspeed

if(!point_in_rectangle(obj_cursor.x,obj_cursor.y,view_xview+40,view_yview+30,view_xview+view_wview-40,view_yview+view_hview-30))
{
    scrollspeed = clamp(scrollspeed + scrollspeedGrowth,0,scrollspeedMax)
    var dir = point_direction(view_xview+view_wview/2,view_yview+view_hview/2,obj_cursor.x,obj_cursor.y);
    view_xview+=lengthdir_x(scrollspeed,dir);
    view_yview+=lengthdir_y(scrollspeed,dir);
    }
    
    else
    {
    scrollspeed = clamp(scrollspeed - (scrollspeedGrowth),0,scrollspeedMax)
    var dir = point_direction(view_xview+view_wview/2,view_yview+view_hview/2,obj_cursor.x,obj_cursor.y);
    view_xview+=lengthdir_x(scrollspeed,dir);
    view_yview+=lengthdir_y(scrollspeed,dir);
    }
I made some variables and buttons so my artist and I could play around and find a camera speed/acceleration rate that felt good for us, but the problem I'm having with this "deceleration" is sometimes the view shake violently under certain conditions that I can't reproduce reliably but I think has to do with changing direction rapid and view catching up with the cursor? I hate to bother you more about this but I'm pretty stumped at the moment.
 
Last edited by a moderator:
some of my menus on are unusable when the cursor is at the bottom of the view because the menus that are draw to the view are outside of the room technically i suppose?
Uhh... what? First: any UI should be handled in the draw GUI event. That way it is always on top and always on screen and can be scaled separately from the rest of your game. Look into the GUI event if you haven't already, it's a very powerful tool for UI. Your cursor should probably be drawn in the GUI event as well. Second: your menu buttons should be "inside" your rectangle. Like in starcraft or any other RTS, you can scroll down but to do so you have to put your cursor at the bottom of the screen well below your command bar. They may have even made certain areas on the UI "immune" from scrolling, such as the block of commands in the bottom right corner or the minimap in the bottom left.

As for "slowing down" what you have almost works, but you'll need to do with "dir" what you did with "scrollspeed"

Initilize the variable "scroll_dir" in the create event, and set it to 0 or whatever, it doesn't matter. Then your code should look like this:

Code:
if(!point_in_rectangle(obj_cursor.x,obj_cursor.y,view_xview+40,view_yview+30,view_xview+view_wview-40,view_yview+view_hview-30))
{  //Actively Scrolling
  scrollspeed = clamp(scrollspeed + scrollspeedGrowth,0,scrollspeedMax)
  scroll_dir = point_direction(view_xview+view_wview/2,view_yview+view_hview/2,obj_cursor.x,obj_cursor.y);
}
else
{ //No longer scrolling, slow down
  scrollspeed = clamp(scrollspeed - (scrollspeedGrowth),0,scrollspeedMax)
}
view_xview+=lengthdir_x(scrollspeed,scroll_dir);
view_yview+=lengthdir_y(scrollspeed,scroll_dir);
Notice how we only update scroll_dir in the "scrolling" block and then we just keep scrolling. You'll probably want your rate of deceleration to be higher than your rate of acceleration or your camera will feel very muddy.
 
M

MANGOSENTINEL

Guest
Hmm I see for the deceleration

As for my GUI, it has been draw on the view in the Draw Event, I remember trying to use the Draw GUI event but I had issues with it for some reason that I've forgotten or maybe didn't fully understand at the time, so I'll be addressing that today as well. I'll let you know how the deceleration implementation goes in a bit.
 
M

MANGOSENTINEL

Guest
Deceleration turned out great! I think everything on that front if perfect now, thanks again!
 
Top