Sunday, December 16, 2012

Developing a Developer: Weekly Report 6


I've finished the first book. It's been as good as I remembered. It's been a hard week. I'm having many assignments at college and I think I'll have to slower the pace. Anyway, let's get started.

Platforms



Last week I posted the source code of platforms, the adventures of a green square in a dungeon. I've finished the game and the old green square has evolved: now it's a raccoon.

If you look at the source code, you can see that the raccoon is composed of several images. Each image represents an action. One image represents standing, another for jumping and, finally, four for walking. I've experienced the following challenges:

  • Making the raccoon walk at the proper pace. At first, the raccoon images alternated to fast in comparison with the distance than it actually walked. This is because I made the images change every frame. I changed it to one image every four frames.
  • Making the raccoon “look” at the right direction. This is easily achieved through a Pygame built-in function: “pygame.transform.flip”. Although this is very easy, I had to be very careful storing directions in variables for the raccoon to remember where was looking at after walking.
  • Making the raccoon “fit” in the old square. Well, this is not the actual challenge, the thing is that my old green square was 20x20 pixels large. The raccoon looked so small in it... Well, I ended up changing the player square size to 50x50. This was tedious, because I had to change the entire level for the player to fit in the space between the first platform and the staircase... Lesson learnt the hard way.

I enjoyed the part of searching files for my game. It's amazing the amount of open resources available on the Internet.

Windows executables

This part took me a lot of time. The author of the book teaches us how to use a script to convert Python scripts into Windows executables. It hasn't work with my Pygame applications. In fact, it doesn't work with the author's scripts. In the past I used the book's method and I didn't encounter any of these problems. Something has changed either in py2exe or in Pygame. Apparently the problem is related with the font and mixer modules.

Gladly, someone else found this problem first and posted a solution. Check the link: http://www.pygame.org/wiki/Pygame2exe. I decided to use my own font file instead of the default. It works.

Honestly, I would like to hide every file that there's in the distribution but the exe file. This would keep things simple for the user. There must be a way to do this.

I got to understand a little about distributing applications in this pdf: http://cs.iupui.edu/~aharris/pygame/Appendix_C.pdf. However, I feel quite intimidated about the distutils Python module. My old C and Ada compilers output the executable with a single command. Having to write a setup script is quite strange for me. Perhaps I'll need to take a look at it in the future.

Monday, December 10, 2012

Developing a Developer: Weekly Report 5


This week I've corrected a few bugs there were in staircase.py. One of them – for example – allowed the player to jump while falling if he/she fell without jumping... Anyway, the current version includes further changes (I even changed the name of the project to “Platforms” and divided the program into files instead of keeping a single file). The program is organized in classes and since I read chapter 19 I've included a few things related with imported images. Let's begin.



Screenshot of Platforms

 

This game test is divided into several classes. Perhaps, the most important class is “Collisionable”. This class contains a Pygame “rect” object reference as an attribute. This “rect” object is the actual responsible of controlling collision between itself and other objects (including the player). Many of the remaining classes are children of “Collisionable” and they implement their own behaviour (platforms, mobile platforms, buttons, etc).

While chapter 19 taught me to load external images and place them into the game, it has only examples on putting a static picture and moving it around the screen. In this project, I've used texture images like the following:

Background texture
Platform's texture

 

If you pay attention to this texture images, you will see that the leftmost part fits with the rightmost part of the picture. The same happens with the top and the bottom of the picture. The only thing I have to do is to replicate the picture over and over again throughout the entire surface I want to have that texture. In order to fulfill that purpose, I created the Texture class. When a Texture object is created, the constructor copies the picture pixel by pixel into the Pygame “rect” object that requires that texture. In my opinion, this algorithm is quite slow and inefficient, but it allows me to store the calculated surface into an attribute and therefore needing to make the calculation only once in the execution of the game.

The pictures were downloaded from http://opengameart.org.

Monday, December 3, 2012

Developing a Developer: Weekly Report 4


I've read the second part of the 17th chapter (which covers animations) and the entire 18th chapter (which covers input). This week I've decided to change the Reverse Engineering phase I did in the previous weeks. Now, either I improve the program (in the way I did making a new AI for Reversi) or I made my own program. In fact, I've done many pygame tests this week and I think the best way to show my current skills is through a video.







Rotating Ball


You can download the script here.

It's just a ball that bounces across the screen. The most interesting part about it is that I used polar coordinates for the mark that gives the impression of rotation and them I transform them into Cartesian coordinates in the following way:

x = r * cos(o)
y = r * sin(o)

x, y = Cartesian coordinates
r = distance from the centre of the ball to the centre of the small mark (which is a circle, by the way)
o = angle that constantly increments or decrements depending on the rotation direction



Spiral


You can download the script here.

OK, drawing a ball is easy, because Pygame has a built-in function called “pygame.draw.circle” which does the job for us. In order to draw a spiral, I've implemented a class which contains the formula of the spiral and remembers the value of the always incrementing angle in polar coordinates. Outside this class, in the game loop the angle is incremented and a straight line is drawn from the previous point to the new point calculated using the nextPoint method in the spiral class.

There are a few more sophistications that you can find in the source code, but I find particularly interesting the precision attribute in the spiral class. This attributes set the increment of the angle in polar coordinates and thus it will determine the smoothness of the line. It might be confusing, but since the precision attribute determines the increment of the angle, the slower it's value the higher the actual precision is going to be (counterintuitive, my mistake). See the pictures below.


Spiral: PRECISION = 1

Spiral: PRECISION = 0.005



Balls and Input



You can download the script here.

I basically take the rotating ball I've explained above and wrapped it into a class. Then, each time the player clicks on the screen, I call to the class constructor and create a new ball with new attributes. That way, I can have many rotating balls with different attributes such as speed, direction, rotation direction, etc. I confess I enjoy clicking and clicking insanely into the screen.

clickBall.py




Platforms


You can download the script here.

In this test I've had many headaches. It works with both input and collision detection in the context of a platforms game. The code is a little bit long to explain everything here, so I'll explain directly the most difficult function:


def controlFalling(self, rects):
    # control jumping
    if self.jumping:
        self.rect.top -= self.JUMPSPEED
        self.jumped += self.JUMPSPEED
        if self.jumped >= self.MAXJUMP:
            self.jumping = False
            self.falling = True

        # check if we hurt our head with some sort of ceiling
        collisionCeiling = 0
        for r in rects:
            if self.rect.colliderect(r):
                collisionCeiling = r.bottom

        # if this is the case, correct position
        if collisionCeiling > self.rect.top:
            self.rect.top = collisionCeiling
            self.jumping = False
            self.falling = True

    # fall
    else:
        self.rect.bottom += self.FALLINGSPEED

    # see if rects collide and detect current ground
    collisionGround = self.floorY
    for r in rects:
        if self.rect.colliderect(r):
            collisionGround = r.top

    # if the object is in the ground, correct it's position
    if collisionGround < self.rect.bottom:
        self.rect.bottom = collisionGround
        self.falling = False


 

There are two cases to consider.
a) The object is jumping
b) The object is falling

In both cases, I firstly move the object (upwards if it's jumping and backwards if it's falling) and then I see if it's colliding with something. If this is the case, I correct the position of the object, for I don't want to display it overlapping with anything. In the case of jumping, we also stop the jumping boolean attribute of the player, because the player must fall (although he could grab something in the ceiling and hang... Mmmm... Maybe for another test).