Monday, April 29, 2013

Ludum Dare - Post-mortum

Well, that was quite an experience. Ludum Dare 26 has come and gone and I have very little to show for it. Due to a lack of planning on my part and some issues with conflicting responsibilities eating up my time, I didn't end up with anything to submit at the end of this game jam. Not to say I didn't get anything from the competition though.  I did get my feet wet, so to speak, and in doing so I think I came away with a number of ways I can improve upon my time management next round.

For instance, I discovered that I was spending a good amount of time coding things I could have easily gotten from existing libraries. The problem was, that I wasn't familiar with these libraries ahead of time, so I instead spent too much time coding it from scratch. Something as simple as a particle effect library or physics library could have helped me out immensely. I intend to spend the next few months getting myself acquainted with these libraries and finding out what I can do to avoid spending precious time trying to rewrite these things.

Another lesson learned was that next time I shouldn't try to do this from home. Family life, and other responsibilities can easily demand attention that will prevent a game from being finished. I ended up doing the majority of my coding in the morning (when the family was still sleeping) and in the evening (after everyone had retired). The whole 10-12 hours in between were just a cluster f*ck where I rarely accomplished anything of value.

I did manage to get a few interesting classes and functions out of this whole experience as well. The theme for the game was that it should be minimalistic in some way. I wanted to apply this to the artistic direction, so many of the objects drawn to the screen were basic shapes. I made them hallow, so that I could easily fill in the objects with whatever color I wanted. It was my intention to create several arrays with different color schemes that could be interchanged for each level. I had that working the first morning.

Next thing I wanted was some sort of map generation. I had a friend willing to work on this for me. He had already worked on something similar for one of his previous classes, and he figured he could easily rewrite it for me.

Another thing I was working on, (didnt get finished with it, but I have some promising code so far) was player movement that moved the screen around with it. I'd never done anything like that with a previous game so it was something new. I really should have attempted something like it prior to this game jam, since I spent a lot of time on this. I was also trying to incorporate a parallex effect using layered images moving at different speeds to give the game some depth. This was also somewhat new to me as I had read about it but not actually attempted it myself. Like I said, I have some promising code, but nothing completely working.

I also managed to get a pretty neat looking background for the game. I created a class that you can pass a surface (to draw to), a color scheme object, and the number of objects to be created: and the class would create a background object with small moving particles. It was a pretty neat effect, and as soon as I have some time, I'll post the code along with a little video clip or gif.

So I ended up with some pretty neat stuff and a neat idea for a game, but unfortunately it just didn't get to the "playable" phase. I might continue working on it, but I'm not sure when I'll have the time. At least I came away with a few lessons and even some neat code that I can implement elsewhere. All in all, it was a good time and I can't wait for the next Ludum Dare to come around.

Cheers,

-Newt

Friday, April 26, 2013

Ludum Dare - First Attempt

Okay, so I decided to take a stab at Ludum Dare 26. For those of you not familiar, it's a game jam. You are given a theme, and tasked with creating a game in a single weekend. Since this is my first go at the game jam, I'm going the more relaxed, less competetive route.

I'm still new at game development in general and I figured this would help me with my time management as well as just general development practice.

Anywho, it's been a couple hours since we were notified of the theme: Minimalistic. I still haven't yet nailed down a good idea for the game, but I've started working on the framework (basic functions, menu screens, etc.).

I've been throwing around some ideas for the game concept with a friend and so far we haven't come up with anything solid. Hopefully we'll have something by tomorrow morning. By then I should have most of the framework nailed down. Once I have a better idea of the type of game I can start to put together some sort of timeline and break the project down into tasks that need to be completed.

I'm thinking I might find some good music to use over at http://dig.ccmixter.org/

Some of the creative commons licensed music I've found there in the past has been really good. I'm sure I'll find something suitable.

Well, no time to waste... back to brainstorming

cheers,
-Newt

Tuesday, April 23, 2013

Pygame - Bad Guy Stuff

So I got to thinking about different bad guy types for the little zombie game I'm making. I've got a normal zombie that follows the player around, but that's boring as hell. I was trying to come up with something new. Something easy to do would be to adjust the speed of the zombies. Maybe randomize the speed upon intitialization of the sprite. Still kind of boring though...

What else can we do? Maybe zombies that leave a trail of acid or something? I thought this was an interesting idea, so I went ahead and started on it. Basically all you need is a function to create rectangles for you. Something like this:

def drawTrail(self,screen):
    old_x=self.rect.topleft[0]
    old_y=self.rect.bottom-(self.rect.height*.25)
    if self.trailcount == 0:
      if len(self.trailarray) > 10:
        self.trailarray = self.trailarray[1:]
      self.trailarray.append((old_x,old_y))
    self.trailcount+=1
    if self.trailcount == 15:
      self.trailcount=0
    for i in self.trailarray:
    pygame.draw.rect(screen, (0,255,0), (i[0],i[1],self.rect.width,self.rect.height/4))

It's not perfect, but it works. This function exists inside the badguy sprite class. It's called when the update function is called in the game loop.

What it does is takes the sprite position and puts the values into old_x and old_y. Then it checks a variable called trail count. I used this to tell the function when to append new coordinates to our array list.

The array length is how I tell where to draw the acid. If the array gets over 10 elements, I have it cut off the oldest ones.

Anyway, it works. You might want to change it up a bit and have it use images instead of just a green rectangle.

That's all I've got for now. I'm sure you can think of other ways to use this function. If you have a particle effect library, you might want to use something like this to draw little puffs of smoke as your player sprite moves. I dunno, I thought it was pretty useful.

-Newt

Wednesday, April 10, 2013

Game Flair

I know I'm deviating a bit from that last couple posts about designing a simple game with pygame libraries. But I stumbled upon a video that got me thinking about some of the little things you can add to a game to make them more interesting. If you're interested this is the video I'm referring to.

One really simple idea that they had was to use tweening or easing. Instead of just placing a new sprite or game object on the screen, we have it drop into place or instead of game objects just vanishing, you can use this to slowly scale them out of existence.

The way they did this was using something like:

x += (target-x) *.1

This is probably the most simple approach to tweening. Basically we give the sprite or rectangle a target and a starting point. Then we move the x by 10 percent until it gets to the destination or target. Simple right?

And it provides a really nice effect to the game for very little cost.

That's the easiest implementation of tweening/easing. If you want to get fancier you have to do a bit more math.

Here's a basic function to get you started

def linearTween(time, startPoint, change, endPoint):
  return change * time / endPoint + startPoint

time is the current time in frames, steps, whatever counter you want to use. As long as it is a percentage of the endPoint

startPoint is the value where we begin

change is the difference between the beginning and destination values

endPoint is the end time or frame or whatever for your tween.

Basically it's the same thing as x += (target-x) *.1 since you have a start, a target, diff, and percentage of change. However in this way we have a number of variables we can use in a bunch of different ways.

Check out this site: http://www.timotheegroleau.com/

He has a generator for easing functions that you can use for all sorts of fun stuff. In fact, Tim Groleau has a collection of material on the subject if you're interested.

-Newt

Wednesday, April 3, 2013

Pygame Sprite Animation

Okay, animation. Not just moving rectangles either, that stuff is covered by a number of other tutorials freely available on the interwebz. I posted an earlier version of this class a while back, but I noticed some weirdness with the way I was changing images. This is a revised version. Let's just jump right into the code and I'll explain as best I can after.

class Player(pygame.sprite.Sprite):
  # initialize the player object
  # requires x and y coordinates
  def __init__(self,x,y):
    # initialize the sprite (parent class)
    pygame.sprite.Sprite.__init__(self)
    # Set initial variables
    #change_x and y are used to move the object around the screen
    self.change_x=0
    self.change_y=0
    #facing keeps track of the direction
    #this is important so we know which set of images to use
    self.facing = 0 
    #here are the images we're going to loop over depending on the direction
    #start by setting the image to the image to start with
    self.image = pygame.image.load('hero.png')
    self.down = [pygame.image.load('hero.png').convert_alpha(),
                 pygame.image.load('hero1.png').convert_alpha(),
                 pygame.image.load('hero2.png').convert_alpha(),
                 pygame.image.load('hero3.png').convert_alpha(),
                 pygame.image.load('hero2.png').convert_alpha(),
                 pygame.image.load('hero1.png').convert_alpha()]
    self.left = [pygame.image.load('herol.png').convert_alpha(),
                 pygame.image.load('herol1.png').convert_alpha(),
                 pygame.image.load('herol2.png').convert_alpha(),
                 pygame.image.load('herol3.png').convert_alpha(),
                 pygame.image.load('herol2.png').convert_alpha(),
                 pygame.image.load('herol1.png').convert_alpha()]
    self.right = [pygame.image.load('heror.png').convert_alpha(),
                  pygame.image.load('heror1.png').convert_alpha(),
                  pygame.image.load('heror2.png').convert_alpha(),
                  pygame.image.load('heror3.png').convert_alpha(),
                  pygame.image.load('heror2.png').convert_alpha(),
                  pygame.image.load('heror1.png').convert_alpha()]
    self.up = [pygame.image.load('herou.png').convert_alpha(),
               pygame.image.load('herou1.png').convert_alpha(),
               pygame.image.load('herou2.png').convert_alpha(),
               pygame.image.load('herou3.png').convert_alpha(),
               pygame.image.load('herou2.png').convert_alpha(),
               pygame.image.load('herou1.png').convert_alpha()]


    # give the object a rectangle using image size
    self.rect = self.image.get_rect()
    # set the topleft location to the x and y coordinates
    self.rect.topleft = [x,y]
 
  # Used to move the object
  def changespeed(self,x,y):
    self.change_x+=x
    self.change_y+=y
     
  # This is where we update the images and location of the player object
  def update(self):

    # Update position according to our speed (vector)
    new_x=old_x+self.change_x
    new_y=old_y+self.change_y
     
    # Put the player in the new spot
    self.rect.topleft = (new_x,new_y)

    # This next bit is where we change images
    # First we see if the object is stationary. If both change coordinates are 0,
    # we're not moving and we can set the image to the 0 element in the array
    if self.change_x == 0 and self.change_y == 0:

      if self.facing == 3:
        self.image = self.left[0]
      if self.facing == 2:
        self.image = self.down[0]
      if self.facing == 4:
        self.image = self.right[0]
      if self.facing == 1:
        self.image = self.up[0]

    # The rest is just repeated code changing only the direction the player
    # object is moving. In this case < 0 means we're moving left   
    if self.change_x < 0:

      # set facing to 3 or left
      self.facing = 3
      # if imgvar is 0, we just started moving
      if self.imgvar == 0:
        # set image to the first element
        self.image = self.left[0]
        self.imgvar +=1
      # if imgvar is anything else, we've been moving
      else:
        # make sure imgvar isn't greater than the length of the image array
        if self.imgvar > len(self.left)-1:
          self.imgvar = 0
        # update imgvar and change the image
        self.imgvar += 1
        self.image = self.left[self.imgvar]  
  

Okay, that's a lot to take in (that's what she said), if you're not accustomed to pygame. First you'll notice that my Player class implements a pygame parent class. This can be seen in the first line:

class Player(pygame.sprite.Sprite):

I utilize the sprite class for a number of reasons. Collision detection is one thing that sprites do very well. There is a complete list of sprite functions here:  http://www.pygame.org/docs/ref/sprite.html

In any case, the important bits are the class attributes and functions. The class attributes to take note of are the change_x and y, as well as the facing and image array attributes.

change_x and change_y are used to move the object around the screen. They do this by utilizing the changespeed function. This function changes these values, which are later used in the update function to move the player. The first thing that update does is it moves the player rectangle to the new coordinates by adding the change_x and  change_y values to the current x/y values. Again, I won't go into too much detail because there are plenty of better tutorials out there on moving objects.

Next we have the facing attribute. This is just an integer that keeps track of the directions your character is facing. With the setup shown above we only have animation images for 4 directions, but you could easily add 4 more for diagonal movements. You'll notice this also gets changed in the update function based on the change_x and change_y values. If change_x is a positive number we'd say facing is right, or whatever number we're using to designate moving right.

The image arrays (up,down,left, and right) are where we store the images we have to loop through to get our animation. The current image is stored under the (you guessed it) image variable. All we do to animate our character is move along to the next image in our array, setting it as the current image.

Within the update function, the comments kind of explain what's happening. We move our rectangle, then check the change_x and y variables to determine which way we're facing and which image from which array we need to be on. Just keep in mind that I only put in the left direction. You have to repeat that chunk of code to include the other directions. 

Now you're saying, "This is all well and good, but how do we actually do anything with this class?" and "Why didn't you go into the other features of pygame, like the load.image function you used?" and "God this guy is retarded"

The short answer is 1)that you create an object within your game loop, use pygame events to utilize the changespeed function, and call the object's update function each loop. 2)I think there are much better explanations in the pygame documentation. 3)You're right, but there's nothing I can do about that.

I'll get more into how to use the sprite next week. We can plug it into the surface demo I did last week and we'll see how it works out.

-Newt