Related
I have pretty much everything done for this little game except I can't seem to get the enemy to just aimlessly float around. They spawn at the top of the window but it's rather bland having them stand in line Civil War style. I'm pretty sure it's something to do with class Enemy, but not sure. Any tips on how to get the player and aliens moving around would be appreciated!
import sys, logging, os, random, math, open_color, arcade
#check to make sure we are running the right version of Python
version = (3,7)
assert sys.version_info >= version, "This script requires at least Python {0}.{1}".format(version[0],version[1])
#turn on logging, in case we have to leave ourselves debugging messages
logging.basicConfig(format='[%(filename)s:%(lineno)d] %(message)s', level=logging.DEBUG)
logger = logging.getLogger(__name__)
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
MARGIN = 30
SCREEN_TITLE = "Intergalactic slam"
NUM_ENEMIES = 5
STARTING_LOCATION = (400,100)
BULLET_DAMAGE = 10
ENEMY_HP = 10
HIT_SCORE = 10
KILL_SCORE = 100
PLAYER_HP = 100
class Bullet(arcade.Sprite):
def __init__(self, position, velocity, damage):
'''
initializes the bullet
Parameters: position: (x,y) tuple
velocity: (dx, dy) tuple
damage: int (or float)
'''
super().__init__("assets/Player/PNG/Sprites/Missiles/spaceMissiles_012.png", 0.5)
(self.center_x, self.center_y) = position
(self.dx, self.dy) = velocity
self.damage = damage
def update(self):
'''
Moves the bullet
'''
self.center_x += self.dx
self.center_y += self.dy
class Enemy_Bullet(arcade.Sprite):
def __init__(self, position, velocity, damage):
super().__init__("PNG/laserGreen1.png", 0.5)
(self.center_x, self.center_y) = position
(self.dx, self.dy) = velocity
self.damage = damage
def update(self):
self.center_x += self.dx
self.center_y += self.dy
class Player(arcade.Sprite):
def __init__(self):
super().__init__("assets/Player/PNG/Sprites/Ships/spaceShips_005.png", 0.5)
(self.center_x, self.center_y) = STARTING_LOCATION
self.hp = PLAYER_HP
class Enemy(arcade.Sprite):
def __init__(self, position):
'''
initializes an alien enemy
Parameter: position: (x,y) tuple
'''
super().__init__("PNG/shipGreen_manned.png", 0.5)
self.hp = ENEMY_HP
(self.center_x, self.center_y) = position
class Window(arcade.Window):
def __init__(self, width, height, title):
super().__init__(width, height, title)
file_path = os.path.dirname(os.path.abspath(__file__))
os.chdir(file_path)
self.set_mouse_visible(True)
arcade.set_background_color(open_color.black)
self.bullet_list = arcade.SpriteList()
self.enemy_list = arcade.SpriteList()
self.enemy_bullet_list = arcade.SpriteList()
self.player = Player()
self.score = 0
self.win = False
self.lose = False
def setup(self):
'''
Set up enemies
'''
for i in range(NUM_ENEMIES):
x = 120 * (i+1) + 40
y = 500
enemy = Enemy((x,y))
self.enemy_list.append(enemy)
def update(self, delta_time):
self.bullet_list.update()
self.enemy_bullet_list.update()
if (not (self.win or self.lose)):
for e in self.enemy_list:
for b in self.bullet_list:
if (abs(b.center_x - e.center_x) <= e.width / 2 and abs(b.center_y - e.center_y) <= e.height / 2):
self.score += HIT_SCORE
e.hp -= b.damage
b.kill()
if (e.hp <= 0):
e.kill()
self.score += KILL_SCORE
if (len(self.enemy_list) == 0):
self.win = True
if (random.randint(1, 75) == 1):
self.enemy_bullet_list.append(Enemy_Bullet((e.center_x, e.center_y - 15), (0, -10), BULLET_DAMAGE))
for b in self.enemy_bullet_list:
if (abs(b.center_x - self.player.center_x) <= self.player.width / 2 and abs(b.center_y - self.player.center_y) <= self.player.height / 2):
self.player.hp -= b.damage
b.kill()
if (self.player.hp <= 0):
self.lose = True
def on_draw(self):
arcade.start_render()
arcade.draw_text(str(self.score), 20, SCREEN_HEIGHT - 40, open_color.white, 16)
arcade.draw_text("HP: {}".format(self.player.hp), 20, 40, open_color.white, 16)
if (self.player.hp > 0):
self.player.draw()
self.bullet_list.draw()
self.enemy_bullet_list.draw()
self.enemy_list.draw()
if (self.lose):
self.draw_game_loss()
elif (self.win):
self.draw_game_won()
def draw_game_loss(self):
arcade.draw_text(str("LOSER!"), SCREEN_WIDTH / 2 - 90, SCREEN_HEIGHT / 2 - 10, open_color.white, 30)
def draw_game_won(self):
arcade.draw_text(str("WINNER!"), SCREEN_WIDTH / 2 - 90, SCREEN_HEIGHT / 2 - 10, open_color.white, 30)
def on_mouse_motion(self, x, y, dx, dy):
'''
The player moves left and right with the mouse
'''
self.player.center_x = x
def on_mouse_press(self, x, y, button, modifiers):
if button == arcade.MOUSE_BUTTON_LEFT:
x = self.player.center_x
y = self.player.center_y + 15
bullet = Bullet((x,y),(0,10),BULLET_DAMAGE)
self.bullet_list.append(bullet)
def main():
window = Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
window.setup()
arcade.run()
if __name__ == "__main__":
main()
You'd want to change the self.center_x and self.center_y of each Enemy on every update, like you're already doing for each Bullet, but make the dx and dy values random in some way. For example:
class Enemy(arcade.Sprite):
def __init__(self, position):
...
(self.center_x, self.center_y) = position
(self.dx, self.dy) = (0, 0)
def update(self):
self.dx += random.random() - 0.5
self.dy += random.random() - 0.5
self.center_x += self.dx
self.center_y += self.dy
Now, this may look more like "twitching wildly" than "floating": many times a second, the thing potentially changes course completely. That's technically random movement, but it's not something a spaceship would do.
If it's too twitchy, make it so that dx and dy change more slowly, for example by dividing the random.random() - 0.5 by a fixed number. If it's too floaty, make it so that every update changes it more.
If you want the enemy to prefer moving down, or towards the player, get out the trigonometry and adjust dx and dy to match.
I'm new to programming and would appreciate guidance/ feedback.
Below is a full working script:
I've managed to get the player sprite to be controlled by WASD, the asteroid sprite is also now rendered on the screen, with some physics to move it around. It should rebound off walls too but doesn't. But for some reason the update function isn't correctly calling on the Asteroid class, I believe - unless something else is wrong with it.
Greatly appreciate all help so ar and future guidance!
import arcade
import random
""" Universal variables """
SPRITE_SCALING_PLAYER = 0.5
SPRITE_SCALING_ASTEROID = 0.35
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
MOVEMENT_SPEED = 5
class Player(arcade.Sprite):
# PLAYER
def update(self):
# COLLISION
self.center_x += self.change_x
self.center_y += self.change_y
if self.left < 0:
self.left = 0
elif self.right > SCREEN_WIDTH - 1:
self.right = SCREEN_WIDTH - 1
if self.bottom < 0:
self.bottom = 0
elif self.top > SCREEN_HEIGHT - 1:
self.top = SCREEN_HEIGHT - 1
class Asteroid(arcade.Sprite):
# ASTEROID
def __init__(self, filename, sprite_scaling):
super().__init__(filename, sprite_scaling)
self.change_x = 0
self.change_y = 0
def update(self):
# Move the asteroid
self.center_x += self.change_x
self.center_y += self.change_y
# rebound
if self.left < 0:
self.change_x *= -1
if self.right > SCREEN_WIDTH:
self.change_x *= -1
if self.bottom < 0:
self.change_y *= -1
if self.top > SCREEN_HEIGHT:
self.change_y *= -1
# MAIN GAME CLASS
class MyGame(arcade.Window):
""" Our custom Window Class"""
def __init__(self):
""" Initializer """
# Call the parent class initializer
super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Alien")
# Background image will be stored in this variable
self.background = ("space_bg.png")
# Variables that will hold sprite lists
self.all_sprite_list = ["ufo_sprite.png", "asteroid.gif"]
# Set up player
self.player_sprite = self.all_sprite_list[0]
# Set up asteroid
self.asteroid_sprite = self.all_sprite_list[1]
# Don't show the mouse cursor
self.set_mouse_visible(False)
# arcade.set_background_color(arcade.color.BLACK)
def setup(self):
""" Set up the game and initialize the variables. """
# background
self.background = arcade.load_texture(self.background)
# Sprite lists
self.all_sprite_list = arcade.SpriteList()
# Set up the player
self.player_sprite = Player("ufo_sprite.png", SPRITE_SCALING_PLAYER)
self.player_sprite.center_x = (SCREEN_WIDTH * 0.50)
self.player_sprite.center_y = (SCREEN_HEIGHT * 0.50)
self.all_sprite_list.append(self.player_sprite)
# Set up asteroid
self.asteroid_sprite = Asteroid("asteroid.gif", SPRITE_SCALING_ASTEROID)
Asteroid.center_x = random.randrange(SCREEN_WIDTH)
Asteroid.center_y = random.randrange(SCREEN_HEIGHT)
Asteroid.change_x = random.randrange(-4, 4)
Asteroid.change_y = random.randrange(-4, 4)
self.all_sprite_list.append(self.asteroid_sprite)
def on_draw(self):
# needed before other drawn elements
arcade.start_render()
# draw background
arcade.draw_texture_rectangle(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2,
SCREEN_WIDTH, SCREEN_HEIGHT, self.background)
# draw sprites
self.all_sprite_list.draw()
def update(self, delta_time):
""" Movement and game logic """
self.all_sprite_list.update()
def on_key_press(self, key, modifiers):
"""Called whenever a key is pressed. """
if key == arcade.key.W:
self.player_sprite.change_y = MOVEMENT_SPEED
elif key == arcade.key.S:
self.player_sprite.change_y = -MOVEMENT_SPEED
elif key == arcade.key.A:
self.player_sprite.change_x = -MOVEMENT_SPEED
elif key == arcade.key.D:
self.player_sprite.change_x = MOVEMENT_SPEED
# elif key == arcade.key.SPACE:
# self.player_sprite.change_x = MOVEMENT_SPEED
def on_key_release(self, key, modifiers):
"""Called when the user releases a key. """
if key == arcade.key.W or key == arcade.key.S:
self.player_sprite.change_y = 0
elif key == arcade.key.A or key == arcade.key.D:
self.player_sprite.change_x = 0
# elif key == arcade.key.SPACE:
# self.player_sprite.change_y = (SCREEN_HEIGHT * 0.005)
def main():
""" Main method """
window = MyGame()
window.setup()
arcade.run()
arcade.schedule(update, 1 / 80)
if __name__ == "__main__":
main()
Could you please tell us the exact problem (exception, sprites not moving as expected,...)
However, the code you provided gave me the following ideas:
calling the constructor of the inherited class
class Asteroid(arcade.Sprite):
def __init__(self):
super(Asteroid, self).__init__()
# or
arcade.Sprite.__init__(self)
more here (it's odd but I found nothing about calling the base constructor under the classes-inheritance-section in the official docs, maybe someone can provide something
using variables which don't exist or are created below
self.center_x += self.change_x * delta_time
self.center_y += self.change_y * delta_time
self.change_x is created below (so it does not exist at that time) and delta_time isn't anywhere else (maybe it's just an incomplete snippet?)
Do you really want to create an instance of Sprite after you made your own child class of Sprite (Asteroid)
self.asteroid_sprite = Asteroid("asteroid.gif", SPRITE_SCALING_ASTEROID)
instead of
self.asteroid_sprite = arcade.Sprite("asteroid.gif", SPRITE_SCALING_ASTEROID)
EDIT:
try to do this before assigning the variables in the Asteroid constructor:
def __init__(self, *args, **kwargs):
super(Asteroid, self).__init__(*args, **kwargs)
args and kwargs are placeholders for the things you are passing in below like the imagepath
I am writing a relatively complex platformer game in which a player moves using wasd and shoots with the mouse. The goal is to get the bullet to travel to the location of the mouse when it was clicked. I have code that sort of works but as the angle gets farther from 0 or 90 (straight left/right or straight up/down) the the bullets final location gets farther from the cursor. I am fairly certain the issue is simply that since the change in x and y are floating points and x,y location of the bullet cannot be floating point there is a rounding issue occurring. I have tried numerous different methods based on forum searches and all of them have the same problem. I have attached the most relevant file (pay particular attention to the bullets init class). Any advice or help would be appreciated. For the record this is just the player class NOT the main.
import pygame
import level
import platform
##import enemies
import math
pygame.init()
## sets up colors that need to be used in every part of the program
black=(0,0,0)
white=(255,255,255)
red=(255,0,0)
green=(0,255,0)
blue=(0,0,255)
class Player(pygame.sprite.Sprite):
## This class represents the player. It is inhariting from the Sprite class in Pygame
window=None
screen_width=0
screen_height=0
width=None
height=None
x_velocity=0
y_velocity=0
chrono_level=500
ball_damage=0
bomb_damage=0
blast_radius=0
gravity=0
isjumping=False
isducking=False
level=None
direction=None
def __init__(self,argwindow,argsheight,argswidth,argcolor=white,argwidth=40,argheight=60,argx=0,argy=0,argball_damage=5,argbomb_damage=15,argbomb_radius=10,argchrono_level=500):
## Constructor. Pass in the color, width, and height
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([argwidth,argheight])
self.image.fill(argcolor)
self.rect=self.image.get_rect()
## sets up the variables inital variables
self.window=argwindow
self.width=argwidth
self.height=argheight
self.screen_height=argsheight
self.screen_width=argswidth
self.rect.x=argx
self.rect.y=argy
self.x_velocity=0
self.y_velocity=0
self.ball_damage=argball_damage
self.bomb_damage=argbomb_damage
self.bomb_radius=argbomb_radius
self.chrono_level=argchrono_level
self.isjumping=False
self.isducking=False
def update(self):
## check gravity
self.calc_grav()
## move left or right
self.rect.x+=self.x_velocity
## check for any collisions
platform_hit_list=pygame.sprite.spritecollide(self,self.level.platform_list,False)## this is the pygame generatred collision detection builtin to the sprite class
for platform in platform_hit_list:
if self.x_velocity > 0:##i.e sprite was moving right
self.rect.right = platform.rect.left ##puts the right side of the sprite flush with the left side of the platform
elif self.x_velocity < 0:
self.rect.left = platform.rect.right
self.x_velocity=0
## move sprite up or down
self.rect.y+=self.y_velocity
## check for any collisions
platform_hit_list=pygame.sprite.spritecollide(self,self.level.platform_list,False)## this is the pygame generatred collision detection builtin to the sprite class
for platform in platform_hit_list:
if self.y_velocity > 0:## i.e. sprite is falling
self.rect.bottom = platform.rect.top ## puts bottom of player flush with top of platform
self.isjumping=False
elif self.y_velocity < 0:
self.rect.top = platform.rect.bottom
self.y_velocity=0
## check direction
pos = pygame.mouse.get_pos()
if pos[0] > (self.rect.x+(self.width/2)): ##i.e. cursor is farther to the right then the middle of the sprite
self.direction="Right"
else: ##pos[0] < (self.rect.x+(self.width/2))
self.direction="Left"
def jump(self):
if not self.isjumping:
self.y_velocity=-15
self.isjumping=True
def calc_grav(self):
if self.y_velocity ==0:
self.y_velocity=1
else:
self.y_velocity+=self.gravity
if self.rect.y >= self.screen_height - self.rect.height and self.y_velocity >= 0:
self.y_velocity = 0
self.rect.y = self.screen_height - self.rect.height
self.isjumping=False
def move_left(self):## called if the player hits the left arrow key or the a key
self.x_velocity=-5
def move_right(self):## called is the player hits the right arrow key or the d key
self.x_velocity=5
def stop(self):## called if the player lets up on either arrow key or the a or d key
self.x_velocity=0
def shoot(self,argmouse_position):
if self.direction=="Left":
bullet_start_x=self.rect.x
bullet_start_y=(self.rect.y+(self.height/2))
elif self.direction=="Right":
bullet_start_x=(self.rect.x+self.width)
bullet_start_y=(self.rect.y+(self.height/2))
bullet=player_bullet(bullet_start_x,bullet_start_y,argmouse_position)
return (bullet)
class player_bullet(pygame.sprite.Sprite):
bullet_x=None
bullet_y=None
bullet_x_velocity=None
bullet_y_velocity=None
target_x=None
target_y=None
speed=10
def __init__(self,argx,argy,argmouse_positon):
pygame.sprite.Sprite.__init__(self)
print "it inited"
self.image = pygame.Surface([4, 10])
self.image.fill(black)
self.rect=self.image.get_rect()
self.rect.x=argx
self.rect.y=argy
self.bullet_x=argx
self.bullet_y=argy
self.target_x=argmouse_positon[0]
self.target_y=argmouse_positon[1]
dx=self.target_x-self.bullet_x
dy=self.target_y-self.bullet_y
angle=math.atan2(dy,dx)
print angle
self.bullet_x_velocity=self.speed*math.cos(angle)
self.bullet_y_velocity=self.speed*math.sin(angle)
def update(self):
print self.rect.x
print self.bullet_x_velocity
self.rect.x+=self.bullet_x_velocity
print self.rect.x
self.rect.y+=self.bullet_y_velocity
def collide(self,argdisplay_width,argdisplay_height,argplatform_list):
Platform_hit_list=pygame.sprite.spritecollide(self, argplatform_list, False)
if len(Platform_hit_list) > 0:
return True
elif self.rect.x > argdisplay_width or self.rect.x < 0:
return True
elif self.rect.y > argdisplay_height or self.rect.y < 0:
return True
else:
return False
I have written a similar game mechanic, where instead of a bullet I could shoot any projectile, of any size, of any sprite. What I did was add my current position to number of pixels I have to move to (defined as vx, vy below). I found the number of pixels i have to move by dividing the differences in the axis, by distance, and the multiplying a speed ( usually 10). Here is the projectile class below(the mask stuff is so bullets don't go into the buildings):
class projectile:
"""an image goes towards the target from starting location"""
def __init__(self, xorg, yorg, x, y, sprite, speed):
self.x = xorg
self.y = yorg
self.gotox = x
self.gotoy = y
self.sprite = sprite
self.xsize = self.sprite.get_width()
self.ysize = self.sprite.get_height()
self.rect = Rect(self.x, self.y, self.xsize, self.ysize)
# divided by 2, because we want the middle of the projectile to goto the destination, not the edge
self.gotox -= self.xsize / 2
self.gotoy -= self.ysize / 2
self.speed = speed
#differance in the x and y axis of destination to current position
self.dx = self.gotox - self.x
self.dy = self.gotoy - self.y
self.slope = (self.dy / max(1, self.dx))
self.gotox += self.gotox * self.slope
self.gotoy += self.gotoy * self.slope
self.dist = max(1, hypot(self.dx, self.dy))
self.state = "alive"
self.rect = Rect(self.x, self.y, self.xsize, self.ysize)
def move(self):
"""moves based on where the target was during time of shooting
untill it hits targer, or hits a wall"""
global currentMask
dist = max(1, hypot(self.dx, self.dy))
# found the num of pixels I have to move (speed is usually 10)
self.vx = self.speed * (self.dx / self.dist)
self.vy = self.speed * (self.dy / self.dist)
if currentMask.get_at((int(self.x + self.vx), int(self.y + self.vy))) != (0, 0, 0) and currentMask.get_at(
(int(self.x + self.vx + self.xsize), int(self.y + self.vy + self.ysize))) != (0, 0, 0) and int(
self.y + self.vy + self.ysize) <= 800 - self.ysize and int(
self.y + self.vy) >= 0 + self.ysize and int(self.x + self.vx) >= self.xsize and int(
self.x + self.vx + self.xsize) <= 1200 - self.xsize:
# added the num of pixels i have to move, to my current postition
self.x += self.vx
self.y += self.vy
else:
self.state = "dead"
self.rect = Rect(self.x, self.y, self.xsize, self.ysize)
screen.blit(self.sprite, (self.x, self.y))
I'm working on a 2D python game project for my CS class, and I've hit a bump, I don't know what the problem is:
The project is a large part of my grade, and up until now I've had an A+
This project is incredibly frustrating
NEW
ok so i've got everything working so far, except for some reason My protaganist() is stuck at the top left corner of the game screen !
Also, i need ideas on how to create a jump action
If anyone could help I would be incredibly grateful!
I am importing a game engine my teacher made available from his book website, but i it is too long for me to add but i will try to add some of it at the bottom
Here is all my code:
import gameEngine
import pygame
import math
pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.mixer.init()
sndAtk = pygame.mixer.Sound("OOT_AdultLink_Attack1.wav")
#goal is to create a game
#must have menu to start game
#menu should have a start and quit button.. start runs gaming operations and quit exits program
#sprites for character and enemies and bullets maybe, use one large image and simply move visibiliy
#this saves memory as 1 image is loaded instead of many
"""
class game(gameEngine.scene):
def __init__(self, scene):
self.background()
self.sprites["spawn.gif", "badguys.gif"]
"""
"""
protaganist is our hero sprite
should run left and right, jump left and right
and attack left and right...
I might add in the bow and jump attack
"""
class scrollinggrass(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("gamebackground.jpg")
self.rect.centerx = 20
self.rect.centery = 500
self.rect = self.image.get_rect()
self.dx = 10
self.dy = 0
self.checkKeys()
def checkKeys(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
print("working")
self.forward(3)
run.play()
if keys[pygame.K_LEFT]:
self.forward(-3)
class hearts(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("heart.png")
self.setTransparentColor = self.imageMaster.get_at((1,1))
self.imageMaster.set_colorkey(self.setTransparentColor)
self.setPosition((550 , 30))
class badguy(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("badguy1.png")
self.rect = self.imageMaster.get_rect()
self.health = 2
self.DEAD = 1
self.state = 0
class protaganist(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.imageList = []
self.rect = self.imageMaster.get_rect()
self.STANDING = 0
self.RUNNING = 1
self.ATTACKING = 2
self.JUMPING = 3
self.DEAD = 10
self.imageFrame = 0
self.state = self.STANDING
self.hearts = 1
self.heartPts = self.hearts * 3
self.stats()
self.loadImages()
# self.image = self.imageList[0]
self.checkKeys()
def stats(self):
#sets it up so each heart is essentially 3 hit points
if self.heartPts >= 3:
self.hearts = 1
elif self.heartPts >= 6:
self.hearts = 2
elif self.heartPts == 9:
self.hearts = 3
elif self.heartPts > 9:
self.heartPts = 9
# changes state to dead if hp == 0
if self.heartPts == 0:
self.state = DEAD
def loadImages(self):
self.setPosition((320 , 380))
self.setImage("heroSTANDING.gif")
self.setTransparentColor = self.imageMaster.get_at((1,1))
self.imageMaster.set_colorkey(self.setTransparentColor)
def checkKeys(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
self.state = runRight
self.frame += 1
if self.frame >= len(self.imageList):
self.frame = 1
self.image = self.imageList[self.frame]
# self.image = self.image.get_rect()
# self.rect.center = (320, 240)
if keys[pygame.K_LEFT]:
self.state = 1
while keys[pygame.K_g]:
self.state = Attacking
sndAtk.play()
if self.state == self.DEAD:
self.image = self.deadImgList[0]
self.frame += 1
self.image = self.deadImgList[self.frame]
#self.image = self.image.get_rect()
#self.rect.center = (320, 240)
class game(gameEngine.Scene):
def __init__ (self):
gameEngine.Scene.__init__(self)
pygame.display.set_caption("Link's Mediocre Adventure")
background = pygame.Surface(screen.get_size())
background.fill((0, 0, 0))
screen.blit(background, (0, 0))
pro = protaganist(self)
baddy = badguy(self)
baddy1 = badguy(self)
heart = hearts(self)
grass = scrollinggrass(self)
goodlySprites = self.makeSpriteGroup((grass, pro, heart))
baddySprites = self.makeSpriteGroup((baddy, baddy1))
# self.addSpriteGroup(goodlySprites)
self.addGroup((baddySprites))
clock = pygame.time.Clock()
keepGoing = True
while keepGoing:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
if pro.state == pro.ATTACKING:
if pro.collidesGroup(baddySprites):
baddy.health -= 1
baddy1.health -= 1
if baddy.health == 0:
baddy.reset()
elif baddy1.health == 0:
baddy.reset()
elif pro.state != pro.ATTACKING:
if pro.collideGroup(baddySprites):
pro.heartPts -= 1
goodlySprites.update()
baddySprites.update()
goodlySprites.draw(screen)
baddySprites.draw(screen)
pygame.display.flip()
def main():
game.start()
if __name__ == "__main__":
game()
game engine
class SuperSprite(pygame.sprite.Sprite):
""" An enhanced Sprite class
expects a gameEngine.Scene class as its one parameter
Use methods to change image, direction, speed
Will automatically travel in direction and speed indicated
Automatically rotates to point in indicated direction
Five kinds of boundary collision
"""
def __init__(self, scene):
pygame.sprite.Sprite.__init__(self)
self.scene = scene
self.screen = scene.screen
#create constants
self.WRAP = 0
self.BOUNCE = 1
self.STOP = 2
self.HIDE = 3
self.CONTINUE = 4
#create a default text image as a placeholder
#This will usually be changed by a setImage call
self.font = pygame.font.Font("freesansbold.ttf", 30)
self.imageMaster = self.font.render(">sprite>", True, (0, 0,0), (0xFF, 0xFF, 0xFF))
self.image = self.imageMaster
self.rect = self.image.get_rect()
#create properties
#most will be changed through method calls
self.x = 200
self.y = 200
self.dx = 0
self.dy = 0
self.dir = 0
self.rotation = 0
self.speed = 0
self.maxSpeed = 10
self.minSpeed = -3
self.boundAction = self.WRAP
self.pressed = False
self.oldCenter = (100, 100)
self.states = {}
self.currentState = "default"
def update(self):
self.oldCenter = self.rect.center
self.checkEvents()
self.__rotate()
self.__calcVector()
self.__calcPosition()
self.checkBounds()
self.rect.center = (self.x, self.y)
def checkEvents(self):
""" overwrite this method to add your own event code """
pass
def __rotate(self):
""" PRIVATE METHOD
change visual orientation based on
rotation property.
automatically called in update.
change rotation property directly or with
rotateBy(), setAngle() methods
"""
oldCenter = self.rect.center
self.oldCenter = oldCenter
self.image = pygame.transform.rotate(self.imageMaster, self.rotation)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
def __calcVector(self):
""" calculates dx and dy based on speed, dir
automatically called in update
"""
theta = self.dir / 180.0 * math.pi
self.dx = math.cos(theta) * self.speed
self.dy = math.sin(theta) * self.speed
self.dy *= -1
def __calcPosition(self):
""" calculates the sprites position adding
dx and dy to x and y.
automatically called in update
"""
self.x += self.dx
self.y += self.dy
def checkBounds(self):
""" checks boundary and acts based on
self.BoundAction.
WRAP: wrap around screen (default)
BOUNCE: bounce off screen
STOP: stop at edge of screen
HIDE: move off stage and wait
CONTINUE: keep going at present course and speed
automatically called by update
"""
scrWidth = self.screen.get_width()
scrHeight = self.screen.get_height()
#create variables to simplify checking
offRight = offLeft = offTop = offBottom = offScreen = False
if self.x > scrWidth:
offRight = True
if self.x < 0:
offLeft = True
if self.y > scrHeight:
offBottom = True
if self.y < 0:
offTop = True
if offRight or offLeft or offTop or offBottom:
offScreen = True
if self.boundAction == self.WRAP:
if offRight:
self.x = 0
if offLeft:
self.x = scrWidth
if offBottom:
self.y = 0
if offTop:
self.y = scrHeight
elif self.boundAction == self.BOUNCE:
if offLeft or offRight:
self.dx *= -1
if offTop or offBottom:
self.dy *= -1
self.updateVector()
self.rotation = self.dir
elif self.boundAction == self.STOP:
if offScreen:
self.speed = 0
elif self.boundAction == self.HIDE:
if offScreen:
self.speed = 0
self.setPosition((-1000, -1000))
elif self.boundAction == self.CONTINUE:
pass
else:
# assume it's continue - keep going forever
pass
def setSpeed(self, speed):
""" immediately sets the objects speed to the
given value.
"""
self.speed = speed
def speedUp(self, amount):
""" changes speed by the given amount
Use a negative value to slow down
"""
self.speed += amount
if self.speed < self.minSpeed:
self.speed = self.minSpeed
if self.speed > self.maxSpeed:
self.speed = self.maxSpeed
def setAngle(self, dir):
""" sets both the direction of motion
and visual rotation to the given angle
If you want to set one or the other,
set them directly. Angle measured in degrees
"""
self.dir = dir
self.rotation = dir
def turnBy (self, amt):
""" turn by given number of degrees. Changes
both motion and visual rotation. Positive is
counter-clockwise, negative is clockwise
"""
self.dir += amt
if self.dir > 360:
self.dir = amt
if self.dir < 0:
self.dir = 360 - amt
self.rotation = self.dir
def rotateBy(self, amt):
""" change visual orientation by given
number of degrees. Does not change direction
of travel.
"""
self.rotation += amt
if self.rotation > 360:
self.rotation = amt
if self.rotation < 0:
self.rotation = 360 - amt
def setImage (self, image):
""" loads the given file name as the master image
default setting should be facing east. Image
will be rotated automatically """
self.imageMaster = pygame.image.load(image)
self.imageMaster = self.imageMaster.convert()
def setDX(self, dx):
""" changes dx value and updates vector """
self.dx = dx
self.updateVector()
def addDX(self, amt):
""" adds amt to dx, updates vector """
self.dx += amt
self.updateVector()
def setDY(self, dy):
""" changes dy value and updates vector """
self.dy = dy
self.updateVector()
def addDY(self, amt):
""" adds amt to dy and updates vector """
self.dy += amt
self.updateVector()
def setComponents(self, components):
""" expects (dx, dy) for components
change speed and angle according to dx, dy values """
(self.dx, self.dy) = components
self.updateVector()
def setBoundAction (self, action):
""" sets action for boundary. Values are
self.WRAP (wrap around edge - default)
self.BOUNCE (bounce off screen changing direction)
self.STOP (stop at edge of screen)
self.HIDE (move off-stage and stop)
self.CONTINUE (move on forever)
Any other value allows the sprite to move on forever
"""
self.boundAction = action
def setPosition (self, position):
""" place the sprite directly at the given position
expects an (x, y) tuple
"""
(self.x, self.y) = position
def moveBy (self, vector):
""" move the sprite by the (dx, dy) values in vector
automatically calls checkBounds. Doesn't change
speed or angle settings.
"""
(dx, dy) = vector
self.x += dx
self.y += dy
self.checkBounds()
def forward(self, amt):
""" move amt pixels in the current direction
of travel
"""
#calculate dx dy based on current direction
radians = self.dir * math.pi / 180
dx = amt * math.cos(radians)
dy = amt * math.sin(radians) * -1
self.x += dx
self.y += dy
def addForce(self, amt, angle):
""" apply amt of thrust in angle.
change speed and dir accordingly
add a force straight down to simulate gravity
in rotation direction to simulate spacecraft thrust
in dir direction to accelerate forward
at an angle for retro-rockets, etc.
"""
#calculate dx dy based on angle
radians = angle * math.pi / 180
dx = amt * math.cos(radians)
dy = amt * math.sin(radians) * -1
self.dx += dx
self.dy += dy
self.updateVector()
def updateVector(self):
#calculate new speed and angle based on dx, dy
#call this any time you change dx or dy
self.speed = math.sqrt((self.dx * self.dx) + (self.dy * self.dy))
dy = self.dy * -1
dx = self.dx
radians = math.atan2(dy, dx)
self.dir = radians / math.pi * 180
def setSpeedLimits(self, max, min):
""" determines maximum and minimum
speeds you will allow through
speedUp() method. You can still
directly set any speed you want
with setSpeed() Default values:
max: 10
min: -3
"""
self.maxSpeed = max
self.minSpeed = min
def dataTrace(self):
""" utility method for debugging
print major properties
extend to add your own properties
"""
print "x: %d, y: %d, speed: %.2f, dir: %.f, dx: %.2f, dy: %.2f" % \
(self.x, self.y, self.speed, self.dir, self.dx, self.dy)
def mouseDown(self):
""" boolean function. Returns True if the mouse is
clicked over the sprite, False otherwise
"""
self.pressed = False
if pygame.mouse.get_pressed() == (1, 0, 0):
if self.rect.collidepoint(pygame.mouse.get_pos()):
self.pressed = True
return self.pressed
def clicked(self):
""" Boolean function. Returns True only if mouse
is pressed and released over sprite
"""
released = False
if self.pressed:
if pygame.mouse.get_pressed() == (0, 0, 0):
if self.rect.collidepoint(pygame.mouse.get_pos()):
released = True
return released
def collidesWith(self, target):
""" boolean function. Returns True if the sprite
is currently colliding with the target sprite,
False otherwise
"""
collision = False
if self.rect.colliderect(target.rect):
collision = True
return collision
def collidesGroup(self, target):
""" wrapper for pygame.sprite.collideany
simplifies checking sprite - group collisions
returns result of collision check (sprite from group
that was hit or None)
"""
collision = pygame.sprite.spritecollideany(self, target)
return collision
def distanceTo(self, point):
""" returns distance to any point in pixels
can be used in circular collision detection
"""
(pointx, pointy) = point
dx = self.x - pointx
dy = self.y - pointy
dist = math.sqrt((dx * dx) + (dy * dy))
return dist
def dirTo(self, point):
""" returns direction (in degrees) to
a point """
(pointx, pointy) = point
dx = self.x - pointx
dy = self.y - pointy
dy *= -1
radians = math.atan2(dy, dx)
dir = radians * 180 / math.pi
dir += 180
return dir
def drawTrace(self, color=(0x00, 0x00, 0x00)):
""" traces a line between previous position
and current position of object
"""
pygame.draw.line(self.scene.background, color, self.oldCenter,
self.rect.center, 3)
self.screen.blit(self.scene.background, (0, 0))
def addState(self, stateName, stateImageFile):
""" Creates a new sprite state with the associated name
and image. Useful to build multi-state sprites.
"""
#load the image
tempImage = pygame.image.load(stateImageFile)
tempImage.convert()
self.states[stateName] = tempImage
def setState(self, stateName):
""" attempts to set the sprite to the indicated state
(image)
"""
self.imageMaster = self.states[stateName]
self.rect = self.imageMaster.get_rect()
self.currentState = stateName
def getState(self):
""" returns the current state name
(default if no states have been explicitly set)
"""
return self.currentState
if pro.state == pro.ATTACKING:
if pro.collidesWith(baddySprites):
baddy.health -= 1
if baddy.health == 0:
baddy.reset()
elif pro.state != pro.ATTACKING:
if pro.collidesWith(baddySprites):
pro.heartPts -= 1
baddySprites is a sprite group, so I bet you have to use collidesGroup instead of collidesWith.
if pro.state == pro.ATTACKING:
if pro.collidesGroup(baddySprites):
baddy.health -= 1
if baddy.health == 0:
baddy.reset()
elif pro.state != pro.ATTACKING:
if pro.collidesGroup(baddySprites):
pro.heartPts -= 1
But even if you do this, it seems like you'll still have problems. Namely, this code only ever deducts health from baddy and not baddy1. I'm assuming sprite groups support iteration. If so, you should perform collision detection independently on each enemy.
for enemy in baddySprites:
if pro.state == pro.ATTACKING:
if pro.collidesWith(enemy):
enemy.health -= 1
if enemy.health == 0:
enemy.reset()
elif pro.state != pro.ATTACKING:
if pro.collidesWith(enemy):
pro.heartPts -= 1
Working on a pong game while I am reading the Python Programming for the beginner book and I am lost as to what I am missing in this game's code. Everything in the game works except for whenever the ball overlaps the board sprite it doesn't detect it and it goes right to the edge of the window, ending the game.
# single player pong
# the player must rebound the ball to avoid it going off screen.
from livewires import games, color
games.init(screen_width = 1152, screen_height = 768, fps = 60)
class board(games.Sprite):
"""The object used to bounce the ball."""
image = games.load_image("paddle.png")
def __init__(self):
"""initialize the board"""
super(board, self).__init__(image = board.image,
y = games.mouse.y,
right = games.screen.width)
def update(self):
"""
:return:move mouse to y position
"""
self.y = games.mouse.y
if self.top < 0:
self.top = 0
if self.bottom > games.screen.height:
self.bottom = games.screen.height
self.check_rebound()
def check_rebound(self):
"""Check for ball rebound"""
for ball in self.overlapping_sprites:
ball.rebound()
class ball(games.Sprite):
""" A bouncing pizza."""
image = games.load_image("ball.png")
def __init__(self):
"""initialize the ball"""
super(ball, self).__init__(image = ball.image,
x = games.screen.width/2,
y = games.screen.height/2,
dx = 1,
dy = 1)
def end_game(self):
""" End the game. """
end_message = games.Message(value = "Game Over",
size = 90,
color = color.red,
x = games.screen.width/2,
y = games.screen.height/2,
lifetime = 5 * games.screen.fps,
after_death = games.screen.quit)
games.screen.add(end_message)
def update(self):
""" Reverse a velocity component if edge of screen is reached and end game if the right wall is reached."""
if self.left < 0:
self.dx = -self.dx
if self.bottom > games.screen.height or self.top < 0:
self.dy = -self.dy
if self.right > games.screen.width:
self.end_game()
self.destroy()
def rebound(self):
"Ball rebounds from board."
self.dx = -self.dx
self.dy = -self.dy
def main():
""" Play the game. """
the_board = board()
games.screen.add(the_board)
the_ball = ball()
games.screen.add(the_ball)
games.screen.add(the_ball)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
# start it up!
main()
Apologies if the code is sorted a bit wonky, I had to quadruple space it to work on the site.
Remove dy = -dy in the rebound method.