Sprite Stacking in AGS

Started by eri0o, Sat 26/11/2022 13:29:16

Previous topic - Next topic

eri0o



Hey, I am starting to play around with Sprite Stacking in AGS. Has someone ever done this here?

Sliced.ash
Code: ags
// new module header
#define MAX_SLICED_OBJECTS 32
#define MAX_OVERLAYS 2048
#define OVERLAYS_STEP 32

managed struct SlicedObject {
  import void Set(float x, float y, float z, int graphic_first_slice, int graphic_last_slice);
  import static SlicedObject* Create(float x, float y, float z, int graphic_first_slice, int graphic_last_slice); // $AUTOCOMPLETEIGNORESTATIC$
  writeprotected int GraphicFirstSlice, GraphicLastSlice, SliceRange, SliceWidth, SliceHeight;
  writeprotected float X, Y, Z;
};

struct SlicedWorld{
  import void AddSlicedObject(int x, int y, int z, int graphic_first_slice, int graphic_last_slice);
  import void Render(float angle_radians = 0.0);
  protected SlicedObject* SlicedObjects[MAX_SLICED_OBJECTS];
  protected int ObjectCount;
  protected Overlay* Overlays[MAX_OVERLAYS];
};

Sliced.asc
Code: ags
// new module script
// -- SLICED OBJECT --

void SlicedObject::Set(float x, float y, float z, int graphic_first_slice, int graphic_last_slice)
{
  this.X = x;
  this.Y = y;
  this.Z = z;
  this.GraphicFirstSlice = graphic_first_slice;
  this.GraphicLastSlice = graphic_last_slice;
  this.SliceRange = graphic_last_slice - graphic_first_slice;
  this.SliceWidth = Game.SpriteWidth[graphic_first_slice];
  this.SliceHeight = Game.SpriteHeight[graphic_first_slice];
}

static SlicedObject* SlicedObject::Create(float x, float y, float z, int graphic_first_slice, int graphic_last_slice)
{
  SlicedObject* sobj = new SlicedObject;
  sobj.Set(x, y, z, graphic_first_slice, graphic_last_slice);
  return sobj;
}

// -- SLICED WORLD --

void SlicedWorld::AddSlicedObject(int x, int y, int z, int graphic_first_slice, int graphic_last_slice)
{
  SlicedObject* sobj = SlicedObject.Create(IntToFloat(x), IntToFloat(y), IntToFloat(z), graphic_first_slice, graphic_last_slice);
  
  this.SlicedObjects[this.ObjectCount] = sobj;
  this.ObjectCount++;
}


void SlicedWorld::Render(float angle_radians)
{
  float pre_cos = Maths.Cos(angle_radians);
    float pre_sin = - Maths.Sin(angle_radians);
  
  for(int i=0; i<this.ObjectCount; i++)
  {
    SlicedObject* sobj = this.SlicedObjects[i];
    
    int overlay_id_start = i*OVERLAYS_STEP;
    int slice_range = sobj.SliceRange;
    
    for(int j=0; j<slice_range; j++) {
      int overlay_id = overlay_id_start + j;
      if(this.Overlays[overlay_id] == null || !this.Overlays[overlay_id].Valid) {
        this.Overlays[overlay_id] = Overlay.CreateRoomGraphical(0, 0, sobj.GraphicFirstSlice + j);
      }
      
      Overlay* ovr = this.Overlays[overlay_id];
      
      float dist = IntToFloat(j) + sobj.Z;
      
      float lx = dist * pre_cos;
      float ly = dist * pre_sin;
      
      ovr.X = FloatToInt(sobj.X - lx);
      ovr.Y = FloatToInt(sobj.Y - ly);
      ovr.Rotation = Maths.RadiansToDegrees(angle_radians);
    }
  }
}

Usage code

Code: ags
#define SPR_BARREL 1
#define SPR_BARREL_LAST 8
#define SPR_CHAIR 9
#define SPR_CHAIR_LAST 19
#define SPR_CHEST 20
#define SPR_CHEST_LAST 30

SlicedWorld sWorld;

function room_AfterFadeIn()
{

}

function room_Load()
{
  sWorld.AddSlicedObject(32, 32, 0, SPR_BARREL, SPR_BARREL_LAST);
  sWorld.AddSlicedObject(64, 32, 0, SPR_BARREL, SPR_BARREL_LAST);
  sWorld.AddSlicedObject(64, 96, 0, SPR_CHAIR, SPR_CHAIR_LAST);
  sWorld.AddSlicedObject(128, 96, 0, SPR_CHAIR, SPR_CHAIR_LAST);
  sWorld.AddSlicedObject(200, 48, 0, SPR_CHEST, SPR_CHEST_LAST);
  sWorld.AddSlicedObject(230, 48, 0, SPR_CHEST, SPR_CHEST_LAST);
  sWorld.AddSlicedObject(160, 96, 0, SPR_CHEST, SPR_CHEST_LAST);
  sWorld.AddSlicedObject(32, 111, 0, SPR_CHEST, SPR_CHEST_LAST);
}

float angl;
function room_RepExec()
{
  angl += 0.0125;
  sWorld.Render(angl);
}

So far my code and usage is like so, but I am not sure on this design. Any ideas?

(link to above project zip file to download)

Snarky

Neat! Having never before heard of sprite stacking, I found this article a good introduction: https://www.davidepesce.com/2020/06/26/sprite-stacking-in-godot/

eri0o

Thanks for this @Snarky !! This page has something I have been trying to figure it out

QuoteWhen the camera is rotating, it's not that simple, cause things are sometimes in front or in the back depending on where the camera is, so we need some math magic. The dot product is used to check the similarity between the "up vector" and the position of the object:

z_index = -position.dot(up_vector)

But I don't know what that means yet!  (laugh)

Also, here's me changing the size of each overlay experimentally, to play with depth.


Matti

This is very cool. Sprite stacking is new to me too, but it's definitely an interesting technique. Thanks for sharing, eri0o.

eri0o

So, I was more testing the waters in the "can it be done" sense... Like, can AGS take two thousand overlays and smoothly move them around and can the zordering work? Can we rotate all that? And yeah, it appears so. But properly figuring out a pseudo-3d like camera here, I haven't yet figured that...

eri0o

#5




Haven't yet advanced much, but testing around with lots of slices, above is an example from Magica Voxel. Need to properly figure out the camera stuff, but it's been promissing.

Edit: oh, apparently I can use the camera height of twice the size, in the room, and by keeping the viewport the same, I can make the image squashed on vertical, which gives the illusion of it being rotating in a perspective like look. Very interesting!

Edit2: UHM, if I map x,y,z to cylindrical coordinates I can position something in a rotated world and still have game controls that make sense!

Retro Wolf

Sprite stacking? Wave function collapse? what are you up to eriOo?  :-D

eri0o

Just looking into cool new ways to use AGS  :-D  but I think I won't be able to hit my MAGS goal, so probably will not be doing anything now, maybe in the future.

Cassiebsg

That is awesome!  :-D
There are those who believe that life here began out there...

SMF spam blocked by CleanTalk