Search code examples
pythonpython-3.xooppygamegame-engine

PyGame draw a square inside a square


I'm writing a grid-based game where instances of the Character class can move around using their move(direction) method.

class Character(object):                    #can move around and do cool stuff
    def __init__(self, name, HP, internal_column, internal_row):
        self.name = name
        self.HP = HP
        self.internal_column = internal_column
        self.internal_row = internal_row

    moves_left = 15
    direction = "N"

    def move(self, direction):      #how characters move around

        if self.moves_left == 0:
            print("No more moves left")
            return

        elif direction == "N":                    
                self.internal_row -= 1
                self.direction = "N"
        elif direction == "W":
                self.internal_column -= 1
                self.direction = "W"
        elif direction == "E":
                self.internal_column += 1
                self.direction = "E"
        elif direction == "S":
                self.internal_row += 1
                self.direction = "S"

        self.moves_left = self.moves_left - 1

The direction variable keeps track of where the character is pointing. I draw the graphics using PyGame:

for row in range(MAPSIZE):           # Drawing grid
        for column in range(MAPSIZE):
            for i in range(0, len(Map.Grid[column][row])):
                Color = WHITE

                if len(Map.can_move) > 0:   # Creating colored area around character showing his move range
                    if (math.sqrt((Map.can_move[0].internal_column - column)**2 + (Map.can_move[0].internal_row - row)**2)) <= Map.can_move[0].moves_left:

                        Color = MOVECOLOR

                if len(Map.Grid[column][row]) > 1:
                    Color = RED
                if Map.Grid[column][row][i].name == "Tree":
                    Color = GREEN
                if str(Map.Grid[column][row][i].__class__.__name__) == "Character":
                    Color = BROWN        

            pygame.draw.rect(Screen, Color, [(TILEMARGIN + TILEWIDTH) * column + TILEMARGIN,
                                             (TILEMARGIN + TILEHEIGHT) * row + TILEMARGIN,
                                             TILEWIDTH,
                                             TILEHEIGHT])

Here is the full code, where the constants and the Map class are defined:

import random   
import pygame
import math

pygame.init()                                 
Clock = pygame.time.Clock()                   
Screen = pygame.display.set_mode([650, 650])  
DONE = False                                  
MAPSIZE = 25   #how many tiles

TILEWIDTH  = 20    #pixel size of tile                               
TILEHEIGHT = 20
TILEMARGIN = 4

BLACK = (0, 0, 0)                             
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
BROWN = (123, 123, 0)
MOVECOLOR = (150, 250, 150)


KeyLookup = {
    pygame.K_LEFT: "W",
    pygame.K_RIGHT: "E",
    pygame.K_DOWN: "S",
    pygame.K_UP: "N"
}

class MapTile(object):                       #The main class for stationary things that inhabit the grid ... grass, trees, rocks and stuff.
    def __init__(self, name, internal_column, internal_row):
        self.name = name
        self.internal_column = internal_column
        self.internal_row = internal_row


class Character(object):                    #can move around and do cool stuff
    def __init__(self, name, HP, internal_column, internal_row):
        self.name = name
        self.HP = HP
        self.internal_column = internal_column
        self.internal_row = internal_row

    moves_left = 15
    direction = "N"

    def move(self, direction):      #how characters move around
        if self.collision_check(direction) == True:
            print("Collision")
            return
        if self.moves_left == 0:
            print("No more moves left")
            return

        elif direction == "N":                    
                self.internal_row -= 1
                self.direction = "N"
        elif direction == "W":
                self.internal_column -= 1
                self.direction = "W"
        elif direction == "E":
                self.internal_column += 1
                self.direction = "E"
        elif direction == "S":
                self.internal_row += 1
                self.direction = "S"

        self.moves_left = self.moves_left - 1


    def collision_check(self, direction):       
        if direction == "N":
            if self.internal_row == 0:
                return True
            if len(Map.Grid[self.internal_column][(self.internal_row)-1]) > 1:
                return True
        elif direction == "W":
            if self.internal_column == 0:
                return True
            if len(Map.Grid[self.internal_column-1][(self.internal_row)]) > 1:
                return True
        elif direction == "E":
            if self.internal_column == MAPSIZE-1:
                return True
            if len(Map.Grid[self.internal_column+1][(self.internal_row)]) > 1:
                return True
        elif direction == "S":
            if self.internal_row == MAPSIZE-1:
                return True
            if len(Map.Grid[self.internal_column][(self.internal_row)+1]) > 1:
                return True

        return False

    def attack(self, direction):
        if self.collision_check(direction) == True:
            print("attacked")


class Goblin(Character):
    def __init__(self):
        Character.__init__(self, "Goblin", random.randint(15,20), random.randint(0,MAPSIZE-1), random.randint(0,MAPSIZE-1))

    def random_move(self):
        i = random.randint(0,3)
        if i == 0:
            self.move("N")
        elif i == 1:
            self.move("S")
        elif i == 2:
            self.move("W")
        elif i == 3:
            self.move("E")

        self.moves_left = 10


class Archer(Character):
    def __init__(self):
        Character.__init__(self, "Archer", random.randint(15,20), random.randint(0,MAPSIZE-1), random.randint(0,MAPSIZE-1))


class Warrior(Character):
    def __init__(self):
        Character.__init__(self, "Warrior", random.randint(15,20), random.randint(0,MAPSIZE-1), random.randint(0,MAPSIZE-1))


class Scout(Character):
    def __init__(self):
        Character.__init__(self, "Scout", random.randint(15,20), random.randint(0,MAPSIZE-1), random.randint(0,MAPSIZE-1))


class Rogue(Character):
    def __init__(self):
        Character.__init__(self, "Rogue", random.randint(15,20), random.randint(0,MAPSIZE-1), random.randint(0,MAPSIZE-1))


class Wizard(Character):
    def __init__(self):
        Character.__init__(self, "Wizard", random.randint(15,20), random.randint(0,MAPSIZE-1), random.randint(0,MAPSIZE-1))



class Map(object):              #The main class; where the action happens
    global MAPSIZE
    wild_characters = []
    can_move = []
    no_moves = []
    Grid = []

    for row in range(MAPSIZE):     # Creating grid
        Grid.append([])
        for column in range(MAPSIZE):
            Grid[row].append([])

    for row in range(MAPSIZE):     #Filling grid with grass
        for column in range(MAPSIZE):
            TempTile = MapTile("Grass", column, row)
            Grid[column][row].append(TempTile)

    for row in range(MAPSIZE):     #Putting some rocks near the top
        for column in range(MAPSIZE):
            TempTile = MapTile("Rock", column, row)
            if row == 1:
                Grid[column][row].append(TempTile)

    for i in range(10):         #Trees in random places
        random_row = random.randint(0, MAPSIZE - 1)
        random_column = random.randint(0, MAPSIZE - 1)
        TempTile = MapTile("Tree", random_column, random_row)
        Grid[random_column][random_row].append(TempTile)

    def generate_hero(self):            #Generate a character and place it randomly
        random_row = random.randint(0, MAPSIZE - 1)      
        random_column = random.randint(0, MAPSIZE - 1)
        id_number = len(Map.can_move) + random.randint(0,100)
        temp_hero = Character(str("Hero " + str(id_number)), 10, random_column, random_row)
        self.Grid[random_column][random_row].append(temp_hero)
        self.can_move.append(temp_hero)


    def generate_goblin(self):            #Generate a character and place it randomly
        random_row = random.randint(0, MAPSIZE - 1)

        random_column = random.randint(0, MAPSIZE - 1)
        temp_goblin = Goblin()
        self.Grid[random_column][random_row].append(temp_goblin)
        Map.wild_characters.append(temp_goblin)

    def update(self):           #Important function
        for column in range(MAPSIZE):                           #These nested loops go through entire grid 
            for row in range(MAPSIZE):                          #They check if any objects internal coordinates
                for i in range(len(Map.Grid[column][row])):     #disagree with its place on the grid and update it accordingly

                    if Map.Grid[column][row][i].internal_column != column:
                        TempChar = Map.Grid[column][row][i]
                        Map.Grid[column][row].remove(Map.Grid[column][row][i])
                        Map.Grid[int(TempChar.internal_column)][int(TempChar.internal_row)].append(TempChar)

                    elif Map.Grid[column][row][i].internal_row != row:
                        TempChar = Map.Grid[column][row][i]
                        Map.Grid[column][row].remove(Map.Grid[column][row][i])
                        Map.Grid[int(TempChar.internal_column)][int(TempChar.internal_row)].append(TempChar)


        temparr = Map.no_moves[:]
        for characterobject in temparr:    #This moves any characters with moves to the can move list 
            if len(temparr) > 0:
                if characterobject.moves_left > 0:
                    print("character moved from no moves to moves")
                    Map.can_move.append(characterobject)
                    Map.no_moves.remove(characterobject)

        arr = Map.can_move[:] # copy
        for item in arr:
            if item.moves_left == 0:
                print("character moved from moves to no moves")
                Map.can_move.remove(item)
                Map.no_moves.append(item)


Map = Map()
Map.generate_hero()

while not DONE:     #Main pygame loop

    for event in pygame.event.get():         #catching events
        if event.type == pygame.QUIT:
            DONE = True       

        elif event.type == pygame.MOUSEBUTTONDOWN:
            Pos = pygame.mouse.get_pos()
            column = Pos[0] // (TILEWIDTH + TILEMARGIN)  #Translating the position of the mouse into rows and columns
            row = Pos[1] // (TILEHEIGHT + TILEMARGIN)
            print(str(row) + ", " + str(column))

            for i in range(len(Map.Grid[column][row])):
                print(str(Map.Grid[column][row][i].name))  #print stuff that inhabits that square

        elif event.type == pygame.KEYDOWN:

            wild_char_arr = Map.wild_characters[:]
            for wildcharacter in wild_char_arr:
                if wildcharacter.name == "Goblin":
                    print("Goblin moved")
                    wildcharacter.random_move() 

            if event.key == 97:      # Keypress: a
                print("New turn.")
                temparr = Map.no_moves[:]
                for item in temparr:
                    if item.moves_left == 0:
                        item.moves_left = 15

            elif event.key == 115:    # Keypress: s
                print("Generated hero.")
                Map.generate_hero()

            elif event.key == 100:  # Keypress: d
                Map.generate_goblin()
                print("Generated goblin.")

            elif event.key == 102:  # Keypress: f
                Map.can_move[0].attack("N") 

            elif len(Map.can_move) > 0:
                Map.can_move[0].move(KeyLookup[event.key])

            else:
                print("invalid")

            Map.update()

    Screen.fill(BLACK)



    for row in range(MAPSIZE):           # Drawing grid
        for column in range(MAPSIZE):
            for i in range(0, len(Map.Grid[column][row])):
                Color = WHITE

                if len(Map.can_move) > 0:   # Creating colored area around character showing his move range
                    if (math.sqrt((Map.can_move[0].internal_column - column)**2 + (Map.can_move[0].internal_row - row)**2)) <= Map.can_move[0].moves_left:
                        Color = MOVECOLOR

                if len(Map.Grid[column][row]) > 1:
                    Color = RED
                if Map.Grid[column][row][i].name == "Tree":
                    Color = GREEN
                if str(Map.Grid[column][row][i].__class__.__name__) == "Character":
                    Color = BROWN

            pygame.draw.rect(Screen, Color, [(TILEMARGIN + TILEWIDTH) * column + TILEMARGIN,
                                             (TILEMARGIN + TILEHEIGHT) * row + TILEMARGIN,
                                             TILEWIDTH,
                                             TILEHEIGHT])

    Clock.tick(60)      
    pygame.display.flip()     


pygame.quit()

The problem I'm having is displaying the character's direction graphically. All I want to do is draw a small, red square inside the character's grid tile on the side they are facing. For example, if they are facing north, the small red square would be on the top side of the character's grid square.

How can I do this?


Solution

  • Ended up doing it like this:

        if str(Map.Grid[column][row][i].__class__.__name__) == "Character":     #Drawing the little red square showing character direction
            if Map.Grid[column][row][i].direction == "N":
                    Color = RED
                pygame.draw.rect(Screen, Color, [(TILEMARGIN + TILEWIDTH) * column + TILEMARGIN + 8,
                                         (TILEMARGIN + TILEHEIGHT) * row + TILEMARGIN,
                                         TILEWIDTH/4,
                                         TILEHEIGHT/4])
        if str(Map.Grid[column][row][i].__class__.__name__) == "Character": #Drawing the little red square showing character directio
            if Map.Grid[column][row][i].direction == "S":
                Color = RED
                pygame.draw.rect(Screen, Color, [(TILEMARGIN + TILEWIDTH) * column + TILEMARGIN + 8,
                                         (TILEMARGIN + TILEHEIGHT) * row + TILEMARGIN*5 - 1 ,
                                         TILEWIDTH/4,
                                         TILEHEIGHT/4])
        if str(Map.Grid[column][row][i].__class__.__name__) == "Character": #Drawing the little red square showing character directio
            if Map.Grid[column][row][i].direction == "E":
                Color = RED
                pygame.draw.rect(Screen, Color, [(TILEMARGIN + TILEWIDTH) * column + TILEMARGIN + 15,
                                         (TILEMARGIN + TILEHEIGHT) * row + TILEMARGIN*5 - 8,
                                         TILEWIDTH/4,
                                         TILEHEIGHT/4])
        if str(Map.Grid[column][row][i].__class__.__name__) == "Character": #Drawing the little red square showing character directio
            if Map.Grid[column][row][i].direction == "W":
                Color = RED
                pygame.draw.rect(Screen, Color, [(TILEMARGIN + TILEWIDTH) * column + TILEMARGIN
                                                 ,
                                         (TILEMARGIN + TILEHEIGHT) * row + TILEMARGIN*5 - 8 ,
                                         TILEWIDTH/4,
                                         TILEHEIGHT/4])