Nineslice script with scaling

NeoShade

Member
Hi All,

I'm trying something a little weird in my current project, and that is to be able to scale my entire game somewhat arbitrarily. I think I have a fair underestanding of how to get it all to work, but i'm running into a slight bug when it comes to implementing a nineslice script. I suspect that it might come down to the rendering engine, but I'm not sure, so figured I'd ask the question here. There's a fair bit to dive into here, so sorry in advance, and I understand if nobody wants to dig through it all.

This bug can be reproduced in a blank project with one object, one sprite (288*288) and one script.


Ob_Camera Create Event
GML:
// Scale limits
display_scale = min(display_get_width() div 80, display_get_height() div 45);
game_maxscale = min(48, display_scale);
game_minscale = 8;

// Set scale variables
game_scale = game_maxscale - 5;
game_width = game_scale * 80;
game_height = game_scale * 45;
game_oldscale = game_scale;

// Update the camera size
camera_update_size();

Ob_Camera Step Event
GML:
// Increase scale
if (keyboard_check_pressed(vk_pageup))
&& (game_scale < game_maxscale)
    {
    game_oldscale = game_scale;
    game_scale += 1;
    camera_update_size();
    if (game_scale == game_maxscale)
        { window_set_fullscreen(true); }
    }


// Decrease scale
if (keyboard_check_pressed(vk_pagedown))
&& (game_scale > game_minscale)
    {
    window_set_fullscreen(false);
    game_scale -= 1;
    camera_update_size();
    }
   

// Fullscreen toggle
if (keyboard_check_pressed(vk_f11))
    {
    // Toggle to fullscreen mode
    if (window_get_fullscreen() == false)
        {
        game_oldscale = game_scale;
        game_scale = game_maxscale;
        camera_update_size();
        window_set_fullscreen(true);
        }
    // Toggle to windowed mode
    else
        {
        window_set_fullscreen(false);
        game_scale = game_oldscale;
        camera_update_size();
        }
    }

Ob_Camera Alarm0 Event
GML:
// Recenter window
window_center();

Ob_Camera Room Start Event
GML:
// Prepare views in each room
view_visible[0] = true;
view_enabled = true;

Ob_Camera Draw GUI Event
GML:
// Draw debug grid
//draw_set_colour($202020);
//for (var i = 0; i < game_width; i += game_scale)
//    { draw_line(i, 0, i, game_height); }
//for (var i = 0; i < game_height; i += game_scale)
//    { draw_line(0, i, game_width, i); }

// Draw camera debug values
draw_set_colour(c_yellow);
draw_text(8, 8,  "CAMERA");
draw_set_colour(c_white);
draw_text(8, 24, game_minscale);
draw_text(8, 40, game_scale);
draw_text(8, 56, game_maxscale);
draw_text(8, 72, window_get_fullscreen());

// Draw nineslice debug values
draw_set_colour(c_yellow);
draw_text(96, 8,  "NINESLICE");
draw_set_colour(c_white);
var s = (game_scale / 48);
draw_text(96, 24, s);
draw_text(96, 40, s * 48);

draw_nineslice();

Script
GML:
// Performs all tasks required to resize the game window
function camera_update_size()
    {
    // Recalculate the game scale
    game_width = game_scale * 80;
    game_height = game_scale * 45;
   
    // Update the window, surface, and view size
    window_set_size(game_width, game_height);
    surface_resize(application_surface, game_width, game_height);
    camera_set_view_size(view_camera[0], game_width, game_height);
   
    // Recenter the window
    alarm[0] = 1;
    }
   
   
function draw_nineslice()
    {
   
    var x1 = game_scale*5;
    var x2 = game_width-game_scale*6;
    var y1 = game_scale*5;
    var y2 = game_scale*11;
   
    var tile = 96;
    var scale = game_scale / tile;
       
    // Corners
    draw_sprite_part_ext(Sp_Nineslice, 0, 0*tile, 0*tile, 1*tile, 1*tile, x1, y1, scale, scale, c_white, 1);
    draw_sprite_part_ext(Sp_Nineslice, 0, 2*tile, 0*tile, 1*tile, 1*tile, x2, y1, scale, scale, c_white, 1);
    draw_sprite_part_ext(Sp_Nineslice, 0, 0*tile, 2*tile, 1*tile, 1*tile, x1, y2, scale, scale, c_white, 1);
    draw_sprite_part_ext(Sp_Nineslice, 0, 2*tile, 2*tile, 1*tile, 1*tile, x2, y2, scale, scale, c_white, 1);
   
    var rows = ((x2 - x1) div (tile * scale)) - 1;
    var cols = ((y2 - y1) div (tile * scale)) - 1;
   
    // Horizontal edges
    for (var i = 0; i < rows; ++i)
        {
        draw_sprite_part_ext(Sp_Nineslice, 0, 1*tile, 0*tile, 1*tile, 1*tile, x1+(tile*scale)+(i*tile*scale), y1, scale, scale, c_white, 1);
        draw_sprite_part_ext(Sp_Nineslice, 0, 1*tile, 2*tile, 1*tile, 1*tile, x1+(tile*scale)+(i*tile*scale), y2, scale, scale, c_white, 1);
        }
   
    // Vertical edges
    for (var i = 0; i < cols; ++i)
        {
        draw_sprite_part_ext(Sp_Nineslice, 0, 0*tile, 1*tile, 1*tile, 1*tile, x1, y1+(tile*scale)+(i*tile*scale), scale, scale, c_white, 1);
        draw_sprite_part_ext(Sp_Nineslice, 0, 2*tile, 1*tile, 1*tile, 1*tile, x2, y1+(tile*scale)+(i*tile*scale), scale, scale, c_white, 1);
        }
    }

Finally, tthe issue I'm having. Check out the attached screenshot, you'll see that there are spots near the corners of the frame that the tiling is not perfect. This only happens at certain levels of scale.
In theory, this shouldn't happen, because the sprite parts should be scaling to a size that then tiles correctly for the scale level. Can't seem to work out why this is happening. Any help would be much apreciated.
 

Attachments

NeoShade

Member
Thanks FrostyCat, I figured it would be something along those lines, I just wasn't sure if there was a more elegant way to fix it. Regardless, drawing a bit extra works like a charm. For anybody wondering, I made the following changes to the nineslice function (note the +5 pixels added to some of the tile sizes)

GML:
function draw_nineslice()
    {
    
    var x1 = game_scale*5;
    var x2 = game_width-game_scale*6;
    var y1 = game_scale*5;
    var y2 = game_scale*11;
    
    var tile = 96;
    var scale = game_scale / tile;
        
    // Corners
    draw_sprite_part_ext(Sp_Nineslice, 0, 0*tile, 0*tile, 1*tile+5, 1*tile+5, x1, y1, scale, scale, c_white, 1);
    draw_sprite_part_ext(Sp_Nineslice, 0, 2*tile, 0*tile, 1*tile,   1*tile+5, x2, y1, scale, scale, c_white, 1);
    draw_sprite_part_ext(Sp_Nineslice, 0, 0*tile, 2*tile, 1*tile+5, 1*tile,   x1, y2, scale, scale, c_white, 1);
    draw_sprite_part_ext(Sp_Nineslice, 0, 2*tile, 2*tile, 1*tile,   1*tile,   x2, y2, scale, scale, c_white, 1);
    
    var rows = ((x2 - x1) div (tile * scale)) - 1;
    var cols = ((y2 - y1) div (tile * scale)) - 1;
    
    // Horizontal edges
    for (var i = 0; i < rows; ++i)
        {
        draw_sprite_part_ext(Sp_Nineslice, 0, 1*tile, 0*tile, 1*tile, 1*tile+5, x1+(tile*scale)+(i*tile*scale), y1, scale, scale, c_white, 1);
        draw_sprite_part_ext(Sp_Nineslice, 0, 1*tile, 2*tile, 1*tile, 1*tile,   x1+(tile*scale)+(i*tile*scale), y2, scale, scale, c_white, 1);
        }
    
    // Vertical edges
    for (var i = 0; i < cols; ++i)
        {
        draw_sprite_part_ext(Sp_Nineslice, 0, 0*tile, 1*tile, 1*tile+5, 1*tile, x1, y1+(tile*scale)+(i*tile*scale), scale, scale, c_white, 1);
        draw_sprite_part_ext(Sp_Nineslice, 0, 2*tile, 1*tile, 1*tile,   1*tile, x2, y1+(tile*scale)+(i*tile*scale), scale, scale, c_white, 1);
        }
    
    // Center
    for (var ix = 0; ix < rows; ++ix) {
    for (var iy = 0; iy < cols; ++iy)
        {
        draw_sprite_part_ext(Sp_Nineslice, 0, 1*tile, 1*tile, 1*tile, 1*tile, x1+(tile*scale)+(ix*tile*scale), y1+(tile*scale)+(iy*tile*scale), scale, scale, c_white, 1);
        }}
    }
 
Top