Friday, March 20, 2015

Pygame basics

It's now time to start working on the space shooter game, specifically on the menu screen.

The menu screen of "Space Shooter!"  When you mouse-over a button, the text turns green.  The "NEW GAME" and "LOAD GAME" buttons print to the console, and "EXIT" quits the game.
First we need to import the needed modules.  The “os” and “sys” Python modules “allow us to do things like create platform independent file paths” (Shinners 2004).  The “pygame” module lets us use functions “dealing with graphics, sound, and other features that Pygame provides” (Sweigart 2012 p. 9). After we import the pygame module, we need to call pygame.init() “before calling any other Pygame function.”  The function pygame.display.set_mode((800, 600)) sets the window to 800 x 600, makes it resizable, and returns a pygame.Suface object, which is stored in DISPLAYSURF. Then pygame.display.set_caption('Space Shooter!') sets the title for the window.

Before drawing anything, I saw a black window with “Space Shooter!” in the title bar.  The functions print() and input() won’t work with this window because they are for command line interface programs (Sweigart 2012 p. 8), so you have to handle input from the mouse and keyboard through events.


According to Sweigart, the while loop in the second half of the program (labeled as the “main game loop”) does three things in a game:

1. Handles events.

2. Updates the game state.

3. Draws the game state to the screen.

The game state refers to the values of all of the variables at any moment.  This code handles four events: The resizing of the screen, the release of a mouse button, the movement of the mouse, and quitting.  Resizing the screen changes the size of the menu and title accordingly and redraws the screen.  Releasing the left mouse button over the "new game" and "load game" buttons prints "NEW GAME" and "LOAD GAME" respectively in the console.  Doing so over the quit button sets the event to.  Moving a mouse over a button temporarily changes the text to green, and finally, quit closes Pygame and exits.


import os, pygame, sys
from pygame.locals import *

if not pygame.font: print('Warning, fonts disabled')
if not pygame.mixer: print('Warning, sound disabled')

# Initialize screen
pygame.init()
DISPLAYSURF = pygame.display.set_mode((800, 600),RESIZABLE)
pygame.display.set_caption('Space Shooter!') #I'll think of a more creative name later.

size = DISPLAYSURF.get_size()
width = size[0]
height = size[1]

title = pygame.Rect(((width/16)+1, (height/8)+1, 7*width/8, height/4))
menu1 = pygame.Rect(((width/4)+1, (height/2)+1, 2*width/4, height/10))
menu2 = pygame.Rect(((width/4)+1, (height/2)+1+(3*height/20), 2*width/4, height/10))
menu3 = pygame.Rect(((width/4)+1, (height/2)+1+(6*height/20), 2*width/4, height/10))

#Draws dark blue rectangles.
pygame.draw.rect(DISPLAYSURF, (0,0,150), menu1)
pygame.draw.rect(DISPLAYSURF, (0,0,150), menu2)
pygame.draw.rect(DISPLAYSURF, (0,0,150), menu3)

#Draws blue ovals on top of the rectangles.
pygame.draw.ellipse(DISPLAYSURF, (0,0,150), title)
pygame.draw.ellipse(DISPLAYSURF, (0,0,255), menu1)
pygame.draw.ellipse(DISPLAYSURF, (0,0,255), menu2)
pygame.draw.ellipse(DISPLAYSURF, (0,0,255), menu3)

#pygame.font.Font takes in a font name and an integer for its size.
#Free Sans Bold comes with Pygame (Sweigart 2012 p. 30).
font_title = pygame.font.Font('freesansbold.ttf', 64)
font_menu = pygame.font.Font('freesansbold.ttf', 32)

#Creates and draws the text.
surface_title = font_title.render('SPACE SHOOTER!', True, (0,255,0))
rect_title = surface_title.get_rect() #get_rect() is my favorite Pygame function.
rect_title.center = (width/2,(height/4)+3)
surface_new_game = font_menu.render('NEW GAME', True, (0,0,0))
rect_new_game = surface_new_game.get_rect()
rect_new_game.center = (width/2,(height/2)+(height/20)+3)
surface_load_game = font_menu.render('LOAD GAME', True, (0,0,0))
rect_load_game = surface_load_game.get_rect()
rect_load_game.center = (width/2,(height/2)+(4*height/20)+3)
surface_exit = font_menu.render('EXIT', True, (0,0,0))
rect_exit = surface_exit.get_rect()
rect_exit.center = (width/2,(height/2)+(7*height/20)+3)

while True: #main game loop
    DISPLAYSURF.blit(surface_title, rect_title)
    DISPLAYSURF.blit(surface_new_game, rect_new_game)
    DISPLAYSURF.blit(surface_load_game, rect_load_game)
    DISPLAYSURF.blit(surface_exit, rect_exit)
    for event in pygame.event.get():
        if event.type == VIDEORESIZE:
            #This line creates a new display in order to clear the screen.
            #I figured this out myself, so if you know of a better way to do this, let me know.
            DISPLAYSURF = pygame.display.set_mode((event.w, event.h),RESIZABLE)

            width = event.w
            height = event.h

            #Creates the rectangle objects that will be behind the title and menu buttons.
            title = pygame.Rect(((width/16)+1, (height/8)+1, 7*width/8, height/4))
            menu1 = pygame.Rect(((width/4)+1, (height/2)+1, 2*width/4, height/10))
            menu2 = pygame.Rect(((width/4)+1, (height/2)+1+(3*height/20), 2*width/4, height/10))
            menu3 = pygame.Rect(((width/4)+1, (height/2)+1+(6*height/20), 2*width/4, height/10))

            #Draws dark blue rectangles.
            pygame.draw.rect(DISPLAYSURF, (0,0,150), menu1)
            pygame.draw.rect(DISPLAYSURF, (0,0,150), menu2)
            pygame.draw.rect(DISPLAYSURF, (0,0,150), menu3)

            #Draws blue ovals on top of the rectangles.
            pygame.draw.ellipse(DISPLAYSURF, (0,0,150), title)
            pygame.draw.ellipse(DISPLAYSURF, (0,0,255), menu1)
            pygame.draw.ellipse(DISPLAYSURF, (0,0,255), menu2)
            pygame.draw.ellipse(DISPLAYSURF, (0,0,255), menu3)

            #Creates and draws the text.
            rect_title = surface_title.get_rect()
            rect_title.center = (width/2,(height/4)+3)
            rect_new_game = surface_new_game.get_rect()
            rect_new_game.center = (width/2,(height/2)+(height/20)+3)
            rect_load_game = surface_load_game.get_rect()
            rect_load_game.center = (width/2,(height/2)+(4*height/20)+3)
            rect_exit = surface_exit.get_rect()
            rect_exit.center = (width/2,(height/2)+(7*height/20)+3)
    
            pygame.display.update() #Necessary to update the screen

        #When you let go of the left mouse button in the area of a button, the button does something.
        if event.type == MOUSEBUTTONUP:
            if event.button == 1:
                if (menu1.left < event.pos[0] < menu1.right) and (menu1.top < event.pos[1] < menu1.bottom):
                    print("NEW GAME") #Placeholder
                if (menu2.left < event.pos[0] < menu2.right) and (menu2.top < event.pos[1] < menu2.bottom):
                    print("LOAD GAME") #Placeholder
                if (menu3.left < event.pos[0] < menu3.right) and (menu3.top < event.pos[1] < menu3.bottom):
                    pygame.event.post(pygame.event.Event(QUIT)) #Exits the game

        #When you mouse-over a button, the text turns green.
        if event.type == MOUSEMOTION:
            if (menu1.left < event.pos[0] < menu1.right) and (menu1.top < event.pos[1] < menu1.bottom):
                surface_new_game = font_menu.render('NEW GAME', True, (0,255,0))
            else:
                surface_new_game = font_menu.render('NEW GAME', True, (0,0,0))
            if (menu2.left < event.pos[0] < menu2.right) and (menu2.top < event.pos[1] < menu2.bottom):
                surface_load_game = font_menu.render('LOAD GAME', True, (0,255,0))
            else:
                surface_load_game = font_menu.render('LOAD GAME', True, (0,0,0))
            if (menu3.left < event.pos[0] < menu3.right) and (menu3.top < event.pos[1] < menu3.bottom):
                surface_exit = font_menu.render('EXIT', True, (0,255,0))
            else:
                surface_exit = font_menu.render('EXIT', True, (0,0,0))           

        if event.type == QUIT:
            pygame.quit()
            sys.exit()
    pygame.display.update()



Shinners, P. (2004, June 17). Line by line chimp. Retrieved from http://www.pygame.org/docs/tut/chimp/ChimpLineByLine.html

Sweigart, A. (2015). Invent your own computer games with Python. Retrieved from http://inventwithpython.com/inventwithpython_3rd.pdf


Sweigart, A. (2012). Making games with Python & Pygame. Retrieved from http://inventwithpython.com/makinggames.pdf




Friday, March 13, 2015

Python programming basics

We will be programming using IDLE (Interactive DeveLopment Environment), software which makes it easy to write Python programs (Sweigart, 2012, p.2). IDLE was included in the installation of Python.  In Windows 8, you can find IDLE (Python GUI) in the Apps view (from the Start screen) under Python 3.2.

When we first run IDLE, we see the interactive Python shell, which we can use to input instructions for the interpreter (p. 3).  We can use the interactive shell to give instructions one line at a time, but to write a full program we need to open the file editor (Sweigart, 2015, p. 15). To access the file editor, open the File menu and select New Window.  The file editor looks like the interactive shell without the >>> prompt (p. 16).

So now it is time to code.  I start by importing the random module, which will allow me to use randInt() to generate random integers (Sweigart, 2015, p. 27).  Then I delve into object oriented programming by creating a Thing class.  I then create two instructors for the class.  Afterwards, I assign integers to several variables.  We should think of Python as pseudo-code, according to Hetland in his Instant Python crash course. In Python, variables don’t have types and don’t need to be declared. We can make variables simply by assigning to them, and we can assign to multiple variables at the same time.  After creating variables, I print them and their sum.  Then I ask the user for their name and greet them.  Then I ask the user to give a size for a list of Things and randomly generate a "size" for each Thing in the list.  Finally, I print the values of each Thing (which you may notice can be accessed directly) and tell the user goodbye.

I've included comments for more clarification.


import random

class Thing:

    # "Always remember the *self* argument" (Hetland n.d.)
    # __init__ is a pre-defined method name of the constructor.
    def __init__(self):
        self.size = []
        self.name = []

    def __init__(self,name,size):
        self.name = name
        self.size = size

a,b,c = 1,2,3 #a = 1, b = 2, c = 3
d = c + 1 #d = 3
e = random.randint(1,10) #Generates a number between 1 and 10

#If you omit the str function, you will get "TypeError: Can't convert 'int' object to str implicitly"
print(str(a) + ' + ' + str(b) + ' + ' + str(c) + ' + ' + str(d) + ' + '+ str(e) + ' = ' + str(a + b + c + d + e))

print('What is your name?')#(Sweigart p. 17)
name = str(input())
print('Hello ' + name + '!')

ask = 0

#https://docs.python.org/3.2/reference/compound_stmts.html
#https://docs.python.org/3.2/library/stdtypes.html#boolean-operations-and-or-not
while ask == 0:
    print('How many things do you want in your list? (Between 0 and 10)')
    size = input() #(Sweigart p. 24)
    size = int(size)
    #https://docs.python.org/3.2/tutorial/introduction.html#lists
    if 0 < size <= 10: #This is the same as "0 < size and size <= 10"
        ask = 1

i = 0
thingList = []
while i < size:
    print('What should the name of Thing ' + str(i) + ' be? (Give a string)')
    thingName = input()
    thingSize = random.randint(1,100) #Includes 1 and 100
    thingList.append(Thing(thingName,thingSize))
    i = i + 1

for t in thingList:
    print(t.name + ' is size ' + str(t.size))

print('Goodbye!')

Sample output:


Hetland, M. L. (n.d.). Instant Python. Retrieved from http://hetland.org/writing/instant-python.html

Sweigart, A. (2015). Invent your own computer games with Python. Retrieved from http://inventwithpython.com/inventwithpython_3rd.pdf

Sweigart, A. (2012). Making games with Python & Pygame. Retrieved from http://inventwithpython.com/makinggames.pdf