GameMaker: Automatic pixel-perfect scaling

A comparison between default scaling behaviour and the example's scaling behaviour
A comparison

This post explains how to replicate GameMaker: Studio's application_surface in older versions.

Doing so is more or less a requirement if you want a scaled window with pixel-perfect rendering and/or do not desire to modify your tilesets (although a tool had been made since to help with this).

The idea

The premise is simple enough - you force the game to draw into a surface, and then draw the said surface after everything else is done drawing.

The code

For this you'll need two objects:

Information about object: obj_pscale

  • Sprite: <no sprite>
  • Solid: false
  • Visible: true
  • Depth: 100000000
  • Persistent: true
  • Parent: <no parent>
  • Mask: <same as sprite>
  • Create Event:

    • execute code:
      if instance_number(object_index) > 1 then instance_destroy()
      if !variable_global_exists('pscale') global.pscale = true
      // find if there's "job" to do here as such:
      exists = global.pscale
      if (exists) {
          screen = surface_create(view_wview, view_hview)
          // fallback if surfaces are not supported:
          if (!surface_exists(screen)) exists = false
      }
      // nothing to do here for a reason:
      if (!exists) {
          instance_destroy()
          exit
      }
      // find scaling parameters:
      scale_x = view_wport / view_wview
      scale_y = view_hport / view_hview
      // create min-depth helper instance:
      scale_inst = instance_create(x, y, obj_pscale2)
      scale_inst.scale_x = scale_x
      scale_inst.scale_y = scale_y
      scale_inst.scale_inst = id
      

    Destroy Event:

    • execute code:
      // don't forget to clean-up:
      surface_free(screen)
      

    Draw Event:

    • execute code:
      d3d_transform_set_identity()
      // If you are porting this example back to GM6/GM7, uncomment code below.
      // For some reason things work differently there.
      /*
      d3d_transform_add_translation(-view_xview, -view_yview, 0)
      surface_set_target(screen)
      if background_showcolor draw_clear(background_color)
      */
      

    As of GM8.1, d3d_transform_set_identity() line seems unnecessary, but also does no harm.

    Information about object: obj_pscale2

  • Sprite: <no sprite>
  • Solid: false
  • Visible: true
  • Depth: -1000000
  • Persistent: false
  • Parent: <no parent>
  • Mask: <same as sprite>
  • Draw Event:

    • execute code:
      /* this object serves for drawing surface
         with game graphics to screen, scaled. */
      // reset drawing target and draw scaled surface:
      surface_reset_target()
      d3d_transform_set_identity()
      // if main scaling instance is gone, destroy this one too:
      if (!instance_exists(scale_inst)) {
          instance_destroy()
      } else {
          draw_surface_ext(scale_inst.screen, 0, 0, scale_x, scale_y, 0, c_white, 1)
          surface_set_target(scale_inst.screen)
      }
      

    In Studio, you would have to check for surface still existing, but older versions did this work for you (for better or worse).

    With that done, you create obj_pscale when you need it and that's all.


    You may also grab the example GMK used for the screenshot at the beginning of this post:

    Example GMK

    Related posts:

    3 thoughts on “GameMaker: Automatic pixel-perfect scaling

    Leave a Reply

    Your email address will not be published. Required fields are marked *

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