Friday, July 10, 2015

Prototyping - Food Tube

Anyway... so I've decided to go in a different direction because it is a bit foolish to spend too much time on any one aspect when entering into a new project. It's just hard to know what you need or will need. By jumping around, you end up with the likelihood of being able to use previously created things in different areas -- kind of like getting a power-up in a metroidvania game that allows you to progress further. Weird.

With that said, I started creating a little bit of functionality for the final boss. It's a good idea to do as little as possible at a time to get a result, and then build on the results. For example, the very first thing I did was display the ripped screen to the window. All this is is the regular image file.


Then, using the ripped tiles from earlier exercises, I created the mapping data, and loaded the images by tiles instead. It resulted in the exact same image as above except now it was drawn tile by tile instead. From here, it was time to add the food tubes that block the way. First, I simply added the sprite to the map.


If you've ever played Metroid before and made it here, you know that when Samus damages these things, they shrink down. If she cannot kill them in a timely manner, they will regenerate. Well, the first thing I did was order the various sprites from healthiest to weakest in a sheet:


So, now we can say that at the most healthy, we want the sprite at (0,0). When it is a little damaged, we want the next healthiest at (16, 0). And so on. And since they are in order, we can create an attribute on the Food object called health which goes from 0 to 4. If we take the health, and multiply it by the tile size, we can get the correct x coordinate to start cropping from. In code, it looks like this:

class Food(object):
    '''
    This is the food tube that supports Mother Brain. It starts fully replenished, 
    and when damaged, goes through 3 additional states of decay. If Samus does not 
    completely destroy it in a set time, it will regenerate [not implemented].
    
    The health of the food tube determines where the spritesheet should be copied from. 
        self.health * TILE_SIZE
    At full health, health is 0 and so = 0
    At full health, health is 1 and so = 16
    At next health, health is 2 and so = 32
    At next health, health is 3 and so = 48
    And when dead, do not draw at all.
    
    All food tubes occupy two tiles in the room. Provide the top col and row, and 
    the bottom is calculated.
    
    10, 6 is where demo tube is located on room.
    '''
    def __init__(self, col, row):
        self.sheet, self.rect = load_image(join("assets","food_sheet.png"))
        self.image = pygame.Surface((TILE_SIZE, TILE_SIZE)).convert()
        self.image.blit(self.sheet, (0,0), (0, 0, 16, 16))
        
        self.top = (col, row)
        
        self.health = 0
        self.is_alive = True
    
    def transform(self, change=1):
        '''
        Food tube will change based on damage. Once dead, cannot transform.
        '''
        if not self.is_alive: return
        
        self.health += change
        self.health = max(0, min(self.health,4))
        
        if self.health >= 4:
            self.is_alive = False
        self.image.blit(self.sheet, (0,0), (self.health * TILE_SIZE, 0, TILE_SIZE, TILE_SIZE))
        
    def draw(self, surface):
        print("self.health={0}".format(self.health))
        if not self.is_alive: return
    
        x, y = pixel_from_tile(self.top[0], self.top[1])
        surface.blit(self.image, (x, y))
        surface.blit(self.image, (x, y+TILE_SIZE))

Oh... one particular thing to note about this code that someone reading might find interesting is that to force a value to be between two values (ex: 0 <= x <= 4), then you can call min() on the value to bring it into high bounds, and then max() on that to bring it into bounds low bounds. The reason I bring this up is because it seems a little counter-intuitive. But, it does work.

When I want to test some aspect like this without having to noodle around with a bunch of additional details, I just set the various states to a key event. So, pressing left damages the tube and pressing right heals it. Once the health fully expires, pressing left and right does nothing.

    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
                
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    food.transform()
                elif event.key == pygame.K_RIGHT:
                    food.transform(-1)

The next thing to do is start getting Motherbrain set up. I also just realized that I could have ordered those food sprites in reverse so that the health actually makes sense in context to a higher number being more healthy instead of how it is now where zero is most healthy. I'll actually probably wait until I'm making the real thing to make the change.

No comments: