Jump to content


Photo

Stumped on why this works only some times


  • This topic is locked This topic is locked
6 replies to this topic

#1 TheouAegis

TheouAegis

    GMC Member

  • GMC Member
  • 10102 posts
  • Version:GM8

Posted 08 April 2012 - 01:56 PM

I have a script that's supposed to simulate screen edge sprite clipping as seen on the NES, or at least the NTSC NES systems (not sure if it was in the PAL versions). The script works perfectly for a 16x sprite, but anything wider than that glitches out and draws garbage. The script seems to only work when _left is half the sprite's width. I think the code for the head works just fine but with the same problem; i tested on a sprite with a 16x16 sprite for head and it looked like it worked.

So my question is how do I make this script work correctly for sprites larger than 16 pixels? UPDATE EDIT FOR ELABORATION: The script works just fine for sprites that are 16px wide. However, when a sprite is 32px wide, the script works only when the leftmost 16px are clipped, but not when the leftmost 8px or leftmost 24px are cut. I haven't tested with a 24px sprite yet. So in other words, it only works when half the sprite is clipped.

Sorry if the solution is too simple for Advanced Users forum, but I need someone that actually knows how to use draw_sprite_part_ext() to look over this.

By the way, "head" is jut a variable that stores a sprite_index, since for some twisted reason I felt like torturing myself by limiting base sprites to a height of 16px and anything taller than that would be drawn by code. ... Don't ask; it made sense to me back when I first started doing it.

//Emulate's NES limitations by clipping sprites (8x8) from metasprites on the left screen edge
if clip_sprites
{
    //var _lbox,_left,_wide,_xspr;
    
    _lbox=sprite_get_bbox(bbox_left);
    
    if _lbox<view_xview
    {
        //Calculate partial sprite pixels outside leftmost view
        _left=min(ceil((view_xview-_lbox)/8)*8,abs(sprite_width));
        
        //Calculate remaining visible pixels for full sprite
        _wide=sprite_width-_left*image_xscale;
        
        //Calculate the x-coordinate for the partial sprite
        _xspr=_lbox+_left;
        
        draw_sprite_part_ext(sprite_index,-1,_left,0,_wide,sprite_height,_xspr,y-sprite_yoffset,image_xscale,image_yscale,c_white,1)
        
        if object_index=obj_BonePillar
            draw_sprite_part_ext(head,-1,_left,0,(sprite_get_width(head)*image_xscale-_left*image_xscale)*-1,sprite_get_height(head),_xspr,y-sprite_yoffset-sprite_get_height(head),image_xscale*-1,image_yscale,c_white,1);
    }
    else
    //Hide the sprite if no partials are fully visible on the right
    if _lbox+8>view_xview+view_wview
    {
        draw_sprite_ext(sprite_index,-1,x,y,0,0,0,0,0);
    }
    else
    {
        //Draw sprite as normal
        draw_sprite_ext(sprite_index,-1,x,y,image_xscale,image_yscale,0,c_white,1);
            
        if object_index=obj_BonePillar
            draw_sprite_ext(head,-1,x,y-sprite_get_height(head),image_xscale*-1,image_yscale,0,c_white,1);
    }
    
    if head 
    {
        _lbox = x+(sprite_get_bbox_left(head)-sprite_get_xoffset(head));
        
        if _lbox<view_xview
        {
            //Calculate partial sprite pixels outside leftmost view
            _left=min(ceil((view_xview-_lbox)/8)*8,abs(sprite_width));
        
            //Calculate remaining visible pixels for full sprite
            _wide=sprite_get_width(head)*image_xscale-_left*image_xscale;
        
            //Calculate the x-coordinate for the partial sprite
            _xspr=_lbox+_left;
                    
            draw_sprite_part_ext(head,-1,_left,0,_wide,sprite_get_height(head),_xspr,y-sprite_yoffset-sprite_get_yoffset(head),image_xscale,image_yscale,c_white,1)
        }
        else
        if _lbox+8>view_xview+view_wview
        {
            draw_sprite_ext(sprite_index,-1,x,y,0,0,0,0,0);
        }
        else
        {
            //Draw sprite as normal
            draw_sprite_ext(head,-1,x,y-sprite_yoffset,image_xscale,image_yscale,0,c_white,1);
        }
    }
}
else
{
    //Draw sprites as normal
    draw_sprite_ext(sprite_index,-1,x,y,image_xscale,image_yscale,0,c_white,1);
    if head 
        if head=spr_BonePillar
            draw_sprite_ext(head,-1,x,y-sprite_get_height(head),image_xscale*-1,image_yscale,0,c_white,1);
        else
            draw_sprite_ext(head,-1,x,y-sprite_yoffset,image_xscale,image_yscale,0,c_white,1);
}

Yes I know the draw_sprite_ext(sprite_index,-1,0,0,0,0,0) can be left out, but right now it's functioning as a placeholder. ... >_< And I know the sprites weren't deleted exactly in this way (it was hardcoded into the games themselves), but let's just consider this a learning experience for me. I'd appreciate any help on this matter. Thank you.

Edited by TheouAegis, 10 April 2012 - 05:42 PM.

  • 0

#2 TheouAegis

TheouAegis

    GMC Member

  • GMC Member
  • 10102 posts
  • Version:GM8

Posted 11 April 2012 - 04:03 PM

Okay, more clarification, maybe.

I think it works fine if image_xscale=1 for all sprites. Not too sure. I know it works for sprites 24px wide at image_xscale=1. But when I set image_xscale=-1, the code works fine for the first 8px, but then at the 16px cutoff for a 24px sprite at image_xscale=-1 it clips the rightmost 8px and draws the leftmost 16px on the other side. Hard to explain. And if I have a 32px sprite, I think it draws fine with image_xscale=1, but when I use image_xscale=-1 the script only works when 16px are cut off, not when 8px or 24px are cut off, at which point it draws garbage on the screen. If you guys really need me to upload a GMK of it so you can play with it, that's fine, but you should be able to just copy this script, paste it into your own GMK, and simply call it during the Draw Event. The only thing that's missing from it is the script that draws the sprite as normal, so it might not draw the sprites in your game at all if theyr'e completely within view.
  • 0

#3 torigara

torigara

    GMC Member

  • GMC Member
  • 6507 posts

Posted 11 April 2012 - 05:31 PM

At a glance, the first mistake is this.

_lbox=sprite_get_bbox(bbox_left);

It should be either sprite_get_bbox_left(sprite_index) or just bbox_left depending on whether you want the sprite's property or the actual coordinates in the room (I guess you expected the latter, but you may have to divide it by image_xscale.)

The third to sixth arguments of draw_sprite_part (left, top, width and height) are specified in the sprite coordinates system, that is, (0,0) is the top left corner of the sprite. And the value of (left+width) shouldn't exceed the original width of the sprite (without taking account of scaling), or it will draw garbage.

        //Calculate remaining visible pixels for full sprite
        _wide=sprite_width-_left*image_xscale;

I think that calculation has to be: sprite_get_width(sprite_index) - _left.

Edited by torigara, 11 April 2012 - 05:41 PM.

  • 0

#4 TheouAegis

TheouAegis

    GMC Member

  • GMC Member
  • 10102 posts
  • Version:GM8

Posted 12 April 2012 - 04:15 AM

I've been moving things around. If I drage image_xscale over to the sprite_width it yields nearly the same thing.

sprite_get_bbox('bbox_left') is a script I use. I forgot that I put that in there. It accounts for bounding box shifts when using negative scaling in order to prevent the sprite from being shifted during the drawing..

The more I mess with it, the weirder the results. Right now I"m getting a creepy-yet-cool "monster outside my window" effect. I'm working on some alternate formulas. Your post did help me, though. I'll get back to you with an update. My latest attempt (was working on it while I typed this up) at least has the "window" from above in the right location now. This is a pain in my butt and I'm probably the only person that will ever use this script.

UPDATE (SAME NIGHT)
Here is the current script:
    _lbox=sprite_get_bbox('bbox_left'); //This script returns the real bbox_left value
    
    if _lbox<view_xview //Checks if any part of the sprite is off screen
    {
        //Calculate partial sprite pixels outside leftmost view
        _left=min( ceil((view_xview-_lbox)/8)*8 , sprite_get_width(sprite_index) );
        
        //Calculate remaining visible pixels for full sprite
        _wide = sprite_get_width(sprite_index)-_left
                + min( 0 , sprite_get_width(sprite_index)*sign(image_xscale))*sign(image_xscale);
        
        //Calculate the x-coordinate for the partial sprite
        _xspr=floor(_lbox/2)*2+_left;
        
        draw_sprite_part_ext(sprite_index,-1,_left,0,_wide,sprite_get_height(sprite_index),_xspr,y-sprite_get_yoffset(sprite_index),image_xscale,image_yscale,c_white,1)


So _left finds out how much of the sprite is to the left of view_xview and rounds it up to the next multiple of 8. This not only determines how many "sprites" are to not be drawn, but also where to start the drawing of the main sprite. If the whole sprite is to the left of view_xview, the sprite's actual width is used instead (sans x-scaling).

As of this edit, _wide determines how much of the sprite is left to draw. Now, this seemingly works right with image_xscale=1 but as soon as I negate the image_xscale I get different results. I finally got it so it at least draws without garbage. I just can't get it to draw the sprite with the correct scaling and undrawing the correct parts.

And _xspr is supposed to be where the sprite is drawn, but for image_xscale=-1 it draws in the correct place but it looks wrong because I think _wide isn't being set correctly and as such the sprite is being drawn outside of the view (confirmed).

Edited by TheouAegis, 13 April 2012 - 12:20 AM.

  • 0

#5 torigara

torigara

    GMC Member

  • GMC Member
  • 6507 posts

Posted 12 April 2012 - 10:04 AM

Well, I recommend to write down some diagrams on a piece of paper to aid examining calculations. First, the sprite coordinates:
// The sprite coordinates

O----L-------R--+
|    |*******|  |
|    |*******|  |
+----+-------+--+
|<-->|<----->|
left   width

|<----- W ----->|


* = the portion you want to draw on the screen.
L = left edge of the portion you want to draw.
R = right edge of the portion (in practice, it is 1 pixel right to the edge for a technical reason.)
O = the top left corner of the sprite (always 0.)
W = the original width of the sprite, i.e. sprite_get_width(sprite_index)

"left" and "width are two parameters you put into draw_sprite_part(_ext).
left = L
width = R - left
Always make sure that they satisfy following constraints, or it will end up in drawing garbage.
left >= 0
width > 0
left + width <= W // where W is the actual width of the sprite without scaling.

Now suppose that you want to cut the left part of the sprite because it is partially out of the view. In the picture below, image_xscale is set to 2 so the sprite width get doubled on the screen.
// The screen coordinates

 view_xview
     +--------------------------------------
     |
  P  |    x
  |  |    |
  O'---L'-|------------R'
  |    |**|************|    image_xscale = 2
  |    |**X************|
  +----+---------------+

  |<-->|<------------->|  M+N = sprite_width = sprite_get_width(sprite_index)*image_xscale
    M           N


* = the portion drawn on the screen.
L', R' = coordinates of the left and right edge of the drawn part of sprite, respectively.
X = the origin of the sprite.
x = the coordinates you draw the sprite.
O' = the top left corner of the sprite.
P = the left edge of the sprite on the screen.

M is the number of pixels you want to cut from the sprite, and N is the width of the portion drawn on the screen. The relationship of those values are follows:
P = O' = x - sprite_xoffset // = x - sprite_get_xoffset(sprite_index) * image_xscale
M = ceil((view_xview - P) / 8) * 8
N = sprite_width - M
L' = P + M = O' + M
R' = L' + N
They map to following sprite coordinates:
// The sprite coordinates

O----L----------R
|    |**********|
|    |**********|
+----+----------+
|<-->|<-------->|
left    width

|<----- W ----->| sprite_get_width(sprite_index)
So, left and width (arguments for draw_sprite_part) are calculated as:
left = (L' - O') / image_xscale = M / image_xscale
width = N / image_xscale = (sprite_width - M) / image_xscale = W - M / image_xscale

 
So far so good. Now, consider the case that image_xscale is negative. The sprite will get flipped, but we still have to cut the part that comes left on the screen.
// The screen coordinates

 view_xview
     +--------------------------------------
     |
     |         x       O'
     |         |       |
  P----R'------|-------L'
  |    |*******|*******|    image_xscale = -2
  |    |*******X*******|
  +----+---------------+

  |<-->|<------------->| M+N = abs(sprite_width) = sprite_get_width(sprite_index)*abs(image_xscale)
    M           N
P is the right edge of the sprite (that comes left on the actual screen.) Note that image_xoffset and sprite_width will also come negative taking account of image_xscale (I think.) Considering those, the relationship of those values are as follows.
O' = x - sprite_xoffset // = x + abs(sprite_xoffset)
P = O' + sprite_width // = O' - abs(sprite_width)
M = ceil((view_xview - P) / 8) * 8 // unchanged
N = abs(sprite_width) - M
R' = P + M
L' = R' + N

The catch is that we now have to cut the right part of the orignal sprite:
// The sprite coordinates

O
|
L----------R----+
|**********|    |
|**********|    |
+----------+----+
|<-------->|
   width

|<----- W ----->| sprite_get_width(sprite_index)
left = 0
width = N / abs(image_xscale) = W - M / abs(image_sxcale)

  • 1

#6 TheouAegis

TheouAegis

    GMC Member

  • GMC Member
  • 10102 posts
  • Version:GM8

Posted 13 April 2012 - 02:18 AM

~D~~~~~~~~~~~
~~O~~~~~~~~~~
~~~U~~~~~~~~~
~~~~B~~~~~~~~
~~~~~L~~~~~~~
~~~~~~E~~~~~~
~~~~~~~-~~~~~
~~~~~~~~P~~~~
~~~~~~~~~O~~~
~~~~~~~~~~S~~
~~~~~~~~~~~T~

Edited by TheouAegis, 13 April 2012 - 02:43 AM.

  • 0

#7 TheouAegis

TheouAegis

    GMC Member

  • GMC Member
  • 10102 posts
  • Version:GM8

Posted 13 April 2012 - 02:41 AM

NEVERMIND, I GOT IT!!!! ... I think. I hope. Not entirely sure yet. Here's my current code:

_lbox=x-abs(sprite_xoffset);
if _lbox<view_xview
{
    _left=min(ceil((view_xview-_lbox)/(8*abs(image_xscale)))*8*abs(image_xscale),abs(sprite_width));
    _wide=(sprite_width-_left)/abs(image_xscale);
    _xspr=_lbox+_left;
    _left=min(_left,sprite_width+_left)/image_xscale;  //This was the clincher right here! Dastardly code...
    draw_sprite_part_ext(...);
}

It might not be what you were telling me to do and I couldn't figure out what you were telling me to do, but you definitely helped a lot. Thanks again Tori!

*edited to include different x-scaling*

Edited by TheouAegis, 13 April 2012 - 02:58 AM.

  • 0




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users