GameMaker: Click ‘n drag to pan view

(mouseover/click to play GIF)Or check here for a non-animated illustration.

This is a post about doing the kind of "click/tap and drag to scroll" effect in GameMaker.

Commonly used across games and software alike.

The code

You'll need a few variables for this:

// in a Create event
dragging = false;
drag_x = 0;
drag_y = 0;

Then, on mouse press/touch, you write down where the mouse was,

// typically in Step
if (mouse_check_button_pressed(mb_left)) {
    dragging = true;
    drag_x = mouse_x;
    drag_y = mouse_y;
}

and during drag operations you add the difference between the original and current mouse position to the camera coordinates so that the mouse cursor still points at the same spot of the room and the camera adjusts accordingly:

// also typically in Step
if (dragging) {
    if (mouse_check_button(mb_left)) {
        var camera = view_camera[0];
        camera_set_view_pos(camera,
            camera_get_view_x(camera) + drag_x - mouse_x,
            camera_get_view_y(camera) + drag_y - mouse_y
        );
    } else {
        dragging = false;
    }
}

So, for example, if you pressed the mouse at 100,100 and then moved it 10 pixels to the left, reported mouse_x would be 90, thus the view would be moved 10 pixels to the right to compensate.

And this will even work when your view is rotated and/or scaled! Pretty neat, isn't it

You can download a test project (for GM LTS and newer) from GitHub.

Limiting the view

If you want the view to stay within room boundaries while doing this, you can change the camera_set_view_pos call to something like this:

camera_set_view_pos(camera,
    clamp(
        camera_get_view_x(camera) + drag_x - mouse_x,
        0, room_width - camera_get_view_width(camera)
    ),
    clamp(
        camera_get_view_y(camera) + drag_y - mouse_y,
        0, room_height - camera_get_view_height(camera)
    )
);

If your view is rotated, this gets a little more complex, depending on how exactly you want to fit it within the boundaries.

For older GM

If you are using GameMaker: Studio or GM8.1, instead of the camera_set_view_pos call you would have

view_xview[0] += drag_x - mouse_x;
view_yview[0] += drag_y - mouse_y;

And for clamping, you could add

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

after those lines.

You may download the 2013 edition of this example for GM8.


And that's all!

This post was originally published on September 15, 2013 and revised in 2025 for better wording and code examples for modern GameMaker

Related posts:

29 thoughts on “GameMaker: Click ‘n drag to pan view

  1. This might seem really obvious – but remember to check “Enable the use of views” in your room settings (under the Views tab). Without this, your mouse drag will move things too quickly.

  2. how would I add an object on screen that i can drag around that also moves with the view when dragging it is done? In the objects step:

    if global.drag == true
    {
    x = mouse_x
    y = mouse_y
    }
    if global.drag == false
    {
    x = view_xview + x
    y = view_yview + y
    }

    Have any ideas getting this to work?

    • I’m not entirely sure what you are trying to accomplish – if you want the object to always be fixed at a position, you’d do something like

      x = view_xview + xstart;
      y = view_yview + ystart;

      since doing the way shown would just rapidly accelerate object past screen bounds as soon as view is moved (as you are basically using view coordinates as object speed).

      Perhaps readjust xstart\ystart (or make separate variables for this) if you want the object to be on-screen but draggable.

      • thanks so much for your response. Im basically trying to move an object around the view with the mouse button. But it fails to stay in position within the view once ive finished moving it.

        x = view_xview + xpos
        y = view_yview + ypos

        if global.move_obj == true
        {
        xpos = mouse_x
        ypos = mouse_y
        }

        ive taken what youve said into mind but am still having some trouble :S
        this launches the object off screen the higher the x and y values are.

  3. Thanks!
    I have no idea why I was updating draw_x and draw_y with new mouse values each step, in my own code. Now it’s working how it’s supposed to :)

  4. Hey, thanks for that.

    Is there a way to have the view not snap back to start when mouse button is released? I need to be able to scroll the view, and have it stop, then gain control over the mouse again to move objects around etc.

    • That does not seem to happen in the example. Do you have built-in view following enabled for some object in your game, perhaps?

        • Yes, you can set `view_object` to `noone` to stop the built-in following behaviour, and later set it back to `obj_player` (or whatever the name is in your case) to resume it.

  5. In these two lines, is there a reason you are not using the clamp function?
    view_xview = max(0, min(view_xview, room_width – view_wview))
    view_yview = max(0, min(view_yview, room_height – view_hview))

    Like this:
    view_xview = clamp(view_xview, 0, room_width – view_wview);
    view_yview = clamp(view_yview, 0, room_height – view_yview);

    I would think that the single function call simplifies the code a little and possibly runs a little faster, since there is no nested function calls.

    • clamp only exists in GameMaker: Studio and in later updates of GameMaker 8.1. Since most examples on my blog are intended to work in 8.0, 8.1, and Studio, current approach is used.
      In GameMaker 8.0 you can technically also use median instead of clamp, but I’m not sure, whether it’s less, or more confusing, than a min-max pair (which is pretty standard).
      On the performance… while a valid point, view dragging code is not really a good place to seek micro-optimizations in, as it is executed once per game step at best.

      • Hi, I found this article very useful… but i’d like to ask you a new question:
        Is there any way to have any inertia scrolling after the mouse is released?
        I don’t like the way the view scroll suddenly stops after I release the mouse button…
        Thanx in advance

        • You could store the distance moved per frame, like

          delta_x = drag_x - (mouse_x - view_xview) - view_xview
          view_xview += delta_x

          and then translate the last known delta into velocity when the mouse is released. Such “velocity” would be implemented as a simple pair of variables that are added to view x/y each step and reduced (“friction”) afterwards.

  6. Thankyou so much for this! Could not find this info anywhere!
    For anyone using love2d this is how I added it, with using the camera from here http://nova-fusion.com/2011/04/19/cameras-in-love2d-part-1-the-basics/. Not sure if there is a better way as I’m fairly new to it, but it works… (map.width and map.height are the size of map)

    function love.update(dt)
    if love.mouse.isDown("l") then
    dragging(true, drag_x, drag_y)
    end
    end

    function dragging(mouseDown, drag_x, drag_y)
    if mouseDown == true then
    --dragging logic
    local x, y = camera:mousePosition()
    camera._x = camera._x + drag_x - x
    camera._y = camera._y + drag_y - y
    --boundaries
    camera._x = math.max(0, math.min(camera._x, map.width - love.graphics.getWidth()))
    camera._y = math.max(0, math.min(camera._y, map.height - love.graphics.getHeight()))
    end
    end

    function love.mousepressed(x, y, button)
    if button == 'l' then
    drag_x, drag_y = camera:mousePosition()
    end
    end

    function love.mousereleased( x, y, button )
    if button == 'l' then
    mouseDown = false
    end
    end

  7. Hi there! I’ve noticed your blog has russian! Is it your own decision (and are YOU RUSSIAN) or just a default thing?

    • Own decision. You can kind of tell from my About page – “I live in Ukraine […] I speak Ukrainian, Russian, and English freely.”.

      And yes, this means many posts are written twice.

Leave a Reply

Your email address will not be published. Required fields are marked *
Note: JavaScript is currently required to post comments.

This site uses Akismet to reduce spam. Learn how your comment data is processed.