JujuAdams / Bulb

2D lighting and shadows for GameMaker 2022 LTS
http://www.jujuadams.com/Bulb/
MIT License
86 stars 10 forks source link

Surface overlap when split screen. #38

Closed dougietubby closed 3 months ago

dougietubby commented 3 months ago

Video of Issue: Here

I've gotten Bulb to work in split screen (using Juju's Input) but when players get close to each other, the surface from one viewport is drawn a second time on the other, causing this weird visual glitch. Without using updating the surface dimensions to the camera, the lighting surface only will apply to one viewport.

The following is the added code I've used to create split screen cameras.

Begin Step


/// @description Insert description here
// You can write your code in this editor
#region Copied Code
//Feather disable all

//Scan for new input from a new source, and assign it to an unconnected player
var _new_source = input_source_detect_new();
if (_new_source != undefined)
{
    var _i = 0;
    repeat(INPUT_MAX_PLAYERS)
    {
        if (!input_player_connected(_i))
        {
            input_source_set(_new_source, _i, true);
            input_verb_consume(all, _i);
            break;
        }

        ++_i;
    }
}

//If a player presses a pause button, disconnect them
var _i = 0;
repeat(INPUT_MAX_PLAYERS)
{
    if (input_player_connected(_i))
    {
        if (input_check_pressed("leave", _i)) input_source_clear(_i);

    }

    ++_i;
}

//Do something when we see a change in the state of our players
var _status = input_players_get_status();
if (is_struct(_status))
{
    //We've seen at least one disconnection - destroy an existing player object
    if (array_length(_status.new_disconnections) > 0)
    {
        var _i = 0;
        repeat(array_length(_status.new_disconnections))
        {
            var _new_player = _status.new_disconnections[_i];
            with(oPlayer)
            {
                if (padID == _new_player) instance_destroy();   
            }
            ++_i;
        }
        scr_create_camera();
    }

    //We've seen at least one connection - create a new player object
    if (array_length(_status.new_connections) > 0)
    {
        var _i = 0;
        repeat(array_length(_status.new_connections))
        {
            var _new_player = _status.new_connections[_i];

            #region Check for double spawns
            //Make sure we don't accidentally spawn one than one object per player

            var _existing = false;
            with(oPlayer)   
            {
                if (padID == _new_player)
                {
                    _existing = true;
                    break;
                }
            }

            #endregion

            if (!_existing)
            {
                //Spawn a player at the necessary spawn point
                with(oPlayer_spawnpoint)
                {
                    if (padID == _new_player)
                    {
          var _ghost = instance_create_layer(x,y,"Player_And_Renderer", oPlayer);
          _ghost.padID = padID;

                    }
                }

            }

            ++_i;
        }
    scr_create_camera();
    }
}

#endregion

Here is the scr_create_camera script:

function scr_create_camera(){
    var _count = instance_number(oPlayer);
    var _width = 640;   // ADD: Get dimensions of display and scale down.
    var _height = 360;
    var _scale = 3;

    #region Cleanup Old Cameras
    //  Destroy all previous cameras
    if (is_undefined(global.Cameras) == false){
        for (var i = 0; i < array_length(global.Cameras); i++){
            camera_destroy(global.Cameras[i]);  
        }
    }

    //  Reset Cameras
    global.Cameras = undefined;
    view_visible[0] = false;
    view_visible[1] = false;
    view_visible[2] = false;
    view_visible[3] = false;
    #endregion

    //  Create NEW Cameras
    for (var i = 0; i < _count; i++){
        var _player = instance_find(oPlayer, i);
        view_visible[i] = true;

        global.Cameras[i] = camera_create_view(0,0,_width,_height,0,_player,-1,-1,_width,_height);
        view_set_camera(i, global.Cameras[i]);
        lighting.SetSurfaceDimensionsFromCamera(global.Cameras[i]);

        //  Set Size based on player count.
        #region 1 Player
        if (i == 0){
            view_set_xport(0, 0);   
            view_set_yport(0, 0);
            view_set_wport(0, _width);
            view_set_hport(0, _height);
            camera_set_view_size(global.Cameras[0], _width, _height);
            lighting.SetSurfaceDimensionsFromCamera(global.Cameras[0]);

        }
        #endregion

        #region 2 Players
        if (i == 1){
            view_set_xport(0, 0);   
            view_set_yport(0, 0);
            view_set_wport(0, _width);
            view_set_hport(0, _height/2);
            camera_set_view_size(global.Cameras[0], _width, _height/2);
            lighting.SetSurfaceDimensionsFromCamera(global.Cameras[0]);

            view_set_xport(1, 0);
            view_set_yport(1, _height/2);
            view_set_wport(1, _width);
            view_set_hport(1, _height/2);
            camera_set_view_size(global.Cameras[1], _width, _height/2);
            lighting.SetSurfaceDimensionsFromCamera(global.Cameras[1]);

        }
        #endregion

        ... etc

    //  Reset Size
    window_set_size(_width*_scale, _height*_scale);
    surface_resize(application_surface,_width*_scale,_height*_scale);

}
JujuAdams commented 3 months ago

GameMaker's split screen feature (achieved by using multiple views) works by drawing the entire room for each view. This means you're drawing each Bulb renderer for each view. This is not what we want.

You can use view_current to check which GameMaker view is currently being rendered. You can then check against view_current when drawing your Bulb renderer to the screen with .DrawOnCamera(). If view_current is 0 then only draw the first renderer; if view_current is 1 then only draw the second renderer.

dougietubby commented 3 months ago

Thanks for the help Mr. JujuAdams.

I added the check for current view within the following "Draw End" event. I fixes the issue for 2 and 4 player split screen (since all viewports have the same resolution), but distorts the lighting surface when there's 3 players. Video is here.

Draw End

var _t = get_timer();

// Update Lighting System based on camera
if (global.Cameras != undefined){
var _height = array_length(global.Cameras);

    for (var i = 0; i < _height; i++){
        if (view_current == i){
            lighting.UpdateFromCamera(global.Cameras[i]);
            lighting.DrawOnCamera(global.Cameras[i]);
        }
    }
}
drawEndTime = get_timer() - _t;

Do I add a check for 3 players and try rendering it differently? Or would you recommend simply making 3 players all have the same viewport resolution?

Thanks again, Dougie (BIG FAN!!!!)

JujuAdams commented 3 months ago

I don't know. It is likely that you're setting the wrong surface dimensions for the widescreen player but that is something you will need to diagnose.

dougietubby commented 3 months ago

I'll give it a shot. Thanks again for the help!