Wednesday, September 25, 2013

Hitboxes and you!

Pygame, you did a bad thing.  With sprites, if one wants to position them, they manipulate the rect property.

What do you mean that isn't a bad design move!  Ok, if that was all it was it wouldn't be an issue... except that the rect property is also used in hit detection... and that the sprite image always, and I mean always is redrawn on the top left hand corner of this rect, wherever the rectangle controlling the sprite is located.  Anyone that has ever played or made a bullet-hell shooter can tell you that is a terrible idea.

So, when I set the rect property to allow for things like the sprite of Arianna to nudge up against the wall, well, bad things happen.


Now, fellow reader, do not despair, for I was able to find a reasonable workaround.

First, I made a new property simply known as hitbox.  The hitbox was shaped to my liking.

Next, I had to rewrite the function that checked for collisions.  Before it looked like this:

    def checkCollision (self,sprite):
        level_bumps = spritecollide(sprite,self.sprites,False)
        for items in level_bumps:
            items.onCollision(sprite)



It was using the native function that came with pygame to check for collisions between sprites.  Course, both rely on the sprite's rect property to work, which I am no longer using for collision detection with moving characters like the PC or enemies.

Course, the native function relied on a function that pygame Rectangle objects have: colliderect().  So, I rewrote the function thusly:

    def checkCollision (self,sprite):
        for item in self.sprites:
            if sprite.hitbox.colliderect(item):
                item.onCollision(sprite)


So that the onCollision function would be called right when the one sprite was seen to be in contact with another.

Finally, it would be simply making sure that the hitbox moved with the rest of the sprite.  I put that in the update function, where the last line reads:

self.hitbox.center = self.rect.center

At present that simply means that the hitbox is centered on the sprite.  I could change that later, which I probably will.

The result?


Saturday, September 21, 2013

The world is right now!

Ok, I fixed that bug in the room!

Here's how!

First, I changed the sprites used for the walls and that with DirtySprite as opposed to regular sprites.

Next, I used the OrderedUpdates class, which in Pygame is a subclass of the RenderUpdates class, which is a subclass of the RenderPlain class - the class responsible for redrawing DirtySprites.

And... yes!  No more negaverse!

Tuesday, September 17, 2013

Sprite Groups and You

This is what happens when you don't set a redraw order:
Chaos!  Utter Chaos!
I originally had it so that each sprite was in a separate group for redraw.  Now the sprites are all in the same group.  The code itself for drawing the room hasn't changed much from the last time it was posted, only that its to one group now as opposed to multiple groups.  I think I am going to change that.

Stay tuned for the exciting conclusion to this problem.

Monday, September 16, 2013

I'm still working on it!

The beauty of that video?

Well, there was the problem of figuring out how to divide rooms.  Well, one way might be not to, and to only divide regions of an area as opposed to individual rooms.

Course, one thing at a time.  I need to make sure the doors work.

Friday, August 30, 2013

Finally! She bumps into walls (sorta)!



Would you like to know how I did it?

Ok.

So, in the level class that is being used to hold the level sprites I use the collision detection algorithms that come with Pygame.

level_bumps = spritecollide(sprite,self.walls,False)
 
Now, it isn't quite as simply as it sounds, mostly because all it does is give a list of all the sprites of a given group that intercept with a given sprite (that last argument is whether or not I want to destroy the sprites afterwards... which is no).  While this does make things easier on me (meaning that I don't have to program checking for when one pixel loves another pixel), I still have to program what should be done when two sprites collide.  After all, any sauvy reader should be able to point out that the floor is made up of sprites.

At the moment, the functionality is for walls that are not corners, as the video points out.  Still, there was an interesting way that I achieved this.  Though the power of objects!

So, first, we capture those sprites that meet another sprite:


def checkCollision (self,sprite):  level_bumps = spritecollide(sprite,self.walls,False)  for items in level_bumps:     items.onCollision(sprite) 

Notice that weird function?  Yeah, that doesn't come with Pygame.  I added those using a class that is a subclass of Sprite that I made that simply has that function.  Whether it does anything depends on whether or not I bothered to fill anything in to the overloaded class that would be, say, a wall.

Here is what it looks like for a wall by the way:

def onCollision (self,sprite):
        offset = 5
        if self.orientation == NORTH:
            relocate = self.rect.bottom+offset
            sprite.rect.top = relocate
        elif self.orientation == SOUTH:
            relocate = self.rect.top-offset
            sprite.rect.bottom = relocate
        elif self.orientation == EAST:
            relocate = self.rect.right+offset
            sprite.rect.left = relocate
        elif self.orientation == WEST:
            relocate = self.rect.left-offset
            sprite.rect.right = relocate


The result?  Well, you can see the video, can you?

Tuesday, August 27, 2013

Saturday, August 24, 2013

She's Out!

Well, there was a series of funny problems, starting here:
I swore, I banged my head against the keyboard, and cried.  Then I learn that the fix was only one line long:

pygame.key.set_repeat(100,100)  

Now it works!

It ain't over yet.

Friday, August 23, 2013

Her Immuration will be over soon!

Also, an idea as to what it would look like in motion.  I'll let you know when its fix and she's properly moving.

Monday, August 19, 2013

A moment of dirty programming

It could have more to do with the fact that when I was formally schooled in the ways of programming that the teaching language at my college was Java, and Java can be a nit-picky little bastard at times that comes with object oriented practices and static casting drilled into would-be programmers that has me feeling a little... well, off.

At the moment it revolves around the concept of dynamic type-casting.  While in Java you had to tell the complier specifically what type of value a variable was going to store (static typing), the Python interpreter simply assumes what a variable's type is based on how it is being used on a given line (dynamic typing).  Not the first language to be this way, but for the most part, as par instruction from school, its usually a good idea to, even in a dynamically typed language, to not have a variable be reassigned from being a number to a string for example.  The fear being of using the variable in a point of the program as string when the algorithm requires an integer or a float.

Course, today, its using an integer as a boolean.  Here's the naughtiness:

    def parseEvent (self,event):
        if event.type == KEYDOWN:
            if event.key == K_UP:
                self.direction = "back"
                self.moveDown(-1)
                self.motion ++
            elif event.key == K_DOWN:
                self.direction = 'front'
                self.moveDown(1)
                self.motion ++
            elif event.key == K_LEFT:
                self.direction = 'left'
                self.moveAcross(-1)
                self.motion ++
            elif event.key == K_RIGHT:
                self.direction = 'right'
                self.moveAcross(1)
                self.motion ++
        elif event.type == KEYUP:
            self.motion = 0

    def update (self):
        if self.motion:
            self.image = self.sprite_images[direction+"_"+"walk"+self.motion%2]
        else:
            self.image = self.sprite_images[direction+"_standing"]


So, to explain.  The variable motion is a counter that is measuring state, which, when redrawing the sprite, is used to determine which arch in the walking motions are to be drawn.  If motion equals zero then it is assumed that the character isn't moving at all, and therefore the sprite frame for standing still is drawn.

So, as you see, the motion variable is being used as a boolean.  Maybe not as bad as other examples floating around, though a piece of me can see that this was a bizarre solution to the problem involving redraw.

Friday, August 16, 2013

Lets talk about states

... but first!  Complete sprite sheet!!!


Second, there is an algorithm being planned for the movement.

Now, when the player presses a key on the keyboard or clicks/moves the mouse or tap/swips a touchscreen the machine recieves an event from the interupt prompts.  Detail at the moment on this mechanism isn't that important, just that these events determine things in the game like the direction that the character is facing.

For the purposes of this, the states are going to be for determining which lovely image of Arianna would be used for the motion.  Now, I have thought this through.


There will be a direction variable and a boolean for movement.  The sprite will not only move if the boolean is true, but the animations will cycle in accordance to the direction.  Else the stationary images are used and the sprite does not move.

I'll show you what I have when it comes.

Wednesday, August 14, 2013

Almost!



Just need to fix them up a bit!

Course, the hard part was trying to imagine what the side of a gait would look like, and to the point how that would be shown... again, I'm a programmer, not a graphics artist.  Nevertheless she is coming along.

If anyone is reading, I like comments!  I thrive on them!

Tuesday, August 13, 2013

She's a coming!

I took a break from posting to work on the sprite for Arianna.  I am almost done!!!

I have the backwards sprites now!!!



And the sides are on their way!!!

Now, I am blessed with a symmetrical character, for her facing left is the same as her facing right, so I would have the sprite ready tomorrow and I would start programming the character!

Be on the lookout!

Friday, August 9, 2013

See Arianna Run!



She is coming along as you can see!  I predict that I would have her sprites ready and an attempt to put her into the test room to move around and do things like testing the collision detection and the darkness mechanics.

In the meantime I'm working on the other parts of her sheet.  For this time it would be the front, back, and side.  The hard part would be in depicting her in mid-motion.  Hopefully, once I figure that out it can be used for other characters in the game.

Course, I have the back!

Yeah, I'm a programmer first.  Its not impossible, but it ain't easy.

Thursday, August 8, 2013

GIMPing it! Stone Wall Recipe

For anyone that is curious the graphic editing software that I am using for the graphics is GIMP - a freeware editing software that can be plugged in all over, and I use it because the cost of Photoshop is in the hundreds, I'm cheap, and considering that I would like to make money someday from software I have a moral qualm against software piracy. Soapbox and confessions aside, I have decided to use Thursday's entry to detail all the ways I use GIMP to trick people into thinking that I am actually capable of graphic design.  This will run for as long as I have these 'recipes' for making pretty sprite art with minimum skill.

Brick/Stone Walls

This isn't hard if you abuse the selection tools.  For the walls as depicted in the test room from a past post, you first decide how large the tile will be in pixels, and then roughly divide that lengthwise into nine sections.  You may have padding if the dimensions don't divide evenly.

This tile is 50x50 pixels, so there is padding.
So, the next think you do is use the rectangle tool to select the first two sections from the bottom out to the halfway/thirdway point.  Fill it with the colour of your choice.



Next, go into the Select menu and select the border option, and leave it at 1px.  Using the burn tool, darken the selected area to your liking.



Then, select a small rectangular region at the top of that brick, but lower than the border and lighten it with the Dodge tool to your liking.




 Then, copypasta!





Next, copypasta that entire row, and past it on the next row, such that it is above the previous row.




Remember to evoke more copypasta magic!




Now, select the new row and use the dodge tool.




Now, remember a few paragraphs earlier I instruct to divide the working area into 9 sections?  The bottom row is taking up the first two sections, and now the current row being contended with is taking up another two... that is going to change.  Use the scale tool and grew this two section row into three sections.

The even more amazing part is the fact that the scale tool will automatically add the extra shading that would further define the bricks as they appear closer to the player.

For the third row, wash (and fix the occasional blunder that the pencil tool and eye dropper tools know how)...



...rinse...





... and repeat.  The third row should be the remaining four sections.





Add some colour or texture for the padding (if there is padding left over).



Tada!  You've just laid down some brick!  Now, that wasn't so hard now was it?

Wednesday, August 7, 2013

Meet Arianna

This is the start of building the sprite work for the main character, Sister Arianna - an acolyte who is part of a religious order whose tenet's include things like the worship of one omnipresent father-like god, purity of mind, body, and soul, stewardship of the world, and high fervor.  The order believes that the undead along the lands is a blight upon the land and feel that it is their divine duty to purge the undead... through any means necessary.

At this early stage the only thing I have decide about her character is that she isn't going to be a templar - at least at the start.  As that picture should suggest, she is a more passive character that would be doing the behind the scenes legwork of her church: running orphanages, tending to sanitariums, and teaching children the tenets of the faith.  The actual job of killing zombies would be left to templars and hunters of varying degree.

To get this character into the mess she would need to be to make the game interesting would involve her going on a pilgrimage that goes horribly wrong, leaving her to fend for herself in a world that she knows is hostile.

There will be more developments, and various points of the game's story may be ditched as the mechanics become more clear.  Nevertheless this is Arianna, your PC.

For anyone that feels that such a character would be hard to relate to, I have thought of possible characters that were unlikely PC for a single-player non-WRPG, and sheltered nun is up on the list.  The hope, as is the case in a good story, is that the things that happen to a character shape a character as events in one's life shape them.  I believe that is called 'character development'.

As an aside, I later added the tabard on her dress I showed an eariler verson of her to someone:
That person complained that the character looked too 'bridal'.


Tuesday, August 6, 2013

Laying down some tile!

I finally got it to work!

Pictured is a test room that will be used to test the functionality of other aspects of the game, both basic and advance.  There were some share of challenges with this, but as I said from yesterday's post, baby steps.

Right now its just the drawing of the sprites.  Once that is done it would be implementing the PC to be able to take button input and move accordingly, and then collision detection.  After all, the walls should behave like walls.

Nevertheless, as I went to coding this test room I came across a stumbling block as I was coding it.  In earlier coding the sprite subclass I had for the walls was set such that I give it a compass direction represented as an integer (north, south, east, west).  As for the corners I would give it two compass directions: one for each of the joining walls.  As an example, if the corner was where a north and west facing wall intercepted than it was north/west.

Course, this was coded under the understanding that the fillSprite command was simply going to make a deep copy of an already created object, not pass a constructor.  So, seeing as the constructor required an orientation for walls but none for floor... well, one can see the clusterf*ck that was to come.

The solution was simply having two different fill functions: one for the floor and one for the walls.  The thinking is simply that when building the levels the only things that would be batched-put would be walls and floors.  Every other sprite: doors, chests, and NPCs would be manually placed anyways.

Here's the one for the floor:

def fillFloor(sprite_construct,leftTop,rightBottom,group):
    proto = sprite_construct()
    for x in range(leftTop[0],rightBottom[0]+1,proto.rect.width):
         for y in range(leftTop[1],rightBottom[1]+1,proto.rect.height):
            sprite = sprite_construct()
            sprite.rect.center = (x,y)
            group.add(sprite)


The '+1' on the range was to fix a problem where the sprites to be located along the right-most and bottom-most position were not unfilled, so this is done with an understanding of the Python range function, which is built into the language.

Now, here's the one for the walls, the interesting one:

def fillWall(sprite_construct,orientation,start,finish,axis,group):
    proto = sprite_construct(orientation)
    for c in range(start,finish+1,proto.rect.width):
        sprite = sprite_construct(orientation)
        if orientation == NORTH or orientation == SOUTH:
            sprite.rect.center = (c,axis)
        else:
            sprite.rect.center = (axis,c)
        group.add(sprite)


I realized when building walls that they would be one sprite long by so many sprites wide (or vice versa).  So, I simply place x/y position of the start, the x/y position of the end, the y/x position of the wall along the y/x of the screen (the axis) and the wall's orientation.  The function would position the wall according to whether it was a horizontal wall (a North or South facing wall) or a vertical one (East or West).

So, the calls that brought about that room?

        #first the floor
        fillFloor(StoneFloor,(0,0),(300,300),self.floor)
        #then walls
        fillWall(StoneWall,SOUTH,75,225,275,self.walls)
        fillWall(StoneWall,NORTH,75,225,25,self.walls)
        fillWall(StoneWall,EAST,75,225,25,self.walls)
        fillWall(StoneWall,WEST,75,225,275,self.walls)
        #corners are inserted manually
        self.walls.add(InnerStoneCorner(NORTH,WEST,(25,25)))
        self.walls.add(InnerStoneCorner(NORTH,EAST,(25,275)))
        self.walls.add(InnerStoneCorner(SOUTH,WEST,(275,25)))
        self.walls.add(InnerStoneCorner(SOUTH,EAST,(275,275)))
        #lets test the outer corners
        self.walls.add(OuterStoneCorner(SOUTH,WEST,(125,175)))
        self.walls.add(OuterStoneCorner(SOUTH,EAST,(125,125)))
        self.walls.add(OuterStoneCorner(NORTH,WEST,(175,175)))
        self.walls.add(OuterStoneCorner(NORTH,EAST,(175,125)))
        #as are doors and arches
        self.doors.add(StoneEntrance(NORTH,(150,25)))
        self.doors.add(WoodenDoor(NORTH,(150,25)))


And voila!  A room.

Monday, August 5, 2013

Baby Steps

First Entry!!!  As this is the first entry some explanation is in order.

If you were redirected from my CV from an application somewhere in the internet you know who I am.  Else, check the about page if you must know.  As much as I would like a job I also value my privacy.  If my identity doesn't matter to you, for all intensive purposes I am Mikodite Yvette.

Intros aside let me tell you about my current project.

Its a 2D, birds-eye-view adventure game where the player is a priestess navigating dark catacombs in an undead-infested swamp.  Maybe not the most exciting idea for a game that you have ever heard, but the twist to justify it all is the fact that there will be a lighting mechanic that involves deciding between being able to see but notifying enemies of your location, and stumbling around in the dark undetected, or at least in much of the game one would be doing that.

Course, before I can even start building levels I would need a proof of concept... a little something that
  1. Test to make sure that the mechanics are properly drawn and coded.
  2. Prove that the idea has some merit.
So, of course, the first major hurtle: getting the game engine to actually draw something onto the screen.

The engine of choice is Pygame, a freeware engine for Python with the Lesser GNU license.  Its good for making 2D games (I've heard that it can make 3D games as well, but one thing at a time).  Python can be a nice little language that is at the forefront of rapid application development, course I have found that programming in it have its own, at times, bizarre challenges, which I will elaborate on in future entries.

Now, to a weird problem I was having, and how I solved it!

A design decision was to have a class that was responsible for storing and managing the level sprites.  This Level class would be the super class of other classes that would override it, populating the sprite groups with all the sprites of the level - you know, the enemies, the walls, doors, etc.

Part of this was a function I called 'fillSprite'.  I give it two cordinates and it fill draws the sprite of my choosing between those too points, because I am lazy and do not want to so much as copypasta every floor tile for a dungeon. The code at time of writing:

 def fillSprite(sprite_construct,topRight,leftBottom,group):
    sprite = sprite_objects[sprite_construct]()
    for x in range(topRight[0],leftBottom[0],sprite.rect.width):
         for y in range(topRight[1],leftBottom[1],sprite.rect.height):
            sprite.rect.center = (x,y)
            group.add(sprite)


Where topRight/leftBottom are tuples, and group is Group, a subclass of the Sprite class that comes with Pygame that acts as a container for other classes.

"Why is there a method call from a dict element?" asks the Python developers I keep running into on StackOverflow, "And the hell with nested statements!?  What are you, stupid?"

Well, the idea is that one loop iterates through the x coordinates while the other goes through the y.  If there is a way to do that without nesting loops I would love to hear it.

Next, the tiles in the game are subclasses of sprite, intending to make it easier to actually create the objects (I mean, typing StoneFloor() is easier than making a sprite like it), and the sprite was created at the function call.

Next, this came from an original implementation of this function, where I would give it a sprite and the function would create a deepcopy of the sprite to put on it.  The problem with that approach was that it produced the following runtime error:

Traceback (most recent call last):
  File "K:\The Undead Horde\level.py", line 79, in <module>
    level.draw(screen)
  File "K:\The Undead Horde\level.py", line 38, in draw
    self.floor.draw(screen)
  File "C:\Python32\lib\site-packages\pygame\sprite.py", line 475, in draw
    self.spritedict[spr] = surface_blit(spr.image, spr.rect)
pygame.error: display Surface quit


The hell?  I asked myself.  So, after playing around with the code I found out on StackOverflow that it does that if you deepcopy any pygame surface (and the sprite class uses the Surface class).  So I removed the deepcopies.  Course, anyone that understands object oriented programming understands why I would want a deep copy as opposed to a shallow one (that pointer = pointer provides), so a did some horror in the programming language.

Where the level tile classes were declared I put in a dict class that stored the... wait for it... constructors of the classes associated with a string key that would be provided to the function and would call the contructor.

How does that look you might ask?  With the following call:
fillSprite('StoneFloor',(0,0),(300,300),self.floor)

As I said: Baby steps.