Monday, February 9, 2015

Sprites

     Alright, finally we should be getting into some really cool stuff (how many times have I said that?). According to my book, the whole thing we did with the ball isn't very good for making games. Let's all just take a moment to bang our heads against a wall.
     Well, there is a good reason. What if there was an actual background or multiple objects were moving on the screen (two things that you always, always see in a game)? Well, we have to use sprites, and conveniently enough, Pygame has something to help us with that.
     Okay, you're probably asking, "what's a sprite?" People use that term to refer to practically anything in a 2D game (usually animated). Sprites have animations and interact with other objects on the screen. They also have sprite classes, the base class being Sprite (whoa, didn't see that coming). We use it as a template to create our own subclass, and the code to create it looks a little something like this:

class ObjClass(pygame.sprite.Sprite):
    def __init__(self, image_file, location):
          pygame.sprite.Sprite.__init__(self)
          self.image = pygame.image.load(image_file)
          self.rect = self.image.get_rect()
          self.rect.left, self.rect.top = location

     So we basically tell the program we want to create a subclass (ObjClass), then we promise to define itself, the image, and the location. We initialize, or as I like to remember, "set" the self, and then we load the image. After that, we find the size of the image (because no matter the transparency, all pictures are square) and find the location. The location contains 2 items, x and y, so we were able to assign them to two items on the other side, left and top (the order in which they are listed counts!). Oh, and I almost forgot; the two main properties of a sprite is the image itself and the rectangular area around it.
     Now that the subclass has been defined, we should actually use it. I'm thinking of using a different ball image (the old one is just a bit too big), so here's a new one! This image's size is 50x50.


     That's better. I saved it as tinyball.png (but you can always use a different name if you want to). Since we used generic variables, we'll have to define those variables. I guess I'll add a bunch of balls so we'll be using a for loop in the program.
     First, we have to define the image_file.

img_file="tinyball.png"

     Note that img_file is a different variable than the one we used, image_file. This is because we do not directly define the image_file in the def block. Remember, you specify the object in the parenthesis of a def function when you trigger it.
     Speaking of triggering the def, let's specify the location and image with for loops.

for x in range(0, 3):
    for y in range(0, 3):
        pos = [x * 130 + 10, y * 130 + 10]
        ball = ObjClass(img_file, pos)
        screen.blit(ball.image, ball.rect)
        pygame.display.flip()

     X resembles the x axis, and, you guessed it, y resembles the y axis. The x loop triggers the y loop which triggers the other stuff. Don't hurt your head thinking about it. It defines the position by taking the value of x and y's current iterations and doing a little math to them to change the position. We then assign the ObjClass to a variable called ball, in which we also define image_file as img_file and location as pos. It then is blitted onto the screen and the screen flips.
     Remember, it's always a good idea to keep your programs as flexible as possible. There might be better ways to write this, but this was the only solution I found to the bug I encountered. You can try creating a more flexible version of this if you please.
     Anyway, the program will then look like this.


     ...Nine balls? I'm honestly not entirely sure why there are NINE. But, I guess Python's going to do what Python's going to do.
     I'm feeling pretty wasted by this article now. I guess I'll see you when I see you! But before I go...
     Everyone programs a different way. I challenge you to find ways to program that feel the most comfortable, flexible, and/or efficient to you. You'll really have to think about this one!

Sunday, January 4, 2015

A Quick Tip: Perfect Side Switching

     Last night I came up with an idea. Actually, I read it from my programming book, but it gave me an even better idea. So, I added a second challenge to the last article. The challenge was to make it so that when the ball went off one side of the screen, it reappeared on the other. The likeliest code you may have chosen would probably be the following.

import pygame, sys
import time
pygame.init()
screen=pygame.display.set_mode([640, 480])
screen.fill([255, 255, 255])
ball = pygame.image.load("Ball.png")
x=50
y=50
yspeed=5

screen.blit(ball, [x, y])
pygame.display.flip()
while True:
    pygame.time.delay(20)
    pygame.draw.rect(screen, [255, 255, 255], [x, y, 150, 150], 0)
    y = y + yspeed
    if y > screen.get_height():
       y = -150
    screen.blit(ball, [x, y])
    pygame.display.flip()
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

     This makes it so that if the y position goes past the bottom of the screen, it reappears 150 pixels (the ball's height) above the screen and sink back down again. This is nice and all, but the ball completely disappears before reappearing up top. That's when I started thinking, "What if you want to make a game with a topview but do not want players to hide freakin' off screen?" I, my friend, spent countless minutes to figure out the answer, but I have discovered it.
     The trick I found is that we have to add TWO balls. This way, when a ball starts going off of the bottom of the screen, the other one starts appearing up top, so it's half of a ball on the bottom, half on the top.
     Why don't you think about how to use this idea for a bit. If you want to be a programmer, you have to REALLY think and do your own things. Try tinkering with the code for a while and see what you come up with.
     Done yet?
     Okay. Well, I'll show you my thought process, step by step.
     To start off, the 2nd ball has to be at a different height than the 1st one. The screen is 480 pixels tall, so that's how far apart they need to be at all times (because half on top, half on bottom? there is a 480 pixel difference). Ball 1 is at position 50, so at 480 pixels above is ball 2 at position -430.

import pygame, sys
import time
pygame.init()
screen=pygame.display.set_mode([640, 480])
screen.fill([255, 255, 255])
ball = pygame.image.load("Ball.png")
x=50
y=50
yspeed=5
y2 = -430
screen.blit(ball, [x, y])
pygame.display.flip()

     That will set a ball way offscreen, but it will show itself as ball 1 is leaving. Speaking of leaving, when they reach point 480 (the number retrieved from the get_height function), they have to reappear back up. At first, I just set their points to 430, but I noticed that the top ball came down before the bottom ball even actually hit the bottom. Fatal common sense mistake, when in the first part of the program, ball 2 is at point 0 when ball 1 is at point 480. They ALWAYS have to be 480 pixels apart, so the point they teleport to has to be -480.
     (Also, don't forget to do all the normal functions you did to ball 1 to ball 2 as well.)

while True:
    pygame.time.delay(20)
    pygame.draw.rect(screen, [255, 255, 255], [x, y, 150, 150], 0)
    pygame.draw.rect(screen, [255, 255, 255], [x, y2, 150, 150], 0)
    y = y + yspeed
    y2 = y2 + yspeed
    if y > screen.get_height():
        y = -480
    if y2 > screen.get_height():
        y2 = -480
    screen.blit(ball, [x, y])
    screen.blit(ball, [x, y2])
    pygame.display.flip()
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

     Now run that program. the balls should move perfectly. Also, you might be wondering, "But this is insufficient! What if a deadly object hits one ball offscreen?" Come on, use your head! You can turn off the ball's reaction to anything if it is not onscreen. And if you're skeptic if this really is timed perfectly, just try recording it with something and slowing it down. I did that to answer the question, and it is flawless. Your eyes are just messing with you, dude.
     Well, that's all I have to say on the matter. See you next time! Until then, I challenge you to, of course, experiment. Also, I believe you now know enough about blitting that you can try creating some sort of animation! Just, try it if you want to.

     Oh... I forgot to say something. Just so you know, the pygame.time.delay function measures time in milliseconds, so that's why I set it to 2,000 a couple articles ago. Just thought you should know that.

Sunday, November 2, 2014

Bouncing

     Looks like I missed an article on Halloween... I've been trying to write an article but other stuff keeps happening... Oh well. I probably wouldn't be able to find a good Halloween theme for this topic, anyway.
     Welp, here's today's! Hopefully you aren't caught up by the time you read this.
     Anyway, I hope you still have the picture of that ball in your Python folder--we're using it some more! As you can see from the title of the article, we are going to "bounce the ball."
     Secretly, objects cannot make contact with the sides of the window, so you just have to determine the edge of the screen. For fun, I am going to make the ball move up and down this time (and don't forget, its picture size is 150 x 150 pixels!). In order to allow the ball to change directions, it's best to use a variable. Another thing you shouldn't forget is that when you increase the Y axis, the image goes downward. But, enough chit-chat, let's finally get down to business!
     We already have a lot of it done already from the previous article. All we have to do is change the x = x + 5 into y + [yvariable]. In order to do this, we will add a new variable at the top of the code (outside of the block). I will be calling mine yspeed, and its value will be 5. This variable will make the speed easily managable if we ever need to change it (and we will).
     Now, take the x = x + 5 line and change it to y = y + yspeed (or whatever your variable is). Now, here's a word of advice: there is a function called screen.get_height() (as well as screen.get_width()). This is an easy way to... well, it's obvious. it finds the screen's height and stores it as a number for the following code it ties into. Inside our True block, we will add this function in an "if" block by typing, "if y > screen.get_height() - 150 or y <= 0:"
     Inside this line, we will say "yspeed = - yspeed," turning the 5 into a negative 5 (because, you know, of the -). The "y <= 0" part will check if the number representing y is above or equal to the number 0 (the top of the screen) and reverses its sign again, turning it back into a positive.
     Keep in mind that the y variable is only a number, not actually the screen's height. That's pretty confusing, but since y always the same number as the ball's position, it works.
     So, long story short, the entire program should look like this:

import pygame, sys
import time
pygame.init()
screen=pygame.display.set_mode([640, 480])
screen.fill([255, 255, 255])
ball = pygame.image.load("Ball.png")
x=50
y=50
yspeed=5
screen.blit(ball, [x, y])
pygame.display.flip()
while True:
    pygame.time.delay(20)
    pygame.draw.rect(screen, [255, 255, 255], [x, y, 150, 150], 0)
    y = y + yspeed
    if y > screen.get_height() - 150 or y < 0:
        yspeed = - yspeed
    screen.blit(ball, [x, y])
    pygame.display.flip()
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

     When you run the program, the ball should start moving up & down, never leaving the screen. It may not be a huge game produced by a popular corporation, but, hey, it's still pretty cool! We're finally getting somewhere!
     Well, that's all there is about bouncing. See you next time!.. Should I find the time and inspiration to write the next article! Until then, I challenge you to make the ball bounce diagonally! Heres a tip: The screen.get_width() and screen.get_height() functions find the position of the right and bottom sides of the screen.
     Here's something for an extra challenge: Try to get the ball to appear on one side of the screen when it reaches the other! You can just have it move vertically or horizontally if you'd like.

Monday, August 25, 2014

Movement

     A red ball on the screen is mighty exciting, isn't it? Well, it's about to get better. But just a little. We're going to move the ball. We're going to add 3 new lines between the exit block and the line before:

import pygame, sys
pygame.init()
screen=pygame.display.set_mode([640, 480])
screen.fill([255, 255, 255])
ball = pygame.image.load("Ball.png")
screen.blit(ball, [50, 50])
pygame.display.flip()
pygame.time.delay(2000)
screen.blit(ball, [200, 50])
pygame.display.flip()
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT: sys.exit()

     Whoa. 2,000 seconds. But, just run the program. All of a sudden...



     I don't necessarily think that counts as moving. Remember, blitting is copying. So, in video games, nothing actually moves. A new version of an image is created for every movement it makes, pixel by pixel. This means we have to wipe the original image from existence. Actually, truth be told, that can't exactly be done. We have to paint over the original. We have to erase the original image before we flip the screen and show the new one. There's only one line of code that needs to be added, right before the flip() function. And just so you know, the picture is 150 by 150 pixels.

import pygame, sys
pygame.init()
screen=pygame.display.set_mode([640, 480])
screen.fill([255, 255, 255])
ball = pygame.image.load("Ball.png")
screen.blit(ball, [50, 50])
pygame.display.flip()
pygame.time.delay(2000)
screen.blit(ball, [200, 50])
pygame.draw.rect(screen, [255, 255, 255], [50, 50, 150, 150], 0)
pygame.display.flip()
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT: sys.exit()

     Now when you run it, the problem should be solved.


     Much better!
     So, that's our ball that moves simply once. Let's see if we can get the animation smoother! Let's change the program so it makes smaller steps, and make it keep moving.

import pygame, sys
pygame.init()
screen=pygame.display.set_mode([640, 480])
screen.fill([255, 255, 255])
ball = pygame.image.load("Ball.png")
x=50
y=50
screen.blit(ball, [x, y])
pygame.display.flip()
while True:
    pygame.time.delay(20)
    pygame.draw.rect(screen, [255, 255, 255], [x, y, 150, 150], 0)
    x=x+5
    screen.blit(ball, [x, y])
    pygame.display.flip()
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

     You see, it spawns the ball at the points of x and y, and while the program is True (still running), it will move the ball directly to the right every 20 milliseconds, erasing the original with a block of white. In order to avoid erasing the current one, the previous one has to be painted over first. Remember that for fluid movement!
     When you run the program, the ball will move smoothly, but then it goes off of the screen, since it doesn't have any functions to bounce off of the side of the screen or anything. But the good thing is that it doesn't strobe white as it could have done!
     Wow, this article is getting really long. Think of it as an apology for the rate of my article writing this year. Anyway, see you next time!

Sunday, July 6, 2014

A Quick Review

     So, how was your 4th of July (and if you're Canadian, how was your Canada day)? Sorry I missed writing an article on that day. But, here's a little program gift to you.
Download the file here (.zip). Put it in your Python folder and extract the files.
And if you're Canadian, here's one for CanaDay. Follow the instructions above to use the program.

     Anyway, those images can really tie into what we're currently learning about. Images (evidently).
     Actually, we'll probably never use those programs ever again, so let's just go back to our red dot.
     We should all know what image.load() does. It loads whatever is in the parenthesis, thought it has to be a string. That's how it goes. Let's just move on. The image it loads is its own surface, like screen (do you understand why those shapes we made needed a surface to be put on now?). This surface is only in memory, though. Screen is the one we can see, though. We make that surface in line 3 of all of our Pygame programs. screen.blit(ball, [50, 50]) copies the image's surface and pastes it onto the screen surface. display.flip() takes the display surface and (if you think about it literally) flips what's in the window over so the display surface can be visible.
     Um, wait a minute. I have no idea who would know what blit means without looking up a special programming dictionary or something. You see, when we use screen.blit(ball, [50, 50]) or something like that, we're copying the pixels of a surface and putting them on another. This is known as blitting. It's just the programming term for copying a bunch of pixels. [50, 50] is the location from the top left corner (remember the coordinate chart for programming!) where the pixels will be copied.
     I suppose that will be it for this article. See you next time!

Wednesday, June 25, 2014

The Red Circle

     I know, that last article was REALLY confusing. Personally, I'd never program a drawing from scratch. It's easier to use images! In this article, we'll be working with pictures.
So, how tedious would it be to put down each individual pixel for a game? That's what pictures are for! First up, I'll show you how to insert an image. First you need to save the following image as Ball.png, and put it in...wherever you keep your Python programs. For me, it's Python 32, and if your computer is anything like mine, your programs folder will be in your Computer section, in Local Disk (C:). Now, here's that picture:


     Remember, Ball.png. Once it's all good and saved, type in this as a new program:


import pygame, sys
pygame.init()
screen=pygame.display.set_mode([640, 480])
screen.fill([255, 255, 255])
ball = pygame.image.load("Ball.png")
screen.blit(ball, [50, 50])
pygame.display.flip()
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT: sys.exit()


     When you run the program, it should display a big red dot around the top-left corner.


     Wow. A red dot. That's so amazing. But, hey! We're one step closer to being talented programmers!
     We'll continue all this picture stuff next time. Bye!

Tuesday, June 24, 2014

Sine Waves

     You all should have seen this coming. I don't know about you, but this is my worst enemy.
     Math.
     Actually, my worst enemy is creative writing, but that set aside, we have math calculations coming into Python. This is math that won't be done for us, such as print(55 * 10000000e+7). This math works with pixels, and Python won't just go around telling you all 307,200 points. It's all in the luck of the mind. But, I will be nice and won't let you do the math. Yet.
     I've set up a program that draws something called a sine wave (it's some sort of wavy line that works with sound, such as spiking when the volume gets louder). It sets up pixels in various spots to create the basic shape of a sine wave. Open your version of Python and enter this into a new program:



import pygame, sys
import math
pygame.init()
screen = pygame.display.set_mode([640, 480])
screen.fill([0, 0, 0])
for x in range(0, 640):
    y = int(math.sin(x/64.0 * 4 * math.pi) * 200 + 240)
    pygame.draw.rect(screen, [255, 255, 255], [x, y, 1, 1], 1)
pygame.display.flip()
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()



     Just look at that line of math! Can anyone follow that?!...eh. We'll discuss the math functions later. When you run the program, you should get this:


     I had to do white against black instead of black against white this time because it looks cool.
     ...Well. This is...a really embarrassing sine wave. It was torn to shreds and all that's left to show is a few white dots. But, hey, this might come in handy later on for a physics game or something.
     Round as those dots may seem, they're completely square. Each dot is 1 pixel squared. Also, here's a note: the width of the pixels are 1. If it was 0, then there would be nothing on the screen.
     Alright, I'm getting sick of that pathetic sine wave I drew. It's time to fix it! Pygame has a method to draw a single line, and a method to draw multiple lines between multiple points. It's known as pygame.draw.lines(), and must meet the 5 requirements to work.

Surface
On what "surface" to draw the line (in our case, screen).
Color
What color the line will be.
Close
The program needs to know whether or not it should connect the last dot to the first one. We don't want to stick a line through our sine wave, so we'll label it False.
Points
A list of points to connect to.
Width
How thick the line is. If the thickness is 0, it will be nothing.

     So, what we'd write is this:
pygame.draw.lines(screen, [255, 255, 255], False, plotPoints, 1)
     But that's only the appearance of the line. Python doesn't know where to place the line, so we'll have to do a things a bit differently. Retype the program to be like this:



import pygame, sys
import math
pygame.init()
screen=pygame.display.set_mode([640, 480])
screen.fill([0, 0, 0])
plotPoints = []
for x in range(0, 640):
    y = int(math.sin(x/640.0 * 4 * math.pi) * 200 + 240)
    plotPoints.append([x, y])
pygame.draw.lines(screen, [255, 255, 255], False, plotPoints, 2)
pygame.display.flip()
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()



     Notice that I changed the line's thickness to 2. This is just so it looks better. When you run the program, you should get this:


     I stretched the waves out a little more so they'll look good. Actually, I think there was a typo somewhere. But, there is our complete sine wave! Was that evil or what? This is what happens when you draw with programming.
     I guess that's the end of this article. I challenge you to experiment with the lines and points to see what you can do with them! Good luck, and see you next time!