This question already has answers here:
How to make smooth movement in pygame
(2 answers)
How can i make a block follow another block in pygame [duplicate]
(2 answers)
Closed 2 years ago.
So I followed the answers in another question asked on StackOverflow but it seems that I have missed something. I went ahead after reading the answer and copied the code and adjusted it to my variables and class names.
The following is the error code that Idle gives me:
Traceback (most recent call last):
File "D:\Programme (x86)\Python\Games\Zombie Game\Zombie Game_Test1.py", line 133, in <module>
Zombie.move_towards_Char(Char)
TypeError: move_towards_Char() missing 1 required positional argument: 'Char'
This is where I looked:
How to make an enemy follow the player in pygame?
import pygame
import turtle
import time
import math
import random
import sys
import os
pygame.init()
WHITE = (255,255,255)
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
BLACK = (0,0,0)
BGColor = (96,128,56)
ZColor = (225,0,0)
PColor = (0,0,255)
MOVE = 2.5
size = (1920, 1080)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Zombie Game")
class Char(pygame.sprite.Sprite):
def __init__(self, color, pos, radius, width):
super().__init__()
self.image = pygame.Surface([radius*2, radius*2])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
pygame.draw.circle(self.image, color, [radius, radius], radius, width)
self.rect = self.image.get_rect()
def moveRightP(self, pixels):
self.rect.x += pixels
pass
def moveLeftP(self, pixels):
self.rect.x -= pixels
pass
def moveUpP(self, pixels):
self.rect.y -= pixels
pass
def moveDownP(self, pixels):
self.rect.y += pixels
pass
class Zombie(pygame.sprite.Sprite):
def __init__(self2, color, pos, radius, width):
super().__init__()
self2.image = pygame.Surface([radius*2, radius*2])
self2.image.fill(WHITE)
self2.image.set_colorkey(WHITE)
pygame.draw.circle(self2.image, color, [radius, radius], radius, width)
self2.rect = self2.image.get_rect()
self2.rect.center = pos
def move_towards_Char(self2, Char):
dx, dy = self2.rect.x - Char.rect.x, self2.rect.y - Char.rect.y
dist = math.hypot(dx, dy)
dx, dy = dx / dist, dy / dist
self2.rect.x += dx * self2.speed
self2.rect.y += dy * self2.speed
def moveRightZ(self2, pixels):
self2.rect.x += pixels
pass
def moveLeftZ(self2, pixels):
self2.rect.x -= pixels
pass
def moveUpZ(self2, pixels):
self2.rect.y -= pixels
pass
def moveDownZ(self2, pixels):
self2.rect.y += pixels
pass
all_sprites_list = pygame.sprite.Group()
playerChar = Char(PColor, [0, 0], 15, 0)
playerChar.rect.x = 960
playerChar.rect.y = 505
all_sprites_list.add(playerChar)
carryOn = True
clock = pygame.time.Clock()
zombie_list = []
zombie_rad = 15
zombie_dist = (200, 900)
next_zombie_time = pygame.time.get_ticks() + 10000
zombie_list = []
zombie_rad = 15
zombie_dist = (200, 900)
next_zombie_time = 10000
while carryOn:
for event in pygame.event.get():
if event.type==pygame.QUIT:
carryOn=False
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_x:
carryOn=False
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
playerChar.moveLeftP(MOVE)
if keys[pygame.K_d]:
playerChar.moveRightP(MOVE)
if keys[pygame.K_w]:
playerChar.moveUpP(MOVE)
if keys[pygame.K_s]:
playerChar.moveDownP(MOVE)
current_time = pygame.time.get_ticks()
if current_time > next_zombie_time:
next_zombie_time = current_time + 2000
on_screen_rect = pygame.Rect(zombie_rad, zombie_rad, size[0]-2*zombie_rad, size[1]-2*zombie_rad)
zombie_pos = (-1, -1)
while not on_screen_rect.collidepoint(zombie_pos):
dist = random.randint(*zombie_dist)
angle = random.random() * math.pi * 2
p_pos = (playerChar.rect.centerx, playerChar.rect.centery)
zombie_pos = (p_pos[0] + dist * math.sin(angle), p_pos[1] + dist * math.cos(angle))
new_pos = (random.randrange(0, size[0]), random.randrange(0, size[1]))
new_zombie = Zombie(RED, zombie_pos, zombie_rad, 0)
zombie_list.append(new_zombie)
screen.fill(BGColor)
screen.blit(playerChar.image,playerChar.rect)
for zombie in zombie_list:
screen.blit(zombie.image,zombie.rect)
pygame.display.flip()
clock.tick(60)
pygame.quit()
The major issue is, that you do the zombie movement calculations with integral data types. If the movement of a zombie is 1 pixel and the movement is diagonal, then the x and y component of the movement is < 1. Using an integral data type, this may results in 0 movement, because of truncating to int. Note the members of pygame.Rect are integral values.
You've to switch to floating point values to solve the issue. Use pygame.math.Vector2 to do the calculations.
Add a member pos of type Vector2 to the class Zombie which stores the floating point position of the zombie:
class Zombie(pygame.sprite.Sprite):
def __init__(self2, color, pos, radius, width):
super().__init__()
self2.image = pygame.Surface([radius*2, radius*2])
self2.image.fill(WHITE)
self2.image.set_colorkey(WHITE)
pygame.draw.circle(self2.image, color, [radius, radius], radius, width)
self2.rect = self2.image.get_rect()
self2.speed = 1
self2.pos = pygame.Vector2(pos[0], pos[1])
# [...]
Add a new method draw to the class Zombie, which draws (blit) a zombie at the position pos:
class Zombie(pygame.sprite.Sprite):
# [...]
def draw(self2):
self2.rect.center = (int(round(self2.pos.x)), int(round(self2.pos.y)))
screen.blit(self2.image, self2.rect)
Do the calculation of the movement of the zombie based on Vector2. Ensure that the distance between the player and the zombie is greater than 0 and that the zombie does not step over of the position of the player (min(len, self2.speed)):
class Zombie(pygame.sprite.Sprite):
# [...]
def move_towards_Char(self2, Char):
deltaVec = pygame.Vector2(Char.rect.center) - self2.pos
len = deltaVec.length()
if len > 0:
self2.pos += deltaVec/len * min(len, self2.speed)
Call the methods move_towards_Char and draw for each zombie, in the main loop of the application:
while carryOn:
for event in pygame.event.get():
if event.type==pygame.QUIT:
carryOn=False
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_x:
carryOn=False
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
playerChar.moveLeftP(MOVE)
if keys[pygame.K_d]:
playerChar.moveRightP(MOVE)
if keys[pygame.K_w]:
playerChar.moveUpP(MOVE)
if keys[pygame.K_s]:
playerChar.moveDownP(MOVE)
current_time = pygame.time.get_ticks()
if current_time > next_zombie_time:
next_zombie_time = current_time + 2000
on_screen_rect = pygame.Rect(zombie_rad, zombie_rad, size[0]-2*zombie_rad, size[1]-2*zombie_rad)
zombie_pos = (-1, -1)
while not on_screen_rect.collidepoint(zombie_pos):
dist = random.randint(*zombie_dist)
angle = random.random() * math.pi * 2
p_pos = (playerChar.rect.centerx, playerChar.rect.centery)
zombie_pos = (p_pos[0] + dist * math.sin(angle), p_pos[1] + dist * math.cos(angle))
new_pos = (random.randrange(0, size[0]), random.randrange(0, size[1]))
new_zombie = Zombie(RED, zombie_pos, zombie_rad, 0)
zombie_list.append(new_zombie)
# update all the positions of the zombies
for zombie in zombie_list:
zombie.move_towards_Char(playerChar)
screen.fill(BGColor)
screen.blit(playerChar.image,playerChar.rect)
# draw all the zombies
for zombie in zombie_list:
zombie.draw()
pygame.display.flip()
clock.tick(60)
L{Zombie.move_towards_Char} is a self method. You need to create object of Zombie class passing the required args mentioned in L{Zombie.init}.
Something like below:
zm = Zombie(color=<color>, pos=<pos>, radius=<radius>, width=<width>)
zm.move_towards_Char(Char)
Related
I'm making a racing game with almost realistic cars in pygame. I made the cars succesfully and I want to make the car move a bit slower when it's not touching the drive way. For that, I want to use pygame mask collisions. Instead of using bounding boxes, they use the pixels and count them based on how many pixels of something are touching something. This all works correctly, but when I use the cam.follow_object(player) function, the game behaves very weird and there rise unexplainable bugs.
I included each individual picture and file, but I also included it on github because I know it's annoying to download all images one by one.
Assets:
app.py:
import pygame
from pygame.locals import *
from random import choice, randint
import math, car, ui, pySave, camera, level
pygame.init()
# Basic Variables
screen_width = 1000
screen_height = 1000
fps = 80
screen = pygame.display.set_mode((screen_width, screen_height))
clock = pygame.time.Clock()
cam = camera.Camera(1, screen)
#cam.zoom_game(1.55)
stage = "home"
# Loading Images
images = {
"start" : pygame.transform.scale(pygame.image.load("road_texture.png"), (200*cam.zoom, 75*cam.zoom)).convert_alpha(),
"bcg" : pygame.transform.scale(pygame.image.load("map2.png"), (1000*cam.zoom, 1000*cam.zoom)).convert_alpha(),
"car" : pygame.image.load("car.png").convert_alpha()
}
# Functionality Functions
def play_game():
global stage
stage = "game"
# Instansiating stuff
pos_save = pySave.Save_Manager("saved_info", "pos")
start_button = ui.Button((500,500), images["start"], play_game, text="startgame")
test_car = car.CarSprite(images["car"], 400, 400,[2, 2.3, 2.7], 0.013, rotations=360, camera=cam)
speedometer = ui.TextWithBackground((100,50), (100,950), images["start"])
level = level.Level(images["bcg"], test_car,cam,pos = (0,100))
# Groups and Lists
car_group = pygame.sprite.Group()
# Adding to Groups
car_group.add(test_car)
# Game Functions
def render():
if stage == "home":
screen.fill((255,255,255))
start_button.update("Start Game")
start_button.draw(screen)
elif stage == "game":
screen.fill((0,76,18))
level.update(screen)#screen.blit(images["bcg"], (0-cam.scroll[0],0-cam.scroll[1]))
car_group.update()
car_group.draw(screen)
#cam.follow(test_car, )
#speedometer.draw(screen)
speedometer.update(screen,round(test_car.speed*60,2) , " km/h")
#cam.zoom_game(2)
def collisions():
pass
run = True
while run:
clock.tick_busy_loop(80)
render()
collisions()
for event in pygame.event.get():
if event.type == QUIT:
run = False
pos_save.save("x", test_car.rect.x)
pos_save.save("y", test_car.rect.y)
pos_save.apply()
print(f"Quit with {round(clock.get_fps(), 2)} FPS")
quit()
pygame.display.update()
car.py:
import pygame
from pygame.locals import *
from random import choice, randint
import math, camera
class CarSprite( pygame.sprite.Sprite ):
def __init__( self, car_image, x, y, max_speed, accel,rot_speed=[1.8, 2.2, 3] ,rotations=360, camera="" ):
pygame.sprite.Sprite.__init__(self)
self.rotated_images = {}
self.min_angle = ( 360 / rotations )
for i in range( rotations ):
rotated_image = pygame.transform.rotozoom( pygame.transform.scale(car_image, (12*camera.zoom,25*camera.zoom)), 360-90-( i*self.min_angle ), 1 )
self.rotated_images[i*self.min_angle] = rotated_image
self.min_angle = math.radians( self.min_angle )
self.image = self.rotated_images[0]
self.rect = self.image.get_rect()
self.rect.center = ( x, y )
self.reversing = False
self.heading = 0
self.speed = 0
self.velocity = pygame.math.Vector2( 0, 0 )
self.position = pygame.math.Vector2( x, y )
self.speed_hardening = 1
self.acc = False
self.steer_strenght_acc = rot_speed[0]
self.steer_strength_normal= rot_speed[1]
self.steer_strength_drift= rot_speed[2]
self.steer_strength = rot_speed[1]
self.drift_point = 0.00
self.accel = accel
self.max_speed = self.accel * 150
self.cam = camera
def turn( self, ori=1 ):
if self.speed > 0.1 or self.speed < 0. :
self.heading += math.radians( self.steer_strenght * ori )
image_index = int((self.heading + self.min_angle / 2) / self.min_angle) % len(self.rotated_images)
image = self.rotated_images[image_index]
if self.image is not image:
x,y = self.rect.center
self.image = image
self.rect = self.image.get_rect()
self.rect.center = (x,y)
def accelerate( self):
self.speed += self.accel
def brake( self ):
if self.speed > 0:
self.speed -= self.accel * 3
if abs(self.speed) < 0.1:
self.speed = 0
self.velocity.from_polar((self.speed, math.degrees(self.heading)))
def move(self):
keys = pygame.key.get_pressed()
if keys[K_w]:
self.accelerate()
if keys[K_s]:
self.brake()
if keys[K_a]:
self.turn(-1)
if keys[K_d]:
self.turn()
if keys[pygame.K_s] or keys [pygame.K_w]:
self.acc = True
else:
self.acc = False
def update( self ):
self.move()
self.speed_hardening = self.speed / 100
self.speed = round(self.speed, 3)
if self.acc:
self.steer_strenght = self.steer_strenght_acc
else:
self.steer_strenght = self.steer_strength_normal
if self.speed > self.max_speed and not pygame.key.get_pressed()[K_SPACE] and not self.drift_point > 0:
self.speed += self.accel / 4 - self.speed_hardening / 2
if self.speed > self.max_speed * 1.8:
self.speed = self.max_speed * 1.8
if self.speed < -self.max_speed / 4:
self.speed = -self.max_speed / 4
if not pygame.key.get_pressed()[K_SPACE]:
self.velocity.from_polar((self.speed, math.degrees(self.heading)))
self.speed += self.drift_point
self.drift_point -= 0.0001
if self.drift_point < 0:
self.drift_point = 0
self.speed -= self.drift_point
else:
self.steer_strenght = self.steer_strength_drift
self.drift_point += 0.0001
if self.drift_point > self.accel / 1.5:
self.drift_point = self.accel / 1.5
if not self.acc and not self.speed < 0.04:
self.speed -= (self.accel / 2) + self.speed_hardening
if self.speed < 0.05:
self.speed = 0
self.position += self.velocity
self.rect.center = self.position
level.py
import pygame
from pygame.locals import *
from random import choice, randint
import math, car, ui, pySave, camera
screen_width = 1000
screen_height = 1000
class Level:
def __init__(self,image, car, camera, pos=(0,0)):
# Convert the images to a more suitable format for faster blitting
self.image = image.convert()
self.road = image
self.cam = camera
self.x,self.y = pos
self.bcg_mask = pygame.mask.from_surface(self.road)
self.car = car
self.get_car_mask()
def update(self, screen):
# Calculate the overlap between the car mask and the background mask
overlap = self.bcg_mask.overlap_mask(
self.car_mask,
(self.car.rect.x, self.car.rect.y)
)
self.x = 0 - self.cam.scroll[0]
self.y = 0 - self.cam.scroll[1]
# Fill the screen with the background color
screen.blit(self.road.convert_alpha(), (self.x, self.y))
screen.blit(overlap.to_surface(unsetcolor=(0,0,0,0), setcolor=(255,255,255,255)), (self.x, self.y))
# Print the overlap count to the console
print(overlap.count())
def get_car_mask(self):
# Convert the car image to a more suitable format for faster blitting
carimg = self.car.image.convert()
carimg.set_colorkey((0,0,0))
self.carimg = carimg
self.car_mask = pygame.mask.from_surface(self.carimg)
camera.py:
import pygame
from pygame.locals import *
class Camera:
def __init__(self, speed, screen):
self.scroll = [5,5]
self.speed = speed
self.screen = screen
self.zoom = 1
def move_on_command(self):
keys = pygame.key.get_pressed()
if keys[K_UP]:
self.scroll[1] -= self.speed
if keys[K_DOWN]:
self.scroll[1] += self.speed
if keys[K_RIGHT]:
self.scroll[0] += self.speed
if keys[K_LEFT]:
self.scroll[0] -= self.speed
def follow(self, obj, speed=12):
#self.scroll[0], self.scroll[1] = obj.rect.x, obj.rect.y
if (obj.rect.x - self.scroll[0]) != self.screen.get_width()/2:
self.scroll[0] += ((obj.rect.x - (self.scroll[0] + self.screen.get_width()/2)))
if obj.rect.y - self.scroll[1] != self.screen.get_height()/2:
self.scroll[1] += ((obj.rect.y - (self.scroll[1] + self.screen.get_height()/2)))
def zoom_game(self, zoom):
self.zoom = zoom
ui.py :
import pygame
from pygame.locals import *
from random import choice, randint
class Button:
def __init__(self, pos, image, action, click_times=1, dissapear=True , text="", textcolor = (255,255,255), fontsize=30):
self.image = image
self.rect = self.image.get_rect(center=pos)
self.clicked = False
self.clicked_times = click_times
self.dissapear = dissapear
self.dont_draw = False
self.function = action
self.text= text
if not self.text == "":
self.font = pygame.font.SysFont("Arial", fontsize, False, False)
self.color = textcolor
def update(self, var=""):
pressed = pygame.mouse.get_pressed()[0]
if not self.text == "":
self.text = self.font.render(f"{var}", 1, self.color)
self.text_rect = self.text.get_rect(center = self.rect.center)
if pressed and not self.clicked and not self.clicked_times <= 0 and self.rect.collidepoint(pygame.mouse.get_pos()):
self.function()
self.clicked = True
self.clicked_times -= 1
if not pressed:
self.clicked = False
if self.clicked_times <= 0:
if self.dissapear:
self.dont_draw = True
def draw(self, screen):
if not self.dont_draw:
screen.blit(self.image, (self.rect.x,self.rect.y))
screen.blit(self.text, (self.text_rect.x, self.text_rect.y))
class TextWithBackground:
def __init__(self, image_size, pos, image, fontsize=30, colour=(255, 255, 255)):
self.image = pygame.transform.scale(image, image_size)
self.rect = self.image.get_rect(center=pos)
self.font = pygame.font.SysFont("Arial", fontsize, False, False)
self.color = colour
self.text = self.font.render("", 1, self.color)
self.other_text = None
self.other_rect = None
def update(self, screen, variable, othertext=""):
self.text = self.font.render(f"{variable}", 1, self.color)
self.text_rect = self.text.get_rect(center=self.rect.center)
if othertext:
self.other_text = self.font.render(f"{othertext}", 1, self.color)
self.other_rect = self.other_text.get_rect(topleft=(self.rect.center[0] - 5, self.rect.center[1] - 5))
screen.blit(self.image, (self.rect.x, self.rect.y))
screen.blit(self.text, (self.rect.x, self.rect.y))
if self.other_text:
screen.blit(self.other_text, (self.other_rect.x, self.other_rect.y))
Here you can find my images:
car.png
map2.png
road_texture.png
See PyGame collision with masks. You need to calculate the offset between the car and the map when you get the overlap area of the masks:
class Level:
# [...]
def update(self, screen):
self.x = 0 - self.cam.scroll[0]
self.y = 0 - self.cam.scroll[1]
# Calculate the overlap between the car mask and the background mask
offset = (self.car.rect.x - self.x, self.car.rect.y - self.y)
overlap = self.bcg_mask.overlap_mask(self.car_mask, offset)
# Fill the screen with the background color
screen.blit(self.road.convert_alpha(), (self.x, self.y))
screen.blit(overlap.to_surface(unsetcolor=(0,0,0,0), setcolor=(255,255,255,255)), (self.x, self.y))
# Print the overlap count to the console
print(overlap.count())
This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Problem with Pygame movement acceleration, platformer game
(1 answer)
Closed 1 year ago.
I tried to make the enemies(yellow box) follow the player(red box) with a constant velocity from all directions.
The problem of my code is the speed of the enemy from x+(Right) and y+(Bottom) axis to the player is slower than x-(Left) and y-(Above).
Image: https://i.stack.imgur.com/qJNp7.png
I think the problem is in here. I can't find it. It may be somewhere else.
for enemy_types in Global_Obj[2]:
zetas = math.sqrt((enemy_types.rect.center[1] - FirstPlayer.rect.center[1])**2 + (FirstPlayer.rect.center[0] - enemy_types.rect.center[0])**2)
print(enemy_types.rect.center)
x,y = enemy_types.rect.center
x += dt * enemy_types.speed * (FirstPlayer.rect.center[0] - enemy_types.rect.center[0]) / zetas
y += dt * enemy_types.speed * (FirstPlayer.rect.center[1] - enemy_types.rect.center[1]) / zetas
enemy_types.rect.center = (x,y)
Here is all of my code.
# -*- coding: utf-8 -*-
"""
Created on Wed Sep 8 21:56:06 2021
#author: Toon
"""
import pygame
import cv2
import numpy as np
import math
import random
import time
dt = 0.01
window = (1280, 720)
win = pygame.display.set_mode(window)
background = pygame.Surface(window)
pygame.display.set_caption("First Game")
run = True
class playerIO():
def __init__(self):
self.name = 'Player'
self.width = 30
self.height = 30
self.speed = 1000
self.color = (255,0,0)
self.HPwidth = 50
self.HPheight = 8
self.HPoffset = 10
self.MAXHP = 1000
self.HP = self.MAXHP
self.HPregeneration_per_sec = 5
self.MANAwidth = 50
self.MANAheight = 8
self.MANAoffset = 10
self.MAXMANA = 3000
self.MANA = self.MAXMANA
self.MANAregeneration_per_sec = 10
self.image = pygame.Surface([self.width, self.height])
self.image.fill(self.color)
self.rect = self.image.get_rect(topleft=(80, 80))
self.bullet_delay = [0, 0, 0]
def draw(self):
self.HPbar_high = self.rect.y-self.HPheight-self.HPoffset
self.MANAbar_high = self.HPbar_high-self.MANAheight-self.MANAoffset
pygame.draw.rect(win, self.color, self.rect)
pygame.draw.rect(win, (255,255,255), (self.rect.x + self.width/2 - self.HPwidth/2, self.HPbar_high, self.HPwidth, self.HPheight))
pygame.draw.rect(win, (0,255,0), (self.rect.x + self.width/2 - self.HPwidth/2, self.HPbar_high, self.HPwidth*self.HP/self.MAXHP, self.HPheight))
pygame.draw.rect(win, (255,255,255), (self.rect.x + self.width/2 - self.MANAwidth/2, self.MANAbar_high, self.MANAwidth, self.MANAheight))
pygame.draw.rect(win, (0,0,255), (self.rect.x + self.width/2 - self.MANAwidth/2, self.MANAbar_high, self.MANAwidth*self.MANA/self.MAXMANA, self.MANAheight))
class BulletIO():
def __init__(self):
self.MANA_usage = 5
self.zeta = 0
self.width = 15
self.height = 15
self.speed = 1600
self.damage = 8
self.color = (0,255,0)
self.image = pygame.Surface([self.width, self.height])
self.image.fill(self.color)
self.rect = self.image.get_rect()
self.reload_delay = 0.2 #shot delay
def variant(self,X,Y, Zeta):
self.rect.x = X
self.rect.y = Y
self.zeta = Zeta
def draw(self):
pygame.draw.rect(win, self.color, self.rect)
class LaserIO():
def __init__(self):
self.MANA_usage = 10
self.zeta = 0
self.width = 8
self.height = 8
self.speed = 500
self.damage = 20
self.color = (0,0,160)
self.image = pygame.Surface([self.width, self.height])
self.image.fill(self.color)
self.rect = self.image.get_rect()
self.reload_delay = 0 #shot delay
def variant(self,X,Y, Zeta):
self.rect.x = X
self.rect.y = Y
self.zeta = Zeta
def draw(self):
pygame.draw.rect(win, self.color, self.rect)
class LaserBeamIO():
def __init__(self):
self.MANA_usage_per_sec = 1000
self.zeta = 0
self.width = 8
self.height = 8
self.speed = 500
self.damage = 20
self.color = (0,160,160)
self.image = pygame.Surface([self.width, self.height])
self.image.fill(self.color)
self.rect = self.image.get_rect()
self.reload_delay = 5 #shot delay
self.charge_time = 0
self.beam_distance = 60
self.max_charge_time = 10
self.charge_size_per_sec = 20
self.charge_damage_per_sec = 100
def charge_beam(self, player, Zeta):
self.charge_time+=dt
self.width += self.charge_size_per_sec * dt
self.height += self.charge_size_per_sec * dt
self.damage += self.charge_damage_per_sec * dt
self.image = pygame.Surface([self.width, self.height])
self.image.fill(self.color)
self.rect = self.image.get_rect()
self.rect.center = (player[0] + self.beam_distance * math.cos(Zeta), player[1] + self.beam_distance * math.sin(Zeta))
self.zeta = Zeta
def draw(self):
pygame.draw.rect(win, self.color, self.rect)
class EnemyIO():
def __init__(self):
self.name = 'enemy'
self.width = 15
self.height = 15
self.zeta = 0
self.speed = 200
self.color = (255,255,0)
self.MAXHP = 16
self.regeneration = 0.2
self.HPwidth = 20
self.HPheight = 5
self.HPoffset = 8
self.HP = self.MAXHP
self.damage = 10
self.image = pygame.Surface([self.width, self.height])
self.image.fill(self.color)
self.rect = self.image.get_rect(topleft=(20, 20))
def randompos(self, spawn_offset = 200):
while 1 :
try:
rand_pole = random.randint(1,4)
if rand_pole == 1 or rand_pole == 2:
self.rect.x = random.randint(FirstPlayer.rect.x+1+spawn_offset, window[0]-self.width)
else:
self.rect.x = random.randint(1,FirstPlayer.rect.x-spawn_offset)
if rand_pole == 1 or rand_pole == 4:
self.rect.y = random.randint(FirstPlayer.rect.y+1+spawn_offset, window[1]-self.height)
else:
self.rect.x = random.randint(1,FirstPlayer.rect.y-spawn_offset)
return 0
except:
pass
def draw(self):
self.HPbar_high = self.rect.y-self.HPheight-self.HPoffset
pygame.draw.rect(win, self.color, self.rect)
pygame.draw.rect(win, (255,255,255), (self.rect.x + self.width/2 - self.HPwidth/2, self.HPbar_high, self.HPwidth, self.HPheight))
pygame.draw.rect(win, (0,255,0), (self.rect.x + self.width/2 - self.HPwidth/2, self.HPbar_high, self.HPwidth*self.HP/self.MAXHP, self.HPheight))
def direction(mouse, obj):
#y = 1 clockwise
zeta = math.atan2((mouse[1]-obj[1]),(mouse[0]-obj[0]))
return zeta
def enemy_spawn(Enemy_max = 30, slow_rate = 2):
num = len(Global_Obj[2])
if num < Enemy_max:
# random spawn enemy 1/100
rand = random.randint(1, int((num+1)*slow_rate))
if rand == 1:
ene = EnemyIO()
ene.randompos()
Global_Obj[2].append(ene)
#Global_Obj[0] are player obj type, Global_Obj[1] are bullet obj type, Global_Obj[2] are enemy obj type, Global_Obj[3] are beam obj type
Global_Obj = [[],[],[],[]]
FirstPlayer = playerIO()
Global_Obj[0].append(FirstPlayer)
win.blit(background,(0, 0))
mouse_pos = (-1,-1)
beam_load = False
pygame.init()
# define the RGB value for white,
# green, blue colour .
text_color = (0, 0, 0)
white = (255, 255, 255)
# set the pygame window name
pygame.display.set_caption('Game')
# create a font object.
# 1st parameter is the font file
# which is present in pygame.
# 2nd parameter is size of the font
font = pygame.font.Font('freesansbold.ttf', 32)
# create a text surface object,
# on which text is drawn on it.
text = font.render('Game start', True, text_color)
# create a rectangular object for the
# text surface object
textRect = text.get_rect()
# set the center of the rectangular object.
textRect.center = (window[0] // 2, window[1] // 2)
inteface_run = True
# infinite loop
while inteface_run:
# completely fill the surface object
# with white color
win.fill(white)
# copying the text surface object
# to the display surface object
# at the center coordinate.
win.blit(text, textRect)
# iterate over the list of Event objects
# that was returned by pygame.event.get() method.
for event in pygame.event.get():
if pygame.mouse.get_pressed()[0] or event.type == pygame.QUIT:
inteface_run = False
# Draws the surface object to the screen.
pygame.display.update()
pygame.time.delay(1000)
font = pygame.font.Font('freesansbold.ttf', 16)
end_time = 300 # sec
t0= time.time()
while run:
pygame.display.flip()
pygame.time.delay(int(dt*1000))
# player move event
keys = pygame.key.get_pressed()
if keys[pygame.K_ESCAPE]:
run = False
if keys[pygame.K_SPACE] :
FirstPlayer.rect.x, FirstPlayer.rect.y = (80,80)
if (keys[pygame.K_a] or keys[pygame.K_LEFT]):
FirstPlayer.rect.x, FirstPlayer.rect.y = (FirstPlayer.rect.x - FirstPlayer.speed * dt, FirstPlayer.rect.y)
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
FirstPlayer.rect.x, FirstPlayer.rect.y = (FirstPlayer.rect.x + FirstPlayer.speed * dt, FirstPlayer.rect.y)
if keys[pygame.K_w] or keys[pygame.K_UP]:
FirstPlayer.rect.x, FirstPlayer.rect.y = (FirstPlayer.rect.x, FirstPlayer.rect.y - FirstPlayer.speed * dt)
if keys[pygame.K_s] or keys[pygame.K_DOWN]:
FirstPlayer.rect.x, FirstPlayer.rect.y = (FirstPlayer.rect.x, FirstPlayer.rect.y + FirstPlayer.speed * dt)
if FirstPlayer.rect.x < 0:
FirstPlayer.rect.x = 0
if FirstPlayer.rect.x+FirstPlayer.width > window[0]:
FirstPlayer.rect.x = window[0] - FirstPlayer.width
if FirstPlayer.rect.y < 0:
FirstPlayer.rect.y = 0
if FirstPlayer.rect.y+FirstPlayer.height > window[1]:
FirstPlayer.rect.y = window[1] - FirstPlayer.height
#player HP regen
if FirstPlayer.HP < FirstPlayer.MAXHP:
FirstPlayer.HP +=FirstPlayer.HPregeneration_per_sec*dt
if FirstPlayer.HP > FirstPlayer.MAXHP:
FirstPlayer.HP = FirstPlayer.MAXHP
#player MANA regen
if FirstPlayer.MANA < FirstPlayer.MAXMANA:
FirstPlayer.MANA +=FirstPlayer.MANAregeneration_per_sec*dt
if FirstPlayer.MANA > FirstPlayer.MAXMANA:
FirstPlayer.MANA = FirstPlayer.MAXMANA
#bullet reload time
for i in range(len(FirstPlayer.bullet_delay)):
if FirstPlayer.bullet_delay[i]>0:
FirstPlayer.bullet_delay[i]-= dt
if FirstPlayer.bullet_delay[i]<0:
FirstPlayer.bullet_delay[i] =0
# bullet click event
mouse_pos = pygame.mouse.get_pos()
if pygame.mouse.get_pressed()[0] and FirstPlayer.MANA >= BulletIO().MANA_usage and FirstPlayer.bullet_delay[0]==0:
bullet = BulletIO()
FirstPlayer.MANA -= bullet.MANA_usage
zeta = direction(mouse_pos, FirstPlayer.rect.center)
bullet.variant(*FirstPlayer.rect.center, zeta)
FirstPlayer.bullet_delay[0] = bullet.reload_delay
Global_Obj[1].append(bullet)
if pygame.mouse.get_pressed()[2] and FirstPlayer.MANA >= LaserIO().MANA_usage and FirstPlayer.bullet_delay[1]==0:
laser = LaserIO()
FirstPlayer.MANA -= laser.MANA_usage
zeta = direction(mouse_pos, FirstPlayer.rect.center)
laser.variant( *FirstPlayer.rect.center, zeta)
FirstPlayer.bullet_delay[1] = laser.reload_delay
Global_Obj[1].append(laser)
# Global_Obj[1] bullet obj type movement
for bullet_type in Global_Obj[1]:
bullet_type.rect.x += dt * bullet_type.speed * math.cos(bullet_type.zeta)
bullet_type.rect.y += dt * bullet_type.speed * math.sin(bullet_type.zeta)
if bullet_type.rect.x > window[0] or bullet_type.rect.x < 0 or bullet_type.rect.y > window[1] or bullet_type.rect.y < 0:
Global_Obj[1].remove(bullet_type)
# Global_Obj[3] beam obj type movement
for bullet_type in Global_Obj[3]:
bullet_type.rect.x += dt * bullet_type.speed * math.cos(bullet_type.zeta)
bullet_type.rect.y += dt * bullet_type.speed * math.sin(bullet_type.zeta)
if bullet_type.rect.x > window[0] or bullet_type.rect.x < 0 or bullet_type.rect.y > window[1] or bullet_type.rect.y < 0:
Global_Obj[3].remove(bullet_type)
# enemy
enemy_spawn()
# Global_Obj[2] enemy obj type movement
for enemy_types in Global_Obj[2]:
zetas = math.sqrt((enemy_types.rect.center[1] - FirstPlayer.rect.center[1])**2 + (FirstPlayer.rect.center[0] - enemy_types.rect.center[0])**2)
print(enemy_types.rect.center)
x,y = enemy_types.rect.center
x += dt * enemy_types.speed * (FirstPlayer.rect.center[0] - enemy_types.rect.center[0]) / zetas
y += dt * enemy_types.speed * (FirstPlayer.rect.center[1] - enemy_types.rect.center[1]) / zetas
enemy_types.rect.center = (x,y)
# enemy-player damage check
for enemy_type in Global_Obj[2]:
if FirstPlayer.rect.colliderect(enemy_type.rect):
FirstPlayer.HP -= enemy_type.damage
Global_Obj[2].remove(enemy_type)
# bullet - enemy damage check
for bullet_type in Global_Obj[1]:
for enemy_type in Global_Obj[2]:
if bullet_type.rect.colliderect(enemy_type.rect):
enemy_type.HP -= bullet_type.damage
if enemy_type.HP<=0:
Global_Obj[2].remove(enemy_type)
Global_Obj[1].remove(bullet_type)
break
# beam - enemy damage check
for bullet_type in Global_Obj[3]:
for enemy_type in Global_Obj[2]:
if bullet_type.rect.colliderect(enemy_type.rect):
enemy_type.HP -= bullet_type.damage
if enemy_type.HP<=0:
Global_Obj[2].remove(enemy_type)
#end game
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 2 and FirstPlayer.bullet_delay[2]==0: # left mouse button
beam = LaserBeamIO()
Global_Obj[3].append(beam)
beam_load = True
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 2 and beam_load:
zeta = direction(mouse_pos, FirstPlayer.rect.center)
beam.charge_beam( FirstPlayer.rect.center, zeta)
FirstPlayer.bullet_delay[2] = beam.reload_delay
beam_load = False
if beam_load:
if FirstPlayer.MANA <= beam.MANA_usage_per_sec * dt:
zeta = direction(mouse_pos, FirstPlayer.rect.center)
beam.charge_beam( FirstPlayer.rect.center, zeta)
FirstPlayer.bullet_delay[2] = beam.reload_delay
beam_load = False
else:
FirstPlayer.MANA -= beam.MANA_usage_per_sec * dt
zeta = direction(mouse_pos, FirstPlayer.rect.center)
beam.charge_beam( FirstPlayer.rect.center, zeta)
if FirstPlayer.HP <= 0:
run = False
te = int(time.time() - t0)
if te >= end_time:
run = False
# draw obj
win.blit(background,(0, 0))
for obj_types in Global_Obj:
for obj in obj_types:
obj.draw()
text = font.render(f'timer:{te}/{end_time} sec', True, white)
# create a rectangular object for the
# text surface object
textRect = text.get_rect()
# set the center of the rectangular object.
textRect.center = (window[0] // 2, window[1]-40)
win.blit(text, textRect)
pygame.quit()
Feel free to give me any suggestions to improve my code. I'm very new to Pygame.
Your enemies do move at the same speed in all directions, which can be measured by adding a few lines:
# Global_Obj[2] enemy obj type movement
for enemy_types in Global_Obj[2]:
zetas = math.dist(enemy_types.rect.center, FirstPlayer.rect.center)
x, y = enemy_types.rect.center
dx = dt * enemy_types.speed * (FirstPlayer.rect.center[0] - enemy_types.rect.center[0]) / zetas
dy = dt * enemy_types.speed * (FirstPlayer.rect.center[1] - enemy_types.rect.center[1]) / zetas
x += dx
y += dy
print((dx ** 2 + dy ** 2) ** 0.5)
enemy_types.rect.center = (x, y)
The code consistently prints out 2.
I suspect that it has something to do with the amount of time it takes to evaluate negative/positive calculations, which can be measured using the time.perf_counter() method.
Tip: Your
zetas = math.sqrt((enemy_types.rect.center[1] - FirstPlayer.rect.center[1])**2 + (FirstPlayer.rect.center[0] - enemy_types.rect.center[0])**2)
can be replaced with
zetas = math.dist(enemy_types.rect.center, FirstPlayer.rect.center)
a much more efficient method.
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. [...]
When you do
enemy_types.rect.center = (x,y)
it is the same as you would do:
enemy_types.rect.center = (int(x), int(y))
The fraction component of the coordinate get lost. This causes that the movement to the left and to the top is faster than to the right and to the bottom.
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 of the rectangle:
class EnemyIO():
def __init__(self):
# [...]
self.x, self.y = self.rect.center
for enemy_types in Global_Obj[2]:
zetas = math.sqrt((enemy_types.rect.center[1] - FirstPlayer.rect.center[1])**2 + (FirstPlayer.rect.center[0] - enemy_types.rect.center[0])**2)
enemy_types.x += dt * enemy_types.speed * (FirstPlayer.rect.center[0] - enemy_types.rect.center[0]) / zetas
enemy_types.y += dt * enemy_types.speed * (FirstPlayer.rect.center[1] - enemy_types.rect.center[1]) / zetas
enemy_types.rect.center = round(enemy_types.x), round(enemy_types.y)
my first ever Python program has hit a block I don't think I have the knowledge to solve myself.
It's a controllable spaceship on a 2d surface, I want to add momentum / inertia
I have it so the ship keeps travelling on the vector it previously was, when the engine is stopped.
However I can only get it to 'snap' to the new vector it rotates to face instantly.
What I want to happen is that inertia vector slowly aligns with the new pointing vector as it accelerates- like rotational acceleration? ( I'm not too hot on the math ) - I can rotate the inertia vector , but I would need to compare it somehow with the new pointing vector , and modify it based upon their difference?
if anyone could advise as to how I might start to approach this, that would be great - I suspect I coming at this from completely the wrong way.
Heres some of the code ( be gentle please!)
the sprite used is this : - ship.png
import pygame
import sys
from math import sin, cos, pi, atan2
from pygame.locals import *
import random
from random import randint
from pygame.math import Vector2
import operator
"""solar system generator"""
"""set screen size and center and some global namespace colors for ease of use"""
globalalpha = 255
screenx = int(1200)
screeny = int(700)
centerx = int(screenx / 2)
centery = int(screeny / 2)
center = (centerx, centery)
black = ( 0, 0, 0)
white = (255, 255, 255)
red = (209, 2, 22)
TRANSPARENT = (255,0,255)
numstars = 150
DISPLAYSURF = pygame.display.set_mode((screenx, screeny), 0, 32)
clock = pygame.time.Clock()
globaltimefactor = 1
shipimage = pygame.image.load('ship.png').convert()
DISPLAYSURF.fill(black)
screen_rect = DISPLAYSURF.get_rect()
class Playership(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.imageorig = pygame.image.load('ship.png').convert_alpha()
self.startpos = (screen_rect.center)
self.image = self.imageorig.copy()
self.rect = self.imageorig.get_rect(center=self.startpos)
self.angle = 0
self.currentposx = 600
self.currentposy = 350
self.tuplepos = (self.currentposx, self.currentposy)
self.speed = 1
self.rotatespeed = 1.5
self.initialvec = (600, 0)
self.destination = 0
self.anglechange = 0
self.currentspeed = 0
self.maxspeed = 5
self.engineon = False
self.newvec = (600, 0)
self.newdestination = 0
self.acceleration = 0.015
self.inertiaspeed = 0
self.transitionalvec = self.initialvec
def get_angleafterstopping(self):
newvec = self.initialvec
self.newvec = newvec
def get_destinationafterstopping(self):
x_dist = self.newvec[0] - self.tuplepos[0]
y_dist = self.newvec[1] - self.tuplepos[1]
self.newdestination = atan2(-y_dist, x_dist) % (2 * pi)
def get_destination(self):
x_dist = self.initialvec[0] - self.tuplepos[0]
y_dist = self.initialvec[1] - self.tuplepos[1]
self.destination = atan2(-y_dist, x_dist) % (2 * pi)
def moveship(self):
if self.engineon is True:
self.currentspeed = self.currentspeed + self.acceleration
if self.currentspeed > self.maxspeed:
self.currentspeed = self.maxspeed
elif self.currentspeed < 0:
self.currentspeed = 0
self.inertiaspeed = self.currentspeed
elif self.engineon is False:
self.currentposx = self.currentposx + (cos(self.newdestination) * self.inertiaspeed * globaltimefactor)
self.currentposy = self.currentposy - (sin(self.newdestination) * self.inertiaspeed * globaltimefactor)
self.tuplepos = (self.currentposx, self.currentposy)
self.rect.center = self.tuplepos
return
self.get_destination()
self.currentposx = self.currentposx + (cos(self.destination) * self.currentspeed * globaltimefactor)
self.currentposy = self.currentposy - (sin(self.destination) * self.currentspeed * globaltimefactor)
self.tuplepos = (self.currentposx, self.currentposy)
self.rect.center = self.tuplepos
def rotateship(self, rotation):
self.anglechange = self.anglechange - (rotation * self.rotatespeed * globaltimefactor)
self.angle += (rotation * self.rotatespeed * globaltimefactor)
self.image = pygame.transform.rotate(self.imageorig, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
initialvec = self.tuplepos + Vector2(0, -600).rotate(self.anglechange * globaltimefactor)
initialvec = int(initialvec.x), int(initialvec.y)
self.initialvec = initialvec
myship = Playership()
all_sprites_list = pygame.sprite.Group()
all_sprites_list.add(myship)
firsttimedone = False
def main():
done = False
while not done:
keys_pressed = pygame.key.get_pressed()
if keys_pressed[pygame.K_LEFT]:
myship.rotateship(1)
if keys_pressed[pygame.K_RIGHT]:
myship.rotateship(-1)
if keys_pressed[pygame.K_UP]:
myship.engineon = True
myship.moveship()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit();
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
myship.engineon = False
myship.currentspeed = 0
myship.get_angleafterstopping()
myship.get_destinationafterstopping()
DISPLAYSURF.fill(black)
all_sprites_list.update()
all_sprites_list.draw(DISPLAYSURF)
pygame.draw.line(DISPLAYSURF, white, (myship.tuplepos), (myship.initialvec))
pygame.draw.line(DISPLAYSURF, red, (myship.tuplepos), (myship.newvec))
pygame.display.flip()
if myship.engineon is False:
myship.moveship()
clock.tick(50)
pygame.display.set_caption("fps: " + str(clock.get_fps()))
if __name__ == '__main__':
pygame.init()
main()
pygame.quit(); sys.exit();
EDIT :
I fixed it : just required a better understanding of vectors
ship starts off with acceleration and velocity both stated as vectors.
self.position = vec(screenx / 2, screeny / 2)
self.vel = vec(0, 0)
self.acceleration = vec(0, -0.2) # The acceleration vec points upwards from the starting ship position
rotating the ship rotates that vector in place
self.acceleration.rotate_ip(self.angle_speed)
self.angle += self.angle_speed
self.image = pygame.transform.rotate(self.imageorig, -self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
accelerating is this :
self.vel += self.acceleration * self.enginepower * globaltimefactor
updating position :
self.position += self.vel
self.rect.center = self.position
I was making it harder than it needed to be, velocity needed to be constant until acted upon by the rotated acceleration vector. I didn't know how to add vectors together etc.
I fixed it : just required a better understanding of vectors
ship starts off with acceleration and velocity both stated as vectors.
self.position = vec(screenx / 2, screeny / 2)
self.vel = vec(0, 0)
self.acceleration = vec(0, -0.2) # The acceleration vec points upwards from the starting ship position
rotating the ship rotates that vector in place
self.acceleration.rotate_ip(self.angle_speed)
self.angle += self.angle_speed
self.image = pygame.transform.rotate(self.imageorig, -self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
accelerating is this :
self.vel += self.acceleration * self.enginepower * globaltimefactor
updating position :
self.position += self.vel
self.rect.center = self.position
I was making it harder than it needed to be, velocity needed to be constant until acted upon by the rotated acceleration vector. I didn't know how to add vectors together etc.
So I'm trying to make a game where there are 2 objects and am trying to make it so that my program knows when the 2 objects collide.
if players[0].mask.overlap(players[1].mask, offset):
print("collided")`
My objects both rotate clockwise and counterclockwise and have masks both for the head and the body. (I have 2 images which are both the same size but one contains only the head and another contains only the body, in order to use mask.from_surface().)
def turn(self, event):
self.surface = self.ojSurface
if event == 0:
self.deg -= self.turnSpd
elif event == 1:
self.deg += self.turnSpd
self.surface = pygame.transform.rotate(self.surface, self.deg)
self.newPos = (self.pos[0] - self.surface.get_width() / 2, self.pos[1] - self.surface.get_height() / 2)
self.mask = pygame.mask.from_surface(self.surface)
However, what I find is when I try to check for collision when they are rotated the mask isn't in the same place as the image. It should update the mask every frame through the move function which is called every loop
def move(self):
self.pos[0] = pygame.mouse.get_pos()[0]
self.pos[1] = pygame.mouse.get_pos()[1]
self.newPos = (self.pos[0] - self.surface.get_width() / 2, self.pos[1] - self.surface.get_height() / 2)
self.mask = pygame.mask.from_surface(self.surface)
Faulty Hit Detection
If you want to peruse what I have so far here it is:
import pygame
class Player:
def __init__(self, pos,surface,screen):
self.pos = [pos[0],pos[1]]
self.newPos = (pos[0] - surface.get_width()/2, pos[1] - surface.get_height()/2)
self.deg = 0
self.surface = surface
self.ojSurface = surface
self.screen = screen
self.mask = pygame.mask.from_surface(self.surface)
def turn(self, event):
self.surface = self.ojSurface
#clockwise
if event == 0:
self.deg -= 0.1
#counter clockwise
elif event == 1:
self.deg += 0.1
self.surface = pygame.transform.rotate(self.surface, self.deg)
#resetting pos and mask
self.newPos = (self.pos[0] - self.surface.get_width() / 2, self.pos[1] - self.surface.get_height() / 2)
self.mask = pygame.mask.from_surface(self.surface)
def move(self):
self.pos[0] = pygame.mouse.get_pos()[0]
self.pos[1] = pygame.mouse.get_pos()[1]
self.newPos = (self.pos[0] - self.surface.get_width() / 2, self.pos[1] - self.surface.get_height() / 2)
self.mask = pygame.mask.from_surface(self.surface)
def draw(self):
self.screen.blit(self.surface, self.newPos)
screenRes = (640,480)
screen = pygame.display.set_mode(screenRes)
closed = False
players = [Player((320,240),pygame.image.load("body.png"), screen),Player((480,240),pygame.image.load("body.png"), screen)]
controls = [[pygame.K_a,pygame.K_s],[pygame.K_k,pygame.K_l]]
while not closed:
screen.fill((0, 0, 0))
keys = pygame.key.get_pressed()
offset = (int(players[0].newPos[0] - players[1].newPos[0]), int(players[0].newPos[1] - players[1].newPos[1]))
#collision
if players[0].mask.overlap(players[1].mask, offset):
print("collided")
#controls
for i in range(len(players)):
if keys[controls[i][0]]:
players[i].turn(0)
if keys[controls[i][1]]:
players[i].turn(1)
players[i].draw()
players[0].move()
pygame.display.update()
for event in pygame.event.get():
# standard quit
if event.type == pygame.QUIT:
pygame.display.quit()
closed = True
pygame.quit()
You're calculating the offset in the wrong order. Subtract players[0]'s position from players[1]'s position:
offset = (
int(players[1].newPos[0] - players[0].newPos[0]),
int(players[1].newPos[1] - players[0].newPos[1]),
)
So I've reviewed your GitHub code, and while I'm not completely sure (I skimmed your code, looking for certain methods being called), I believe that you're not updating your masks every frame. Somewhere in your code, you'll have to update the masks every frame/loop, allowing for the collision to work properly. Try to update all the masks, the head, body and the one in the Player class. Hope this helps!
I am trying to create a simple game with pygame. Currently I am working on having multiple "star" objects and have them move down the screen. How do I make the class take the "i1" without overwriting it every time the game loops runs?
Class code:
class Stars(object): # ok, no way around classes now
def __init__(self, i, i1):
self.stars_speed = 2
self.stars_radius = 5
#self.stars_n = 5
self.color = white
self.i1 = i1
self.i = i
def draw(self):
pygame.draw.circle(gameDisplay, self.color, (self.i, self.i1),self.stars_radius)
print(self.i1)
#print(i1)
if self.i1 < display_height:
self.i1 = 200
print ("moving star")
print(self.i1)
else:
self.i1 = 0
self.i = random.randint(0,display_width)
print ("respawning star")
It is not clear to me why you are doing this:
if self.i1 < display_height:
self.i1 = 200
This part will make the circle stay at the same place always.
I wrote the following code that does the task you described in the question. Hope it will help you.
import pygame
import random
display_width = 800
display_height = 600
radius = 10
white = (255, 255, 255)
black = (0, 0, 0)
pygame.init()
gameDisplay = pygame.display.set_mode((display_width, display_height))
def stars(x, y, color):
pygame.draw.circle(gameDisplay, color, [x, y], radius)
def game_loop():
circle_x = random.randint(radius, display_width - radius)
circle_y = -radius
while True:
circle_y += 5
if circle_y > display_height + radius:
circle_y = 0 - radius
circle_x = random.randint(0, display_width)
gameDisplay.fill(black)
stars(circle_x, circle_y, white)
pygame.display.update()
game_loop()