python game

J

jacksonkemp1234

I made this game where you move a player over bears, but the bears keep loading over the plaeyer making it hard to see it, also when i move down the player goes down to the right

here is my code:

import pygame, sys, random
from pygame.locals import *
from threading import Timer

#set up pygame
pygame.init()
mainClock = pygame.time.Clock()

#set up the window
WINDOW_WIDTH = 400
WINDOW_HEIGHT = 400
windowSurface = pygame.display.set_mode((WINDOW_WIDTH,
WINDOW_HEIGHT),0)
pygame.display.set_caption('Get the Bears')

#set up color constants
BLACK = (0,0,0)
BLUE = (0, 0, 255)
#set winning text
textFont = pygame.font.SysFont("impact", 60)
text = textFont.render("YOU WIN!", True, (193, 0, 0))

#set up the player and bear data structures
bearCounter = 0
NEW_BEAR = 40
BEAR_SIZE = 64
playerImage = pygame.image.load('hunter.png')
bearImage = pygame.image.load('bear.png')

player = pygame.Rect(300, 100, 40, 40)
bears = []
for i in range(20):
bears.append(pygame.Rect(random.randint(0, WINDOW_WIDTH - BEAR_SIZE),
random.randint(0, WINDOW_HEIGHT - BEAR_SIZE),
BEAR_SIZE, BEAR_SIZE))
#movement variables
moveLeft = False
moveRight = False
moveDown = False
moveUp = False

MOVE_SPEED = 15

#run the game loop
startGame = True
while startGame == True:
#check for quit
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
#keyboard variables
if event.key == K_LEFT:
moveRight = False
moveLeft = True
if event.key == K_RIGHT:
moveRight = True
moveLeft = False
if event.key == K_UP:
moveUp = True
moveDown = False
if event.key == K_DOWN:
moveUp = False
moveDown = True
if event.type == KEYUP:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == K_LEFT:
moveLeft = False;
if event.key == K_RIGHT:
moveRight = False;
if event.key == K_UP:
moveUp = False;
if event.key == K_DOWN:
moveDown = False;

bearCounter += 1
if bearCounter >= NEW_BEAR:
#clear bear array and add new bears
bearCounter = 0
bears.append(pygame.Rect(random.randint(0, WINDOW_WIDTH - BEAR_SIZE),
random.randint(0, WINDOW_HEIGHT - BEAR_SIZE),
BEAR_SIZE, BEAR_SIZE))
#draw black background
windowSurface.fill(BLACK)

#move player
if moveDown and player.bottom < WINDOW_HEIGHT:
player.top += MOVE_SPEED
if moveUp and player.top > 0:
player.top -= MOVE_SPEED
if moveLeft and player.left > 0:
player.left -= MOVE_SPEED
if moveDown and player.right < WINDOW_WIDTH:
player.right += MOVE_SPEED

windowSurface.blit(playerImage, player)
for bear in bears:
windowSurface.blit(bearImage, bear)

#check if player has intersected with bear
for bear in bears[:]:


def explosion():
for bear in bears:
if player.colliderect(bear) and (moveLeft == False and
moveRight == False and moveUp == False and
moveDown == False):
bears.remove(bear)
if player.colliderect(bear) and (moveLeft == False and
moveRight == False and moveUp == False and
moveDown == False):
t = Timer(1, explosion)
t.start()
if len(bears) == 0:
bearCounter = 0
windowSurface.blit(text, (90, 104))
startGame = False

#draw the window
pygame.display.update()
mainClock.tick(40)

while startGame == False:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
 
D

Denis McMahon

windowSurface.blit(playerImage, player)
for bear in bears:
windowSurface.blit(bearImage, bear)

Try changing this to draw the bears first, then the player.
 
J

Joshua Landau

This is prob'ly the freakiest thing I've ever run...

Anyhoo, I recommend that when you post slabs of code to a mailing list
you at least make it runnable for us. We don't have the images. I
"fixed" it by doing:
| playerImage = pygame.Surface((40, 40))
| bearImage = pygame.Surface((64, 64))
|
| playerImage.fill(pygame.Color("red"))
| bearImage.fill(pygame.Color("blue"))

I'll not just give you answers, here, but try and make you understand
what you need to do to make your code shiny. I'll answer your
questions on the way, though.

I made this game where you move a player over bears, but the bears keep loading over the plaeyer making it hard to see it, also when i move down theplayer goes down to the right

here is my code:

import pygame, sys, random
from pygame.locals import *
from threading import Timer

You probably want to split up these lines; all the cool kids do:
| import sys
| import random
| import pygame
|
| from pygame.locals import *
| from threading import Timer

This is just style; you might wonder why people do this but it's
something I've found makes sense in the long run for some reason, and
it's recommended as such.

#set up pygame
pygame.init()
mainClock = pygame.time.Clock()

#set up the window
WINDOW_WIDTH = 400
WINDOW_HEIGHT = 400
windowSurface = pygame.display.set_mode((WINDOW_WIDTH,
WINDOW_HEIGHT),0)
pygame.display.set_caption('Get the Bears')

#set up color constants
BLACK = (0,0,0)
BLUE = (0, 0, 255)

Ah! Pygame has a color module.

For example, instead of:
| windowsurface.fill(BLACK)

it's better to do:
| windowsurface.fill(pygame.Color("black"))

#set winning text
textFont = pygame.font.SysFont("impact", 60)
text = textFont.render("YOU WIN!", True, (193, 0, 0))

Just a niggle here; it'd be better to call this "win_text" or some
other more descriptive name.
It's also better to use under_score_names; they're more readable. It
took me about a year to realise this, but it's true. It's also
recommended.
#set up the player and bear data structures
bearCounter = 0
NEW_BEAR = 40
BEAR_SIZE = 64

I'll come back to this later.
playerImage = pygame.image.load('hunter.png')
bearImage = pygame.image.load('bear.png')

I replaced this with:
| player_image = pygame.Surface((40, 40))
| bear_image = pygame.Surface((64, 64))
|
| player_image.fill(pygame.Color("red"))
| bear_image.fill(pygame.Color("blue"))

Note that I'm changing things like the naming scheme as I go.
player = pygame.Rect(300, 100, 40, 40)

Although it's OK to use pygame.Rect as a "player" now, it's not a
"player" and you really shouldn't let this be a habit. It'd be better
to call this player_rect, or some more useful name.
bears = []
for i in range(20):
bears.append(pygame.Rect(random.randint(0, WINDOW_WIDTH - BEAR_SIZE),
random.randint(0, WINDOW_HEIGHT - BEAR_SIZE),
BEAR_SIZE, BEAR_SIZE))

Split this up, damnit!
| for i in range(10):
| x = random.randint(0, WINDOW_WIDTH - BEAR_SIZE)
| y = random.randint(0, WINDOW_HEIGHT - BEAR_SIZE)
|
| bears.append((x, y), (BEAR_SIZE, BEAR_SIZE))

See how this way it's more obvious what it all means, there's no silly
line-wrapping *and* it's easier to read.
#movement variables
moveLeft = False
moveRight = False
moveDown = False
moveUp = False

MOVE_SPEED = 15

ER MER GERD

Think about what you're doing here. If I ask you what direction an
object on the screen is going in, do you say:
1) Left, about 15 pixels a frame
2) West at 5 klicks per hour
3) Left=True, Right=False, Up=False, Down=False, Speed=15
?

Well, it's not (3). So let us think about this.

A good "classical" newbie method, which works for non-newbie stuff too, is just

| directions = {"left", "right", "up", "down"}
| movement_direction = "left"

However, it's not the best. In "programming terms" you'd want variable
speed in *both* directions. My preferred method is:

| velocity = [the_value_of_x, the_value_of_y]

so you'd write above:

| velocity = [0, 0]

See how nice that is? We'll come back to this later.
#run the game loop
startGame = True
while startGame == True:

Noooo! Not you too!

<START PERSONAL OPINION, NOT SHARED BY EVERYONE>
You have here a (really weird - I'll come to that later) "infinite" loop.
This is best described like so:

| while True:
| stuff()
| if finished():
| break

That's what "break" is for, after all.

Even better (I think) you can do:

| while "game is running":
| stuff()
| if finished():
| break

which explains what you're doing concisely without changing the
meaning of the code.

A second advantage of this method
<END>

Plus, if you *insist* on "while FLAG", choose a good name. "startGame"
is wrong, because it's not only running during the start of the game.

You mean:
| while game_running:
#check for quit
This is a misplaced comment.
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()

You don't need pygame.quit(), by the way. That only needs to be done
if you want to close pygame *before* the script is over. It also means
that you can't "catch" an exit nicely like this, so this should just
be removed.
if event.type == KEYDOWN:
#keyboard variables
if event.key == K_LEFT:
moveRight = False
moveLeft = True
if event.key == K_RIGHT:
moveRight = True
moveLeft = False
if event.key == K_UP:
moveUp = True
moveDown = False
if event.key == K_DOWN:
moveUp = False
moveDown = True
if event.type == KEYUP:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == K_LEFT:
moveLeft = False;
if event.key == K_RIGHT:
moveRight = False;
if event.key == K_UP:
moveUp = False;
if event.key == K_DOWN:
moveDown = False;

OH MOTHER OF GOD!

When you write something like that, the #1 reason is that you've done
something a bad way.
Please note that it's wrong; you shouldn't deal with keeping track of
whether keys are up - pygame does that *much better* for you.

Let's rewrite this using our new velocity stuff!:
| for event in pygame.event.get():
| if event.type == QUIT:
| sys.exit()
|
| pressed = pygame.key.get_pressed()
|
| velocity[0] = pressed[K_RIGHT] - pressed[K_LEFT]
| velocity[1] = pressed[K_DOWN] - pressed[K_UP]

That's it. Really!

Since we have a simple definition of velocity, velocity[0] is just the
amount you move right minus the amount you move left, and similar for
velocity[1].

Oh, and suddenly it all works (after changes to the rest of the code)!
Bugs just disappear!
bearCounter += 1
if bearCounter >= NEW_BEAR:
#clear bear array and add new bears
bearCounter = 0
bears.append(pygame.Rect(random.randint(0, WINDOW_WIDTH - BEAR_SIZE),
random.randint(0, WINDOW_HEIGHT - BEAR_SIZE),
BEAR_SIZE, BEAR_SIZE))

Again, split this up:
| bear_counter += 1
| if bear_counter >= NEW_BEAR:
| #clear bear array and add new bears
| bear_counter = 0
|
| x = random.randint(0, WINDOW_WIDTH - BEAR_SIZE)
| y = random.randint(0, WINDOW_HEIGHT - BEAR_SIZE)
|
| bears.append(pygame.Rect((x, y), (BEAR_SIZE, BEAR_SIZE)))

But what does "clear bear array" mean? You don't clear any lists,
especially not arrays! You only set "bearCounter" to 0. Why? That
makes it a timeout, not a counter of bears!

Thus you should do something more like this:
| bear_countdown = 40
....
| bear_countdown -= 1
| if bear_countdown <= 0:
| bear_countdown = 40
| more_bears()

You have a spontaneous indent from here on! That's not good!
It means that your "while startGame == False" runs *inside* "while
startGame == True", which is quite paradoxical. It means you don't
move as much as you do. I've fixed that for me. To be fair though, I
have no idea what your indents are meant to be like. It seems you
haven't thought about it as much as you should have. I'll get back to
that at the end.
#draw black background
windowSurface.fill(BLACK)

Remember;
| window_surface.fill(pygame.Color("black"))
#move player
if moveDown and player.bottom < WINDOW_HEIGHT:
player.top += MOVE_SPEED
if moveUp and player.top > 0:
player.top -= MOVE_SPEED
if moveLeft and player.left > 0:
player.left -= MOVE_SPEED
if moveDown and player.right < WINDOW_WIDTH:
player.right += MOVE_SPEED

Oh noes! This again!

You may be able to guess that we can do something nice again!
Please note that "player.right += 1" *is the same as* "player.top +=1"
and "player.middle += 1" and so on.
This is obvious with a bit of thought.

Hence:
| player.x += velocity[0]
| player.y += velocity[1]

and we can stop you going off the ends with:
| player.clamp_ip(((0, 0), (WINDOW_WIDTH, WINDOW_HEIGHT)))

Note that you'll move slower like this; you can just multiply by a factor:
| player.x += velocity[0] * SPEED_FACTOR
| player.y += velocity[1] * SPEED_FACTOR
windowSurface.blit(playerImage, player)
for bear in bears:
windowSurface.blit(bearImage, bear)

You wanted the player on top. This won't happen as you've written it
as you draw the player and *then* the bear, so the bear is above.

You want to flip this:

| for bear in bears:
| window_surface.blit(bear_image, bear)
| window_surface.blit(player_image, player)

If you want to be really cool you can do this (requires modern pygame [>=1.8])
| for bear in bears:
| window_surface.blit(bear_image, bear)
| window_surface.blit(player_image, player, special_flags=BLEND_MAX)
#check if player has intersected with bear
for bear in bears[:]:
def explosion():
for bear in bears:
if player.colliderect(bear) and (moveLeft == False and
moveRight == False and moveUp == False and
moveDown == False):
bears.remove(bear)
if player.colliderect(bear) and (moveLeft == False and
moveRight == False and moveUp == False and
moveDown == False):
t = Timer(1, explosion)
t.start()
if len(bears) == 0:
bearCounter = 0
windowSurface.blit(text, (90, 104))
startGame = False

This here is a bit of freaky-clever shenanigans.
Some's OK, some's not.

Really, You have a loop in a function in a loop in a loop. That's too much.
Take the function out of the loop. Then realise it's pointless to have
a loop in there - it's probably not what you want. You probably want:
| def explosion(bear):
| def callback():
| if player.colliderect(bear) and velocity == [0, 0]:
| bears.remove(bear)
| return callback
....
| t = Timer(0.8, explosion(bear))

What?! This is actually a common thing in a lot of languages.
Basically, you take one argument and make a new function inside it
which does what you want. This is what you did in the loop, but out of
the loop. It also plays slightly differently, as it kills each bear
separately.

Before we get carried away, though...
Why are you mixing frame-based and time-based code?! WHY?!

Never do that. I realise it seems like the easy solution - it probably
is - but it's not the right one.
#draw the window
pygame.display.update()
mainClock.tick(40)

while startGame == False:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()

This is inside your "while startGame == True", which is rehduculush.

Moving it out doesn't seem to work with "while True:"..."break", as
you've have imbedded loops. The "proper" solution, á mon avis, is an
exception. This is a bit advanced but you don't need to know much
about it.

Up at the top do:

| class GameWon(Exception): pass

That makes an "exception class" you can use.

Instead of:
| if len(bears) == 0:
| bear_countdown = 0
| window_surface.blit(winning_text, (90, 104))
| break

write:

| pygame.display.flip() # We need this extra one as we're
bypassing some extra stuff doing it this way
| raise GameWon

This will break out of things until it meets something that can
"handle" it. To do that you need to wrap your whole main "while
startGame == True" loop in a "try:"..."except:"

Like so:
| try:
| while "game is running":
....
| except GameWon:
| while "showing the winning screen":
| for event in pygame.event.get():
| if event.type == QUIT:
| sys.exit()

Note that I don't need pygame.quit(), as before.


I made a couple more changes, but nothing significant.

NOTE THAT ALTHOUGH I'LL POST THE CODE WITH ALL THE CHANGES I MADE, THE
POINT ISN'T TO GIVE YOU REFACTORED CODE.
It's to explain to you what's there to improve, and know what to do
next time, etc.

# CODE FOLLOWING

import sys
import random
import pygame

from pygame.locals import *
from threading import Timer

#set up pygame
pygame.init()
main_clock = pygame.time.Clock()

#set up the window
WINDOW_WIDTH = 400
WINDOW_HEIGHT = 400
window_surface = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT), 0)
pygame.display.set_caption('Get the Bears')

window_filler = window_surface.copy()
window_filler.fill(pygame.Color("black"))
window_filler.set_alpha(50)

#set winning text
text_font = pygame.font.SysFont("impact", 60)
winning_text = text_font.render("YOU WIN!", True, (193, 0, 0))

#set up the player and bear data structures
bear_countdown = 40
BEAR_SIZE = 64

player_image = pygame.Surface((40, 40))
bear_image = pygame.Surface((64, 64))

player_image.fill(pygame.Color("dark red"))
bear_image.fill(pygame.Color("dark blue"))

player = pygame.Rect(300, 100, 40, 40)
bears = []

for i in range(10):
x = random.randint(0, WINDOW_WIDTH - BEAR_SIZE)
y = random.randint(0, WINDOW_HEIGHT - BEAR_SIZE)

bears.append(pygame.Rect((x, y), (BEAR_SIZE, BEAR_SIZE)))

#movement variables
velocity = [0, 0]
SPEED_FACTOR = 15

class GameWon(Exception): pass

def explosion(bear):
def callback():
if player.colliderect(bear) and velocity == [0, 0]:
bears.remove(bear)
return callback

try:
while "game is running":
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()

pressed = pygame.key.get_pressed()

velocity[0] = pressed[K_RIGHT] - pressed[K_LEFT]
velocity[1] = pressed[K_DOWN] - pressed[K_UP]

if len(bears) == 0:
raise GameWon

bear_countdown -= 1
if bear_countdown <= 0:
#clear bear array and add new bears
bear_countdown = 40

x = random.randint(0, WINDOW_WIDTH - BEAR_SIZE)
y = random.randint(0, WINDOW_HEIGHT - BEAR_SIZE)

bears.append(pygame.Rect((x, y), (BEAR_SIZE, BEAR_SIZE)))

window_surface.blit(window_filler, (0, 0))

#move player
player.x += velocity[0] * SPEED_FACTOR
player.y += velocity[1] * SPEED_FACTOR

player.clamp_ip(((0, 0), (WINDOW_WIDTH, WINDOW_HEIGHT)))

for bear in bears:
window_surface.blit(bear_image, bear, special_flags=BLEND_ADD)
window_surface.blit(player_image, player, special_flags=BLEND_ADD)

#check if player has intersected with bear
for bear in bears[:]:
if player.colliderect(bear) and velocity == [0, 0]:
Timer(0.6, explosion(bear)).start()

#draw the window
pygame.display.update()
main_clock.tick(40)

except GameWon:
window_surface.blit(winning_text, (90, 104))
pygame.display.flip()

while "showing the winning screen":
pygame.display.flip()
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()

# END CODE

Apologies for the massive post, and not checking it for errors. It's
too long to care about that.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top