j_yerbe
j_yerbe

Reputation: 131

Making bullets travel to cursor

I had an earlier post deleted because there are similar posts. I appreciate that but because I am so inexperienced with pygame (I literally began using it last week), I cant make heads or tails of the code. Also, I am finding it difficult to apply to my game. Firstly, I do not need it to relate to a moving player character as they will always travel from a set position (400, 450). Also, I preferably need it to do this when the left mouse button is pressed but if it is easier to use a key then that is fine. I simply do not have the expertise to use past posts and apply it to my program. Thanks. Just to clarify, my game is going to be a duck hunt-like target shooter game.


#Setting window dimensions and caption (Module 1)

pygame.init()
window = pygame.display.set_mode((800, 575))
pygame.display.set_caption("TARGET PRACTICE")

#Colour variables (Module 1)

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (200, 0, 0)
GREEN = (0, 200, 0)
BLUE = (0, 0, 200)


exec = True

#Target class created (Module 5)

class Target:
  def __init__(self, x, y, h, w, v):
    self.x = x
    self.y = y
    self.h = h
    self.w = w
    self.v = v


#Instantiation of targets (Module 5)

target_1 = Target(0, 80, 60, 40, 0.5)
target_2 = Target(0, 100, 60, 40, 0.5)
target_3 = Target(0, 50, 60, 40, 0.5)
target_4 = Target(0, 75, 60, 40, 0.5)
target_5 = Target(0, 45, 60, 40, 0.5)
target_6 = Target(0, 85, 60, 40, 0.5)


#Declaring variables to be used in the while loop (Module 5)
clock = 0

target_2_threshold = 500
target_3_threshold = 1000
target_4_threshold = 1500
target_5_threshold = 2000
target_6_threshold = 2500


#Setting player sprite dimension variables (Module 6)

player_sprite_x = 357.5
player_sprite_y = 450
player_sprite_h = 125
player_sprite_w = 85


while exec:
  pygame.time.delay(1)
  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      exec = False


  #Defines movement of targets and sets delay between drawings (Module 5)   

  clock += 1
  target_1.x += target_1.v
  if clock > target_2_threshold:
        target_2.x += target_2.v
  if clock > target_3_threshold:
        target_3.x += target_3.v
  if clock > target_4_threshold:
        target_4.x += target_4.v
  if clock > target_5_threshold:
        target_5.x += target_5.v
  if clock > target_6_threshold:
        target_6.x += target_6.v

  #Fill the background (Module 5)

  window.fill(RED)

  #Redraw each target in every frame (Module 5)

  pygame.draw.rect(window, BLUE, (target_1.x, target_1.y, target_1.h, target_1.w))
  if clock > target_2_threshold:
      pygame.draw.rect(window, BLUE, (target_2.x, target_2.y, target_2.h, target_2.w)) 
  if clock > target_3_threshold:
      pygame.draw.rect(window, BLUE, (target_3.x, target_3.y, target_3.h, target_3.w))
  if clock > target_4_threshold:
      pygame.draw.rect(window, BLUE, (target_4.x, target_4.y, target_4.h, target_4.w))
  if clock > target_5_threshold:
      pygame.draw.rect(window, BLUE, (target_5.x, target_5.y, target_5.h, target_5.w))
  if clock > target_6_threshold:
      pygame.draw.rect(window, BLUE, (target_6.x, target_6.y, target_6.h, target_6.w))

  #Draw the player sprite (Module 6)

  pygame.draw.rect(window, BLUE, (player_sprite_x, player_sprite_y, player_sprite_w, player_sprite_h))

  pygame.display.update()


pygame.quit()

Upvotes: -1

Views: 132

Answers (2)

Kingsley
Kingsley

Reputation: 14906

Here are some brief notes to get you pointed in the right direction. People often write questions that amount to "Gimme teh codez!", so very open and broad questions are often greeted (quite rightly) with negativity.

So...

First you need to grab the mouse cursor. This is achieved by handling the mouse events in your main event-loop:

while exec:
    #pygame.time.delay(1)   # <-- DON'T DO THIS
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exec = False
        elif event.type == pygame.MOUSEBUTTONUP:    # mouse button released
            mouse_position = pygame.mouse.get_pos()
            # TODO: handle mouse click at <mouse_position>

Note: I like to use MOUSEBUTTONUP rather than DOWN, because this is how modern GUIs tend to work - on-screen operations activate on release, but it's up to you.

Now you have the click-position, how do you launch a bullet from the bottom-centre of the screen to the mouse co-ordinates?

Well, first what is bottom-centre? It's half the width of the window, and maybe some factor from the bottom. In PyGame the top-left corner of the screen is (0, 0), and the bottom-left is ( 0, window_height-1 ). I always store the window size in variables, so computing this position is easy, and it frees the code from having to be modified if the window/screen size changes.

WINDOW_WIDTH  = 800
WINDOW_HEIGHT = 575

# Firing position is 98% bottom, middle of the window/screen.
start_position = ( ( WINDOW_WIDTH // 2,  int( WINDOW_HEIGHT * 0.98 ) )

So then given a mouse-click, the code needs to make the bullet travel from start_position to mouse_position. This can be calculated, by working out an x and y speed which should be applied, each update, to the projectile. Obviously a projectile travelling straight down has a speed of ( 0, something ), and a projectile going directly right has a speed of ( something, 0 ). Obviously if travelling up or left, the something would be negative because of the way PyGame lays-out its co-ordinates. The point is that there are separate components of change: x for the horizontal movement, and y for the vertical .

To calculate this x and y speed (technically a velocity vector), the code needs to determine the line from start to end, and then normalise it (a fancy way of saying "divide it by it's length").

I'm assume you know the line-length formula already, so this part should be easy. It makes the calculations simpler if the code temporarily offsets both points as-if the origin was at (0,0), since the direction is still the same.

Once the code has the velocity vector (x, y), each animation update-cycle simply add these component-speeds to the co-ordinate of the projectile. You may need to store the co-ordinates as real numbers, since adding small amounts (e.g.: 0.2) to an integer tends to throw away the change. This is because some-integer + 0.2 -> some-integer.

Anyway, see how you go with this information. If trouble persists ... ask another question!

Upvotes: 2

furas
furas

Reputation: 142641

Use event pygame.MOUSEBUTTONDOWN to get mouse click and its position.

You can use pygame.math.Vector2() to create vector between mouse and player. And then you can use normalize() to create value which you can use as bullet's direction.

And keep in variable or list bullet's position and bullet's direction

all_bullets = []

while exec:
  pygame.time.delay(1)
  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      exec = False
    if event.type == pygame.MOUSEBUTTONDOWN:
        if event.button == 1:
            print("[shoot!] mouse position:", event.pos)
            dx = event.pos[0] - (player_sprite_x+ player_sprite_w//2)
            dy = event.pos[1] - player_sprite_y
            direction = pygame.math.Vector2(dx, dy).normalize()
            bullet = {'x': player_sprite_x+42, 'y': player_sprite_y, 'direction': direction}
            all_bullets.append(bullet)

Later you can use for-loop to move every bullet on list and keep only bullets which are still on screen

  all_bullets_keep = []

  for item in all_bullets:
    item['x'] += item['direction'][0] # item['direction'][0] * 2
    item['y'] += item['direction'][1] # item['direction'][1] * 2
    # keep bullet if it is still on screen
    if 0 < item['x'] < 800 and 0 < item['y'] < 575:
          all_bullets_keep.append(item)

  all_bullets = all_bullets_keep

  #print(len(all_bullets), end='\r')

Finally you can use for-loop to draw all bullets

  for item in all_bullets:
    pygame.draw.rect(window, BLUE, (item['x']-5, item['y']-5, 10, 10))

It still need to check collision with targets but it would be easier if you would keep targets on list and use pygame.Rect()` to keep position and size because it has special methods to check collisions.

So now you have work to use lists with tagets and pygame.Rect()


import pygame



pygame.init()
window = pygame.display.set_mode((800, 575))
pygame.display.set_caption("TARGET PRACTICE")

#Colour variables (Module 1)

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (200, 0, 0)
GREEN = (0, 200, 0)
BLUE = (0, 0, 200)


exec = True

#Target class created (Module 5)

class Target:
  def __init__(self, x, y, h, w, v):
    self.x = x
    self.y = y
    self.h = h
    self.w = w
    self.v = v


#Instantiation of targets (Module 5)

target_1 = Target(0, 80, 60, 40, 0.5)
target_2 = Target(0, 100, 60, 40, 0.5)
target_3 = Target(0, 50, 60, 40, 0.5)
target_4 = Target(0, 75, 60, 40, 0.5)
target_5 = Target(0, 45, 60, 40, 0.5)
target_6 = Target(0, 85, 60, 40, 0.5)


#Declaring variables to be used in the while loop (Module 5)
clock = 0

target_2_threshold = 500
target_3_threshold = 1000
target_4_threshold = 1500
target_5_threshold = 2000
target_6_threshold = 2500


#Setting player sprite dimension variables (Module 6)

player_sprite_x = 357.5
player_sprite_y = 450
player_sprite_h = 125
player_sprite_w = 85

all_bullets = []

while exec:
  pygame.time.delay(1)
  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      exec = False
    if event.type == pygame.MOUSEBUTTONDOWN:
        if event.button == 1:
            print("[shoot!] mouse position:", event.pos)
            dx = event.pos[0] - (player_sprite_x+ player_sprite_w//2)
            dy = event.pos[1] - player_sprite_y
            direction = pygame.math.Vector2(dx, dy).normalize()
            bullet = {'x': player_sprite_x+42, 'y': player_sprite_y, 'direction': direction}
            all_bullets.append(bullet)

  #Defines movement of targets and sets delay between drawings (Module 5)   

  clock += 1
  target_1.x += target_1.v
  if clock > target_2_threshold:
        target_2.x += target_2.v
  if clock > target_3_threshold:
        target_3.x += target_3.v
  if clock > target_4_threshold:
        target_4.x += target_4.v
  if clock > target_5_threshold:
        target_5.x += target_5.v
  if clock > target_6_threshold:
        target_6.x += target_6.v

  all_bullets_keep = []

  for item in all_bullets:
    item['x'] += item['direction'][0] # item['direction'][0] * 2
    item['y'] += item['direction'][1] # item['direction'][1] * 2
    # keep bullet if it is still on screen
    if 0 < item['x'] < 800 and 0 < item['y'] < 575:
          all_bullets_keep.append(item)

  all_bullets = all_bullets_keep
  #print(len(all_bullets), end='\r')

  #Fill the background (Module 5)

  window.fill(RED)

  #Redraw each target in every frame (Module 5)

  pygame.draw.rect(window, BLUE, (target_1.x, target_1.y, target_1.h, target_1.w))
  if clock > target_2_threshold:
      pygame.draw.rect(window, BLUE, (target_2.x, target_2.y, target_2.h, target_2.w)) 
  if clock > target_3_threshold:
      pygame.draw.rect(window, BLUE, (target_3.x, target_3.y, target_3.h, target_3.w))
  if clock > target_4_threshold:
      pygame.draw.rect(window, BLUE, (target_4.x, target_4.y, target_4.h, target_4.w))
  if clock > target_5_threshold:
      pygame.draw.rect(window, BLUE, (target_5.x, target_5.y, target_5.h, target_5.w))
  if clock > target_6_threshold:
      pygame.draw.rect(window, BLUE, (target_6.x, target_6.y, target_6.h, target_6.w))

  for item in all_bullets:
    pygame.draw.rect(window, BLUE, (item['x']-5, item['y']-5, 10, 10))

  #Draw the player sprite (Module 6)

  pygame.draw.rect(window, BLUE, (player_sprite_x, player_sprite_y, player_sprite_w, player_sprite_h))

  pygame.display.update()


pygame.quit()

Upvotes: 1

Related Questions