Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Direct spritesheet support #93

Open
BobG1983 opened this issue Sep 8, 2019 · 4 comments
Open

Direct spritesheet support #93

BobG1983 opened this issue Sep 8, 2019 · 4 comments
Labels
feature New feature or request help wanted Extra attention is needed question Further information is requested

Comments

@BobG1983
Copy link

BobG1983 commented Sep 8, 2019

I currently have the following really basic function:

fn rect_from_tilemap_coord(x: u16, y: u16, tile_size: Option<u16>) -> Rectangle<u16>{
  let tile_size = tile_size.unwrap_or(16);
  let x = max(0, x - 1);
  let y = max(0, y - 1);
  return Rectangle {
    x: tile_size * x + x,
    y: tile_size * y + y,
    width: tile_size,
    height: tile_size
  }
}

Which is basically a really hacky way to get a Rectangle that represents an area of a spritesheet. I'm likely to create an enum of all the sprites in the spritesheet, but I was wondering if there was some "better" way to do this in the actual engine. A SpriteSheet that wraps an Image and takes grid coords rather than a quad maybe? Or some way to load a spritesheet from which you can get the source rectangle for a given sprite?

It may be that the best way to do it is indeed for me to create myself a tilemap class, that the renderer owns and have an enum that the renderer knows how to convert into a rectangle, but I thought I'd bring this up.

@hecrj
Copy link
Owner

hecrj commented Sep 9, 2019

A SpriteSheet type is definitely something to consider adding to the engine.

It's not there yet because there are many different approaches we could take and I had a hard time choosing one. Thus, I preferred to leave it up to users to implement one and wait until we see a pattern or a common approach, and design based on that.

Ideally, I would like something as type-safe as possible (i.e. something that avoids referencing sprites out of bounds, like the texture_array module). However, I am not sure if the amount of type-safety we can get is worth it in this case.

For instance, enums work well for item sprites, but once you add animation into the mix they become a hassle.

Overall, I am unsure if there is a generic approach to satisfy most use cases here without promoting bad practice. We may be missing additional concepts, maybe we need different types of sprite sheets... I will keep thinking about it!

If anyone has built something similar or has any ideas, feel free to share your thoughts here. It may help us find a good solution.


@RantingBob Meanwhile, for your use case, if you need to refer individually to each sprite, I would use an enum and have a struct wrapping an Image or a Batch taking only values of this enum to perform draw operations.

@hecrj hecrj added feature New feature or request help wanted Extra attention is needed question Further information is requested labels Sep 9, 2019
@BobG1983
Copy link
Author

BobG1983 commented Sep 9, 2019

I don't have any ideas at this time. I'll implement something and come back to you. As an aside, Batch vs Image for spritesheets?

@hecrj
Copy link
Owner

hecrj commented Sep 10, 2019

@RantingBob Batch will allow you to draw many sprites at once efficiently. If you are drawing a tilemap, it's definitely the recommended approach.

@DomtronVox
Copy link

I just started messing around with Coffee, but this is what I made to handle sprite sheets. Functionality is very basic, but seems to work ok. Still needs some sanity checking like making sure a requested sprite is actually within bounds or that a given image can fit the stated number of rows and columns.

use coffee::graphics::{Point, Rectangle, Image, Sprite};

//size of a single sprite in the Sprite Sheet
struct SpriteSize { pub width: u16, pub height: u16}

//An array of sprites packed into a single image, also called an Atlas.
pub struct SpriteSheet {
    atlas: Image,
    sprite_size: SpriteSize,
}


impl SpriteSheet {

    pub fn new(image: Image, rows: u16, columns: u16) -> SpriteSheet {
        let sprite_size = SpriteSize {
            width: image.width() / columns,
            height: image.height() / rows,
        };

        SpriteSheet {
            atlas: image,
            sprite_size,
        }
    }


    //Extract a specific sprite from the sprite sheet
    // @position: provided position of the sprite on the target screen/frame/etc.
    // @row: Which row of the atlas we are requesting. Note row starts at 1
    // @column: Which column of the atlas we are requesting. Note column starts at 1
    // returns: Image object of full atlas, used in drawing 
    //          Sprite object depicting a single sprite in the atlas

    pub fn get_sprite(&self, position: Point, mut row: u16, mut column: u16)
           -> (Image, Sprite) {

        //adjust row/column for calculating sprite position in atlas  
        row = row - 1;
        column = column - 1;

        //define these for brevity
        let sprite_width = self.sprite_size.width;
        let sprite_height = self.sprite_size.height;

        //return full atlas image and requested sprite's location in the atlas.
        (self.atlas.clone(), //note coffee docs says cloning Image is very cheap
         Sprite { 
            source: Rectangle{
                x: column * sprite_width, y: row * sprite_height, 
                width: sprite_width, height: sprite_height,
            },
            position: position, 
            scale: (1.0,1.0) //assume normal scale, lets other code change it as needed 
        })
    }
}

Usage in Game::draw is like this:

let position = Point::new(100.0, 100.0);
let (image, sprite) = self.player_spritesheet.get_sprite(position, 5, 1);
image.draw(sprite, &mut frame.as_target());

Feel free to use it. Might be an ok starting point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request help wanted Extra attention is needed question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants