![]() |
[PyGame] positioning the laser - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: Game Development (https://python-forum.io/forum-11.html) +--- Thread: [PyGame] positioning the laser (/thread-42317.html) |
positioning the laser - flash77 - Jun-16-2024 Hello dear experts, I have a last difficult problem to solve with my space invaders clone. I need some help with this. It involves the boss's turret rotating on its own axis while aiming at the moving player. The turret has 2 cannons. I would like to work out a solution for the right cannon only first (I will try to apply the solution to the left cannon later). The turret rotates and aims at the player, firing a laser every few seconds. I managed to get the laser to fire at the current angle of the right cannon. The problem is the positioning of the laser when firing - its end should be at the end of the right cannon at the start of firing. In the picture "positioning is not correct" you can see what is hapening - the laser is not at the right cannon's end. You can see where the laser is placed when rotating the gun. I tried to make a modell where the right cannon's end is (point(x,y)) when the gun rotates around the point(end_boss_centerx, end_boss_centery) with the angle alpha. I made a drawing, which I used to get a formula for the x component and the y component of the gun's end. The gun is orientated downwards on its picture. Because of this the angle in my code is angle = alpha - 90. I isolated the problem of the firing end boss from my original code in a code snippet to can better focus on it. And it is easier for someone how wants to help me too, I think. I don't know how to make it right and can't go further. It would be great if someone could help me to position the laser in the right place. (I attached some pictures.) Thanks a lot in advance!! import pygame from math import radians, sin, cos import math pygame.init() screen_width = 800 screen_height = 600 fps = 60 end_boss_cooldown = 3000 # bullet cooldown in milliseconds screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption("End Boss Example") ship_image_path = "ship_4.png" gun_image_path = "gun4.png" laser_image_path = "endboss_laser.png" # Create EndBoss class class EndBoss(pygame.sprite.Sprite): def __init__(self, x, y, health): super().__init__() self.x = x self.y = y self.health_start = health self.health_remaining = health self.image = pygame.image.load(ship_image_path) self.rect = self.image.get_rect() self.rect.center = (x, y) # Create EndBossGun class class EndBossGun(pygame.sprite.Sprite): def __init__(self, x, y, angle): super().__init__() self.x = x self.y = y self.angle = angle self.image = pygame.image.load(gun_image_path) self.gun_image = self.image.copy() self.rect = self.gun_image.get_rect() self.rect.center = (x, y) self.last_shot = pygame.time.get_ticks() def aim(self, p): # Rotate end_boss' gun x_dist = p[0] - self.x y_dist = self.y - (screen_height - 100) self.angle = math.degrees(math.atan2(y_dist, x_dist)) self.image = pygame.transform.rotate(self.gun_image, self.angle + 90) self.rect = self.image.get_rect(center=(self.x, self.y)) return self.angle # Create EndBossLaser class class EndBossLaser(pygame.sprite.Sprite): def __init__(self, angle): super().__init__() self.angle = angle self.image = pygame.image.load(laser_image_path) self.image = pygame.transform.rotate(self.image, angle - 90) self.rect = self.image.get_rect() l = math.sqrt(11 ** 2 + 70 ** 2) beta = math.asin(70 / l) xpos = l * cos(beta - angle) ypos = l * sin(beta - angle) self.rect.center = end_boss_centerx + xpos, end_boss_centery + ypos def update(self, speed, angle): pass # Initialize objects end_boss_centerx = screen_width / 2 end_boss_centery = 195 end_boss = EndBoss(end_boss_centerx, end_boss_centery, 300) end_boss_group = pygame.sprite.Group() end_boss_group.add(end_boss) end_boss_gun = EndBossGun(end_boss_centerx, end_boss_centery, 0) end_boss_gun_group = pygame.sprite.Group() end_boss_gun_group.add(end_boss_gun) end_boss_laser_group = pygame.sprite.Group() def draw_bg(): screen.fill((0, 0, 0)) # Dummy spaceship class for testing class Spaceship(pygame.sprite.Sprite): def __init__(self): super().__init__() self.image = pygame.Surface((50, 50)) self.image.fill((255, 255, 255)) self.rect = self.image.get_rect() self.rect.center = (screen_width // 2, screen_height - 50) def update(self): pass def move(self, x): self.rect.centerx = x spaceship = Spaceship() spaceship_group = pygame.sprite.Group() spaceship_group.add(spaceship) single_bullet_group = pygame.sprite.Group() explosion_group = pygame.sprite.Group() font40 = pygame.font.Font(None, 40) def draw_text(text, font, color, x, y): img = font.render(text, True, color) screen.blit(img, (x, y)) def play_end_boss_level(): game_over = 0 last_count = pygame.time.get_ticks() last_end_boss_shot = pygame.time.get_ticks() countdown = 3 run = True while run: p = pygame.mouse.get_pos() clock.tick(fps) draw_bg() if countdown == 0: angle = end_boss_gun.aim(p) time_now = pygame.time.get_ticks() if time_now - last_end_boss_shot > end_boss_cooldown and len(end_boss_group) > 0: end_boss_laser = EndBossLaser(angle) end_boss_laser_group.add(end_boss_laser) last_end_boss_shot = time_now if len(end_boss_group) == 0: game_over = 1 if game_over == 0: end_boss_gun.update() spaceship.update() single_bullet_group.update() end_boss_group.update() end_boss_laser_group.update(1, angle) end_boss_gun_group.update() explosion_group.update() else: if game_over == -1: draw_text('GAME OVER!', font40, (255, 255, 255), int(screen_width / 2 - 100), int(screen_height / 2 + 50)) if game_over == 1: draw_text('YOU WIN!', font40, (255, 255, 255), int(screen_width / 2 - 100), int(screen_height / 2 + 50)) if countdown > 0: draw_text('GET READY!', font40, (255, 255, 255), int(screen_width / 2 - 110), int(screen_height / 2 + 50)) draw_text(str(countdown), font40, (255, 255, 255), int(screen_width / 2 - 10), int(screen_height / 2 + 100)) count_timer = pygame.time.get_ticks() if count_timer - last_count > 1000: countdown -= 1 last_count = count_timer spaceship_group.draw(screen) single_bullet_group.draw(screen) explosion_group.draw(screen) end_boss_group.draw(screen) end_boss_gun_group.draw(screen) end_boss_laser_group.draw(screen) for event in pygame.event.get(): if event.type == pygame.QUIT: run = False spaceship.move(p[0]) pygame.display.flip() pygame.quit() clock = pygame.time.Clock() if __name__ == "__main__": play_end_boss_level() RE: positioning the laser - flash77 - Jun-17-2024 Hi, I got an idea what I could do... I will present the new idea when I implemented it. I just wanted to inform you... RE: positioning the laser - deanhystad - Jun-19-2024 Let pygame do the math. Below I use a 2D vector to compute the location of the rotated part relative to the base object. For you the rotated part is the laser, and the base object is the turret. import pygame from PIL import Image class Block(pygame.sprite.Sprite): """Simple sprite that creates a rectamgle image.""" def __init__(self, color, width, height): super().__init__() pygame.sprite.Sprite.__init__(self) image = Image.new("RGBA", (width, height), color) self.image = pygame.image.fromstring(image.tobytes(), image.size, image.mode) self.clean_image = self.image.copy() self.rect = self.image.get_rect() @property def center(self): """Return center of image.""" return self.rect.center @center.setter def center(self, coordinates): """Set center of image.""" self.rect.center = coordinates def rotate(self, angle): """Rotate angle degreec CCW about the center.""" center = self.center self.image = pygame.transform.rotate(self.clean_image, angle) self.rect = self.image.get_rect() self.center = center class Component(Block): """A sprite that is part of a component sprite.""" def __init__(self, color, width, height, offset): super().__init__(color, width, height) self.base = None self.offset = pygame.math.Vector2(offset) def rotate(self, angle): """Rotate angle degrees CCW about the base.""" super().rotate(angle) self.center = self.base.center + self.offset.rotate(-angle) class Composite: """A "sprite" made up of multiple sprites.""" def __init__(self, base): self.base = base self.parts = [] self.group = pygame.sprite.Group(base) self.angle = 0 self.x, self.y = base.center def add_part(self, part): """Add a part that moves witht he base.""" part.base = self.base self.parts.append(part) self.group.add(part) def move(self, dx=0): """Move the sprites forward or backward.""" x, y = pygame.math.Vector2(dx, 0).rotate(-self.angle) self.x += x self.y += y self.base.center = self.x, self.y def rotate(self, angle): """Increment/decrement the rotation angle..""" self.angle = (self.angle + angle) % 360 def draw(self, screen): """Update and draw all the sprites.""" for sprite in self.group: sprite.rotate(self.angle) self.group.draw(screen) pygame.init() screen = pygame.display.set_mode((300, 300)) clock = pygame.time.Clock() turret = Composite(Block("yellow", 30, 60)) turret.add_part(Component("blue", 60, 10, (45, 20))) turret.add_part(Component("green", 80, 5, (55, 0))) turret.add_part(Component("red", 60, 10, (45, -20))) turret.add_part(Component("green", 10, 20, (100, 0))) running = True key_down = None dx = 1 rx = 0.5 while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN: key_down = event.key elif event.type == pygame.KEYUP: key_down = None if key_down == pygame.K_LEFT: turret.rotate(rx) elif key_down == pygame.K_RIGHT: turret.rotate(-rx) elif key_down == pygame.K_UP: turret.move(dx) elif key_down == pygame.K_DOWN: turret.move(-dx) screen.fill("black") turret.draw(screen) pygame.display.flip() clock.tick(60) pygame.quit() RE: positioning the laser - flash77 - Jun-24-2024 Hello, thank you very much for the detailed example! I have been trying for a long time to adopt (adapt) things from it for my code, but unfortunately I still can't figure it out... So I have to ask for help again. I can't get the offset to work. I don't understand how "offset" is used here: "self.offset.rotate(-angle)". I got problems with line 83 and line 136. Perhaps you could please explain in more detail what I'm doing wrong using my code? I would really appreciate it, as I'm stuck at the moment. Thank you very much in advance! import pygame from math import radians, sin, cos import math pygame.init() screen_width = 800 screen_height = 600 fps = 60 end_boss_cooldown = 3000 # bullet cooldown in milliseconds screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption("End Boss Example") ship_image_path = "ship_4.png" gun_image_path = "gun4.png" laser_image_path = "endboss_laser.png" class EndBossGun(pygame.sprite.Sprite): def __init__(self, x, y, angle): super().__init__() self.x = x self.y = y self.angle = angle self.image = pygame.image.load(gun_image_path) self.gun_image = self.image.copy() self.rect = self.gun_image.get_rect() self.rect.center = (x, y) self.last_shot = pygame.time.get_ticks() def aim(self, p): # Rotate end_boss' gun x_dist = p[0] - self.x y_dist = self.y - (screen_height - 100) self.angle = math.degrees(math.atan2(y_dist, x_dist)) + 90 self.image = pygame.transform.rotate(self.gun_image, self.angle) self.rect = self.image.get_rect(center=(self.x, self.y)) return self.angle # Create EndBoss class class EndBoss(pygame.sprite.Sprite): def __init__(self, x, y, health): super().__init__() self.x = x self.y = y self.health_start = health self.health_remaining = health self.image = pygame.image.load(ship_image_path) self.rect = self.image.get_rect() self.rect.center = (x, y) # Initialize objects end_boss_centerx = screen_width / 2 end_boss_centery = 195 end_boss_center = end_boss_centerx, end_boss_centery end_boss = EndBoss(end_boss_centerx, end_boss_centery, 300) end_boss_group = pygame.sprite.Group() end_boss_group.add(end_boss) end_boss_gun = EndBossGun(end_boss_centerx, end_boss_centery, 0) end_boss_gun_group = pygame.sprite.Group() end_boss_gun_group.add(end_boss_gun) end_boss_laser_group = pygame.sprite.Group() # Create EndBossLaser class class EndBossLaser(pygame.sprite.Sprite): def __init__(self, x, y, offset): super().__init__() self.x = x self.y = y self.offset = pygame.math.Vector2(offset) self.image = pygame.image.load(laser_image_path) self.clean_image = self.image.copy() self.rect = self.clean_image.get_rect() self.rect.center = x, y def rotate(self, angle): # justify laser in the same angle of the gun self.image = end_boss_center + self.offset.rotate(self.clean_image, angle) def draw_bg(): screen.fill((0, 0, 0)) # Dummy spaceship class for testing class Spaceship(pygame.sprite.Sprite): def __init__(self): super().__init__() self.image = pygame.Surface((50, 50)) self.image.fill((255, 255, 255)) self.rect = self.image.get_rect() self.rect.center = (screen_width // 2, screen_height - 50) def update(self): pass def move(self, x): self.rect.centerx = x spaceship = Spaceship() spaceship_group = pygame.sprite.Group() spaceship_group.add(spaceship) single_bullet_group = pygame.sprite.Group() explosion_group = pygame.sprite.Group() font40 = pygame.font.Font(None, 40) def draw_text(text, font, color, x, y): img = font.render(text, True, color) screen.blit(img, (x, y)) def play_end_boss_level(): game_over = 0 last_count = pygame.time.get_ticks() last_end_boss_shot = pygame.time.get_ticks() countdown = 3 run = True while run: p = pygame.mouse.get_pos() clock.tick(fps) draw_bg() if countdown == 0: angle = end_boss_gun.aim(p) time_now = pygame.time.get_ticks() if time_now - last_end_boss_shot > end_boss_cooldown and len(end_boss_group) > 0: # start position laser end_boss_laser = EndBossLaser(end_boss_centerx, end_boss_centery, pygame.math.Vector2(30.0, 70.0)) end_boss_laser.rotate(angle) end_boss_laser_group.add(end_boss_laser) last_end_boss_shot = time_now if len(end_boss_group) == 0: game_over = 1 if game_over == 0: end_boss_gun.update() spaceship.update() single_bullet_group.update() end_boss_group.update() end_boss_gun_group.update() explosion_group.update() else: if game_over == -1: draw_text('GAME OVER!', font40, (255, 255, 255), int(screen_width / 2 - 100), int(screen_height / 2 + 50)) if game_over == 1: draw_text('YOU WIN!', font40, (255, 255, 255), int(screen_width / 2 - 100), int(screen_height / 2 + 50)) if countdown > 0: draw_text('GET READY!', font40, (255, 255, 255), int(screen_width / 2 - 110), int(screen_height / 2 + 50)) draw_text(str(countdown), font40, (255, 255, 255), int(screen_width / 2 - 10), int(screen_height / 2 + 100)) count_timer = pygame.time.get_ticks() if count_timer - last_count > 1000: countdown -= 1 last_count = count_timer spaceship_group.draw(screen) single_bullet_group.draw(screen) explosion_group.draw(screen) end_boss_group.draw(screen) end_boss_gun_group.draw(screen) end_boss_laser_group.draw(screen) for event in pygame.event.get(): if event.type == pygame.QUIT: run = False spaceship.move(p[0]) pygame.display.flip() pygame.quit() clock = pygame.time.Clock() if __name__ == "__main__": play_end_boss_level()
RE: positioning the laser - flash77 - Jun-27-2024 Hi, I managed to roughly rotate the laser around the center of the end boss. Later on, you can probably position the laser more precisely at the ends of the cannons. I created the laser at a starting position and then rotated it by the angle alpha around the center of the end boss. I also had to rotate the laser image itself by the angle alpha. Unfortunately, I haven't managed to properly apply deanhystad's solution to my game yet - sorry... So I experimented with the code I posted below. I would really appreciate some hints if I'm on the right way. Thanks a lot!! import pygame from math import radians, sin, cos import math from pygame import Vector2 pygame.init() screen_width = 800 screen_height = 600 fps = 60 end_boss_cooldown = 3000 # bullet cooldown in milliseconds screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption("End Boss Example") ship_image_path = "ship_4.png" gun_image_path = "gun4.png" laser_image_path = "endboss_laser.png" class EndBossGun(pygame.sprite.Sprite): def __init__(self, x, y, angle): super().__init__() self.x = x self.y = y self.angle = angle self.image = pygame.image.load(gun_image_path) self.gun_image = self.image.copy() self.rect = self.gun_image.get_rect() self.rect.center = (x, y) self.last_shot = pygame.time.get_ticks() def aim(self, p): # Rotate end_boss' gun x_dist = p[0] - self.x y_dist = self.y - (screen_height - 100) self.angle = math.degrees(math.atan2(y_dist, x_dist)) + 90 self.image = pygame.transform.rotate(self.gun_image, self.angle) self.rect = self.image.get_rect(center=(self.x, self.y)) return self.angle # Create EndBoss class class EndBoss(pygame.sprite.Sprite): def __init__(self, x, y, health): super().__init__() self.x = x self.y = y self.health_start = health self.health_remaining = health self.image = pygame.image.load(ship_image_path) self.rect = self.image.get_rect() self.rect.center = (x, y) # Initialize objects end_boss_centerx = screen_width / 2 end_boss_centery = 195 end_boss_center = end_boss_centerx, end_boss_centery end_boss = EndBoss(end_boss_centerx, end_boss_centery, 300) end_boss_group = pygame.sprite.Group() end_boss_group.add(end_boss) end_boss_gun = EndBossGun(end_boss_centerx, end_boss_centery, 0) end_boss_gun_group = pygame.sprite.Group() end_boss_gun_group.add(end_boss_gun) end_boss_gun_length = 70 end_boss_laser_start_pos = end_boss_centerx, end_boss_centery + end_boss_gun_length end_boss_laser_group = pygame.sprite.Group() # Create EndBossLaser class class EndBossLaser(pygame.sprite.Sprite): def __init__(self, x, y, angle): pygame.sprite.Sprite.__init__(self) self.x = x self.y = y self.angle = angle self.image = pygame.image.load("endboss_laser.png") self.clean_image = self.image.copy() self.rect = self.image.get_rect() self.rect.midtop = x, y def rotate_laser_around_endboss_center(self, point, angle, pivot_point=(end_boss_centerx, end_boss_centery)): # justify laser in the same angle of the gun angle_radians = angle / 180 * math.pi x, y = point offset_x, offset_y = pivot_point adjusted_x = (x - offset_x) adjusted_y = (y - offset_y) cos_rad = math.cos(angle_radians) sin_rad = math.sin(angle_radians) qx = offset_x + cos_rad * adjusted_x + sin_rad * adjusted_y qy = offset_y + -sin_rad * adjusted_x + cos_rad * adjusted_y self.rect.center = qx, qy self.image = pygame.transform.rotate(self.clean_image, angle) def update(self): pass def draw_bg(): screen.fill((0, 0, 0)) # Dummy spaceship class for testing class Spaceship(pygame.sprite.Sprite): def __init__(self): super().__init__() self.image = pygame.Surface((50, 50)) self.image.fill((255, 255, 255)) self.rect = self.image.get_rect() self.rect.center = (screen_width // 2, screen_height - 50) def update(self): pass def move(self, x): self.rect.centerx = x spaceship = Spaceship() spaceship_group = pygame.sprite.Group() spaceship_group.add(spaceship) single_bullet_group = pygame.sprite.Group() explosion_group = pygame.sprite.Group() font40 = pygame.font.Font(None, 40) def draw_text(text, font, color, x, y): img = font.render(text, True, color) screen.blit(img, (x, y)) def play_end_boss_level(): game_over = 0 last_count = pygame.time.get_ticks() last_end_boss_shot = pygame.time.get_ticks() countdown = 3 run = True while run: p = pygame.mouse.get_pos() clock.tick(fps) draw_bg() if countdown == 0: angle = end_boss_gun.aim(p) time_now = pygame.time.get_ticks() if time_now - last_end_boss_shot > end_boss_cooldown and len(end_boss_group) > 0: # start position laser end_boss_laser = EndBossLaser(end_boss_centerx, end_boss_centery + end_boss_gun_length, angle) end_boss_laser_group.add(end_boss_laser) end_boss_laser.rotate_laser_around_endboss_center(end_boss_laser_start_pos, angle) last_end_boss_shot = time_now if len(end_boss_group) == 0: game_over = 1 if game_over == 0: end_boss_gun.update() spaceship.update() single_bullet_group.update() end_boss_group.update() end_boss_gun_group.update() explosion_group.update() end_boss_laser_group.update() else: if game_over == -1: draw_text('GAME OVER!', font40, (255, 255, 255), int(screen_width / 2 - 100), int(screen_height / 2 + 50)) if game_over == 1: draw_text('YOU WIN!', font40, (255, 255, 255), int(screen_width / 2 - 100), int(screen_height / 2 + 50)) if countdown > 0: draw_text('GET READY!', font40, (255, 255, 255), int(screen_width / 2 - 110), int(screen_height / 2 + 50)) draw_text(str(countdown), font40, (255, 255, 255), int(screen_width / 2 - 10), int(screen_height / 2 + 100)) count_timer = pygame.time.get_ticks() if count_timer - last_count > 1000: countdown -= 1 last_count = count_timer spaceship_group.draw(screen) single_bullet_group.draw(screen) explosion_group.draw(screen) end_boss_group.draw(screen) end_boss_gun_group.draw(screen) end_boss_laser_group.draw(screen) for event in pygame.event.get(): if event.type == pygame.QUIT: run = False spaceship.move(p[0]) pygame.display.flip() pygame.quit() clock = pygame.time.Clock() if __name__ == "__main__": play_end_boss_level() |