My enemies stop moving if i move my player [duplicate] - python

This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Closed 2 years ago.
https://imgur.com/a/5aoTDwA
Sorry for the lag, but as visible my enemies(green) spawn but after if i move my player(gray) they sometimes stop moving until i move my player again.
But even after moving my player the pre existing enemies dont move closer to the player and retain their distance to the player
this is the file which contains the main loop of the game
this is the file which contains the player class
this is the file which contains the enemy class
and this is the file which is the main run file
i feel the problem exists in my main loop class which is as follows:
import pygame, random
from Folder.scripts.TopDown.script_files.td_Player import Player
from Folder.scripts.TopDown.script_files.td_enemy import Enemy
from Folder.scripts.TopDown.script_files.td_colours import colours
pygame.init()
class Main:
def __init__(self):
self.FPS = 60
self.WIDTH = self.HEIGHT = 1280
self.MAP = pygame.Surface((self.WIDTH, self.HEIGHT))
self.enemies = []
def main(self,WINDOW ,clock):
WINDOW.fill(colours['white'])
player = Player()
camera_pos = (0, 0)
enemy_time = 0
run = True
while run:
clock.tick(self.FPS)
enemy_y = random.randint(0, 1220)
enemy_x = random.randint(0, 1220)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
current_time = pygame.time.get_ticks()
if current_time > enemy_time:
enemy_time = current_time + 1000
self.enemies.append(Enemy(enemy_y, enemy_x))
camera_pos = player.movement(camera_pos)
self.WindowUpdate(WINDOW, player, camera_pos)
pygame.quit()
def WindowUpdate(self,WINDOW, player, camera_pos):
player_x, player_y = player.playerMove()
WINDOW.fill((255, 255, 255))
self.MAP.fill(colours['gray'])
player.renderPlayer(self.MAP)
WINDOW.blit(self.MAP, camera_pos)
for e in self.enemies:
e.renderEnemy(WINDOW)
e.enemyMovement(player_x, player_y)
pygame.display.update()
Enemy class:
import pygame, os, random, math
pygame.init()
# current_time = pygame.time.get_ticks()
# if current_time > self.enemy_time:
# self.enemy_time = current_time + 1000
# self.enemies.append()
class Enemy:
def __init__(self, enemy_y, enemy_x):
self.ENEMY_HEIGHT = self.ENEMY_WIDTH = 64
self.ENEMY_ACCELERATION = 5
self.enemy_rect = pygame.Rect(enemy_x, enemy_y, self.ENEMY_HEIGHT, self.ENEMY_WIDTH)
self.ENEMY = {
'up': pygame.transform.scale(pygame.image.load(
os.path.join(r'C:\Users\ajeen\PycharmProjects\Games\Folder\scripts\TopDown\assets', 'enemy_up.png')),
(self.ENEMY_HEIGHT, self.ENEMY_WIDTH)),
'down': pygame.transform.scale(pygame.image.load(
os.path.join(r'C:\Users\ajeen\PycharmProjects\Games\Folder\scripts\TopDown\assets', 'enemy_down.png')),
(self.ENEMY_HEIGHT, self.ENEMY_WIDTH)),
'left': pygame.transform.scale(pygame.image.load(
os.path.join(r'C:\Users\ajeen\PycharmProjects\Games\Folder\scripts\TopDown\assets', 'enemy_left.png')),
(self.ENEMY_HEIGHT, self.ENEMY_WIDTH)),
'right': pygame.transform.scale(pygame.image.load(
os.path.join(r'C:\Users\ajeen\PycharmProjects\Games\Folder\scripts\TopDown\assets', 'enemy_right.png')),
(self.ENEMY_HEIGHT, self.ENEMY_WIDTH))
}
self.enemy_state = 'up'
def enemyMovement(self, player_x, player_y):
ene_x, ene_y = player_x - self.enemy_rect.x, player_y - self.enemy_rect.y
dist = math.hypot(ene_x, ene_y)
ene_x, ene_y = ene_x // dist, ene_y // dist
self.enemy_rect.x += ene_x * self.ENEMY_ACCELERATION
self.enemy_rect.y += ene_y * self.ENEMY_ACCELERATION
print(self.enemy_rect.x, self.enemy_rect.y)
def renderEnemy(self, WINDOW):
WINDOW.blit(self.ENEMY[self.enemy_state] , (self.enemy_rect.x, self.enemy_rect.y))
I hope the imgur link to the vid showcasing my problem gets embedded. Any input or help will be appreciated

The result of ene_x // dist and ene_y // dist is always 0. To calculate the movement, you must use the / (division) operator rather than the // (floor division) operator
ene_x, ene_y = ene_x // dist, ene_y // dist
ene_x, ene_y = ene_x / dist, ene_y / dist
Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.
The coordinates for Rect objects are all integers. [...]
The fraction part of the movement gets lost when the position of the object incremented:
self.enemy_rect.x += ene_x * self.ENEMY_ACCELERATION
self.enemy_rect.y += ene_y * self.ENEMY_ACCELERATION
If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location (e.g. .topleft) of the rectangle:
x, y = # floating point coordinates
rect.topleft = round(x), round(y)
Add floating point coordinates to the Enemy class:
class Enemy:
def __init__(self, enemy_y, enemy_x):
# [...]
self.enemy_rect = pygame.Rect(enemy_x, enemy_y, self.ENEMY_HEIGHT, self.ENEMY_WIDTH)
self.x, self.y = self.enemy_rect.topleft
# [...]
def enemyMovement(self, player_x, player_y):
ene_x, ene_y = player_x - self.x, player_y - self.y
dist = math.hypot(ene_x, ene_y)
ene_x, ene_y = ene_x / dist, ene_y / dist
self.x += ene_x * self.ENEMY_ACCELERATION
self.y += ene_y * self.ENEMY_ACCELERATION
self.enemy_rect = round(self.x), round(self.y)

Related

Syntax error in the pygame collision detection function python3 [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Closed 1 year ago.
so I try to program a 2d game platform like space invaders, but the problem is in the collision, when I try to define the collision, which I use in her characteristics, the characteristics of the classes (enemy coordinates and the bullet coordinates), the code won't start. And this is the result : `
def isc (eneemy.x, eneemy.y, Bulllet.x, Bulllet.y ):
^
SyntaxError: invalid syntax
this is the code if you need it :
############# LIBRARIES ####################
import pygame, sys, random
import math
from pygame.constants import SYSTEM_CURSOR_WAITARROW
from pygame import mixer
pygame.init()
############ WINDOW OPTIONS ################
Swidth = 800
Sheight =600
screen = pygame.display.set_mode((Swidth, Sheight))
pygame.display.set_caption("Space Invader By Mal4D")
icon = pygame.image.load('places/favicon.png')
pygame.display.set_icon(icon)
############# COLORS #######################
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
YELLOW = (255 , 255 , 0)
########### VARIANTS ######################
isJumping= False
LevelJump = 0.25
rocket = pygame.image.load("rocket/rocket.png")
space = pygame.image.load("places/bg.png")
bullet = pygame.image.load("rocket/bullet.png")
Sound = pygame.mixer.music.load("Sounds/laser.mp3")
Bgsound= pygame.mixer.music.load("Sounds/bg.mp3")
enemy = pygame.image.load("Enemy/enemy.png")
ax=random.randint(0,700)
by=random.randint(20,120)
Bgsound1 = mixer.Sound("Sounds/bg.mp3")
BullletSound = mixer.Sound("Sounds/laser.mp3")
############## Class Players ##########################
class Player():
def __init__(self,x,y,width,height):
self.x=x
self.y=y
self.width=width
self.height=height
self.step=10
self.speed=10
self.isJumping= False
def drawplayer (self,screen):
screen.blit(rocket, (rockeet.x, rockeet.y))
def drawscreen (self,screen):
screen.blit(space, (0,0))
rockeet = Player(350,500,50,50)
########### Bullet #####################
class Bullet():
def __init__(self, x, y , y_change, x_change, state):
self.x=x
self.y= y
self.y_change=y_change
self.x_change= x_change
self.state= state
def fire(x,y):
Bulllet.state = "fire"
screen.blit(bullet,(x+16, y+10))
Bulllet=Bullet(350,500,20,0,"ready")
**def isCollision (ax1, by1, cx, dy):
distance = math.sqrt(math.pow(ax1 - cx, 2) + (math.pow(by1- dy, 2)))
if distance <27:
return True
else:
return False**
################## Enemy ########################
class Enemy():
def __init__(self, x ,y, stepx,stepy):
self.x = x
self.y=y
self.stepx=stepx
self.stepy=stepy
def drawenemy(screen):
screen.blit(enemy,(ax,by))
eneemy = Enemy(random.randint(0,700),random.randint(20,120),3,35)
################# Clussion#########################
score = 0
def isc (eneemy.x, eneemy.y, Bulllet.x, Bulllet.y ):
distance = math.sqrt(math.pow(eneemy.x - Bulllet.x, 2) + (math.pow(eneemy.y- Bulllet.y, 2)))
if distance <27:
return True
else:
return False
################### Game Loop #####################################
#Bgsound1.play(180)
while 1:
pygame.time.delay(10)
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
keys=pygame.key.get_pressed()
rockeet.drawscreen(screen)
Enemy.drawenemy(screen)
if keys[pygame.K_SPACE]:
if Bulllet.state == "ready":
BullletSound.play()
Bulllet.x=rockeet.x
Bulllet.y=rockeet.y
Bullet.fire(Bulllet.x, Bulllet.y)
if keys[pygame.K_LEFT] and rockeet.x - rockeet.step >= 0:
rockeet.x =rockeet.x-rockeet.step
if keys[pygame.K_RIGHT] and rockeet.x + rockeet.width +rockeet.step <= Swidth :
rockeet.x =rockeet.x+rockeet.step
if Bulllet.y <= 0:
Bulllet.y= 500
Bulllet.state = "ready"
if Bulllet.state == "fire":
Bullet.fire(Bulllet.x, Bulllet.y)
Bulllet.y -= Bulllet.y_change
########### Enemy Mouvement #########################################
collision = isCollision( eneemy.x, eneemy.y, Bulllet.x, Bulllet.y)
ax+= eneemy.stepx
if ax<= 0:
eneemy.stepx=3
by+= eneemy.stepy
elif ax >= 750:
eneemy.stepx=-3
by+= eneemy.stepy
if collision:
Bulllet.y= 500
Bulllet.state="ready"
eneemy.x= random.randint(0,700)
eneemy.y=random.randint(20,)
################## Collision ######################
################## OUTRO #######################################
rockeet.drawplayer(screen)
pygame.display.update()
the result:
def isCollision (eneemy.x, eneemy.y, Bulllet.x, Bulllet.y ):
^
SyntaxError: invalid syntax
The formal parameters of a function cannot be structure elements like eneemy.x. Either Change the names of the parameters:
def isc (enemy_x, enemy_y, bullet_x, bullet_y):
distance = math.sqrt(math.pow(enemy_x - bullet_x, 2) + (math.pow(enemy_y- bullet_y, 2)))
if distance <27:
return True
else:
return False
Or pass the objects to the function:
def isc (enemy, bullet):
distance = math.sqrt(math.pow(enemy.x - bullet.x, 2) + (math.pow(enemy.y- bullet.y, 2)))
if distance <27:
return True
else:
return False
collision = isCollision( eneemy.x, eneemy.y, Bulllet.x, Bulllet.y)
collision = isCollision(eneemy, Bulllet)
Note, that you can use math.hypot instead of math.sqrt and math.pow:
distance = math.sqrt(math.pow(enemy_x - bullet_x, 2) + (math.pow(enemy_y- bullet_y, 2)))
distance = math.hypot(enemy_x - bullet_x, enemy_y - bullet_y)

Generate enemies around the player from all directions randomly

The enemy are being generated from above the screen and then move toward player in the middle, I want to generate enemies randomly around the screen from all directions but not inside the screen directly and proceed to move towards the player and also enemy sprites are sometimes joining combining and moving together how to repel the enemy sprites.
I have tried changing x,y coordinates of enemy objects using a random range but sometimes they generate objects inside the play screen, I want enemies to generate outside the playing window.
class Mob(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load('enemy.png').convert_alpha()
self.image = pg.transform.smoothscale(pg.image.load('enemy.png'), (33, 33))
self.image_orig = self.image.copy()
self.radius = int(29 * .80 / 2)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(width - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speed = 4
self.rot = 0
self.rot_speed = 5
self.last_update = pg.time.get_ticks()
def rotate(self):
now = pg.time.get_ticks()
if now - self.last_update > 50:
self.last_update = now
self.rot = (self.rot + self.rot_speed) % 360
new_image = pg.transform.rotozoom(self.image_orig, self.rot, 1)
old_center = self.rect.center
self.image = new_image
self.rect = self.image.get_rect()
self.rect.center = old_center
def update(self):
self.rotate()
dirvect = pg.math.Vector2(rotator.rect.x - self.rect.x,
rotator.rect.y- self.rect.y)
if dirvect.length_squared() > 0:
dirvect = dirvect.normalize()
# Move along this normalized vector towards the player at current speed.
if dirvect.length_squared() > 0:
dirvect.scale_to_length(self.speed)
self.rect.move_ip(dirvect)
if self.rect.top > height + 10 or self.rect.left < -25 or self.rect.right > width + 20:
self.rect.x = random.randrange(width - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speed = random.randrange(1, 4)
[UPDATE]
This the remaining code:
import math
import random
import os
import pygame as pg
import sys
pg.init()
height = 650
width = 1200
os_x = 100
os_y = 45
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (os_x, os_y)
screen = pg.display.set_mode((width, height), pg.NOFRAME)
screen_rect = screen.get_rect()
background = pg.image.load('background.png').convert()
background = pg.transform.smoothscale(pg.image.load('background.png'), (width, height))
clock = pg.time.Clock()
running = True
font_name = pg.font.match_font('Bahnschrift', bold=True)
def draw_text(surf, text, size, x, y, color):
[...]
class Mob(pg.sprite.Sprite):
[...]
class Rotator(pg.sprite.Sprite):
def __init__(self, screen_rect):
pg.sprite.Sprite.__init__(self)
self.screen_rect = screen_rect
self.master_image = pg.image.load('spaceship.png').convert_alpha()
self.master_image = pg.transform.smoothscale(pg.image.load('spaceship.png'), (33, 33))
self.radius = 12
self.image = self.master_image.copy()
self.rect = self.image.get_rect(center=[width / 2, height / 2])
self.delay = 10
self.timer = 0.0
self.angle = 0
self.distance = 0
self.angle_offset = 0
def get_angle(self):
mouse = pg.mouse.get_pos()
offset = (self.rect.centerx - mouse[0], self.rect.centery - mouse[1])
self.angle = math.degrees(math.atan2(*offset)) - self.angle_offset
old_center = self.rect.center
self.image = pg.transform.rotozoom(self.master_image, self.angle, 1)
self.rect = self.image.get_rect(center=old_center)
self.distance = math.sqrt((offset[0] * offset[0]) + (offset[1] * offset[1]))
def update(self):
self.get_angle()
self.display = 'angle:{:.2f} distance:{:.2f}'.format(self.angle, self.distance)
self.dx = 1
self.dy = 1
self.rect.clamp_ip(self.screen_rect)
def draw(self, surf):
surf.blit(self.image, self.rect)
def shoot(self, mousepos):
dx = mousepos[0] - self.rect.centerx
dy = mousepos[1] - self.rect.centery
if abs(dx) > 0 or abs(dy) > 0:
bullet = Bullet(self.rect.centerx, self.rect.centery, dx, dy)
all_sprites.add(bullet)
bullets.add(bullet)
There's not much informations to go by here, but you probably need to check the x and y range your play window has and make sure the random spawn coordinates you generate are outside of it:
In your init:
# These are just example min/max values. Maybe pass these as arguments to your __init__ method.
min_x = min_y = -1000
max_x = max_y = 1000
min_playwindow_x = min_playwindow_y = 500
max_playwindow_x = max_playwindow_y = 600
self.x = (random.randrange(min_x, min_playwindow_x), random.randrange(max_playwindow_x, max_x))[random.randrange(0,2)]
self.y = (random.randrange(min_y, min_playwindow_y), random.randrange(max_playwindow_y, max_y))[random.randrange(0,2)]
This solution should work in basically any setup. For x and y it generates a tuple of values outside the playing window. Then a coinflip decides on the value. This will only spawn mobs that are diagonally outside the playing field, but it will always generate valid random coordinates.
Another approach would be just generating as many random variables as needed to get a valid pair like this:
while min_playingwindow_x <= self.x <= max_playingwindow_x and
min_playingwindow_y <= self.y <= max_playingwindow_y:
# While within screen(undesired) calculate new random positions
self.x = random.randrange(min_x, max_x)
self.y = random.randrange(min_y, max_y)
This can be really slow however if your valid amount of positions is (for example) only 1% of the total positions.
IF you need something really fleshed out, you need to know the corners of both your map and the rectangle that is actually displayed, which is I assume smaller than the entire map(otherwise you cannot spawn enemies outside your view.
(0,0)
+----------------------+
| A |
|-----+-----------+----|
| D | W | B |
|-----+-----------+----|
| C |
+----------------------+(max_x, max_y)
In this diagram W is the window that is acutally visible to the player, and A,B,C,D together are the part of your map that is not currently visible. Since you only want to spawn mobs outside the player's view, you'll need to make sure that the coordinates you generate are inside your map and outside your view:
def generate_coordinates_outside_of_view(map_width=1000, map_height=1000, view_window_top_left=(100, 100),
view_width=600, view_height=400):
"""
A very over the top way to generate coordinates outside surrounding a rectangle within a map almost without bias
:param map_width: width of map in pixels (note that 0,0 on the map is top left)
:param map_height: height of map in pixels
:param view_window_top_left: top left point(2-tuple of ints) of visible part of map
:param view_width: width of view in pixels
:param view_height: height of view in pixels
"""
from random import randrange
# generate 2 samples for each x and y, one guaranteed to be random, and one outside the view for sure.
x = (randrange(0, map_width), (randrange(0, view_window_top_left[0]),
randrange(view_window_top_left[0] + view_width, map_width))[randrange(0, 2)])
y = (randrange(0, map_height), (randrange(0, view_window_top_left[1]),
randrange(view_window_top_left[1] + view_height, map_height))[randrange(0, 2)])
# now we have 4 values. To get a point outside our view we have to return a point where at least 1 of the
# values x/y is guaranteed to be outside the view.
if randrange(0, 2) == 1: # to be almost completely unbiased we randomize the check
selection_x = randrange(0, 2)
selection_y = randrange(0, 2) if selection_x == 1 else 1
else:
selection_y = randrange(0, 2)
selection_x = randrange(0, 2) if selection_y == 1 else 1
return x[selection_x], y[selection_y]
HTH

Sprite moving faster left than right pygame

I think I'm having a rounding problem causing my sprite to move faster/jump farther while moving left.
My sprites update method is calling move, which calls move_single_axis for each axis. Inside this I'm doing some collision detection where I rely on pygame's rect class to both detect the collision, and set the new position.
I think this is the problem but I'm uncertain how to get around the rounding issue because pygame's rect uses integers under the hood.
Here's the update code:
def update(self, dt, game):
self.calc_grav(game, dt)
self.animate(dt, game)
self._old_position = self._position[:]
self.move(dt, game)
self.rect.topleft = self._position
def move(self, dt, game):
# Move each axis separately. Note that this checks for collisions both times.
dx = self.velocity[0]
dy = self.velocity[1]
if dx != 0:
self.move_single_axis(dx, 0, dt)
if dy != 0:
self.move_single_axis(0, dy, dt)
def move_single_axis(self, dx, dy, dt):
#print("hero_destination: ({}, {})".format(dx *dt, dy *dt))
self._position[0] += dx * dt
self._position[1] += dy * dt
#print("Game walls: {}".format(game.walls))
self.rect.topleft = self._position
body_sensor = self.get_body_sensor()
for wall in game.walls:
if body_sensor.colliderect(wall.rect):
if dx > 0: # Moving right; Hit the left side of the wall
#print(" -- Moving right; Hit the left side of the wall")
self.rect.right = wall.rect.left
if dx < 0: # Moving left; Hit the right side of the wall
#print(" -- Moving left; Hit the right side of the wall")
self.rect.left = wall.rect.right - self.COLLISION_BOX_OFFSET
if dy > 0: # Moving down; Hit the top side of the wall
#print(" -- Moving down; Hit the top side of the wall")
self.rect.bottom = wall.rect.top
if dy < 0: # Moving up; Hit the bottom side of the wall
#print(" -- Moving up; Hit the bottom side of the wall")
self.rect.top = wall.rect.bottom
self._position[0] = self.rect.topleft[0]
self._position[1] = self.rect.topleft[1]
Here is the whole source(https://github.com/davidahines/python_sidescroller):
import os.path
import pygame
from pygame.locals import *
from pytmx.util_pygame import load_pygame
import pyscroll
import pyscroll.data
from pyscroll.group import PyscrollGroup
# define configuration variables here
RESOURCES_DIR = 'data'
HERO_JUMP_HEIGHT = 180
HERO_MOVE_SPEED = 200 # pixels per second
GRAVITY = 1000
MAP_FILENAME = 'maps/dungeon_0.tmx'
# simple wrapper to keep the screen resizeable
def init_screen(width, height):
screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
return screen
# make loading maps a little easier
def get_map(filename):
return os.path.join(RESOURCES_DIR, filename)
# make loading images a little easier
def load_image(filename):
return pygame.image.load(os.path.join(RESOURCES_DIR, filename))
class Hero(pygame.sprite.Sprite):
""" Our Hero
The Hero has three collision rects, one for the whole sprite "rect" and
"old_rect", and another to check collisions with walls, called "feet".
The position list is used because pygame rects are inaccurate for
positioning sprites; because the values they get are 'rounded down'
as integers, the sprite would move faster moving left or up.
Feet is 1/2 as wide as the normal rect, and 8 pixels tall. This size size
allows the top of the sprite to overlap walls. The feet rect is used for
collisions, while the 'rect' rect is used for drawing.
There is also an old_rect that is used to reposition the sprite if it
collides with level walls.
"""
def __init__(self, map_data_object):
pygame.sprite.Sprite.__init__(self)
self.STATE_STANDING = 0
self.STATE_WALKING = 1
self.STATE_JUMPING = 2
self.FRAME_DELAY_STANDING =1
self.FRAME_DELAY_WALKING = 1
self.FRAME_DELAY_JUMPING = 1
self.FACING_RIGHT = 0
self.FACING_LEFT = 1
self.MILLISECONDS_TO_SECONDS = 1000.0
self.COLLISION_BOX_OFFSET = 8
self.time_in_state = 0.0
self.current_walking_frame = 0
self.current_standing_frame = 0
self.current_jumping_frame = 0
self.load_sprites()
self.velocity = [0, 0]
self.state = self.STATE_STANDING
self.facing = self.FACING_RIGHT
self._position = [map_data_object.x, map_data_object.y]
self._old_position = self.position
self.rect = pygame.Rect(8, 0, self.image.get_rect().width - 8, self.image.get_rect().height)
def set_state(self, state):
if self.state != state:
self.state = state
self.time_in_state = 0.0
def load_sprites(self):
self.spritesheet = Spritesheet('data/art/platformer_template_g.png')
standing_images = self.spritesheet.images_at((
pygame.Rect(0, 0, 32, 32),
), colorkey= (0,255,81))
self.standing_images = []
for standing_image in standing_images:
self.standing_images.append(standing_image.convert_alpha())
self.image = self.standing_images[self.current_standing_frame]
#property
def position(self):
return list(self._position)
#position.setter
def position(self, value):
self._position = list(value)
def get_floor_sensor(self):
return pygame.Rect(self.position[0]+self.COLLISION_BOX_OFFSET, self.position[1]+2, self.rect.width -self.COLLISION_BOX_OFFSET, self.rect.height)
def get_ceiling_sensor(self):
return pygame.Rect(self.position[0]+self.COLLISION_BOX_OFFSET, self.position[1]-self.rect.height, self.rect.width, 2)
def get_body_sensor(self):
return pygame.Rect(self.position[0]+self.COLLISION_BOX_OFFSET, self.position[1], self.rect.width -self.COLLISION_BOX_OFFSET, self.rect.height)
def calc_grav(self, game, dt):
""" Calculate effect of gravity. """
floor_sensor = self.get_floor_sensor()
collidelist = floor_sensor.collidelist(game.walls)
hero_is_airborne = collidelist == -1
if hero_is_airborne:
if self.velocity[1] == 0:
self.velocity[1] = GRAVITY * dt
else:
self.velocity[1] += GRAVITY * dt
def update(self, dt, game):
self.calc_grav(game, dt)
self._old_position = self._position[:]
self.move(dt, game)
def move(self, dt, game):
# Move each axis separately. Note that this checks for collisions both times.
dx = self.velocity[0]
dy = self.velocity[1]
if dx != 0:
self.move_single_axis(dx, 0, dt)
if dy != 0:
self.move_single_axis(0, dy, dt)
self.rect.topleft = self._position
def move_single_axis(self, dx, dy, dt):
#print("hero_destination: ({}, {})".format(dx *dt, dy *dt))
self._position[0] += dx * dt
self._position[1] += dy * dt
#print("Game walls: {}".format(game.walls))
self.rect.topleft = self._position
body_sensor = self.get_body_sensor()
for wall in game.walls:
if body_sensor.colliderect(wall.rect):
if dx > 0: # Moving right; Hit the left side of the wall
self.rect.right = wall.rect.left
if dx < 0: # Moving left; Hit the right side of the wall
self.rect.left = wall.rect.right - self.COLLISION_BOX_OFFSET
if dy > 0: # Moving down; Hit the top side of the wall
self.rect.bottom = wall.rect.top
if dy < 0: # Moving up; Hit the bottom side of the wall
self.rect.top = wall.rect.bottom
self._position[0] = self.rect.topleft[0]
self._position[1] = self.rect.topleft[1]
class Wall(pygame.sprite.Sprite):
"""
A sprite extension for all the walls in the game
"""
def __init__(self, map_data_object):
pygame.sprite.Sprite.__init__(self)
self._position = [map_data_object.x, map_data_object.y]
self.rect = pygame.Rect(
map_data_object.x, map_data_object.y,
map_data_object.width, map_data_object.height)
#property
def position(self):
return list(self._position)
#position.setter
def position(self, value):
self._position = list(value)
class Spritesheet(object):
def __init__(self, filename):
try:
self.sheet = pygame.image.load(filename).convert()
except pygame.error:
print ('Unable to load spritesheet image: {}').format(filename)
raise SystemExit
# Load a specific image from a specific rectangle
def image_at(self, rectangle, colorkey = None):
"Loads image from x,y,x+offset,y+offset"
rect = pygame.Rect(rectangle)
image = pygame.Surface(rect.size).convert()
image.blit(self.sheet, (0, 0), rect)
if colorkey is not None:
if colorkey is -1:
colorkey = image.get_at((0,0))
image.set_colorkey(colorkey, pygame.RLEACCEL)
return image
# Load a whole bunch of images and return them as a list
def images_at(self, rects, colorkey = None):
"Loads multiple images, supply a list of coordinates"
return [self.image_at(rect, colorkey) for rect in rects]
class QuestGame(object):
""" This class is a basic game.
It also reads input and moves the Hero around the map.
Finally, it uses a pyscroll group to render the map and Hero.
This class will load data, create a pyscroll group, a hero object.
"""
filename = get_map(MAP_FILENAME)
def __init__(self):
# true while running
self.running = False
self.debug = False
# load data from pytmx
self.tmx_data = load_pygame(self.filename)
# setup level geometry with simple pygame rects, loaded from pytmx
self.walls = list()
self.npcs = list()
for map_object in self.tmx_data.objects:
if map_object.type == "wall":
self.walls.append(Wall(map_object))
elif map_object.type == "guard":
print("npc load failed: reimplement npc")
#self.npcs.append(Npc(map_object))
elif map_object.type == "hero":
self.hero = Hero(map_object)
# create new data source for pyscroll
map_data = pyscroll.data.TiledMapData(self.tmx_data)
# create new renderer (camera)
self.map_layer = pyscroll.BufferedRenderer(map_data, screen.get_size(), clamp_camera=True, tall_sprites=1)
self.map_layer.zoom = 2
self.group = PyscrollGroup(map_layer=self.map_layer, default_layer=3)
# add our hero to the group
self.group.add(self.hero)
def draw(self, surface):
# center the map/screen on our Hero
self.group.center(self.hero.rect.center)
# draw the map and all sprites
self.group.draw(surface)
if(self.debug):
floor_sensor_rect = self.hero.get_floor_sensor()
ox, oy = self.map_layer.get_center_offset()
new_rect = floor_sensor_rect.move(ox * 2, oy * 2)
pygame.draw.rect(surface, (255,0,0), new_rect)
def handle_input(self, dt):
""" Handle pygame input events
"""
poll = pygame.event.poll
event = poll()
while event:
if event.type == QUIT:
self.running = False
break
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
self.running = False
break
# this will be handled if the window is resized
elif event.type == VIDEORESIZE:
init_screen(event.w, event.h)
self.map_layer.set_size((event.w, event.h))
event = poll()
# using get_pressed is slightly less accurate than testing for events
# but is much easier to use.
pressed = pygame.key.get_pressed()
floor_sensor = self.hero.get_floor_sensor()
floor_collidelist = floor_sensor.collidelist(self.walls)
hero_is_airborne = floor_collidelist == -1
ceiling_sensor = self.hero.get_ceiling_sensor()
ceiling_collidelist = ceiling_sensor.collidelist(self.walls)
hero_touches_ceiling = ceiling_collidelist != -1
if pressed[K_l]:
print("airborne: {}".format(hero_is_airborne))
print("hero position: {}, {}".format(self.hero.position[0], self.hero.position[1]))
print("hero_touches_ceiling: {}".format(hero_touches_ceiling))
print("hero_is_airborne: {}".format(hero_is_airborne))
if hero_is_airborne == False:
#JUMP
if pressed[K_SPACE]:
self.hero.set_state(self.hero.STATE_JUMPING)
# stop the player animation
if pressed[K_LEFT] and pressed[K_RIGHT] == False:
# play the jump left animations
self.hero.velocity[0] = -HERO_MOVE_SPEED
elif pressed[K_RIGHT] and pressed[K_LEFT] == False:
self.hero.velocity[0] = HERO_MOVE_SPEED
self.hero.velocity[1]= -HERO_JUMP_HEIGHT
elif pressed[K_LEFT] and pressed[K_RIGHT] == False:
self.hero.set_state(self.hero.STATE_WALKING)
self.hero.velocity[0] = -HERO_MOVE_SPEED
elif pressed[K_RIGHT] and pressed[K_LEFT] == False:
self.hero.set_state(self.hero.STATE_WALKING)
self.hero.velocity[0] = HERO_MOVE_SPEED
else:
self.hero.state = self.hero.STATE_STANDING
self.hero.velocity[0] = 0
def update(self, dt):
""" Tasks that occur over time should be handled here
"""
self.group.update(dt, self)
def run(self):
""" Run the game loop
"""
clock = pygame.time.Clock()
self.running = True
from collections import deque
times = deque(maxlen=30)
try:
while self.running:
dt = clock.tick(60) / 1000.
times.append(clock.get_fps())
self.handle_input(dt)
self.update(dt)
self.draw(screen)
pygame.display.flip()
except KeyboardInterrupt:
self.running = False
if __name__ == "__main__":
pygame.init()
pygame.font.init()
screen = init_screen(800, 600)
pygame.display.set_caption('Test Game.')
try:
game = QuestGame()
game.run()
except:
pygame.quit()
raise
I ripped out everything except for the hero and the QuestGame class and could see the incorrect movement, so the problem was not caused by pyscroll (unless there are more issues).
The reason for the movement problems is that you set the self._position in the update method of the hero to the topleft coords of the rect.
self._position[0] = self.rect.topleft[0]
self._position[1] = self.rect.topleft[1]
pygame.Rects can only store integers and truncate floats that you assign to them, so you shouldn't use them to update the actual position of the hero. Here's a little demonstration:
>>> pos = 10
>>> rect = pygame.Rect(10, 0, 5, 5)
>>> pos -= 1.4 # Move left.
>>> rect.x = pos
>>> rect
<rect(8, 0, 5, 5)> # Truncated the actual position.
>>> pos = rect.x # Pos is now 8 so we moved 2 pixels.
>>> pos += 1.4 # Move right.
>>> rect.x = pos
>>> rect
<rect(9, 0, 5, 5)> # Truncated.
>>> pos = rect.x
>>> pos # Oops, we only moved 1 pixel to the right.
9
The self._position is the exact position and should only be set to one of the rect's coords if the hero collides with a wall or another obstacle (because the rect is used for the collision detection).
Move the two mentioned lines into the if body_sensor.colliderect(wall.rect): clause in the wall collision for loop and it should work correctly.
for wall in game.walls:
if body_sensor.colliderect(wall.rect):
if dx > 0: # Moving right; Hit the left side of the wall
self.rect.right = wall.rect.left
self._position[0] = self.rect.left
if dx < 0: # Moving left; Hit the right side of the wall
self.rect.left = wall.rect.right - self.COLLISION_BOX_OFFSET
self._position[0] = self.rect.left
if dy > 0: # Moving down; Hit the top side of the wall
self.rect.bottom = wall.rect.top
self._position[1] = self.rect.top
if dy < 0: # Moving up; Hit the bottom side of the wall
self.rect.top = wall.rect.bottom
self._position[1] = self.rect.top

Pygame health / maxhealth = 0.0 and not 0.66 [duplicate]

This question already has answers here:
How can I force division to be floating point? Division keeps rounding down to 0?
(11 answers)
Closed 5 years ago.
I am trying to make a healthbar in pygame and it is supposed to show in percent. The formula that I'm using is (lives/mlives)*100, mlives stands for max lives.
When the game begins, player has 3 lives to start with. Each time the shielding gets down to 0 or less, -1 is removed from lives. So on the first run everything works perfectly fine. But when -1 if removed from lives the health bar shows 0 and i have tried to print the formula on terminal without multiplying it with 100 to see what it would show when lives / mlives.
now when player has 3 lives the formula gives 1.0 and that is 100%
when the player has 2 lives the formula gives 0.0 instead of 0.66 which is 66%
player has 3 lives
player has 2 lives
game.py -- funciton name is draw_health_bar
#!/usr/bin/python
import os, sys, player, enemy, config, random
from os import path
try:
import pygame
from pygame.locals import *
except ImportError, err:
print 'Could not load module %s' % (err)
sys.exit(2)
# main variables
FPS, BGIMG = 30, 'FlappyTrollbg.jpg'
# initialize game
pygame.init()
screen = pygame.display.set_mode((config.WIDTH,config.HEIGHT))
pygame.display.set_caption("FlappyTroll - Python2.7")
# background
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((255,255,255))
img_dir = path.join(path.dirname(__file__), 'img')
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.width = config.WIDTH
self.height = config.HEIGHT
self.image = pygame.image.load(path.join(img_dir,image_file)).convert_alpha()
self.image = pygame.transform.scale(self.image,(self.width,self.height))
self.rect = self.image.get_rect()
#self.rect.left, self.rect.top = location
self.rect.x, self.rect.y = location
self.speedx = 5
def update(self):
self.rect.x -= self.speedx
if self.rect.x <= -config.WIDTH:
self.rect.x = config.WIDTH
def draw_shield_bar(surf, x, y, percent):
if percent <= 0:
percent = 0
bar_length = 100
bar_height = 10
fill = (percent / 100) * bar_length
bar_outline = pygame.Rect(x,y,bar_length,bar_height)
bar_filled = pygame.Rect(x,y, fill, bar_height)
pygame.draw.rect(surf, config.green, bar_filled)
pygame.draw.rect(surf, config.white, bar_outline, 2)
def draw_health_bar(surf, x, y, percent):
bar_length = 100
bar_height = 10
fill = float(percent) * bar_length
bar_outline = pygame.Rect(x,y,bar_length,bar_height)
bar_filled = pygame.Rect(x,y, fill, bar_height)
pygame.draw.rect(surf, config.red, bar_filled)
pygame.draw.rect(surf, config.white, bar_outline, 2)
def draw_text(surf, text, size, x, y):
font_ = pygame.font.SysFont("Arial", size)
show_kills = font_.render(text, True, config.white)
surf.blit(show_kills, (x, y))
# blitting
screen.blit(background,(0,0))
pygame.display.flip()
# clock for FPS settings
clock = pygame.time.Clock()
def newSprite(group, obj):
group.add(obj)
def main():
all_sprites = pygame.sprite.Group()
bgs = pygame.sprite.Group()
creature = pygame.sprite.Group()
attack = pygame.sprite.Group()
eattack = pygame.sprite.Group()
bgs.add(Background(BGIMG, [0,0]))
bgs.add(Background(BGIMG, [config.WIDTH,0]))
troll = player.FlappyTroll()
creature.add(troll)
for i in range(0,4):
newSprite(all_sprites, enemy.TrollEnemy())
# variable for main loop
running = True
# init umbrella
# event loop
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for e in all_sprites:
if (pygame.time.get_ticks() - e.starttime) >= e.delay:
newEAtk = enemy.EnemyAttack(e.rect.x, e.rect.y)
eattack.add(newEAtk)
e.starttime = pygame.time.get_ticks()
keys = pygame.key.get_pressed()
if (keys[pygame.K_SPACE]) and (pygame.time.get_ticks() - troll.starttime) >= troll.delay:
newAtk = player.FlappyAttack(troll.rect.x, troll.rect.y)
attack.add(newAtk)
troll.starttime = pygame.time.get_ticks()
b_gets_hit = pygame.sprite.groupcollide(eattack, attack, True, True)
p_gets_hit_eatk = pygame.sprite.groupcollide(eattack, creature, True, False)
gets_hit = pygame.sprite.groupcollide(all_sprites, attack, True, True)
p_gets_hit = pygame.sprite.groupcollide(all_sprites, creature, True, False)
if gets_hit or p_gets_hit:
newEnemy = enemy.TrollEnemy()
newSprite(all_sprites, newEnemy)
for p in creature:
if p_gets_hit or p_gets_hit_eatk:
troll.shield -= random.randint(1,5)*1.5
if troll.shield <= 0:
troll.lives -= 1
troll.shield = 100
if troll.lives == 0:
print "#--- GAME OVER ---#"
break
screen.fill([255, 255, 255])
# update
bgs.update()
all_sprites.update()
creature.update()
attack.update()
eattack.update()
# draw
bgs.draw(screen)
all_sprites.draw(screen)
creature.draw(screen)
attack.draw(screen)
eattack.draw(screen)
draw_shield_bar(screen, 5, 5, troll.shield)
draw_health_bar(screen, 5, 20, (troll.lives/troll.mlives))
draw_text(screen, ("Lives: "+str(troll.lives)), 20, (config.WIDTH / 2), 0)
print float(troll.lives/troll.mlives)
# flip the table
pygame.display.flip()
pygame.quit()
if __name__ == '__main__':
main()
player.py (object name is Troll in game.py)
import pygame, config
from pygame.locals import *
from os import path
from random import randint
img_dir = path.join(path.dirname(__file__), 'img')
class FlappyTroll(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.width = 64
self.height = 64
self.image = pygame.image.load(path.join(img_dir,"flappytroll.png")).convert_alpha()
self.image = pygame.transform.scale(self.image,(self.width,self.height))
self.rect = self.image.get_rect()
self.rect.x = self.width*2
self.rect.y = config.HEIGHT/2-self.height
self.speedy = 5
self.starttime = pygame.time.get_ticks()
self.delay = 500
self.shield = 100
self.lives = 3
self.mlives = 3
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
self.rect.y -= self.speedy*2
elif self.rect.y < config.HEIGHT-self.height*1.5:
self.rect.y += self.speedy
Try dividing by 3.0 rather than just 3. I'm pretty sure this will work as it is what Python 2's integer division is coded as. Hope this helps :-) P.S. If you do any more additions, subtractions, divisions or multiplications, be sure to put .0 at the end unless you want another D.P. e.g. 2 * 7.0.

Why does the collide happen many times?

I'm using openGL with Pyglet which is a python package. I have to use this language and this package, it is for an assignment. I have a basic Brickbreaker style game that is basically a keep it up game.
I create a ball and a paddle.
I separately create a bounding box class that will be used to create the hit boxes for each object.
class BoundBox:
def __init__ (self, width, height, pos):
self.width = width
self.height = height
self.pos = pos
Then I create the boxes themselves
paddleBox = BoundBox(200, 20, [0,0])
ballBox = BoundBox(40, 40, [236, 236])
In the update function which is running # pyglet.clock.schedule_interval(update,1/100.0) I call the checkcollide() function which checks if there was a collision:
def checkForCollide():
global collides
if overlap(ballBox, paddleBox):
vel = 1.05
ballVel[0] = ballVel[0]*vel #Ball speeds up when collide happens
ballVel[1] = ballVel[1]*vel
ballVel[1] = -ballVel[1] #Change direction on collision
ballPos[1] = -ballPos[1]
collides += 1 #counts how many collides happen
The overlap function is returning a boolean if there's an overlap in hit boxes:
def overlap(box1, box2):
return (box1.pos[0] <= box2.width + box2.pos[0]) and(box1.width + box1.pos[0] >= box2.pos[0]) and(box1.pos[1] <= box2.height + box2.pos[1]) and(box1.height + box1.pos[1] >= box2.pos[1])
pos[0] is the minimum x
width is the maximum x
pos[1] is the minimum y
height is the maximum y
When I run the game and the ball hits the paddle it flickers about 15 times and increments the collides counter every time it detects a hit. Collides then prints in the console. Why does this flicker happen? How do I stop it?
Here is the program's full code (you must have pyglet installed to run it):
import sys, time, math
from pyglet.gl import *
from euclid import *
from pyglet.window import key
from pyglet.clock import *
window = pyglet.window.Window(512,512)
quadric = gluNewQuadric()
ballPos = Vector2(256,256)
ballVel = Vector2(200,145)
x1 = 0
bar = pyglet.graphics.vertex_list(4, ('v2f', [0,0, 0,20, 200,0, 200,20]))
startTime = time.clock()
collides = 0
#pos is minx, miny
class BoundBox:
def __init__ (self, width, height, pos):
self.width = width
self.height = height
self.pos = pos
def overlap(box1, box2):
return (box1.pos[0] <= box2.width + box2.pos[0]) and(box1.width + box1.pos[0] >= box2.pos[0]) and(box1.pos[1] <= box2.height + box2.pos[1]) and(box1.height + box1.pos[1] >= box2.pos[1])
paddleBox = BoundBox(200, 20, [0,0])
ballBox = BoundBox(40, 40, [236, 236])
#window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT)
glPushMatrix()
glPushMatrix()
glColor3f(1,1,1)
glTranslatef(x1, 0, 0)
bar.draw(GL_TRIANGLE_STRIP)
glPopMatrix()
glTranslatef(ballPos[0], ballPos[1], 0)
glColor3f(1,0,0)
gluDisk(quadric, 0, 20, 32, 1)
glPopMatrix()
#window.event
def on_key_press(symbol, modifiers):
global x1
dist = 30
if symbol == key.RIGHT:
#print "right"
x1 += dist
elif symbol == key.LEFT:
#print "left"
x1 -= dist
def checkForBounce():
if ballPos[0] > 512.0:
ballVel[0] = -ballVel[0]
ballPos[0] = 512.0 - (ballPos[0] - 512.0)
elif ballPos[0] < 0.0:
ballVel[0] = -ballVel[0]
ballPos[0] = -ballPos[0]
if ballPos[1] > 512.0:
ballVel[1] = -ballVel[1]
ballPos[1] = 512.0 - (ballPos[1] - 512.0)
elif ballPos[1] < -100.0:
gameOver()
def gameOver():
global collides
'''global startTime
elapsed = (time.time() - startTime)
score = elapsed * .000000001
finalscore = '%.1f' % round(score, 1)'''
gostr = "GAME OVER"
print gostr
str = "Your score = "
print str
print collides
pyglet.app.exit()
def checkForCollide():
global collides
if overlap(ballBox, paddleBox):
vel = 1.05
ballVel[0] = ballVel[0]*vel #Ball speeds up when collide happens
ballVel[1] = ballVel[1]*vel
ballVel[1] = -ballVel[1] #Change direction on collision
ballPos[1] = -ballPos[1]
collides += 1 #counts how many collides happen
print collides
#glscale(0.5, 1, 1)
def update(dt):
global ballPos, ballVel, ballBox, x1, paddleBox
ballBox = BoundBox(40, 40, [ballPos[0], ballPos[1]])
paddleBox = BoundBox(200, 20, [x1,0])
#print paddleBox.pos
#print ballBox.pos
ballPos += ballVel * dt
checkForBounce()
checkForCollide()
pyglet.clock.schedule_interval(update,1/100.0)
pyglet.app.run()
I don't think you wanted to invert the position here:
def checkForCollide():
global collides
if overlap(ballBox, paddleBox):
vel = 1.05
ballVel[0] = ballVel[0]*vel #Ball speeds up when collide happens
ballVel[1] = ballVel[1]*vel
ballVel[1] = -ballVel[1] #Change direction on collision
ballPos[1] = -ballPos[1]
collides += 1 #counts how many collides happen
What were you trying to do with this line?
ballPos[1] = -ballPos[1]
I suspect that is why you are flickering.

Categories