I am creating a game using Python and Pygame.
I have used this function before using JS with p5.js and Java with libwjgl, but for some reason, it does not work with Pygame.
I'm trying to hit a static target shot from a moving object. Now I'm missing the target. You can see it in the gif. Every projectile should hit the target (now everything misses, but shots in the right direction)
The bullet is fired once player clicks mouse button.
This is how I am doing this:
def shoot(self):
posX, posY = 100, 100 # Static target coordinates
diffX = self.player.x - posX
diffY = self.player.y - posY
distance = math.sqrt((diffX * diffX) + (diffY * diffY)) # Calculate the distance
velX = float((-1 / distance * diffX * 6)) # Calculate volocity required to hit the target
velY = float((-1 / distance * diffY * 6)) # Calculate volocity required to hit the target
# Bullet(x, y, width, height, velocityX, velocityY)
# Will be rendered on the screen
self.bullets.append(Bullet(self.player.x, self.player.y, 10, 10, velX, velY)) # The bullet is added to array, which later gets rendered on the screen
Bullet class:
import pygame
class Bullet(object):
def __init__(self, x, y, width, height, velY, velX):
self.width = width
self.height = height
self.x = x
self.y = y
self.velX = velX
self.velY = velY
self.bullet = pygame.Rect(self.x, self.y, self.width, self.height)
def render(self, screen):
pygame.draw.rect(screen, (255, 255, 255), self.bullet)
def update(self):
self.bullet.x += self.velX
self.bullet.y += self.velY
This works perfectly with other languages I have mentioned, but in Python the launched projectile is off...
Here is how it looks. The red square is the target:
THANK YOU, EVERYONE, FOR THE HELP. I really appreciate it :)
EDIT: FULL GAME CODE
import pygame
from PodSixNet.Connection import connection, ConnectionListener
from objects.Button import Button
from time import sleep
from STATE import STATE
import sys, os
from objects.Text import Text
from resources.hubColours import ColoursClass
from pathlib import Path
from objects.Bullet import Bullet
import math
class DefenseGame(ConnectionListener):
def __init__(self, hub, width, height, soundController, fontController, fps=60):
#Hub
self.hub = hub
#COLOURS
self.color = ColoursClass("alt")
#SOUNDS
self.soundController = soundController
#SCREEN
self.width = width
self.height = height
self.background = pygame.Surface((self.width, self.height), pygame.HWSURFACE | pygame.DOUBLEBUF).convert()
self.background.fill(self.color.getColour(7)) # fill background white
#INFO
self.fps = fps
self.playtime = 0.0
#FONTS
self.fontController = fontController
#STATE
# All player related stuff is stored in the HUB.
# TODO:
# Player class
self.isRunning = True
self.moveLeft = False
self.moveRight = False
self.moveUp = False
self.moveDown = False
self.bullets = []
self.moveAmnt = 5
self.timeLeft = "Waiting for players..."
self.clock = pygame.time.Clock()
#OBJECTS
self.on_init()
#Initialize game objects
def on_init(self):
self.char = pygame.transform.smoothscale(pygame.image.load(str(Path("character.png"))), (50, 50))
# Draws on screen
def render(self, screen): # Update all objects (through object handler) # This will update the contents of the entire display
screen.blit(self.background, (0, 0))
self.drawInfoText("Color: " + self.hub.playerColorName, 10, 10, self.fontController.tiny, screen)
self.drawInfoText("Player Count: " + str(self.hub.getInGamePlayerCount()), 10, 20, self.fontController.tiny, screen)
self.drawInfoText("FPS: " + str(round(self.getFPS(), 2)), 10, 30, self.fontController.tiny, screen)
self.drawInfoText("Network Status: " + self.hub.statusLabel, 10, 40, self.fontController.tiny, screen)
self.player.render(screen)
self.player2.render(screen)
# screen.blit(self.char, (self.posX, self.posY))
if len(self.bullets) > 0:
for b in self.bullets:
b.render(screen)
def onMove(self):
connection.Send({"action": "playerMove", 'x': self.player.x , 'y': self.player.y, 'id': self.hub.playerId})
def shoot(self):
posX, posY = pygame.mouse.get_pos()
diffX = self.player.x - posX
diffY = self.player.y - posY
distance = math.sqrt((diffX * diffX) + (diffY * diffY))
print("DISTANCE: ", distance)
velX = float((-1 / distance * diffX * 20))
velY = float((-1 / distance * diffY * 20))
print(velX, velY)
self.bullets.append(Bullet(self.player.x, self.player.y, 10, 10, velX, velY))
# Looks for events
def handle_events(self, events):
for event in events:
if event.type == pygame.QUIT:
self.exitGame(event)
self.hub.setState(STATE.Home)
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.exitGame(event)
self.hub.setState(STATE.SelectGame)
if event.key == pygame.K_w:
self.moveUp = True
if event.key == pygame.K_a:
self.moveLeft = True
if event.key == pygame.K_s:
self.moveDown = True
if event.key == pygame.K_d:
self.moveRight = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_w:
self.moveUp = False
if event.key == pygame.K_a:
self.moveLeft = False
if event.key == pygame.K_s:
self.moveDown = False
if event.key == pygame.K_d:
self.moveRight = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # Mouse first button
self.shoot()
# Update stuff (loop)
def update(self):
self.Pump() # Connection
connection.Pump() # Connection
timer = self.clock.tick(self.fps)
if self.moveLeft:
self.player.x -= timer
self.onMove()
if self.moveRight:
self.player.x += timer
self.onMove()
if self.moveUp:
self.player.y -= timer
self.onMove()
if self.moveDown:
self.player.y += timer
self.onMove()
if len(self.bullets) > 0:
for b in self.bullets:
b.update()
# sleep(0.001)
# returns FPS
def getFPS(self):
self.clock.tick(self.fps)
return self.clock.get_fps()
# Helper for quickly drawing on screen
def drawInfoText(self, text, x, y, font, screen):
surface = font.render(str(text), 1, self.color.getColour(8))
screen.blit(surface, (x, y))
# Exits the game to home
def exitGame(self, e):
self.soundController.stopSoundEffects()
connection.Send({"action":"exitGame", "isInGame": False})
self.hub.isInGame = False
self.bullet
is a pygame.Rect
object. The position of the bullet is tracked by the position of the rectangle (self.bullet.x
, self.bullet.y
). Since the rectangle position is integral this causes an inaccuracy each time when the position is updated.
Use floating point data to keep track of the position. Use c.
Store the position of the bullet and the velocity to a pygame.math.Vector2
object.
class Bullet(object):
def __init__(self, x, y, width, height, velX, velY):
self.width = width
self.height = height
self.x = x
self.y = y
self.bullet = pygame.Rect(self.x, self.y, self.width, self.height)
self.pos = pygame.math.Vector2(self.bullet.center)
self.vel = pygame.math.Vector2(velX, velY)
Update the position attribute instead of the rectangle position:
class Bullet(object):
# [...]
def update(self):
self.pos = self.pos + self.vel
Update the position of the rectangle before drawing the bullet:
class Bullet(object):
# [...]
def render(self, screen):
self.bullet.center = (int(self.pos[0]), int(self.pos[1]))
pygame.draw.rect(screen, (255, 255, 255), self.bullet)
Use pygame.math.Vector2
to calculate the velocity:
def shoot(self):
posX, posY = 100, 100 # Static target coordinates
traget = pygame.math.Vector2(posX, posY)
start = pygame.math.Vector2(self.player.x, self.player.y)
delta = traget - start
distance = delta.length()
direction = delta.normalize()
vel = direction * 6
self.bullets.append(Bullet(self.player.x, self.player.y, 10, 10, vel[0], vel[1]))