OpenBOR Split Screen - Prototype Test

Kratus

OpenBOR Developer
Staff member
Hey guys,

Me and @kimono are working on a split screen feature to expand the mods possibilities. This is in a very experimental step and needs a lot of improvements yet, so you can find bugs but it's working.

Basically, most native engine functions related to screen scrolling are simulated with scripts on the updated.c event and many codes are the same as the "zoom" script. The "core" functions in this case are the "drawspriteq/drawscreen".

Currently I'm doing some experiments to understand better how it works once we don't have much info about. Technically, the drawspriteq draws all sprites to the screen and moves his offset depending on the player movement. In this case the native "xpos" variant is totally disabled by creating a "wait" in the "at 0" otherwise it can cause some undesired effects like screen shaking or wrong panel positions.

In fact the player is moving on the level but once the "xpos" stays on the "at 0" forever, the "subject_to_screen" property need to be disabled on all entities. Most things will need to be replicated with scripts or alternative functions in this prototype version, but I'm working to improve it and turn the level design easier.

Note that in the video I'm using a resolution of 640x480, so this way the game looks better than a resized 320x240 to 160x120 resolution. The advantage on using a doubled resolution is because you can rescale the 640x480 to look as a native 320x240 simply using drawmethod in case you have only 1 player in-game.

Any tips and suggestions are always welcome, we hope it can be useful for new mods in the future.

How to use it:
1) You will need the file "data/script.txt", set your "alwaysupdate" function to 1
2) You will need the file "data/scripts/updated.c", copy and paste the entire script on it
3) Create an "wait" with "at 0" inside your level file, same as the example below

Level design example:
group 100 100
at 0
wait
at 0

spawn Ralf
health 1000000
aggression -10000
coords 600 230
at 0

Updated.c file content:
C:
void main()
{
    if(openborvariant("in_titlescreen")){
        clearglobalvar();
        clearlocalvar();
    }

    if(openborvariant("in_level")){
        if(!openborvariant("pause")){
            splitScreen(0);
            splitScreen(1);
            clearspriteq();
        }
    }
}

void splitScreen(int player)
{//Simulates a "split screen" feature (Kratus 02-2022)
 //Don't forget to set a "wait" in "at 0" on every level to lock the "xpos" scrolling to avoid screen "shaking"
 //Most things on the game will need to be scripted, once most native functions will not work in the level design

    void ent = getplayerproperty(player, "entity");
  
    if(ent){
        void vScreen    = openborvariant("vscreen");
        float x            = getentityproperty(ent, "x");
        float hRes        = openborvariant("hresolution");
        float vRes        = openborvariant("vresolution");
        float xPos        = openborvariant("xpos");
        float minZ        = openborvariant("PLAYER_MIN_Z");
        float maxZ        = openborvariant("PLAYER_MAX_Z");
        float screenDif    = (vRes/2)*player;
        float scaleX    = 256;
        float scaleY    = 256;
        float zAdd        = 500;
        float xVel        = 1;

        //START AND DEFINE VARIABLES
        if(getglobalvar("screenP"+player) == NULL()){
            changeopenborvariant("viewportx", 0);
            changeopenborvariant("viewporty", 0);
            changeopenborvariant("viewportw", openborvariant("levelwidth"));
            changeopenborvariant("viewporth", openborvariant("levelheight"));
            changeentityproperty(ent, "position", xPos+(hRes/6), minZ+(screenDif/8));
            setglobalvar("xOffset"+player, 0);
            setglobalvar("screenP"+player, allocscreen(openborvariant("levelwidth"), vRes));
        }

        //READJUST SCREENS FOR 1 OR TWO PLAYERS IN REAL TIME
        //DISABLED BECAUSE NEED IMPROVEMENTS
        if(openborvariant("count_players") <= 1){
            // scaleX = 512;
            // scaleY = 512;
            // hRes = hRes/2;
            // screenDif = 0;
        }
      
        //CLEAR SCREEN
        clearscreen(getglobalvar("screenP"+player));

        //ADJUST LEVEL OFFSET AS A "FAKE" XPOS FOR EACH SCREEN IN REAL TIME
        //USED WHEN THE CHARACTER IS MOVED TO RIGHT DIRECTION IN REAL TIME
        if(x + getglobalvar("xOffset"+player) > (hRes/2)){
            if(getglobalvar("xOffset"+player) > (hRes-openborvariant("levelwidth"))){
                setglobalvar("xOffset"+player, getglobalvar("xOffset"+player)-xVel);
                changeentityproperty(ent, "subject_to_screen", 0);
                //changelevelproperty("wall", 0, "x", x-(hRes/2)); //TESTED TO REPLACE THE NATIVE BLOCKADE FEATURE
            }
        }
      
        //USED WHEN THE CHARACTER IS MOVED TO LEFT DIRECTION IN REAL TIME
        //DISABLED TO WORK ONLY FOR THE RIGHT DIRECTION, BUT THE LEFT DIRECTION WORKS CORRETLY TOO
        if(x + getglobalvar("xOffset"+player) < (hRes/2)){
            if(getglobalvar("xOffset"+player) < 0){
                //setglobalvar("xOffset"+player, getglobalvar("xOffset"+player)+xVel);
                changeentityproperty(ent, "subject_to_screen", 0);
            }
            else
            {
                changeentityproperty(ent, "subject_to_screen", 1);
            }
        }
      
        //DEBUG INFO
        drawstring(getglobalvar("xOffset"+player)/10000, 50+screenDif, 0, openborvariant("levelwidth"), openborconstant("MAX_INT")/100000);
        drawstring(getglobalvar("xOffset"+player)/10000, 60+screenDif, 0, getglobalvar("xOffset"+player), openborconstant("MAX_INT")/100000);
        drawstring(getglobalvar("xOffset"+player)/10000, 70+screenDif, 0, x, openborconstant("MAX_INT")/100000);

        //DRAW EVERYTHING IN REAL TIME
        drawspriteq(getglobalvar("screenP"+player), 0, openborconstant("MIN_INT"), maxZ+zAdd, getglobalvar("xOffset"+player), 0);
        changedrawmethod(NULL(),"reset", 1);
        changedrawmethod(NULL(),"enabled", 1);
        changedrawmethod(NULL(),"scalex", scaleX);
        changedrawmethod(NULL(),"scaley", scaleY);
        drawscreen(getglobalvar("screenP"+player), 0, screenDif, (maxZ*9)+player);
        drawspriteq(vScreen, 0, maxZ+zAdd+1, openborconstant("MAX_INT"), 0, 0);
    }
    else
    {
        if(getglobalvar("screenP"+player) != NULL()){setglobalvar("screenP"+player, NULL());}
    }
}

 
Last edited:
This second post is about a zoom effect for bosses. This is ready to use in a simple way by editing the level file.

Same as the split screen script, you will need the "alwaysupdate" and "updated.c" procedures.

Level design example:
spawn obiwanE
@script
void main()
{//Zoom script for bosses (Kratus 02-2022)

//DO NOT MODIFY
setglobalvar("zoomEnt", getlocalvar("self")); //current focused entity
setglobalvar("zoomFlag", 1); //flag to enable zoom, need to be always 1

//CAN MODIFY TO ADJUST ACCORDING TO DIFFERENT CONDITIONS, LIKE LEVEL SIZE, BOSS POSITION, ETC
setglobalvar("zoomX", 0); //value used for zoom X position adjustment
setglobalvar("zoomY", 20); //value used for zoom Y position adjustment
setglobalvar("zoomMin", 256); //minimum zoom value, 256 is the engine default
setglobalvar("zoomMax", 1024); //maximum zoom value
setglobalvar("zoomAdd", 8); //rate to increase/decrease zoom effect
setglobalvar("offsetAdd", 1.5); //rate to move the screen offset
setglobalvar("startDelay", 50); //initial delay before the process starts
setglobalvar("stopDelay", 400); //stop delay before zoom reversion
}
@end_script
coords 800 220
map 1
boss 1
at 0

Updated.c file:
C:
void main()
{
    if(openborvariant("in_titlescreen")){clearglobalvar();clearlocalvar();}
    if(openborvariant("in_level")){zoomBoss();}
}

void zoomBoss()
{//Zoom script for bosses (Kratus 02-2022)
    if(getglobalvar("zoomFlag") == 1){
        void vScreen    = openborvariant("vscreen");
        void zoomEnt    = getglobalvar("zoomEnt");
        float x            = getentityproperty(zoomEnt, "x");
        float y            = getentityproperty(zoomEnt, "y");
        float z            = getentityproperty(zoomEnt, "z");
        float layer        = 1000;
        float maxZ        = openborvariant("PLAYER_MAX_Z")+layer;
        float xPos        = openborvariant("xpos");
        float yPos        = openborvariant("ypos");
        float rate        = 4;
        float time        = openborvariant("elapsed_time")/rate;
        float zoomX        = getglobalvar("zoomX");
        float zoomY        = getglobalvar("zoomY");
        float zoomMin    = getglobalvar("zoomMin");
        float zoomMax    = getglobalvar("zoomMax");
        float zoomAdd    = getglobalvar("zoomAdd");
        float offsetAdd    = getglobalvar("offsetAdd");
        float startDelay= getglobalvar("startDelay");
        float stopDelay    = getglobalvar("stopDelay");
      
        //START VARIABLES
        if(getglobalvar("timeZoom") == NULL()){
            changeopenborvariant("viewportx", 0);
            changeopenborvariant("viewporty", 0);
            changeopenborvariant("viewportw", openborvariant("levelwidth"));
            changeopenborvariant("viewporth", openborvariant("levelheight"));
            changeopenborvariant("textbox", 1);
            setlocalvar("zoomScale", zoomMin);
            setlocalvar("xOffset", 0);
            setglobalvar("timeZoom", time+startDelay);
        }

        //CREATE A SCREEN AND SAVE IN A VARIABLE
        if(getglobalvar("allocScr") == NULL()){
            setglobalvar("allocScr", allocscreen(openborvariant("levelwidth"), openborvariant("vResolution")));
        }
      
        //ADJUST COORDINATES
        x = x-zoomX-xPos;
        y = z-zoomY-yPos;

        //CLEAR ANY PREVIOUS SCREEN VALUE
        clearscreen(getglobalvar("allocScr"));

        //DRAW EVERYTHING TO THE SCREEN
        if(!openborvariant("pause")){
            drawspriteq(getglobalvar("allocScr"), 0, openborconstant("MIN_INT"), maxZ, getlocalvar("xOffset"), 0);
            changedrawmethod(NULL(),"reset", 1);
            changedrawmethod(NULL(),"enabled", 1);
            changedrawmethod(NULL(),"scalex", getlocalvar("zoomScale"));
            changedrawmethod(NULL(),"scaley", getlocalvar("zoomScale"));
            changedrawmethod(NULL(),"centerx", x);
            changedrawmethod(NULL(),"centery", y);
            drawscreen(getglobalvar("allocScr"), x, y, maxZ+1);
            drawspriteq(vScreen, 0, maxZ+1, maxZ+1, 0, 0);
            drawspriteq(vScreen, 0, maxZ+2, openborconstant("MAX_INT"), 0, 0);
            clearspriteq();
        }
      
        //INCREASE OR DECREASE ZOOM ACCORDING TO DEFINED RATE
        if(time > getglobalvar("timeZoom")){

            //ZOOM INCREASE PROCESS
            if(getlocalvar("zoomStop") == NULL() && getlocalvar("zoomScale") < zoomMax){
                setlocalvar("zoomScale", getlocalvar("zoomScale")+zoomAdd);
                setlocalvar("xOffset", getlocalvar("xOffset")-offsetAdd);
            }

            //REACHED THE MAX ZOOM VALUE?? GIVE A LITTLE BREAK
            if(getlocalvar("zoomStop") == NULL() && getlocalvar("zoomScale") >= zoomMax){
                setlocalvar("zoomStop", openborvariant("elapsed_time")+stopDelay);
            }

            //THE LITTLE BREAK ENDED?? START THE ZOOM REVERSAL
            if(getlocalvar("zoomStop") < openborvariant("elapsed_time")){
                setlocalvar("zoomRevert", 1);
            }

            //ZOOM DECREASE PROCESS
            if(getlocalvar("zoomRevert") == 1 && getlocalvar("zoomScale") > zoomMin){
                setlocalvar("zoomScale", getlocalvar("zoomScale")-(zoomAdd*2));
                setlocalvar("xOffset", getlocalvar("xOffset")+offsetAdd*2);
            }

            //REACHED THE MIN ZOOM VALUE?? DISABLE ZOOM EFFECT
            if(getlocalvar("zoomRevert") == 1 && getlocalvar("zoomScale") <= zoomMin){
                changeopenborvariant("textbox", NULL());
                setglobalvar("zoomFlag", 0);
            }
            setglobalvar("timeZoom", time);
        }
    }
  
    //CLEAR ALL USED VARIABLES
    if(getglobalvar("zoomFlag") == 0){
        free(getglobalvar("allocScr"));
        setlocalvar("zoomScale", NULL());
        setlocalvar("zoomStop", NULL());
        setlocalvar("zoomRevert", NULL());
        setlocalvar("xOffset", NULL());
        setglobalvar("allocScr", NULL());
        setglobalvar("zoomEnt", NULL());
        setglobalvar("zoomX", NULL());
        setglobalvar("zoomY", NULL());
        setglobalvar("zoomMin", NULL());
        setglobalvar("zoomMax", NULL());
        setglobalvar("zoomAdd", NULL());
        setglobalvar("offsetAdd", NULL());
        setglobalvar("startDelay", NULL());
        setglobalvar("stopDelay", NULL());
        setglobalvar("timeZoom", NULL());
        setglobalvar("zoomFlag", NULL());
    }
}

 
Last edited:
Both of these are looking pretty good. I like how you are being smart and building in steps. I'll take a look and see if there are any optimizations that jump out. Great work!

DC
 
Thanks Kratus for making the effort to explain any part of your code, this is greatly appreciated! In case if I wish to make a split screen project in wide screen resolution, I must choose the correct number of pixel size to not have sprite distorsions :) .
The horizontal split screen makes sense for competitive games like versus racing challenge for all sorts .

What could be very awesome is a vertical split screen when player 1 and player 2 are far away from each other and no split screen when they are on the same screen.
Thanks again for opening gates to new possibilities at the Openbor engine ;) .

P.S. : The zoom entity script is perfect and thanks for making it easy to use :D .
 
@Kratus
the split screen script should work well enough to allow for a stereo 3D platormer game like MArio Contra, Sonic or Shinobi style.
all that has to be done is to shift the camera position of the second camera or viewport & that's it

3D Beatthemups require one screen to use one graphic for the top screen panel layer/panel entities, and a different graphics for the bottom panel layer/panel entities.
backgrounds & fglayers can be recycled but need shifted values for the different screens, and enemy & player sprites need some other treatment to allow z-based focus and zoom (zoom optional). I relly need to make a simulation of this on video....
 
@DCurrent
Both of these are looking pretty good. I like how you are being smart and building in steps. I'll take a look and see if there are any optimizations that jump out. Great work!
Thanks man :)
Any optimization will be always welcome, you know a lot more than me about how the engine works and would be good if we could replicate the native functionality with scripts. I need to understand better how drawspriteq (and other related functions) works, until now all the progress was based on making experiments.


@kimono
In case if I wish to make a split screen project in wide screen resolution, I must choose the correct number of pixel size to not have sprite distorsions :) .
Yes, in case you want a widescreen using 480x270 resolution, I suggest using a doubled resolution like 960x540, because this way you will avoid distortions.
The problem is if you use 480x270 as native and decrease it by half, this way you have distortions.

What could be very awesome is a vertical split screen when player 1 and player 2 are far away from each other and no split screen when they are on the same screen.
Yeah, certainly it's a good idea for the next step


@oldyz
3D Beatthemups require one screen to use one graphic for the top screen panel layer/panel entities, and a different graphics for the bottom panel layer/panel entities.
It's a good point, in fact I had problems synchronizing the bglayer/fglayers with panels but in this case I can draw multiple screens for each player. Thanks!

enemy & player sprites need some other treatment to allow z-based focus and zoom
In this case we can use the same method I said to kimono. We can use a doubled resolution because this way the image will not be distorted if rescaled by half (ex: 640x480 to 320x240).
 
This second post is about a zoom effect for bosses. This is ready to use in a simple way by editing the level file.

Same as the split screen script, you will need the "alwaysupdate" and "updated.c" procedures.

Level design example:


Updated.c file:
C:
void main()
{
    if(openborvariant("in_titlescreen")){clearglobalvar();clearlocalvar();}
    if(openborvariant("in_level")){zoomBoss();}
}

void zoomBoss()
{//Zoom script for bosses (Kratus 02-2022)
    if(getglobalvar("zoomFlag") == 1){
        void vScreen    = openborvariant("vscreen");
        void zoomEnt    = getglobalvar("zoomEnt");
        float x            = getentityproperty(zoomEnt, "x");
        float y            = getentityproperty(zoomEnt, "y");
        float z            = getentityproperty(zoomEnt, "z");
        float layer        = 1000;
        float maxZ        = openborvariant("PLAYER_MAX_Z")+layer;
        float xPos        = openborvariant("xpos");
        float yPos        = openborvariant("ypos");
        float rate        = 4;
        float time        = openborvariant("elapsed_time")/rate;
        float zoomX        = getglobalvar("zoomX");
        float zoomY        = getglobalvar("zoomY");
        float zoomMin    = getglobalvar("zoomMin");
        float zoomMax    = getglobalvar("zoomMax");
        float zoomAdd    = getglobalvar("zoomAdd");
        float offsetAdd    = getglobalvar("offsetAdd");
        float startDelay= getglobalvar("startDelay");
        float stopDelay    = getglobalvar("stopDelay");
     
        //START VARIABLES
        if(getglobalvar("timeZoom") == NULL()){
            changeopenborvariant("viewportx", 0);
            changeopenborvariant("viewporty", 0);
            changeopenborvariant("viewportw", openborvariant("levelwidth"));
            changeopenborvariant("viewporth", openborvariant("levelheight"));
            changeopenborvariant("textbox", 1);
            setlocalvar("zoomScale", zoomMin);
            setlocalvar("xOffset", 0);
            setglobalvar("timeZoom", time+startDelay);
        }

        //CREATE A SCREEN AND SAVE IN A VARIABLE
        if(getglobalvar("allocScr") == NULL()){
            setglobalvar("allocScr", allocscreen(openborvariant("levelwidth"), openborvariant("vResolution")));
        }
     
        //ADJUST COORDINATES
        x = x-zoomX-xPos;
        y = z-zoomY-yPos;

        //CLEAR ANY PREVIOUS SCREEN VALUE
        clearscreen(getglobalvar("allocScr"));

        //DRAW EVERYTHING TO THE SCREEN
        if(!openborvariant("pause")){
            drawspriteq(getglobalvar("allocScr"), 0, openborconstant("MIN_INT"), maxZ, getlocalvar("xOffset"), 0);
            changedrawmethod(NULL(),"reset", 1);
            changedrawmethod(NULL(),"enabled", 1);
            changedrawmethod(NULL(),"scalex", getlocalvar("zoomScale"));
            changedrawmethod(NULL(),"scaley", getlocalvar("zoomScale"));
            changedrawmethod(NULL(),"centerx", x);
            changedrawmethod(NULL(),"centery", y);
            drawscreen(getglobalvar("allocScr"), x, y, maxZ+1);
            drawspriteq(vScreen, 0, maxZ+1, maxZ+1, 0, 0);
            drawspriteq(vScreen, 0, maxZ+2, openborconstant("MAX_INT"), 0, 0);
            clearspriteq();
        }
     
        //INCREASE OR DECREASE ZOOM ACCORDING TO DEFINED RATE
        if(time > getglobalvar("timeZoom")){

            //ZOOM INCREASE PROCESS
            if(getlocalvar("zoomStop") == NULL() && getlocalvar("zoomScale") < zoomMax){
                setlocalvar("zoomScale", getlocalvar("zoomScale")+zoomAdd);
                setlocalvar("xOffset", getlocalvar("xOffset")-offsetAdd);
            }

            //REACHED THE MAX ZOOM VALUE?? GIVE A LITTLE BREAK
            if(getlocalvar("zoomStop") == NULL() && getlocalvar("zoomScale") >= zoomMax){
                setlocalvar("zoomStop", openborvariant("elapsed_time")+stopDelay);
            }

            //THE LITTLE BREAK ENDED?? START THE ZOOM REVERSAL
            if(getlocalvar("zoomStop") < openborvariant("elapsed_time")){
                setlocalvar("zoomRevert", 1);
            }

            //ZOOM DECREASE PROCESS
            if(getlocalvar("zoomRevert") == 1 && getlocalvar("zoomScale") > zoomMin){
                setlocalvar("zoomScale", getlocalvar("zoomScale")-(zoomAdd*2));
                setlocalvar("xOffset", getlocalvar("xOffset")+offsetAdd*2);
            }

            //REACHED THE MIN ZOOM VALUE?? DISABLE ZOOM EFFECT
            if(getlocalvar("zoomRevert") == 1 && getlocalvar("zoomScale") <= zoomMin){
                changeopenborvariant("textbox", NULL());
                setglobalvar("zoomFlag", 0);
            }
            setglobalvar("timeZoom", time);
        }
    }
 
    //CLEAR ALL USED VARIABLES
    if(getglobalvar("zoomFlag") == 0){
        free(getglobalvar("allocScr"));
        setlocalvar("zoomScale", NULL());
        setlocalvar("zoomStop", NULL());
        setlocalvar("zoomRevert", NULL());
        setlocalvar("xOffset", NULL());
        setglobalvar("allocScr", NULL());
        setglobalvar("zoomEnt", NULL());
        setglobalvar("zoomX", NULL());
        setglobalvar("zoomY", NULL());
        setglobalvar("zoomMin", NULL());
        setglobalvar("zoomMax", NULL());
        setglobalvar("zoomAdd", NULL());
        setglobalvar("offsetAdd", NULL());
        setglobalvar("startDelay", NULL());
        setglobalvar("stopDelay", NULL());
        setglobalvar("timeZoom", NULL());
        setglobalvar("zoomFlag", NULL());
    }
}

Good morning, thank you kratus for sharing, I tried to put her into the game, but the error file shows that his function setting is wrong, how should we set it up? thank you!!!

Function requires a valid sprite handle 3 integer values, 5th integer value is optional: drawsprite(sprite, int x, int y, int z, int sortid)
Script function 'drawsprite' returned an exception,
parameters: <VT_EMPTY> Unitialized, 0, 0, -2500,
 
Good morning, thank you kratus for sharing, I tried to put her into the game, but the error file shows that his function setting is wrong, how should we set it up? thank you!!!

Function requires a valid sprite handle 3 integer values, 5th integer value is optional: drawsprite(sprite, int x, int y, int z, int sortid)
Script function 'drawsprite' returned an exception,
parameters: <VT_EMPTY> Unitialized, 0, 0, -2500,
@xyz555
It seems that you modified the "drawspriteq" to "drawsprite", they are different functions.
You can use the file below as an example about how to apply in your game.



Amazing work as expected the only other developer I seen play with split screen in openbor is @bWWd
@msmalik681
Thanks man! Yeah, I searched the forum to see what had already been done and found an old post from 2013, where he and utunnels are trying to do a split screen but there's not much information about the conclusion.

It would certainly be good to have some tips from @bWWd , looks like he made good progress and it can help us a lot.
 
@xyz555
It seems that you modified the "drawspriteq" to "drawsprite", they are different functions.
You can use the file below as an example about how to apply in your game.




@msmalik681
Thanks man! Yeah, I searched the forum to see what had already been done and found an old post from 2013, where he and utunnels are trying to do a split screen but there's not much information about the conclusion.

It would certainly be good to have some tips from @bWWd , looks like he made good progress and it can help us a lot.
I am very thankful dear Kratus senior for sharing your display, I will study hard.

But the error file shows
It's the boss zoom in and zoom out function
I tried it one morning yesterday
or continue to report errors
Hope you can share a
Zoom in and out display

Let us understand the principle of Kainaka and learn by ourselves
Thank you very much, Kratus, and wish Kratus, good health and a happy life, !
 
This looks cool and has so much potential. You can make like a mini beat em up MMORPG with 4 players (if possible) or for speedruns.
 
@xyz555 : This is only a technical demo but you can take a look how the zoom script works ingame:

There are some cool features too like:
- Player switch between the NPC ally and the player
- Laser shots reflect to kill the enemies with their own projectiles
- Force push and pull at distance
- Saber launch and remote control
- Obiwan Mind Trick that converts all enemies on screen into NPC allies ...
 
@xyz555 : This is only a technical demo but you can take a look how the zoom script works ingame:

There are some cool features too like:
- Player switch between the NPC ally and the player
- Laser shots reflect to kill the enemies with their own projectiles
- Force push and pull at distance
- Saber launch and remote control
- Obiwan Mind Trick that converts all enemies on screen into NPC allies ...

thanks for sharing
Thank you very much, I will study it myself, I wish you good health and a happy life, thank you!!!
 
I believe that all is not lost if we share with persons that are passionated about pixelart and scripting :) .
Likewise for you, maybye one day we can test your game too ;) .
 
Well the video example looks very good , what are major issues with it ? Im sure there has to be some.can it be used on leftright levels ?
Oh i ucommented the line responsible for left direction and it works both directions , thats super great ! can it work also for up/down ?
I want to make the scrolling variable so no matter how fast player is moving, hes always in the center of the screen but when i use scroll values like 10, the screen stutters.
I was making attempts at horizontal split long time ago but faced issue with bg layers not being displayed on 2nd window.
Im a huge fan of splitscreen in games where you can explore tha map on your own separately, that would make a difference on how game is being played.
.Offscreenkill needs to be like over 99999999999 as well, i died for being too far.
Im testing and so far dont see major issues, ill try some spawning but i suspect that mode would need spawner entity that works relative to your position on the level.

OK all working fine but 2 things id change , one is that screen is not following up/down z axis, this is for large levels when you can walk up/down, second is scrolling speed velocity, or the way its done , when set above 10 its jumpy/stuttering, btu i can see the stutter even on 5 , how to solve these? Scroll velocity is not fast enough for walk speeds characters are using, running around is out of the question , too much stutter with high enough scoll speeds
bor - 0080.jpg
Im trying to add z offset to control the camera up/down movement but no luck so far, left it at -100 for now
drawspriteq(getglobalvar("screenP"+player), 0, openborconstant("MIN_INT"), maxZ+zAdd, getglobalvar("xOffset"+player), -100);

Maybe scrolling needs to be in update c not updated c, cause updated doesnt run all the time
I also see that fglayers behaviour is broken, they dont work/ dont move, but theyre visible., maybe theryre dependent on real camera movement

---

OK i got up/down/left/right working for now, the only thing left to fix is scrolling stutter , any ideas ?
Code:
void main()
{
    if(openborvariant("in_titlescreen")){
        clearglobalvar();
        clearlocalvar();
    }

    if(openborvariant("in_level")){
        if(!openborvariant("pause")){
            splitScreen(0);
            splitScreen(1);
            clearspriteq();
        }
    }
}

void splitScreen(int player)
{//Simulates a "split screen" feature (Kratus 02-2022)
 //Don't forget to set a "wait" in "at 0" on every level to lock the "xpos" scrolling to avoid screen "shaking"
 //Most things on the game will need to be scripted, once most native functions will not work in the level design

    void ent = getplayerproperty(player, "entity");
    
    if(ent){
        void vScreen    = openborvariant("vscreen");
        float x            = getentityproperty(ent, "x");
        float z            = getentityproperty(ent, "z");
        float hRes        = openborvariant("hresolution");
        float vRes        = openborvariant("vresolution");
        float xPos        = openborvariant("xpos");
        float yPos        = openborvariant("ypos");
        float minZ        = openborvariant("PLAYER_MIN_Z");
        float maxZ        = openborvariant("PLAYER_MAX_Z");
        float screenDif    = (vRes/2)*player;
        float scaleX    = 256;
        float scaleY    = 256;
        float zAdd        = 500;
        float xVel        = 5;
        float zVel        = 2;

        //START AND DEFINE VARIABLES
        if(getglobalvar("screenP"+player) == NULL()){
            changeopenborvariant("viewportx", 0);
            changeopenborvariant("viewporty", 0);
            changeopenborvariant("viewportw", openborvariant("levelwidth"));
            changeopenborvariant("viewporth", openborvariant("levelheight"));
            changeentityproperty(ent, "position", xPos+(hRes/6), minZ+(screenDif/8));
            setglobalvar("xOffset"+player, 0);
            setglobalvar("zOffset"+player, 0);
            setglobalvar("screenP"+player, allocscreen(openborvariant("levelwidth"), vRes));
        }

        //READJUST SCREENS FOR 1 OR TWO PLAYERS IN REAL TIME
        //DISABLED BECAUSE NEED IMPROVEMENTS
        if(openborvariant("count_players") <= 1){
            // scaleX = 512;
            // scaleY = 512;
            // hRes = hRes/2;
            // screenDif = 0;
        }
        
        //CLEAR SCREEN
        clearscreen(getglobalvar("screenP"+player));

        //ADJUST LEVEL OFFSET AS A "FAKE" XPOS FOR EACH SCREEN IN REAL TIME
        //USED WHEN THE CHARACTER IS MOVED TO RIGHT DIRECTION IN REAL TIME
        if(x + getglobalvar("xOffset"+player) > (hRes/2)){
            if(getglobalvar("xOffset"+player) > (hRes-openborvariant("levelwidth"))){
                setglobalvar("xOffset"+player, getglobalvar("xOffset"+player)-xVel);
                changeentityproperty(ent, "subject_to_screen", 0);
                //changelevelproperty("wall", 0, "x", x-(hRes/2)); //TESTED TO REPLACE THE NATIVE BLOCKADE FEATURE
            }
            
        }

        
        //USED WHEN THE CHARACTER IS MOVED TO LEFT DIRECTION IN REAL TIME
        //DISABLED TO WORK ONLY FOR THE RIGHT DIRECTION, BUT THE LEFT DIRECTION WORKS CORRETLY TOO
        if(x + getglobalvar("xOffset"+player) < (hRes/2)){
            if(getglobalvar("xOffset"+player) < 0){
                setglobalvar("xOffset"+player, getglobalvar("xOffset"+player)+xVel);
                changeentityproperty(ent, "subject_to_screen", 0);
            }
        }
        
        
                //USED WHEN THE CHARACTER IS MOVED TO DOWN DIRECTION IN REAL TIME
        //DISABLED TO WORK ONLY FOR THE RIGHT DIRECTION, BUT THE LEFT DIRECTION WORKS CORRETLY TOO
        if(z + getglobalvar("zOffset"+player) > (vRes/2.8)){
                setglobalvar("zOffset"+player, getglobalvar("zOffset"+player)-zVel);
                changeentityproperty(ent, "subject_to_screen", 0);
            }

        
                    //USED WHEN THE CHARACTER IS MOVED TO UP DIRECTION IN REAL TIME
        //DISABLED TO WORK ONLY FOR THE RIGHT DIRECTION, BUT THE LEFT DIRECTION WORKS CORRETLY TOO
        if(z + getglobalvar("zOffset"+player) < (vRes/2.8)){
                setglobalvar("zOffset"+player, getglobalvar("zOffset"+player)+zVel);
                changeentityproperty(ent, "subject_to_screen", 0);
            }


        
        //DEBUG INFO
        drawstring(getglobalvar("zOffset"+player)/10000, 50+screenDif, 0, openborvariant("levelwidth"), openborconstant("MAX_INT")/100000);
        drawstring(getglobalvar("zOffset"+player)/10000, 60+screenDif, 0, getglobalvar("zOffset"+player), openborconstant("MAX_INT")/100000);
        drawstring(getglobalvar("zOffset"+player)/10000, 70+screenDif, 0, z, openborconstant("MAX_INT")/100000);

        //DRAW EVERYTHING IN REAL TIME
        drawspriteq(getglobalvar("screenP"+player), 0, openborconstant("MIN_INT"), maxZ+zAdd, getglobalvar("xOffset"+player), getglobalvar("zOffset"+player));
        changedrawmethod(NULL(),"reset", 1);
        changedrawmethod(NULL(),"enabled", 1);
        changedrawmethod(NULL(),"scalex", scaleX);
        changedrawmethod(NULL(),"scaley", scaleY);
        drawscreen(getglobalvar("screenP"+player), 0, screenDif, (maxZ*9)+player);
        drawspriteq(vScreen, 0, maxZ+zAdd+1, openborconstant("MAX_INT"), 0, 0);
    }
    else
    {
        if(getglobalvar("screenP"+player) != NULL()){setglobalvar("screenP"+player, NULL());}
    }
}
 
Last edited:
@bWWd

Well the video example looks very good
Thanks for the feedback, it's good to know your opinion.

can it work also for up/down ?
Yes, the drawspriteq can work for all directions, I'm planning to add in the next update.

Offscreenkill needs to be like over 99999999999 as well, i died for being too far.
It's true, many properties need to be changed, like offscreenkill and subject_to_screen.

I want to make the scrolling variable so no matter how fast player is moving, hes always in the center of the screen but when i use scroll values like 10, the screen stutters.
The code is using similar behaviours of the levelproperty "cameraxoffset", that maintain always in the center, but it can be changed too.
I tested your game but for some reason the fps is decreasing from 200 to around 60-30 fps(1 or 2 players), I don't know if there's some conflict with another script.

OK all working fine but 2 things id change , one is that screen is not following up/down z axis, this is for large levels when you can walk up/down, second is scrolling speed velocity, or the way its done , when set above 10 its jumpy/stuttering, btu i can see the stutter even on 5 , how to solve these? Scroll velocity is not fast enough for walk speeds characters are using, running around is out of the question , too much stutter with high enough scoll speeds
This first version of the split screen code depends entirely on the engine cycle, so the screen appears slower due to the fps decreasement. Maybe the resolution of 1280x720 could be heavy for the split screen code, I will make more tests in this aspect.

I also see that fglayers behaviour is broken, they dont work/ dont move, but theyre visible., maybe theryre dependent on real camera movement
It's true too, in fact most native functions related to level design are broken at the moment, but I'm planning to replicate most of them with scripts and let it accessible on the level file by using spawnscripts/in-line scripts, same as I did with the "zoom" effect for bosses.

OK i got up/down/left/right working for now, the only thing left to fix is scrolling stutter , any ideas ?
Thanks for posting your changes in the code, we can work together to improve the script. I will test your code and try a solution for the scrolling stutter.
Please, let me know if you find what's causing the fps decreasement, i will test the same resolution on the original BOR too.

EDIT: I forgot to say. To make the script work properly we can't add any obstacle/enemy with an "at" bigger than "zero", because once the "xpos" is a variant used for many things, it will change both screens and this is why it needs to be deactivated.

I planned the original code based on spawning all entities at once and separate all them with platforms/walls or by making the entities to detect the player's presence at a defined range, same as many mmorpg games, or by limiting your movement to a defined space, same as Sonic games where some enemies only pursue you in a defined space.
 
Last edited:
Well i have the fps drop from 61 to 22, i think it happens when you clone the screen, if you get rid of that, its 61 again.
Strange that it takes such big cpu hit.
I have these utunnels scripts for old version of splitscreen, they dont have cpu hit , it stays 62fps.
I think mergin them would be best option, but he used update and updated , you press special button to activate extra screen.
I find no issue with not being able to use spawning functions , id use spawner entity anyway, im using them in my newest projects, its just blocking the screen that might be issue, spawning walls and removing them is an option but id rather do it differently and spawner entity would control all of that.
Oh wait ! I fugure that its fglayers/bglayers drop the fps in my game, now its 62fps when i disabeld them.
yeah i think fglayers needs serious serious revamp, or redo from scratch.I can run super complex psp emulation 3x the size at 60 fps on my phone but opebor with 3 fglayers drops frames to 20 !!!!! On pc is exactly the same thing.
Suire fglayers/bglayers are nice but their code is a major cpu hog. something is very wrong with it.
I add one bglayer/fglayer and now fps drops to half, thats not good IMO.And transparency is not even used .
Maybe bglayers should be rebuild using code for background and panel type merged just with customizable speeds.

Screen tearing is not relative to fps , i have about 50 but it tears a lot going up/down, works fine left right.BEsides it jumps suddenly like 20 pixels , thats not fps dropped, its the code not being updated often enough in realtime.
 

Attachments

Last edited:
Screen tearing is not relative to fps
In this case specifically, yes, because the screen offset value updates at every engine cycle and it's directly connected with the fps. The "xVel" value I created in fact it's not a velocity variable, it means how many pixels will be jumped at once, this is why by using 10 the screen tears a lot.

However, the main problem is that the game needs to run at the original fps of 200 in this prototype version of the code, that is, even at 60 fps it's not correct. So, at 200 fps the screen offset will move a lot faster than 60 fps due to more engine cycles running, and this way you can reduce the "xVel" value to jump less pixels at once.

I'm developing a code to avoid the engine cycles in future updates.

Oh wait ! I fugure that its fglayers/bglayers drop the fps in my game, now its 62fps when i disabeld them.
yeah i think fglayers needs serious serious revamp, or redo from scratch.I can run super complex psp emulation 3x the size at 60 fps on my phone but opebor with 3 fglayers drops frames to 20 !!!!! On pc is exactly the same thing.
Suire fglayers/bglayers are nice but their code is a major cpu hog. something is very wrong with it.
I add one bglayer/fglayer and now fps drops to half, thats not good IMO.And transparency is not even used .
Maybe bglayers should be rebuild using code for background and panel type merged just with customizable speeds.
I will make tests with fglayers, it's good to know that. In fact I'm planning to create fglayers/bglayers by spliting every screen into multiple screens for each player, and this way we can have all of them working according to the screen offset.

I have these utunnels scripts for old version of splitscreen, they dont have cpu hit , it stays 62fps
Yeah, I downloaded his small camera demo and took a look at the code. In his script he is using the main screen together with only 1 additional screen because, plus he is using the native "xpos" to move the main screen.

But the code I developed is drawing two screens because it's not using the "xpos", and I have plans to create a code to change it dynamically according to the distance between players (1 screen if both players are close, 2 screens if they are far from each other).

 
Back
Top Bottom