Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
Hi i am working on a ice sumo type game and currently in works of a singleplayer mode. My plan is to have the player fend off against bots that spawn maybe every 5 seconds and their sole purpose is to hit the player, would be as simple as going towards the player's current (x,y). The score is based on how many bots the player has knocked out until he dies. The thing is I don't really know how to code it because my collision detection was based off a multiplayer mode where I compared player 1 and player 2's x and y's. Could anyone help me ? Thanks. This is what I have currently everything works right now and I am in works of the "singleplayer" function where I need to include the ai and stuff.
import pygame, sys, time
from pygame.locals import *
import random
import math
#Colors
colorRed=pygame.Color(241,59,62)
colorPurple=pygame.Color(200,254,249)
colorBlue=pygame.Color(52, 207, 235)
colorGreen=pygame.Color(100,182,100)
colorWhite=pygame.Color(255,250,250)
colorBlack=pygame.Color(0,0,0)
colorOrange=pygame.Color(242,164,0)
colorBrown=pygame.Color(148,103,58)
colorBlue2=pygame.Color(37, 45, 204)
#Dimensions
w=800
h=600
pygame.init()
fpsClock=pygame.time.Clock()
screen=pygame.display.set_mode((w,h))
pygame.display.set_caption ('Ice Fighters')
centerX=w//2
centerY=h//2
point=3
pointx=240
gameEnd_p=1
gameEnd_px=340
option=False
x1_wins=0
x2_wins=0
# ~~~~~~~~~~~~~~~~~~~~~ TEXT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bigFont = pygame.font.Font('shark_party.ttf',64)
smallFont = pygame.font.Font("shark_party.ttf",32)
tinyFont = pygame.font.Font('shark_party.ttf', 16)
titleText = bigFont.render("Ice Fighters", True, colorBlue)
multiplayerText=smallFont.render("Multiplayer", True, colorBlue2,)
singleplayerText=smallFont.render("Singleplayer", True, colorBlue2)
intructionsText=smallFont.render("How to Play", True, colorBlue2)
leaderboardText=smallFont.render("Leaderboard", True, colorBlue2)
game_endText=bigFont.render("GAME OVER", True, colorRed)
replayText=smallFont.render ("Play Again", True, colorBlue)
returnText=smallFont.render ("Main Menu", True, colorBlue)
menu_upText=tinyFont.render ("= Menu Up", True, colorBlack)
menu_downText=tinyFont.render ('= Menu Down',True, colorBlack)
menu_returnText=tinyFont.render ('= ENTER', True, colorBlack)
def game_end():
while True:
screen.fill(colorBlack)
for event in pygame.event.get():
#Game Exit
if event.type== QUIT:
pygame.quit()
sys.exit()
if event.type==KEYDOWN:
if event.key==K_s:
global gameEnd_p
global gameEnd_px
if gameEnd_p==1:
gameEnd_px+=75
gameEnd_p-=1
elif gameEnd_p==0:
gameEnd_px-=75
gameEnd_p+=1
if event.key==K_w:
if gameEnd_p==1:
gameEnd_px+=75
gameEnd_p-=1
elif gameEnd_p==0:
gameEnd_px-=75
gameEnd_p+=1
if event.key==K_RETURN:
if gameEnd_p==0:
title()
if gameEnd_p==1:
game()
screen.blit(game_endText,(230,50))
screen.blit(replayText,(300,325))
screen.blit(returnText,(300,400))
pygame.draw.circle(screen,colorRed,(280,gameEnd_px),10)
pygame.display.update()
fpsClock.tick(60)
def title():
while True:
screen.fill(colorBlack)
for event in pygame.event.get():
#Game Exit
if event.type== QUIT:
pygame.quit()
sys.exit()
if event.type==KEYDOWN:
if event.key==K_s:
global point
global pointx
if point==3:
pointx+=75
point-=1
elif point==2:
pointx+=75
point-=1
elif point==1:
pointx+=75
point-=1
elif point==0:
pointx-=225
point+=3
if event.key==K_w:
if point==3:
pointx+=225
point-=3
elif point==2:
pointx-=75
point+=1
elif point==1:
pointx-=75
point+=1
elif point==0:
pointx-=75
point+=1
if event.key==K_RETURN:
if point==2:
option=True
game()
elif point==1:
option=True
instruct()
#Background
title_bg=pygame.image.load("icebiome3.png")
screen.blit(title_bg,(0,0))
screen.blit(titleText, (200,50))
screen.blit(singleplayerText, (300,225))
screen.blit(multiplayerText, (300,300))
screen.blit(intructionsText, (300,375))
screen.blit(leaderboardText, (300,450))
screen.blit(menu_upText, (700,450))
screen.blit(menu_downText, (700,475))
screen.blit(menu_returnText, (700,500))
pygame.draw.circle(screen,colorRed,(280,pointx),10)
pygame.draw.circle(screen,colorWhite,(685,460),10)
pygame.draw.circle(screen,colorRed,(685,485),10)
pygame.draw.circle(screen,colorBlue2,(685,510),10)
pygame.display.update()
fpsClock.tick(60)
def instruct():
while True:
screen.fill(colorBlack)
for event in pygame.event.get():
#Game Exit
if event.type== QUIT:
pygame.quit()
sys.exit()
if event.type==KEYDOWN:
if event.key==K_RETURN:
title()
instruct_bg=pygame.image.load('instruct.png')
instruct_sprite=pygame.image.load('instructsprite.png')
screen.blit(instruct_bg,(0,0))
screen.blit(returnText, (325,550))
pygame.draw.circle(screen,colorRed,(310,568),10)
pygame.display.update()
fpsClock.tick(60)
def singleplayer():
stageR=250
def stage (centerX,centerY):
"""stage (centerX,centerY) - creates a stage with given centerpoint"""
pygame.draw.circle(screen, colorBlue, (centerX,centerY),stageR)
#Character 1
xR=int((stageR//10))
x1=centerX
y1=centerY
x1_dir=0
y1_dir=0
solo_wins=0
def char1 (x1,y1):
"""char1 (x1,y1) - creates char1 at given coordinates"""
pygame.draw.circle(screen, colorRed, (x1,y1),xR)
while True:
screen.fill(colorBlack)
for event in pygame.event.get():
#Game Exit
if event.type== QUIT:
pygame.quit()
sys.exit()
# ~~~~~~~~~~ COLLISION DETECTION ~~~~~~~~~~~
v12 = pygame.math.Vector2(x1-x2, y1-y2)
distance = v12.length()
hit_dist = 2*xR
if distance <= hit_dist:
# vector beteween center points
nv = v12.normalize()
# movement direction and combined relative movement
d1 = pygame.math.Vector2(x1_dir, y1_dir)
d2 = pygame.math.Vector2(x2_dir, y2_dir)
dd = d1 - d2
if dd.length() > 0:
# normalized movement and normal distances
ddn = dd.normalize()
dir_dist = ddn.dot(v12)
norm_dist = pygame.math.Vector2(-ddn[0], ddn[1]).dot(v12)
# minimum distance along the line of relative movement
min_dist = math.sqrt(hit_dist*hit_dist - norm_dist*norm_dist)
if dir_dist < min_dist:
# update postions of the players so that the distance is 2*xR
d1l, d2l = d1.length(), d2.length()
d1n = d1/d1l if d1l > 0 else d1
d2n = d2/d2l if d2l > 0 else d2
x1 -= d1n.x * d1l / (d1l+d2l)
y1 -= d1n.y * d1l / (d1l+d2l)
x2 -= d2n.x * d2l / (d1l+d2l)
y2 -= d2n.y * d2l / (d1l+d2l)
# recalculate vector beteween center points
v12 = pygame.math.Vector2(x1-x2, y1-y2)
nv = v12.normalize()
# reflect movement vectors
rd1 = d1.reflect(nv)
rd2 = d2.reflect(nv)
len1, len2 = rd1.length(), rd2.length()
if len1 > 0:
rd1 = rd1 * len2 / len1
x1_dir, y1_dir = rd1.x, rd1.y
else:
x1_dir, y1_dir = -x2_dir, -y2_dir
if len2 > 0:
rd2 = rd2 * len1 / len2
x2_dir, y2_dir = rd2.x, rd2.y
else:
x2_dir, y2_dir = -x1_dir, -y1_dir
#MOVEMENT
keys = pygame.key.get_pressed()
# -------------------- CHAR1 MOVEMENT WASD --------------------
if keys[K_d] or keys[K_a]:
x1_dir += 0.1 if keys[K_d] else -0.1
else:
x1_dir *= 0.98
if keys[K_w] or keys[K_s]:
y1_dir += 0.1 if keys[K_s] else -0.1
else:
y1_dir *= 0.98
stage (centerX,centerY)
char1 (round(x1),round(y1))
x1+=x1_dir
y1+=y1_dir
pygame.display.update()
fpsClock.tick(60)
def game():
#Stage
stageR=250
def stage (centerX,centerY):
"""stage (centerX,centerY) - creates a stage with given centerpoint"""
pygame.draw.circle(screen, colorBlue, (centerX,centerY),stageR)
#Character 1
xR=int((stageR//10))
x1=int(centerX-(stageR*0.8))
y1=centerY
x1_dir=0
y1_dir=0
global x1_wins
x1_wins=0
def char1 (x1,y1):
"""char1 (x1,y1) - creates char1 at given coordinates"""
pygame.draw.circle(screen, colorRed, (x1,y1),xR)
#Character 2
x2=int(centerX+(stageR*0.8))
y2=centerY
x2_dir=0
y2_dir=0
global x2_wins
x2_wins=0
def char2 (x2,y2):
"""char2 (x2,y2) - creates char1 at given coordinates"""
pygame.draw.circle(screen, colorGreen, (x2,y2),xR)
def score ():
pygame.draw.circle(screen, colorOrange, (50,30), (int(xR-5)))
pygame.draw.circle(screen, colorBlack, (50,30), (int(xR-10)))
pygame.draw.circle(screen, colorOrange, (100,30), (int(xR-5)))
pygame.draw.circle(screen, colorBlack, (100,30), (int(xR-10)))
pygame.draw.circle(screen, colorOrange, (150,30), (int(xR-5)))
pygame.draw.circle(screen, colorBlack, (150,30), (int(xR-10)))
pygame.draw.circle(screen, colorOrange, (750,30), (int(xR-5)))
pygame.draw.circle(screen, colorBlack, (750,30), (int(xR-10)))
pygame.draw.circle(screen, colorOrange, (700,30), (int(xR-5)))
pygame.draw.circle(screen, colorBlack, (700,30), (int(xR-10)))
pygame.draw.circle(screen, colorOrange, (650,30), (int(xR-5)))
pygame.draw.circle(screen, colorBlack, (650,30), (int(xR-10)))
def x1score ():
if x1_wins>0:
pygame.draw.circle(screen, colorOrange, (50,30), (int(xR-5)))
if x1_wins>1:
pygame.draw.circle(screen, colorOrange, (100,30), (int(xR-5)))
if x1_wins>2:
pygame.draw.circle(screen, colorOrange, (150,30), (int(xR-5)))
def x2score ():
if x2_wins>0:
pygame.draw.circle(screen, colorOrange, (750,30), (int(xR-5)))
if x2_wins>1:
pygame.draw.circle(screen, colorOrange, (700,30), (int(xR-5)))
if x2_wins>2:
pygame.draw.circle(screen, colorOrange, (650,30), (int(xR-5)))
while True:
screen.fill(colorBlack)
for event in pygame.event.get():
#Game Exit
if event.type== QUIT:
pygame.quit()
sys.exit()
# ~~~~~~~~~~ COLLISION DETECTION ~~~~~~~~~~~
v12 = pygame.math.Vector2(x1-x2, y1-y2)
distance = v12.length()
hit_dist = 2*xR
if distance <= hit_dist:
# vector beteween center points
nv = v12.normalize()
# movement direction and combined relative movement
d1 = pygame.math.Vector2(x1_dir, y1_dir)
d2 = pygame.math.Vector2(x2_dir, y2_dir)
dd = d1 - d2
if dd.length() > 0:
# normalized movement and normal distances
ddn = dd.normalize()
dir_dist = ddn.dot(v12)
norm_dist = pygame.math.Vector2(-ddn[0], ddn[1]).dot(v12)
# minimum distance along the line of relative movement
min_dist = math.sqrt(hit_dist*hit_dist - norm_dist*norm_dist)
if dir_dist < min_dist:
# update postions of the players so that the distance is 2*xR
d1l, d2l = d1.length(), d2.length()
d1n = d1/d1l if d1l > 0 else d1
d2n = d2/d2l if d2l > 0 else d2
x1 -= d1n.x * d1l / (d1l+d2l)
y1 -= d1n.y * d1l / (d1l+d2l)
x2 -= d2n.x * d2l / (d1l+d2l)
y2 -= d2n.y * d2l / (d1l+d2l)
# recalculate vector beteween center points
v12 = pygame.math.Vector2(x1-x2, y1-y2)
nv = v12.normalize()
# reflect movement vectors
rd1 = d1.reflect(nv)
rd2 = d2.reflect(nv)
len1, len2 = rd1.length(), rd2.length()
if len1 > 0:
rd1 = rd1 * len2 / len1
x1_dir, y1_dir = rd1.x, rd1.y
else:
x1_dir, y1_dir = -x2_dir, -y2_dir
if len2 > 0:
rd2 = rd2 * len1 / len2
x2_dir, y2_dir = rd2.x, rd2.y
else:
x2_dir, y2_dir = -x1_dir, -y1_dir
# ~~~~~~~~~~~ Borders ~~~~~~~~~~~~~~
x1_cdist=((centerX-x1)**2+(centerY-y1)**2)**0.5
if x1_cdist>(stageR+30):
time.sleep(3)
x2=int(centerX+(stageR*0.8))
y2=centerY
x1=int(centerX-(stageR*0.8))
y1=centerY
x2_wins+=1
x1_dir=0
y1_dir=0
print (x2_wins)
if x2_wins>=3:
game_end()
x2_cdist=((centerX-x2)**2+(centerY-y2)**2)**0.5
if x2_cdist>(stageR+30):
time.sleep(3)
x2=int(centerX+(stageR*0.8))
y2=centerY
x1=int(centerX-(stageR*0.8))
y1=centerY
x1_wins+=1
x2_dir=0
y2_dir=0
print (x1_wins)
if x1_wins>=3:
game_end()
#MOVEMENT
keys = pygame.key.get_pressed()
# -------------------- CHAR1 MOVEMENT WASD --------------------
if keys[K_d] or keys[K_a]:
x1_dir += 0.1 if keys[K_d] else -0.1
else:
x1_dir *= 0.98
if keys[K_w] or keys[K_s]:
y1_dir += 0.1 if keys[K_s] else -0.1
else:
y1_dir *= 0.98
# -------------------- CHAR2 MOVEMENT up/down/left/right --------------------
if keys[K_RIGHT] or keys[K_LEFT]:
x2_dir += 0.1 if keys[K_RIGHT] else -0.1
else:
x2_dir *= 0.98
if keys[K_UP] or keys[K_DOWN]:
y2_dir += 0.1 if keys[K_DOWN] else -0.1
else:
y2_dir *= 0.98
score()
stage (centerX,centerY)
char1 (round(x1),round(y1))
char2 (round(x2),round(y2))
x1score()
x2score()
x1+=x1_dir
y1+=y1_dir
x2+=x2_dir
y2+=y2_dir
pygame.display.update()
fpsClock.tick(60)
while True:
singleplayer()
I put some code in classes and functions and now it can display keep players and bots on list and use for-loop to use them.
There is too many changes to explain it.
Player and Bot have always the same code - handle_events() is different. Player check pressed keys, Bot generate random values which are treated as pressed keys.
import pygame, sys, time
from pygame.locals import *
import random
import math
# --- constants ---
#Colors
colorRed = pygame.Color(241,59,62)
colorPurple = pygame.Color(200,254,249)
colorBlue = pygame.Color(52, 207, 235)
colorGreen = pygame.Color(100,182,100)
colorWhite = pygame.Color(255,250,250)
colorBlack = pygame.Color(0,0,0)
colorOrange = pygame.Color(242,164,0)
colorBrown = pygame.Color(148,103,58)
colorBlue2 = pygame.Color(37, 45, 204)
#Dimensions
w = 800
h = 600
# --- classes ---
class Player():
def __init__(self, x, y, R, color, k_left, k_right, k_up, k_down):
self.x_start = x
self.y_start = y
self.R = R
self.color = color
self.x_wins = 0
self.k_left = k_left
self.k_right = k_right
self.k_up = k_up
self.k_down = k_down
self.reset()
def reset(self):
'''set values on restart'''
self.x = self.x_start
self.y = self.y_start
self.x_dir = 0
self.y_dir = 0
def draw(self, screen):
pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), self.R)
def move(self):
self.x += self.x_dir
self.y += self.y_dir
def handle_events(self):
keys = pygame.key.get_pressed()
if keys[self.k_right] or keys[self.k_left]:
self.x_dir += 0.1 if keys[self.k_right] else -0.1
else:
self.x_dir *= 0.98
if keys[self.k_up] or keys[self.k_down]:
self.y_dir += 0.1 if keys[self.k_down] else -0.1
else:
self.y_dir *= 0.98
class Bot():
def __init__(self, x, y, R, color):
self.x_start = x
self.y_start = y
self.R = R
self.color = color
self.x_wins = 0
self.reset()
def reset(self):
self.x = self.x_start
self.y = self.y_start
self.x_dir = 0
self.y_dir = 0
def draw(self, screen):
pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), self.R)
def move(self):
self.x += self.x_dir
self.y += self.y_dir
def handle_events(self):
dx = random.randint(-1, 1)
if dx == 0:
self.x_dir *= 0.98
else:
self.x_dir += 0.1 if dx > 0 else -0.1
dy = random.randint(-1, 1)
if dy == 0:
self.y_dir *= 0.98
else:
self.y_dir += 0.1 if dy > 0 else -0.1
class Score():
def __init__(self, x, y, step):
self.x = x
self.y = y
self.step = step
self.circles = 3
self.wins = 0
def draw(self, screen):
r1 = int(xR-5)
r2 = int(xR-10)
x = self.x
y = self.y
for i in range(self.circles):
pygame.draw.circle(screen, colorOrange, (x, y), r1)
pygame.draw.circle(screen, colorBlack, (x, y), r2)
x += self.step
x = self.x
y = self.y
for i in range(self.wins):
pygame.draw.circle(screen, colorOrange, (x, y), r1)
x += self.step
# --- functions ---
def detect_collision(p1, p2):
v12 = pygame.math.Vector2(p1.x-p2.x, p1.y-p2.y)
distance = v12.length()
hit_dist = 2*xR
if distance <= hit_dist:
# vector beteween center points
nv = v12.normalize()
# movement direction and combined relative movement
d1 = pygame.math.Vector2(p1.x_dir, p1.y_dir)
d2 = pygame.math.Vector2(p2.x_dir, p2.y_dir)
dd = d1 - d2
if dd.length() > 0:
# normalized movement and normal distances
ddn = dd.normalize()
dir_dist = ddn.dot(v12)
norm_dist = pygame.math.Vector2(-ddn[0], ddn[1]).dot(v12)
# minimum distance along the line of relative movement
min_dist = math.sqrt(hit_dist*hit_dist - norm_dist*norm_dist)
if dir_dist < min_dist:
# update postions of the players so that the distance is 2*xR
d1l, d2l = d1.length(), d2.length()
d1n = d1/d1l if d1l > 0 else d1
d2n = d2/d2l if d2l > 0 else d2
p1.x -= d1n.x * d1l / (d1l+d2l)
p1.y -= d1n.y * d1l / (d1l+d2l)
p2.x -= d2n.x * d2l / (d1l+d2l)
p2.y -= d2n.y * d2l / (d1l+d2l)
# recalculate vector beteween center points
v12 = pygame.math.Vector2(p1.x-p2.x, p1.y-p2.y)
nv = v12.normalize()
# reflect movement vectors
rd1 = d1.reflect(nv)
rd2 = d2.reflect(nv)
len1, len2 = rd1.length(), rd2.length()
if len1 > 0:
rd1 = rd1 * len2 / len1
p1.x_dir, p1.y_dir = rd1.x, rd1.y
else:
p1.x_dir, p1.y_dir = -p2.x_dir, -p2.y_dir
if len2 > 0:
rd2 = rd2 * len1 / len2
p2.x_dir, p2.y_dir = rd2.x, rd2.y
else:
p2.x_dir, p2.y_dir = -p1.x_dir, -p1.y_dir
def detect_border(player):
distance = ((centerX-player.x)**2 + (centerY-player.y)**2)**0.5
return distance > (stageR+30)
def game():
players = []
scores = []
p1 = Player(int(centerX-(stageR*0.8)), centerY, int(stageR//10), colorRed, K_a, K_d, K_w, K_s)
players.append(p1)
#p2 = Player(int(centerX+(stageR*0.8)), centerY, int(stageR//10), colorGreen, K_LEFT, K_RIGHT, K_UP, K_DOWN)
#players.append(p2)
b = Bot(int(centerX+(stageR*0.8)), centerY, int(stageR//10), colorGreen)
players.append(b)
b = Bot(centerX, int(centerY+(stageR*0.8)), int(stageR//10), colorOrange)
players.append(b)
b = Bot(centerX, int(centerY-(stageR*0.8)), int(stageR//10), colorWhite)
players.append(b)
b = Bot(centerX, centerY, int(stageR//10), colorBrown)
players.append(b)
s1 = Score(50, 30, 50)
s2 = Score(750, 30, -50)
scores.append(s1)
scores.append(s2)
while True:
for event in pygame.event.get():
#Game Exit
if event.type== QUIT:
pygame.quit()
sys.exit()
# ~~~ COLLISION DETECTION ~~~
for p1 in players:
for p2 in players:
if p1 != p2:
detect_collision(p1, p2)
# ~~~ Borders ~~~
for i, p in enumerate(players):
if detect_border(p):
time.sleep(3)
for x in players:
x.reset()
if i == 0:
scores[1].wins += 1
else:
scores[0].wins += 1
print(scores[0].wins, scores[1].wins)
for s in scores:
if s.wins >= 3:
#game_end()
return
# ~~~ MOVEMENT ~~~
for p in players:
p.handle_events()
for p in players:
p.move()
# --- draws --- (without events and moves)
screen.fill(colorBlack)
# stage
pygame.draw.circle(screen, colorBlue, (centerX,centerY), stageR)
# players
for p in players:
p.draw(screen)
# scores
for s in scores:
s.draw(screen)
# update
pygame.display.update()
fpsClock.tick(60)
# --- main ---
pygame.init()
screen = pygame.display.set_mode((w,h))
screen_rect = screen.get_rect()
centerX = screen_rect.centerx
centerY = screen_rect.centery
pygame.display.set_caption('Ice Fighters')
fpsClock = pygame.time.Clock()
stageR = 250
xR = int((stageR//10))
game()
Related
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)
I'm trying to make one ball push another, the problem is that when the balls collide the red ball gets into the blue, the red moves towards the left mouse click, the blue one is drawn with the right mouse click. I've noticed that the speed (Velocidad) and
self.xb2-=self.dx+overlapx/(tamaño/2)
self.yb2-=self.dy+overlapy/(tamaño/2)
the magnitude of the divisor, tamaño/2, has a significant role in the problem.
here's the code
import pygame, math, random
pygame.init()
vel = 5
ancho = 600
alto = 500
tamaño=30
color = (250,0,0)
color2= (0,0,250)
FPS=60
negro= 0,0,0
posantes = [[300,250], [- 50, - 50]] #posicion anterior, bola y bola1/ initial position bola , initial position bola1
mouse = [[300,250], [ - 50, - 50]] #posicion mouse, click izquierdo , click derecho
pantalla = pygame.display.set_mode((ancho, alto))
reloj = pygame.time.Clock()
class Bola():
def __init__(self, color, xyb, tamaño):
super().__init__()
pygame.draw.circle(pantalla, color, xyb,tamaño)
def colision(self,xyb,xyb2):
self.colision = False
self.xyb = posantes [0]
self.xyb2 = posantes[1]
self.xb,self.yb = posantes[0][0],posantes[0][1]
self.xb2,self.yb2 = posantes[1][0],posantes[1][1]
dist = math.hypot(self.xb2-self.xb,self.yb2-self.yb)
if dist<tamaño*2:
self.colision =True
else:
self.colision =False
def reaccion(self,xyb,xyb2):
overlapx , overlapy = self.xb-self.xb2, self.yb-self.yb2
if self.colision==True:
self.xb2-=self.dx+overlapx/(tamaño/2)
self.yb2-=self.dy+overlapy/(tamaño/2)
posantes[1]=[self.xb2,self.yb2]
print(" !! reaccion ¡¡")
def moverbola(self, xyb, xy,bi):
self.xyb = posantes[bi]
self.xy = mouse[bi]
self.xb,self.yb = self.xyb[0] , self.xyb[1]
self.x,self.y = self.xy[0] , self.xy[1]
self.dx,self.dy = abs(self.x-self.xb) , abs(self.y-self.yb)
dist = math.hypot(self.x-self.xb,self.y-self.yb)
if dist!= 0:
self.dx = self.dx / dist
self.dy = self.dy / dist
if self.xb < self.x:
self.xb+=vel * self.dx
if self.xb > self.x:
self.xb-=vel * self.dx
if self.yb < self.y:
self.yb+=vel * self.dy
if self.yb > self.y :
self.yb -=vel * self.dy
self.xyb = [self.xb,self.yb]
posantes[bi] = self.xyb
terminar = False
while not terminar:
reloj.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
terminar=True
if event.type == pygame.MOUSEBUTTONDOWN and event.button==1:
mouse[0] = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]
if event.type == pygame.MOUSEBUTTONDOWN and event.button==3:
mouse[1] = pygame.mouse.get_pos()[0],pygame.mouse.get_pos()[1]
posantes[1] = mouse[1]
pantalla.fill(negro)
bola1 = Bola(color2, posantes[1], tamaño)
bola = Bola(color,posantes[0], tamaño)
bola.moverbola(posantes[0], mouse[0],0)
bola.colision(posantes[0],posantes[1])
bola.reaccion(posantes[0],posantes[1])
pygame.display.update()
pygame.quit()
quit()
You misunderstood the concept of object-oriented programming. You must create instances of the Bola class before the application loop, but use them in the loop:
bola1 = Bola(color2, [-50, -50], tamaño)
bola = Bola(color, [300, 250], tamaño)
terminar = False
while not terminar:
bola1.draw()
bola.draw()
Set the attribute Bola in the constructor and use it in the methods, e.g. B. to draw the Bola:
class Bola():
def __init__(self, color, xyb, tamaño):
super().__init__()
self.color = color
self.xyb = xyb
self.tamaño = tamaño
def draw(self):
pos = round(self.xyb[0]), round(self.xyb[1])
pygame.draw.circle(pantalla, self.color, pos, self.tamaño)
Ensure methods and attributes have different names (collision, colision_test). The colision_test method returns the result of the test:
class Bola():
# [...]
def colision_test(self,xyb2):
dist = math.hypot(xyb2[0] - self.xyb[0], xyb2[1] - self.xyb[1])
self.colision = dist < tamaño*2
return (self.colision, dist)
Move the Bola in the reaccion and moverbola methods depending on the arguments of the function and change the attribute in which the position is stored:
class Bola():
# [...]
def reaccion(self, xyb_other, reaction_dist):
dx, dy = xyb_other[0] - self.xyb[0], xyb_other[1] - self.xyb[1]
dist = math.hypot(dx, dy)
if dist > 0:
self.xyb = [self.xyb[0] - dx * reaction_dist / dist, self.xyb[1] - dy * reaction_dist / dist]
def moverbola(self, target):
dx, dy = target[0] - self.xyb[0], target[1] - self.xyb[1]
dist = math.hypot(dx, dy)
if dist > 0:
step = min(dist, vel)
self.xyb = [self.xyb[0] + dx * step / dist, self.xyb[1] + dy * step / dist]
Complete exmaple:
import pygame, math, random
pygame.init()
vel = 5
ancho = 600
alto = 500
tamaño=30
color = (250,0,0)
color2= (0,0,250)
FPS=60
negro= 0,0,0
pantalla = pygame.display.set_mode((ancho, alto))
reloj = pygame.time.Clock()
class Bola():
def __init__(self, color, xyb, tamaño):
super().__init__()
self.color = color
self.xyb = xyb
self.tamaño = tamaño
def draw(self):
pos = round(self.xyb[0]), round(self.xyb[1])
pygame.draw.circle(pantalla, self.color, pos, self.tamaño)
def colision_test(self,xyb2):
dist = math.hypot(xyb2[0] - self.xyb[0], xyb2[1] - self.xyb[1])
self.colision = dist < tamaño*2
return (self.colision, dist)
def reaccion(self, xyb_other, reaction_dist):
dx, dy = xyb_other[0] - self.xyb[0], xyb_other[1] - self.xyb[1]
dist = math.hypot(dx, dy)
if dist > 0:
self.xyb = [self.xyb[0] - dx * reaction_dist / dist, self.xyb[1] - dy * reaction_dist / dist]
def moverbola(self, target):
dx, dy = target[0] - self.xyb[0], target[1] - self.xyb[1]
dist = math.hypot(dx, dy)
if dist > 0:
step = min(dist, vel)
self.xyb = [self.xyb[0] + dx * step / dist, self.xyb[1] + dy * step / dist]
bola1 = Bola(color2, [-50, -50], tamaño)
bola = Bola(color, [300, 250], tamaño)
bola_target = [300, 250]
terminar = False
while not terminar:
reloj.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
terminar=True
if event.type == pygame.MOUSEBUTTONDOWN and event.button==1:
bola_target = event.pos
if event.type == pygame.MOUSEBUTTONDOWN and event.button==3:
bola1.xyb = event.pos
bola.moverbola(bola_target)
collision, dist = bola1.colision_test(bola.xyb)
if collision:
reaccion = (tamaño * 2 - dist)
bp = bola.xyb[:]
bp1 = bola1.xyb[:]
bola.reaccion(bp1, reaccion)
bola1.reaccion(bp, reaccion)
pantalla.fill(negro)
bola1.draw()
bola.draw()
pygame.display.update()
pygame.quit()
quit()
I have a 2d pygame water simulation thingy that I followed a tutorial to make. I also found the answer to this question to fix issues with the tutorial: Pygame water physics not working as intended
I have since been trying to convert this program over to using pyopengl to render things. However, I have been struggling to:
A: Draw the water polygon
B: texture the water polygon with a tiled texture
Here is my (rather poor) attempt at converting this code to pyopengl.
import pygame, random
import math as m
from pygame import *
from OpenGL import *
from OpenGL.GLU import *
from OpenGL.GL import *
pygame.init()
WINDOW_SIZE = (854, 480)
screen = pygame.display.set_mode(WINDOW_SIZE,0,32,DOUBLEBUF|OPENGL) # initiate the window
clock = pygame.time.Clock()
def draw_polygon(polygon_points):
glBegin(GL_POLYGON);
for i in polygon_points:
glVertex3fv(i)
#glEnd()
class surface_water_particle():
def __init__(self, x,y):
self.x_pos = x
self.y_pos = y
self.target_y = y
self.velocity = 0
self.k = 0.04
self.d = 0.08
self.time = 1
def update(self):
x = self.y_pos - self.target_y
a = -(self.k * x + self.d * self.velocity)
if self.y_pos > self.target_y:
self.y_pos -= 0.1
if self.y_pos < self.target_y:
self.y_pos += 0.1
self.velocity = round(self.velocity)
self.y_pos += self.velocity
self.velocity += a
self.time += 1
class water_tile():
def __init__(self, x_start, x_end, y_start, y_end, segment_length):
self.springs = []
self.x_start = x_start
self.y_start = y_start
self.x_end = x_end
self.y_end = y_end - 10
for i in range(abs(x_end - x_start) // segment_length):
self.springs.append(surface_water_particle(i * segment_length + x_start, y_end))
def update(self, spread):
passes = 4 # more passes = more splash spreading
for i in range(len(self.springs)):
self.springs[i].update()
leftDeltas = [0] * len(self.springs)
rightDeltas = [0] * len(self.springs)
for p in range(passes):
for i in range(0, len(self.springs)):
if i > 0:
leftDeltas[i] = spread * (self.springs[i].y_pos - self.springs[i - 1].y_pos)
self.springs[i - 1].velocity += leftDeltas[i]
if i < len(self.springs):
rightDeltas[i] = spread * (self.springs[i].y_pos - self.springs[(i + 1)%len(self.springs)].y_pos)
self.springs[(i + 1)%len(self.springs)].velocity += rightDeltas[i]
for i in range(0, len(self.springs)):
if round (leftDeltas[i],12) == 0 or round (rightDeltas[i],12) == 0:
self.springs[i - 1].y_pos = self.y_end+10
if i > 0:
self.springs[i - 1].y_pos += leftDeltas[i] # you were updating velocity here!
if i < len(self.springs):
self.springs[(i + 1)%len(self.springs)].y_pos += rightDeltas[i]
def splash(self, index, speed):
if index >= 0 and index < len(self.springs):
self.springs[index].velocity = speed
def draw(self):
water_surface = pygame.Surface((abs(self.x_end-self.x_start), abs(self.y_start - self.y_end)), depth=8).convert_alpha()
polygon_points = []
polygon_points.append((self.x_start, self.y_start,0))
for spring in range(len(self.springs)):
polygon_points.append((self.springs[spring].x_pos, self.springs[spring].y_pos,0))
polygon_points.append((self.springs[len(self.springs) - 1].x_pos, self.y_start,0))
draw_polygon(polygon_points)
return water_surface
class water_object:
def __init__(self, x_start, x_end, y_start, y_end, segment_length, x_pos, y_pos):
self.water = water_tile(x_start,x_end,y_start,y_end,segment_length)
self.image = self.water.draw()
self.rect = self.image.get_rect()
self.rect.x = x_pos
self.rect.y = y_pos
def update(self):
self.water.update(0.1)
self.image = self.water.draw()
water_list = [water_object(0,276+16,64,0,16,0,20)]
while True:
screen.fill((0,0,0))
for water in water_list:
gluPerspective(45, (WINDOW_SIZE[0]/WINDOW_SIZE[1]), 0.1, 50.0)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
water.update()
#screen.blit(water.image, (water.rect.x,water.rect.y))
#water_test.x_start = water_test.x_start + 1
#if random.randint(0,8) == 1:
#water_test.splash(random.randint(0, len(water_test.springs) - 1),2)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
if event.type == MOUSEBUTTONDOWN:
print (len(water.water.springs))
water.water.splash(random.randint(0, len(water.water.springs) - 1),50)
pygame.display.update()
clock.tick(60)
However, despite my attempt, I couldnt get anything to display on screen at all. How can I fix this/how can I attain the 2 things I have been struggling with?
You cannot draw an OpenGL primitive to a pygame.Surface. Anyway there is no need to do so.
For the best performance, directly draw to the default framebuffer (window).
Since you want to draw a line, you have to use a Line primitive type. GL_POLYGON would draw a filed convex polygon. Use the primitive type GL_LINE_STRIP:
def draw_polygon(polygon_points):
glBegin(GL_LINE_STRIP)
for pt in polygon_points:
glVertex2f(*pt)
glEnd()
Before you draw the line, ser the current color by glColor:
glColor3f(0, 0, 1)
draw_polygon(polygon_points)
The vertex coordinates of the lie are specified in window space. Hence you have to setup an Orthographic projection rather than a Perspective projection. Specify the current matrix by [glMatrixMode] and set the projection matrix by glOrtho. Since the matrix operations do not set a matrix, but multiply the current matrix by the specified matrix, I recommend to load the identity matrix before (glLoadIdentity):
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, WINDOW_SIZE[0], WINDOW_SIZE[1], 0, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
Before you draw the line you have to clear the framebuffer by glClear. The clear color can be defined by glClearColor:
glClearColor(1, 1, 1, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
Complete example:
import pygame
from OpenGL import *
from OpenGL.GL import *
def draw_polygon(surf_rect, polygon_points):
glBegin(GL_LINE_STRIP)
#glBegin(GL_TRIANGLE_STRIP)
for pt in polygon_points:
glVertex2f(*pt)
glVertex2f(pt[0], surf_rect.height)
glEnd()
class WaterParticle():
def __init__(self, x, y):
self.x, self.y = x, y
self.target_y = y
self.velocity = 0
self.k = 0.04
self.d = 0.08
def update(self):
x = self.y - self.target_y
a = -(self.k * x + self.d * self.velocity)
#self.p[1] += -0.1 if x > 0 else 0.1 if x < 0 else 0
self.y += self.velocity
self.velocity += a
class Water():
def __init__(self, x_start, x_end, y_start, segment_length, passes, spread):
n = abs(x_end - x_start + segment_length - 1) // segment_length + 1
self.particles = [WaterParticle(i * segment_length + x_start, y_start) for i in range(n)]
self.passes = passes
self.spread = spread
def update(self):
for particle in self.particles:
particle.update()
left_deltas = [0] * len(self.particles)
right_deltas = [0] * len(self.particles)
for _ in range(self.passes):
for i in range(len(self.particles)):
if i > 0:
left_deltas[i] = self.spread * (self.particles[i].y - self.particles[i - 1].y)
self.particles[i - 1].velocity += left_deltas[i]
if i < len(self.particles)-1:
right_deltas[i] = self.spread * (self.particles[i].y - self.particles[i + 1].y)
self.particles[i + 1].velocity += right_deltas[i]
for i in range(len(self.particles)):
if i > 0:
self.particles[i-1].y += left_deltas[i]
if i < len(self.particles) - 1:
self.particles[i+1].y += right_deltas[i]
def splash(self, index, speed):
if index > 0 and index < len(self.particles):
self.particles[index].velocity += speed
def draw(self, surf_rect):
polygon_points = []
for spring in range(len(self.particles)):
polygon_points.append((self.particles[spring].x, self.particles[spring].y))
glColor3f(0, 0, 1)
draw_polygon(surf_rect, polygon_points)
pygame.init()
window = pygame.display.set_mode((640, 480), pygame.DOUBLEBUF | pygame.OPENGL)
clock = pygame.time.Clock()
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, *window.get_size(), 0, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glClearColor(1, 1, 1, 1)
water_line_y = window.get_height() // 2
water = Water(0, window.get_width(), window.get_height() // 2, 3, 8, 0.025)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.MOUSEBUTTONDOWN:
velocity = water_line_y - event.pos[1]
if velocity > 0:
index = int(len(water.particles) * event.pos[0] / window.get_width())
water.splash(index, velocity)
water.update()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
water.draw(window.get_rect())
pygame.display.flip()
clock.tick(50)
So I want it to count the score every time the snake eats a candy.
I haven't tried much, but I tried to find existing codes and adding them to mine but that just broke the game. I also tried to make my own score board by watching a tutorial, but I don't know where the code should go like at the beginning or end.
import pygame
import random
score = 0
welcome = ("Welcome to our snake game")
print(welcome)
class cube:
height = 20
w = 500
def __init__(movee,start,x=1,y=0,color=(0,0,0)):
movee.pos = start
movee.x = 1
movee.y = 0
movee.color = color
def move(movee, x, y):
movee.x = x
movee.y = y
movee.pos = (movee.pos[0] + movee.x, movee.pos[1] + movee.y)
def draw(movee, surface, eyes=False):
leng = movee.w // movee.height
i = movee.pos[0]
j = movee.pos[1]
pygame.draw.rect(surface, movee.color, (i*leng+1,j*leng+1, leng-2, leng-2))
class snake:
body = []
turns = {}
def __init__(movee, color, pos):
movee.color = color
movee.head = cube(pos)
movee.body.append(movee.head)
def move(movee):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
keys = pygame.key.get_pressed()
for key in keys:
if keys[pygame.K_LEFT]:
movee.x = -1
movee.y = 0
movee.turns[movee.head.pos[:]] = [movee.x, movee.y]
elif keys[pygame.K_RIGHT]:
movee.x = 1
movee.y = 0
movee.turns[movee.head.pos[:]] = [movee.x, movee.y]
elif keys[pygame.K_UP]:
movee.x = 0
movee.y = -1
movee.turns[movee.head.pos[:]] = [movee.x, movee.y]
elif keys[pygame.K_DOWN]:
movee.x = 0
movee.y = 1
movee.turns[movee.head.pos[:]] = [movee.x, movee.y]
for i, c in enumerate(movee.body):
p = c.pos[:]
if p in movee.turns:
turn = movee.turns[p]
c.move(turn[0],turn[1])
if i == len(movee.body)-1:
movee.turns.pop(p)
else:
if c.x == -1 and c.pos[0] <= 0: c.pos = (c.height-1, c.pos[1])
elif c.x == 1 and c.pos[0] >= c.height-1: c.pos = (0,c.pos[1])
elif c.y == 1 and c.pos[1] >= c.height-1: c.pos = (c.pos[0], 0)
elif c.y == -1 and c.pos[1] <= 0: c.pos = (c.pos[0],c.height-1)
else: c.move(c.x,c.y)
def add(movee):
tail = movee.body[-1]
dx, dy = tail.x, tail.y
if dx == 1 and dy == 0:
movee.body.append(cube((tail.pos[0]-1,tail.pos[1])))
elif dx == -1 and dy == 0:
movee.body.append(cube((tail.pos[0]+1,tail.pos[1])))
elif dx == 0 and dy == 1:
movee.body.append(cube((tail.pos[0],tail.pos[1]-1)))
elif dx == 0 and dy == -1:
movee.body.append(cube((tail.pos[0],tail.pos[1]+1)))
movee.body[-1].x = dx
movee.body[-1].y = dy
def draw(movee, surface):
for i, c in enumerate(movee.body):
if i ==0:
c.draw(surface, True)
else:
c.draw(surface)
def drawingAGrid(w, height, surface):
sizein = w // height
x = 0
y = 0
for l in range(height):
x = x + sizein
y = y + sizein
def redrawGrid(surface):
global height, width, s, snack
surface.fill((255,255,255))
s.draw(surface)
snack.draw(surface)
drawingAGrid(width, height, surface)
pygame.display.update()
def Candy(height, item):
positions = item.body
while True:
x = random.randrange(height)
y = random.randrange(height)
if len(list(filter(lambda z:z.pos == (x,y), positions))) > 0:
continue
else:
break
return (x,y)
def gameloop():
global width, height, s, snack, x_pos, y_pos, reset
width = 500
height = 20
win = pygame.display.set_mode((width, width))
s = snake((255, 0, 0), (10, 10))
snack = cube(Candy(height, s), color=(0, 0, 0))
flag = True
clock = pygame.time.Clock()
x_pos, y_pos = s.body[0].pos
while flag:
pygame.time.delay(50)
clock.tick(7)
s.move()
x, y = s.body[0].pos
if not -1 <= x - x_pos <= 1 or not -1 <= y - y_pos <= 1:
movee.reset((10,10))
x_pos, y_pos = s.body[0].pos
if s.body[0].pos == snack.pos:
s.add()
snack = cube(Candy(height, s), color=(0, 0, 0))
redrawGrid(win)
gameloop()
I just want like a scoreboard in any of the corners counting the score.
Use pygame.freetype to render text. e,g, crated a pygame.freetype.SysFont object:
import pygame.freetype
pygame.init()
font = pygame.freetype.SysFont('Times New Roman', 30)
The score is the number of body parts. Use str to convert a number to a text and .render() to render a text to a pygame.Surface object:
score = len(s.body)
text_surf, text_rect = font.render(str(score), (255, 0, 0), size=30)
Define a margin to the border of the window, calculate the text position (e.g. bottom right) and .blit the text to the window surfrace:
margin = 10
text_pos = (width - text_rect.width - margin, width - text_rect.height - margin)
surface.blit(text_surf, text_pos)
Function redrawGrid:
def redrawGrid(surface):
global height, width, s, snack
surface.fill((255,255,255))
s.draw(surface)
snack.draw(surface)
drawingAGrid(width, height, surface)
score = len(s.body)
text_surf, text_rect = font.render(str(score), (255, 0, 0), size=30)
margin = 10
text_pos = (width - text_rect.width - margin, width - text_rect.height - margin)
surface.blit(text_surf, text_pos)
pygame.display.update()
import pygame
import random
import numpy as np
import matplotlib.pyplot as plt
import math
number_of_particles = 70
my_particles = []
background_colour = (255,255,255)
width, height = 500, 500
sigma = 1
e = 1
dt = 0.1
v = 0
a = 0
r = 1
def r(p1,p2):
dx = p1.x - p2.x
dy = p1.y - p2.y
angle = 0.5 * math.pi - math.atan2(dy, dx)
dist = np.hypot(dx, dy)
return dist
def collide(p1, p2):
dx = p1.x - p2.x
dy = p1.y - p2.y
dist = np.hypot(dx, dy)
if dist < (p1.size + p2.size):
tangent = math.atan2(dy, dx)
angle = 0.5 * np.pi + tangent
angle1 = 2*tangent - p1.angle
angle2 = 2*tangent - p2.angle
speed1 = p2.speed
speed2 = p1.speed
(p1.angle, p1.speed) = (angle1, speed1)
(p2.angle, p2.speed) = (angle2, speed2)
overlap = 0.5*(p1.size + p2.size - dist+1)
p1.x += np.sin(angle) * overlap
p1.y -= np.cos(angle) * overlap
p2.x -= np.sin(angle) * overlap
p2.y += np.cos(angle) * overlap
def LJ(r):
return -24*e*((2/r*(sigma/r)**12)-1/r*(sigma/r)**6)
def verlet():
a1 = -LJ(r(p1,p2))
r = r + dt*v+0.5*dt**2*a1
a2 = -LJ(r(p1,p2))
v = v + 0.5*dt*(a1+a2)
return r, v
class Particle():
def __init__(self, (x, y), size):
self.x = x
self.y = y
self.size = size
self.colour = (0, 0, 255)
self.thickness = 1
self.speed = 0
self.angle = 0
def display(self):
pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness)
def move(self):
self.x += np.sin(self.angle)
self.y -= np.cos(self.angle)
def bounce(self):
if self.x > width - self.size:
self.x = 2*(width - self.size) - self.x
self.angle = - self.angle
elif self.x < self.size:
self.x = 2*self.size - self.x
self.angle = - self.angle
if self.y > height - self.size:
self.y = 2*(height - self.size) - self.y
self.angle = np.pi - self.angle
elif self.y < self.size:
self.y = 2*self.size - self.y
self.angle = np.pi - self.angle
screen = pygame.display.set_mode((width, height))
for n in range(number_of_particles):
x = random.randint(15, width-15)
y = random.randint(15, height-15)
particle = Particle((x, y), 15)
particle.speed = random.random()
particle.angle = random.uniform(0, np.pi*2)
my_particles.append(particle)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(background_colour)
for i, particle in enumerate(my_particles):
particle.move()
particle.bounce()
for particle2 in my_particles[i+1:]:
collide(particle, particle2)
particle.display()
pygame.display.flip()
pygame.quit()
I wanted to simulate particles by Lennard-Jones potential. My problem with this code is that I do not know how to use the Verlet algorithm.
I do not know where I should implement the Verlet algorithm; inside the class or outside?
How can I use velocity from the Verlet algorithm in the move method?
Is my implementation of the Verlet algorithm correct, or should I use arrays for saving results?
What else should I change to make it work?
You can keep the dynamical variables, position and velocity, inside the class instances, however then each class needs an acceleration vector to accumulate the force contributions. The Verlet integrator has the role of a controller, it acts from outside on the collection of all particles. Keep the angle out of the computations, the forth and back with trigonometric functions and their inverses is not necessary. Make position, velocity and acceleration all 2D vectors.
One way to implement the velocity Verlet variant is (see https://stackoverflow.com/tags/verlet-integration/info)
verlet_step:
v += a*0.5*dt;
x += v*dt; t += dt;
do_collisions(t,x,v,dt);
a = eval_a(x);
v += a*0.5*dt;
do_statistics(t,x,v);
which supposes a vectorized variant. In your framework, there would be some iterations over the particle collection to include,
verlet_step:
for p in particles:
p.v += p.a*0.5*dt; p.x += p.v*dt;
t += dt;
for i, p1 in enumerate(particles):
for p2 in particles[i+1:]:
collide(p1,p2);
for i, p1 in enumerate(particles):
for p2 in particles[i+1:]:
apply_LJ_forces(p1,p2);
for p in particles:
p.v += p.a*0.5*dt;
do_statistics(t,x,v);
No, you could not have done nothing wrong since you did not actually call the Verlet function to update position and velocity. And no, a strict vectorization is not necessary, see above. The implicit vectorization via the particles array is sufficient. You would only need a full vectorization if you wanted to compare with the results of a standard integrator like those in scipy.integrate using the same model to provide the ODE function.
Code with some add-ons but without collisions, desingularized potential
import pygame
import random
import numpy as np
import matplotlib.pyplot as plt
import math
background_colour = (255,255,255)
width, height = 500, 500
aafac = 2 # anti-aliasing factor screen to off-screen image
number_of_particles = 50
my_particles = []
sigma = 10
sigma2 = sigma*sigma
e = 5
dt = 0.1 # simulation time interval between frames
timesteps = 10 # intermediate invisible steps of length dt/timesteps
def LJ_force(p1,p2):
rx = p1.x - p2.x
ry = p1.y - p2.y
r2 = rx*rx+ry*ry
r2s = r2/sigma2+1
r6s = r2s*r2s*r2s
f = 24*e*( 2/(r6s*r6s) - 1/(r6s) )
p1.ax += f*(rx/r2)
p1.ay += f*(ry/r2)
p2.ax -= f*(rx/r2)
p2.ay -= f*(ry/r2)
def Verlet_step(particles, h):
for p in particles:
p.verlet1_update_vx(h);
p.bounce()
#t += h;
for i, p1 in enumerate(particles):
for p2 in particles[i+1:]:
LJ_force(p1,p2);
for p in particles:
p.verlet2_update_v(h);
class Particle():
def __init__(self, (x, y), (vx,vy), size):
self.x = x
self.y = y
self.vx = vx
self.vy = vy
self.size = size
self.colour = (0, 0, 255)
self.thickness = 2
self.ax = 0
self.ay = 0
def verlet1_update_vx(self,h):
self.vx += self.ax*h/2
self.vy += self.ay*h/2
self.x += self.vx*h
self.y += self.vy*h
self.ax = 0
self.ay = 0
def verlet2_update_v(self,h):
self.vx += self.ax*h/2
self.vy += self.ay*h/2
def display(self,screen, aa):
pygame.draw.circle(screen, self.colour, (int(aa*self.x+0.5), int(aa*self.y+0.5)), aa*self.size, aa*self.thickness)
def bounce(self):
if self.x > width - self.size:
self.x = 2*(width - self.size) - self.x
self.vx = - self.vx
elif self.x < self.size:
self.x = 2*self.size - self.x
self.vx = - self.vx
if self.y > height - self.size:
self.y = 2*(height - self.size) - self.y
self.vy = - self.vy
elif self.y < self.size:
self.y = 2*self.size - self.y
self.vy = - self.vy
#------------ end class particle ------------
#------------ start main program ------------
for n in range(number_of_particles):
x = 1.0*random.randint(15, width-15)
y = 1.0*random.randint(15, height-15)
vx, vy = 0., 0.
for k in range(6):
vx += random.randint(-10, 10)/2.
vy += random.randint(-10, 10)/2.
particle = Particle((x, y),(vx,vy), 10)
my_particles.append(particle)
#--------- pygame event loop ----------
screen = pygame.display.set_mode((width, height))
offscreen = pygame.Surface((aafac*width, aafac*height))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
offscreen.fill(background_colour)
for k in range(timesteps):
Verlet_step(my_particles, dt/timesteps)
for particle in my_particles:
particle.display(offscreen, aafac)
pygame.transform.smoothscale(offscreen, (width,height), screen)
pygame.display.flip()
pygame.quit()