Search code examples
pythonpython-3.xpygame2d-games

How can I extend the unit selection logic in my RTS game to apply to multiple units?


Currently if you left-click on the unit, it becomes 'selected' (or 'de-selected'), and a green square is drawn around it. Then when you right-click somewhere on the screen, the unit moves neatly into the square in the location that you clicked.

Also if you use the up, down, left or right keys it will scroll the screen.

import pygame
import random
pygame.init()

#Define mouse position
mouse_position_x = 525
mouse_position_y = 315

# Define colors
green = (0,255,0)
brown = (150,75,0)

#Define border position
border_x = 0
border_y = 0

#Define character selection box
def character_selection_box():
    pygame.draw.line(screen,green,(character_location_x,character_location_y),(character_location_x+character_width,character_location_y),2) # Top bar
    pygame.draw.line(screen,green,(character_location_x,character_location_y+character_height),(character_location_x+character_width,character_location_y+character_height),2) # Bottom bar
    pygame.draw.line(screen,green,(character_location_x,character_location_y),(character_location_x,character_location_y+character_height),2) # Left bar
    pygame.draw.line(screen,green,(character_location_x+character_width,character_location_y),(character_location_x+character_width,character_location_y+character_height+1),2) # Right bar

#Define round
def assign_square(n):
    div = (n/35)
    rou = round(div)
    mul = (35*rou)
    return int(mul)

#Set window
screen_width = 981
screen_height = 700
game_screen_width = 800
game_screen_height = 700
screen_size = (screen_width,screen_height)
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption("Warpath")

#Set block character
character_width = 35
character_height = 35
character_location_x = 525
character_location_y = 315
movement = 1
unit_selected = 0

#Load images
orc_grunt_forward = pygame.image.load('orc_forward3.png') #(35 by 35 pixel image)
character_image = orc_grunt_forward

#Loop until the user clicks the close button
shutdown = False

#Set clock
clock = pygame.time.Clock()

#Set scroll limit
scroll_x = 0
scroll_y = 0

 # ---------- Main program loop -----------
while not shutdown:

    # --- Main event loop ---
    for event in pygame.event.get():

        # --- If quit button pressed, shutdown
        if event.type == pygame.QUIT: 
            shutdown = True

        # --- If mouse button pressed
        elif event.type == pygame.MOUSEBUTTONDOWN: # If a mouse button is pressed
            mouse_position = pygame.mouse.get_pos() # Get mouse position
            button_type = pygame.mouse.get_pressed() # Check which button was pressed

            # --- If left click pressed and the curser was on a character, select that character 
            if button_type[0] == 1 and mouse_position[0] >= character_location_x and mouse_position[0] <= character_location_x + character_width and mouse_position[1] >= character_location_y and mouse_position[1] <= character_location_y + character_height:
                print("Unit selected",unit_selected)
                print(button_type)
                unit_selected += 1
                unit_selected /= unit_selected #(Otherwise it will add up unit selected if you click more than once)
                int(unit_selected)

            # --- If right click pressed and a character was selected (and it's within the game screen), move the character to the location     
            elif button_type[2] == 1 and unit_selected == 1 and mouse_position[0] > 175: 
                mouse_position_x *= 0
                mouse_position_y *= 0

                if mouse_position[0] >= assign_square(mouse_position[0]):
                    mouse_position_x += assign_square(mouse_position[0])

                elif mouse_position[0] <= assign_square(mouse_position[0]):
                    mouse_position_x -= 35
                    mouse_position_x += assign_square(mouse_position[0])

                if mouse_position[1] >= assign_square(mouse_position[1]):
                    mouse_position_y += assign_square(mouse_position[1])

                elif mouse_position[1] <= assign_square(mouse_position[1]):
                    mouse_position_y -= 35
                    mouse_position_y += assign_square(mouse_position[1])

            # --- If left click pressed and the curser was not on a character, deselect the character        
            elif button_type[0] == 1 and mouse_position[0] < character_location_x or mouse_position[0] > character_location_x + character_width or mouse_position[1] < character_location_y or mouse_position[1] > character_location_y + character_height:
                print("Unit not selected")
                print(button_type)
                unit_selected *= 0
                int(unit_selected)

        # --- If key pressed, scroll the screen
        elif event.type == pygame.KEYDOWN:

            if event.key == pygame.K_RIGHT and scroll_x > -10:
                direction = "right"
                character_location_x -= 35
                mouse_position_x -= 35
                border_x -= 35
                scroll_x -= 1

            if event.key == pygame.K_LEFT and scroll_x < 10:
                direction = "left"
                character_location_x += 35
                mouse_position_x += 35
                border_x += 35
                scroll_x += 1

            if event.key == pygame.K_UP and scroll_y < 10:
                direction = "up"
                character_location_y += 35
                mouse_position_y += 35
                border_y += 35
                scroll_y += 1

            if event.key == pygame.K_DOWN and scroll_y > -10:
                direction = "down"
                character_location_y -= 35
                mouse_position_y -= 35
                border_y -= 35
                scroll_y -= 1

    # --- Game logic ---

    # --- Set character movement      
    if character_location_x < mouse_position_x:
        character_location_x += movement
    if character_location_x > mouse_position_x:
        character_location_x -= movement
    if character_location_y < mouse_position_y:
        character_location_y += movement
    if character_location_y > mouse_position_y:
        character_location_y -= movement

    # --- Drawing ---
    screen.fill(brown) # Draw background
    screen.blit(character_image,(character_location_x,character_location_y)) # Draw character

    if unit_selected == 1: 
        character_selection_box() # Draw character selection box if unit is selected

    clock.tick(30)
    pygame.display.flip()

#Shutdown
if shutdown == True:
    pygame.quit()

The problem is that I can't figure out how to extend this to multiple units - currently if I want to add more units I can only either manage to:

a) Move them all at once

or

b) Paste the same code multiple times adjusting the character variable (not a robust / scalable solution)

How can I adjust my code so that I have a scalable solution where:

1) I can select a single unit and move it, without moving every unit at once

2) I can select multiple units by clicking on each one individually, and move them all at once (not worrying about pathfinding right now)

I also tried using classes to achieve this but it still felt like I was copying / pasting multiple functions rather than having a robust solution.

I've removed any code that doesn't concern the issue while keeping a functioning program.

Thanks


Solution

  • There are few things to do:

    1. Change variables character_* to object that holds all data about the unit.
    2. Create array of units / characters. That way each unit in array can have unique position, velocity ets.
    3. Everywhere in code where you check character_*, change to for loops where you iterate over characters array to check every unit.
    4. Next step should be adding functions like move / shoot to character class, to make keypress event working for multiple units.

    That should give you code where you can select multiple units (if they occupy same spot) and move them independently of deselected units.