stop entity overlap

msmalik681

OpenBOR Developer
Staff member
Is there any way to stop 2 entities from overlapping each other? tried platform but it did not work.
 
@msmalik681

when i was replicating the nslasher demos for the how to play videos, i discovered that there is an order when it comes to the spawning of entities on the level.txt

basically the demos call for enemies to spawn in a triangular type bowling pin-like-situation, but i found that some entities that where supposed to be "in the back" where actually being rendered in the front

cant recall the way it goes exactly but its like this, entities that are closer to the foreground have "visual priority" and thus they should be the first on the spawn hierarchy

so here is an example

we want blanka to be shown in front of donkeykong and donkeykong to be shown in front of bowser when they sapwn?

so the level text goes something like this

spawn blanka 100 100

spawn donkey 100 50

spawn bowser 100 10

i would have to dig up and analize those "demo" levels.txts for a more accurate answer and thing is , this maybe backwards instead....

but it also may be the main reason why some stuff i was doing had visual glitches...
 
no what i mean is normally you can walk right through other entities when you are on the same z level but oi want like street fighter where you can not walk through other entities you will be blocked if you get too close.
 
no what i mean is normally you can walk right through other entities when you are on the same z level but oi want like street fighter where you can not walk through other entities you will be blocked if you get too close.

There's a "hidden" buffer for Z layering that can handle this, but depending on engine version getting access to it can be tricky. Which one are you on?

DC
 
Now that I'm at my desk...

Z position on the screen (actually just a priority list) is pretty straight forward. The higher the Z, the more priority. If two things occupy the same Z, then last one drawn wins. Most level objects, the HUD, and legacy stuff like Holes are all drawn at a fixed Z position determined at the start of a level. I'll get to that below.

The complicated part is translating the 3D world to that a Z priority position on a 2D screen.

Entities have a complex set of math with a couple of exceptions thrown in to determine where they go. See here:


C:
if(f < sprites_loaded)
{
    // var "z" takes into account whether it has a setlayer set, whether there are other entities on
    // the same "z", in which case there is a layer offset, whether the entity is on an obstacle, and
    // whether the entity is grabbing someone and has grabback set

    z = (int)e->position.z;    // Set the layer offset
         
    if(other && e->position.y >= other->position.y + other->animation->platform[other->animpos][PLATFORM_HEIGHT] && !other->modeldata.setlayer)
    {
        float zdepth = (float)( (float)e->position.z - (float)other->position.z +
                                (float)other->animation->platform[other->animpos][PLATFORM_DEPTH] -
                                (float)other->animation->platform[other->animpos][PLATFORM_Z] );

        if(
            e->link                                                        // Linked to entity.
                        && ((e->modeldata.grabback && !e->grabbing)                        // Have grab back AND not grabbing.
                            || (e->link->modeldata.grabback && e->link->grabbing)        // Linked has grab back and is grabbing.
                            || e->grabbing)                                                // Grabbing.
        )
        {

            // Make sure entities get displayed in front of obstacle and grabbee
            sortid = other->sortid + zdepth + 2;
            e->sortid = sortid;
            z = (int)( other->position.z + 2 );
        }

        else
        {
            //if ( e->model->type == TYPE_PLAYER ) debug_printf("zdepth: %f",zdepth);

            // Entity should always display in front of the obstacle
            sortid = other->sortid + zdepth + 1;
            e->sortid = sortid;
            z = (int)( other->position.z + 1 );
        }

    }

    // In most cases we want any spawned entity to
    // default in front of owner.
    if(e->owner)
    {
        // If this entity is not an exception to the rule,
        // move its display order in front of owner.
        if (!(self->modeldata.aimove & AIMOVE1_STAR))
        {
            sortid = e->owner->sortid + 1;
        }
    }

    if(e->modeldata.setlayer)
    {
        z = HOLE_Z + e->modeldata.setlayer;    // Setlayer takes precedence
    }

In a conventional beat em' up game, it is darn near impossible for two entities to occupy the same Z position in world and thus wind up on the same Z drawing priority. That's because positions and velocity are floating point and extremely granular. Turn on position debug and TRY to put two entities on the same Z axis just moving them around. The only times it's feasible are...
  • They are bound together (which as you can see is handled automatically for native grappling).
  • One spawns another at identical position (also handled).
  • Both are at some hard border like pushing against an obstacle (again, handled) or the level Z bounding.
  • The level is 2D with no Z movement - which I am guessing is how your Final Blow project is set up and presumably what you're having issues with.
When two items do happen to share the same Z priority, then whichever entity gets into the drawing buffer last has priority, and that can vary frame by frame - thus the flickering.

The solution in this case, is to manually buffer the Z and use setlayer (open to script). This can get tricky though, because prior to 4.0, there are a lot of "constants" that are not actually constants. Like HOLE_Z, that are in fact determined by the stage Z boundaries (available to openborvariant as player_min_z, player_max_z) - and as you can also see from the code, setlayer works in conjunction with them. These were moved in 4.0 from openborconstant() to openborvariant() to reflect what they actually are.

Another, and perhaps simpler trick for your specific project is using subscreens. Subscreens don't have a Z buffer. They work exactly like the Neo-Geo - whatever you draw to them last gets priority, full stop. That can be a quick and dirty way to get rid of the flickering, and it's exactly how most games like real Final Blow work - one fighter is always drawn second and so always has priority.

DC
 
@DCurrent I am not explaining it right i don't want you to be able to walk though the other player so when you walk into them i want it to be like you get stopped by a wall so they are solid but right now you can walk right though them to the other side.


Screenshot from 2025-07-31 16-00-44.pngScreenshot from 2025-07-31 15-59-52.png
 
@DCurrent I am not explaining it right i don't want you to be able to walk though the other player so when you walk into them i want it to be like you get stopped by a wall so they are solid but right now you can walk right though them to the other side.


View attachment 11158View attachment 11157

Ooooooh... that's a different thing, sorry for the misunderstanding. And yes, there is a solution for that too. There is an "entity" box as it was poorly named in 3.0, and removed in 4.0, because frankly it's a buggy mess I did not want put in until we could properly set it up. You could use it, but I will not document here because I don't want people delving into a depreciated functionality.

Normally this would get into kind of complex math, but for what you are making, I think we can simplify it quite a bit. I would run an enumeration loop, and in it, get positions of both fighters. If they are within a given distance from each other, reposition them back outside of it. Just a really, REALLY simplistic shunting routine on the X axis. For Final Blow, that's all you need.

Something like this:


C-like:
/*
* Keep entities caller can damage from passing
* within 20px of the caller on the X axis.
*/
void dc_maintain_spacing(float min_distance)
{
    int i            = 0;
    int entity_count = 0;
    int can_damage   = 0;
    int entity_type  = 0;
    void acting_entity = NULL();
    void target_entity = NULL();

    float acting_x   = 0;
    float target_x   = 0;
    float diff_x     = 0;
    float push_dir   = 0;

    // Get the entity that called function.
    acting_entity = getlocalvar("self");

    // Get X position and candamage list for acting entity.
    acting_x     = getentityproperty(acting_entity, "x");
    can_damage   = getentityproperty(acting_entity, "candamage");

    // Get total active entities.
    entity_count = openborvariant("count_entities");

    for (i = 0; i < entity_count; i++)
    {
        target_entity = getentity(i);

        // Safety check: Entity must exist.
        if (!getentityproperty(target_entity, "exists"))
        {
            continue;
        }

        // Get entity type.
        entity_type = getentityproperty(target_entity, "type");

        // Skip obstacles or self.
        if (entity_type == openborconstant("TYPE_OBSTACLE") || target_entity == acting_entity)
        {
            continue;
        }

        // Skip entities we cannot damage.
        if (!(entity_type & can_damage))
        {
            continue;
        }

        // Get target X.
        target_x = getentityproperty(target_entity, "x");

        // Calculate difference.
        diff_x = target_x - acting_x;

        // If absolute distance is less than minimum, push target out.
        if (fabs(diff_x) < min_distance)
        {
            // Direction to push: if target is to the right, push right, else push left.
            push_dir = (diff_x >= 0) ? 1 : -1;

            // Move target to maintain spacing.
            changeentityproperty(target_entity, "x", acting_x + (push_dir * min_distance));
        }
    }
}
 
Back
Top Bottom