• All, Gmail is currently rejecting messages from my host. I have a ticket in process, but it may take some time to resolve. Until further notice, do NOT use Gmail for your accounts. You will be unable to receive confirmations and two factor messages to login.

OpenBOR Split Screen - Prototype Test

Hi, Kratus. I'm new to using the split screen script. I'm not sure if it's okay with you as I need help with the split screen issue. Or should I make a new help topic regarding the split screen? But I can talk about what I spotted with the default 0 of split screen, which I changed/reverted to.

C:
            splitScreen(0, 0);
            splitScreen(1, 0);

I was testing the horizontal part. Here are the errors I found in the attachments which you should look through. When I reach to 9, it's okay when the other player is there in one screen. When you move with scrolling from 10-99 away from the other player, the split screen stays there until you exceed to 100 and on. When one of the players dies from a far distance, the split screen flickers until the dead one disappears, resulting the camera to pan to one existing player alive.

bor - 0000.pngbor - 0001.pngbor - 0002.png
 
Last edited:
I was testing the horizontal part. Here are the errors I found in the attachments which you should look through. When I reach to 9, it's okay when the other player is there in one screen. When you move with scrolling from 10-99 away from the other player, the split screen stays there until you exceed to 100 and on. When one of the players dies from a far distance, the split screen flickers until the dead one disappears, resulting the camera to pan to one existing player alive.
This concept is in a prototype stage yet, the horizontal code has some parts disabled because they need improvements.

1715710656232.png
 
Hi, Kratus. I'm using @Bloodbane's 2D OpenBOR template as a base or test for the camera to shift its focus to characters when it comes to 2D. Plus, I'd also like to use it for the level height and video 0 (320x240) mode. Even after/if characters die, they could respawn with the camera instantly shifting its focus to the respawned player ("xpos" variant and player indexed entity's "x" could work?) instead of scrolling (normally), or maybe it scrolls quicker than normal scrolling/panning.

Sometimes, you don't see any player being focused on the screen due to camera not forced to shift its focus.

The camera focus shift needs its improvement wherever the player goes/is at. It seems very difficult/challenging, but it would be worth having it in the end.

I forgot to upload this.

 
to make tyhis work you need to spawn all entities at once at the beginning, then disable fglayers , disable bglayers , use just panel , then you can maybe try to script the camera following players separately.
Having too much stuff at the same time will only give you headache, start simple, you dont need fancy stages with parallaxes, they cause too much trouble.
Would be cool to have ability to enter a house onscreen and one player can be inside the house while other is still outside, but i think this is too much and too complex.
 
Last edited:
Hi, Kratus. I'm using @Bloodbane's 2D OpenBOR template as a base or test for the camera to shift its focus to characters when it comes to 2D. Plus, I'd also like to use it for the level height and video 0 (320x240) mode. Even after/if characters die, they could respawn with the camera instantly shifting its focus to the respawned player ("xpos" variant and player indexed entity's "x" could work?) instead of scrolling (normally), or maybe it scrolls quicker than normal scrolling/panning.

Sometimes, you don't see any player being focused on the screen due to camera not forced to shift its focus.

The camera focus shift needs its improvement wherever the player goes/is at. It seems very difficult/challenging, but it would be worth having it in the end.

I forgot to upload this.

@maxman Due to the prototype stage, the code is a little "crude". Many things necessary to make the implementation easier were not developed yet and it can result in many bugs or undesired side effects when applying it in different games.
Unless you are familiar with subscreen scripts, I suggest skipping for now and wait until I can make some improvements.
 
What are known issues ?
To be honest in my BOR mod I had no issues with the vertical split screen code and it's working as intended, the horizontal is not totally finished yet. The issues start to happen when the code is implemented in other games because so far the only scenario I imagined as a test is a simple BOR mod. But different games will require different changes in the codes, which were not added yet.
 
@Kratus is there a way to make bindentity work with this split screen code? It removes all bindentites once youre too far from start of the level.

Code:
void splitScreen(int player, int type)
{//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");
    
 

    //VERTICAL
    if(type == 1){
        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 minZ        = openborvariant("PLAYER_MIN_Z");
            float maxZ        = openborvariant("PLAYER_MAX_Z");
            float scrollSpd    = getlevelproperty("scrollspeed");
            float xVel        = 1;
            float zVel        = 1;
            float zDif        = 80*player;
            float scaleX    = 256;
            float scaleY    = 256;
            float p1Screen;
            float p2Screen;
            int p1Layer;
            int p2Layer;

            //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", hRes/6+zDif , minZ+zDif);
                setglobalvar("xOffset"+player, 0);
                setglobalvar("zOffset"+player, 0);
                setglobalvar("screenP"+player, allocscreen(hRes/2, vRes));
            }

            //READJUST SCREENS FOR TWO PLAYERS IN REAL TIME
            if((getglobalvar("xOffset0")*(-1)) > (getglobalvar("xOffset1")*(-1))+(hRes/2) && openborvariant("count_players") >= openborvariant("maxplayers")){
                p1Screen = hRes/2;
                p2Screen = 0;
                p1Layer = 0;
                p2Layer = 2;
                drawbox(p1Screen, 0, 1, vRes, maxZ*10, rgbcolor(0, 0, 0), 0);
            }
            else
            if((getglobalvar("xOffset1")*(-1)) > (getglobalvar("xOffset0")*(-1))+(hRes/2) && openborvariant("count_players") >= openborvariant("maxplayers")){
                p1Screen = 0;
                p2Screen = hRes/2;
                p1Layer = 2;
                p2Layer = 0;
                drawbox(p2Screen, 0, 1, vRes, maxZ*10, rgbcolor(0, 0, 0), 0);
            }
            else
            {
                p1Screen = 0;
                p2Screen = 0;
                p1Layer = 0;
                p2Layer = 0;
            }
            
            //CLEAR SCREEN
            clearscreen(getglobalvar("screenP"+player));

            //START CYCLE VARIABLE AGAIN
            setlocalvar("splitCycle", 0);

            //INCREASE OR DECREASE MOVING RATE ACCORDING TO THE NATIVE ENGINE SCROLL SPEED
            //ADJUST LEVEL OFFSET AS A "FAKE" XPOS FOR EACH SCREEN IN REAL TIME
            while(getlocalvar("splitCycle") < scrollSpd){

                //USED WHEN THE CHARACTER IS MOVED TO RIGHT DIRECTION IN REAL TIME
                if(x > (getglobalvar("xOffset"+player)*(-1)) + (hRes/4)){
                    if((getglobalvar("xOffset"+player)*(-1)) < (openborvariant("levelwidth")-hRes/2)){
                        setglobalvar("xOffset"+player, getglobalvar("xOffset"+player)-xVel);
                        changeentityproperty(ent, "subject_to_screen", 0);
                    }
                }
                
                //USED WHEN THE CHARACTER IS MOVED TO LEFT DIRECTION IN REAL TIME
                if(x < (getglobalvar("xOffset"+player)*(-1)) + (hRes/4)){
                    if((getglobalvar("xOffset"+player)*(-1)) > 0){
                        setglobalvar("xOffset"+player, getglobalvar("xOffset"+player)+xVel);
                        changeentityproperty(ent, "subject_to_screen", 0);
                    }
                }
                
                //END THE SCRIPT CYCLE
                setlocalvar("splitCycle", getlocalvar("splitCycle")+1);
            }

            //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(z + getglobalvar("zOffset"+player) > (vRes/2)){
                //setglobalvar("zOffset"+player, getglobalvar("zOffset"+player)-zVel);
            }
            
            //USED WHEN THE CHARACTER IS MOVED TO LEFT DIRECTION IN REAL TIME
            if(z + getglobalvar("zOffset"+player) < (vRes/2)){
                //setglobalvar("zOffset"+player, getglobalvar("zOffset"+player)+zVel);
            }

            //CONFIGURE LAYERS
            int layerID;
            int layerType;
            int layerMode;
            float layerV;
            float layerZ;
            float panelZ;

            //BGLAYERS
            layerID        = 0;
            layerType    = "bglayer";
            layerMode    = "disabled";
            panelZ        = minZ-50;

            //START BGLAYER COUNTER
            setlocalvar(layerType, 0);
            
            while(getlocalvar(layerType) < getglobalvar("max_"+layerType)){
                layerZ = getlayerproperty(layerType, layerID, "z");
                layerV = getlayerproperty(layerType, layerID, "xratio");
                drawspriteq(getglobalvar("screenP"+player), 0, layerZ, panelZ-1-layerID, getglobalvar("xOffset"+player)*layerV, getglobalvar("zOffset"+player));
                layerID = layerID+1;
                setlocalvar(layerType, getlocalvar(layerType)+1);
            }

            //PANEL
            drawspriteq(getglobalvar("screenP"+player), 0, panelZ, minZ-1, getglobalvar("xOffset"+player), getglobalvar("zOffset"+player));

            //PLAYABLE AREA
            drawspriteq(getglobalvar("screenP"+player), 0, minZ, maxZ+1, getglobalvar("xOffset"+player), getglobalvar("zOffset"+player));

            //FGLAYERS
            layerID        = 0;
            layerType    = "fglayer";
            layerMode    = "disabled";

            //START FGLAYER COUNTER
            setlocalvar(layerType, 0);

            while(getlocalvar(layerType) < getglobalvar("max_"+layerType)){
                layerZ = getlayerproperty(layerType, layerID, "z");
                layerV = getlayerproperty(layerType, layerID, "xratio");
                drawspriteq(getglobalvar("screenP"+player), 0, maxZ+2, layerZ, getglobalvar("xOffset"+player)*layerV, getglobalvar("zOffset"+player));
                layerID = layerID+1;
                setlocalvar(layerType, getlocalvar(layerType)+1);
            }
                
            //DRAW EVERYTHING IN REAL TIME
            changedrawmethod(NULL(),"reset", 1);
            changedrawmethod(NULL(),"enabled", 1);
            changedrawmethod(NULL(),"scalex", scaleX);
            changedrawmethod(NULL(),"scaley", scaleY);

            //DRAW EACH PLAYER SCREEN SEPARATELLY
            if(player == 0){drawscreen(getglobalvar("screenP"+player), p1Screen, 0, (maxZ*9)+p1Layer);}
            if(player == 1){drawscreen(getglobalvar("screenP"+player), p2Screen, 0, (maxZ*9)+p2Layer);}

            //DRAW EVERYTHING TO THE MAIN SCREEN
            drawspriteq(vScreen, 0, openborconstant("MIN_INT"), openborconstant("MAX_INT"), 0, 0);
        }
        else
        {
            //RESET VARIABLES
            if(getglobalvar("screenP"+player) != NULL()){
                setlocalvar("splitCycle", NULL());
                setglobalvar("xOffset"+player, NULL());
                setglobalvar("zOffset"+player, NULL());
                setglobalvar("screenP"+player, NULL());
            }
        }
    }
}
 
@Kratus is there a way to make bindentity work with this split screen code? It removes all bindentites once youre too far from start of the level.
This is strange, there's no connection between the split screen and the bindentity removal. Is it removed or it just disappears, reappearing if you go back to the beginning of the level?
 
I solved it by changing binded entity type to ENEMY , NPC works too but its not ideal, you think its possible so the code will not remove other types as well ?

It simply gets removed, i added sound to binded entity to check if its just getting invisible or something but no, the sound stops as well when im about screen width resolution far + 50% of screen width distance. and i gave this entity offscreenkill setting to prevent removal, it just does not work.
Not sure why enemies and npcs arent removed but none entities are removed.
This gets removed :
Code:
name    wcakebind
type    NONE
nomove 1 1
health 1
offscreenkill 999999
shadow    0
palette  data/sprites/fcake.png
 subject_to_screen  0
 subject_to_minz  0
 subject_to_maxz  0
animationscript data/scripts/aniscripts.c
nmove 1 1
speed 0

This does not , only changed type to enemy and nomove
Code:
name    wcakebind
type    ENEMY
nomove 1 1
health 1
offscreenkill 999999
shadow    0
palette  data/sprites/fcake.png
 subject_to_screen  0
 subject_to_minz  0
 subject_to_maxz  0
animationscript data/scripts/aniscripts.c
nmove 1 1
speed 0


Ok theres another issues with even enemy entity to get invisible near bottom of the screen, and if i set Z in levels to 820 then he gets removed when i reach near 820 and comes back when binded enemy goes up , when i change it to 500, he gets removed when he goes down near 500 z

set Forest3
maxplayers 2
lives 1
z 580 820
file data/levels/bg3.txt

---
OK its solved with :
//PANEL
drawspriteq(getglobalvar("screenP"+player), 0, panelZ, minZ+11, getglobalvar("xOffset"+player), getglobalvar("zOffset"+player));

//PLAYABLE AREA
drawspriteq(getglobalvar("screenP"+player), 0, minZ, maxZ+11, getglobalvar("xOffset"+player), getglobalvar("zOffset"+player));


Now the only thing to solve is getting left side assigned to ardvark and right side to ant cause now its like this sometimes

bor - 0017.png
 
Last edited:
I solved it by changing binded entity type to ENEMY , NPC works too but its not ideal, you think its possible so the code will not remove other types as well ?
I believe it's a engine default behaviour, in other projects I use npc as decorative level elements due to the same reason.

Now the only thing to solve is getting left side assigned to ardvark and right side to ant cause now its like this sometimes
I need to look at this, maybe the best way is by hiding the native HUD by putting a very high number in the levels.txt parameters, then drawing everything using drawstring.
This way we can program to switch the name/score if it's necessary, what do you think about the idea?

1733692636534.png
 
Yeah its better than trying to fight native hud, its just sometimes theres ant on right side and sometimes theres ardvark on right side so the hud should be dynamic depending on where character is (on left or right split screen).
Overall i hid the names and points for now.
Ideally id reall want player1 to always be on left screen and player2 always lead right screen but if its too hard then hud drawsting travel from left to right could be good workaround, not sure how jarring jumping hud would be for players.
I could be wrong but i dont think ive seen spit screen game where player1 can take over left or right screen, as far as i remember each player had his own screen that stuck to it no matter what.
 
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());
Thank you for the Zoom script.
I like how you can control the speed of the zoom!
If I use your Zoom script for the character's special skills, do I need to clear all these global variables above or only need to clear with this line
setglobalvar("zoomEnt", NULL());
in the endlevel file is enough?

Thank you very much for your hard work.
 
Thank you for the Zoom script.
I like how you can control the speed of the zoom!
If I use your Zoom script for the character's special skills, do I need to clear all these global variables above or only need to clear with this line
setglobalvar("zoomEnt", NULL());
in the endlevel file is enough?

Thank you very much for your hard work.
I don't know exactly from which part you took this code, but usually I suggest clearing some variables at the endlevel.c in case the level finishes before a zoom effect ends.
 
Back
Top Bottom