Related
Seen below is the code i have written for a maze game where the user controls a green square which they move around the maze using wasd, where they also have a projectile they can shoot using M1 and aiming the mouse cursor, However I am currently having issues with the moving of the projectile and I am not sure how i will be able to fix this problem.
import random
import tkinter as tk
import time
# Set the size of the maze
maze_width = 800
maze_height = 500
#Creating the character size
character_size = 20
maze = tk.Tk()
canvas = tk.Canvas(maze, width = maze_width, height = maze_height)
canvas.pack()
# Create the game over screen, making it red with text displaying that the game is over
game_over_screen = canvas.create_rectangle(0, 0, maze_width, maze_height, fill='red')
game_over_text = canvas.create_text(maze_width/2, maze_height/2, text='GAME OVER!', font=('Arial', 30), fill='white')
# Hide the game over screen so that it is only displayed when the player dies
canvas.itemconfig(game_over_screen, state='hidden')
canvas.itemconfig(game_over_text, state='hidden')
# Create the victory screen which will be siaplayed when the user escapes the maze
victory_screen = canvas.create_rectangle(0, 0, maze_width, maze_height, fill='green')
victory_text = canvas.create_text(maze_width/2, maze_height/2, text='YOU ESCAPED THE MAZE!', font=('Arial', 30), fill='white')
canvas.itemconfig(victory_screen, state='hidden')
canvas.itemconfig(victory_text, state='hidden')
# Create a 2D array to represent the whole maze
maze_array = [[0 for x in range(maze_width//20)] for y in range(maze_height//20)]
# Generate the maze, ensuring that every 20 units of the maze, there is a 50% chance of a wall being created, storing the walls as 1s and the 0 representing free space
for x in range(0, maze_width, 20):
for y in range(0, maze_height, 20):
if x == 0 or x == maze_width - 20 or y == 0 or y == maze_height - 20:
canvas.create_rectangle(x, y, x + 20, y + 20, fill='black', tags='wall')
maze_array[y//20][x//20] = 1
elif random.random() > 0.6:
canvas.create_rectangle(x, y, x + 20, y + 20, fill='black', tags='wall')
maze_array[y//20][x//20] = 1
# Making sure the path starts at the entrance point of the maze in the top left corner
x, y = 20, 20
path = [(x, y)]
# Stating the amount of vertical and horizontal units the path should move by
move_across = 37
move_down = 22
# Move the path sideways by the amount of units stated earlier
for i in range(move_across):
x += 20
path.append((x, y))
maze_array[y//20][x//20] = 0
# Move the path down by the amount of units stated earlier
for i in range(move_down):
y += 20
path.append((x, y))
maze_array[y//20][x//20] = 0
# Place squares over the path to simulate creating a path
for point in path:
x, y = point
#Fill the path using the hexadecimal colour value of the navigable part of the maze
canvas.create_rectangle(x, y, x + 20, y + 20, outline='#d9d9d9',fill='#d9d9d9')
# Adding a black square to the top right corner of the maze, so that the path is less obvious
canvas.create_rectangle(maze_width - 40, 20, maze_width - 20, 40, fill='black', tags='wall')
# Create the entrance and exit
# Change coordinates of the entrance and exit so they move one square in the diagonal direction
canvas.create_rectangle(20, 20, 40, 40, fill='blue')
exit_ = canvas.create_rectangle(maze_width - 40, maze_height - 40, maze_width -20, maze_height -20, fill='blue')
entrance = (20, 20) # top-left corner of the maze
exit = (750, 450)
projectile_x, projectile_y = (entrance)
# Algorithm for aiming the projectile with the mouse pointer
def aim_projectile(direction):
global projectile_direction
x,y = direction.x, direction.y
if x > player_x + character_size:
projectile_direction = 'right'
elif x < player_x:
projectile_direction = 'left'
elif y < player_y:
projectile_direction = 'up'
elif y > player_y + character_size:
projectile_direction = 'down'
# Define projectile so it can be called upon in the fire_projectile function
projectile = None
# Algorithm to fire the projectile
def fire_projectile(event):
global projectile
# Make it so the projectile is deleted if the user tries spawning multiple on the screen
if projectile is not None:
canvas.delete(projectile)
x1,y1,x2,y2 = canvas.coords(player)
center_x = (x1+x2) / 2
center_y = (y1 + y2) / 2
global projectile_x, projectile_y
#Shoot the projectile in the direction that has previously been stated in the movement algorithm
if projectile_direction is not None:
projectile = canvas.create_oval(center_x - 5,center_y - 5, center_x + 5,center_y + 5, fill='red')
if projectile_direction == 'up':
canvas.move(projectile, 0, -20)
projectile_y -= 20
elif projectile_direction == 'down':
canvas.move(projectile, 0, 20)
projectile_y += 20
elif projectile_direction == 'left':
canvas.move(projectile, -20, 0)
projectile_x -= 20
elif projectile_direction == 'right':
canvas.move(projectile, 20, 0)
projectile_x += 20
# Move the projectile after 1/4 seconds
maze.after(250, fire_projectile)
# Bind the canvas to the aim_projectile function
canvas.bind("<Motion>", aim_projectile)
# Bind the left mouse button to the fire_projectile function
canvas.bind("<Button-1>",fire_projectile)
# Create the player model which will be spawned at the entrance of the maze
player_x, player_y = 20, 20
player = canvas.create_rectangle(player_x, player_y, player_x+20, player_y+20, fill='green')
def check_enemy_collision():
# get player and enemy coordinates
player_coords = canvas.coords(player)
enemy_coords = canvas.coords(enemy)
# check if player and enemy coordinates align
if (player_coords[0] == enemy_coords[0] and player_coords[2] == enemy_coords[2]) and (player_coords[1] == enemy_coords[1] and player_coords[3] == enemy_coords[3]):
# Display the game over screen
canvas.itemconfigure(game_over_screen, state='normal')
canvas.itemconfigure(game_over_text, state='normal' )
# Ensure that the game over screen and text are displayed over the walls
canvas.tag_raise(game_over_screen)
canvas.tag_raise(game_over_text)
def check_exit_collision():
# Gather the coordinates of the player and of the exit to the maze
player_x1, player_y1, player_x2, player_y2 = canvas.coords(player)
exit_x1, exit_y1, exit_x2, exit_y2 = canvas.coords(exit_)
# Compare these values, and if they all match up, then the victory screen should be displayed
if player_x1 >= exit_x1 and player_x2 <= exit_x2 and player_y1 >= exit_y1 and player_y2 <= exit_y2:
# Display the victory screen
canvas.itemconfig(victory_screen, state='normal')
canvas.itemconfig(victory_text, state='normal')
#Ensure the text will be displayed over the walls
canvas.tag_raise(victory_screen)
canvas.tag_raise(victory_text)
# Delete the player once they reach the exit
canvas.delete(player)
# Algorithm for moving the player as well as adding collision to the walls of the maze
def move_player(event):
# Calls upon the previously stated x and y values of the player so they can be modified
global player_x, player_y
x1, y1, x2, y2 = canvas.coords(player)
new_x, new_y = player_x, player_y
if event.char == 'w':
new_y -= 20
elif event.char == 's':
new_y += 20
elif event.char == 'a':
new_x -= 20
elif event.char == 'd':
new_x += 20
x1,y1,x2,y2 = canvas.coords(player)
player_x, player_y = int((x1+x2)/2), int((y1+y2)/2)
# Convert new_x and new_y to indexes and store them as integers
new_x_index = int(new_x // 20)
new_y_index = int(new_y // 20)
# Check if the new position would put the player inside any of the walls
if maze_array[new_y_index][new_x_index] == 1:
# If player aligns with the maze walls do not allow them to move
return
# If there is no collision, allow the player to move
canvas.move(player, new_x - player_x, new_y - player_y)
player_x, player_y = new_x, new_y
# Check for collision between the player, enemy and exit every time the player moves
check_enemy_collision()
check_exit_collision()
#bind the 'w','a','s','d' keys to the move_player function
canvas.bind("<KeyPress-w>", move_player)
canvas.bind("<KeyPress-a>", move_player)
canvas.bind("<KeyPress-s>", move_player)
canvas.bind("<KeyPress-d>", move_player)
canvas.focus_set()
# Create the enemy at the exit of the maze
exit = (maze_width-40,maze_height-40)
enemy = canvas.create_rectangle(exit[0], exit[1], exit[0]+20, exit[1]+20, fill='red')
# Function to move the enemy towards the player
def move_enemy():
global player_x, player_y
# Gather the coordinates of the player so the enemy is able to make its way towards the user
x1,y1,x2,y2 = canvas.coords(player)
player_x, player_y = (x1+x2)/2, (y1+y2)/2
global enemy_x, enemy_y
x1,y1,x2,y2 = canvas.coords(enemy)
enemy_x, enemy_y = (x1+x2)/2, (y1+y2)/2
# Finds where the player is and appropriately moves the enemy towards this position
if player_x > enemy_x:
canvas.move(enemy, 20, 0)
elif player_x < enemy_x:
canvas.move(enemy, -20, 0)
if player_y > enemy_y:
canvas.move(enemy, 0, 20)
elif player_y < enemy_y:
canvas.move(enemy, 0, -20)
# Update the new position of the enemy coordinates
x1,y1,x2,y2 = canvas.coords(enemy)
enemy_x, enemy_y = int((x1+x2)/2), int((y1+y2)/2)
# Move the enemy towards the player once every second
maze.after(500, move_enemy)
#Check for collision between the player and the enemy every time the enemy moves
check_enemy_collision()
move_enemy()
# Create the timer text in the top right corner
timer_text = canvas.create_text(maze_width - 20, 20, text='0', font=('Arial', 20), fill='yellow')
def update_timer():
# Increment the timer value
global timer
# Increment the timer by a value of 1
timer += 1
# Update the timer text on the canvas
canvas.itemconfig(timer_text, text=timer)
# Make it so the timer updates after 1 second
canvas.after(1000, update_timer)
# Start the timer and initialise the value as 0, calling upon the funciton to update it
timer = 0
update_timer()
# Initialise the score as 0 and make it so this is displayed in the top left of the screen
score = 0
# Create the text that will be shown at the top of the screen displaying score
score_text = canvas.create_text(80, 20, text='Score: {}'.format(score), font=('Arial', 20), fill='yellow')
def update_score():
# Increase the score by 1
global score
score += 5
# Update the score text on the canvas
canvas.itemconfig(score_text, text='Score: {}'.format(score))
# Schedule the next score update
canvas.after(1000, update_score)
update_score()
# Find the player and exit coordinates and assign them values so the extra points can be assigned
player_x1, player_y1, player_x2, player_y2 = canvas.coords(player)
exit_x1, exit_y1, exit_x2, exit_y2 = canvas.coords(exit_)
# Compare these values, and if they all match up, then the victory screen should be displayed
if player_x1 >= exit_x1 and player_x2 <= exit_x2 and player_y1 >= exit_y1 and player_y2 <= exit_y2:
# Give the user an extra 1000 points if they escape the maze
score += 1000
canvas.itemconfig(score_text, text='Score: {}'.format(score))
maze.mainloop()
Seen below is the code that is problematic, as when I click M1, a projectile is spawned, however it does not move after it has been shot, and the following error message appears:
Traceback (most recent call last):
File "/nix/store/2vm88xw7513h9pyjyafw32cps51b0ia1-python3-3.8.12/lib/python3.8/tkinter/__init__.py", line 1892, in __call__
return self.func(*args)
File "/nix/store/2vm88xw7513h9pyjyafw32cps51b0ia1-python3-3.8.12/lib/python3.8/tkinter/__init__.py", line 814, in callit
func(*args)
TypeError: fire_projectile() missing 1 required positional argument: 'event'
Is there any way I can fix this, as other solutions i have found elsewhere when implemented have not actually been able to properly solve my problem. Thanks to anyone who helps
projectile_x, projectile_y = (entrance)
# Algorithm for aiming the projectile with the mouse pointer
def aim_projectile(direction):
global projectile_direction
x,y = direction.x, direction.y
if x > player_x + character_size:
projectile_direction = 'right'
elif x < player_x:
projectile_direction = 'left'
elif y < player_y:
projectile_direction = 'up'
elif y > player_y + character_size:
projectile_direction = 'down'
# Define projectile so it can be called upon in the fire_projectile function
projectile = None
# Algorithm to fire the projectile
def fire_projectile(event):
global projectile
# Make it so the projectile is deleted if the user tries spawning multiple on the screen
if projectile is not None:
canvas.delete(projectile)
x1,y1,x2,y2 = canvas.coords(player)
center_x = (x1+x2) / 2
center_y = (y1 + y2) / 2
global projectile_x, projectile_y
#Shoot the projectile in the direction that has previously been stated in the movement algorithm
if projectile_direction is not None:
projectile = canvas.create_oval(center_x - 5,center_y - 5, center_x + 5,center_y + 5, fill='red')
if projectile_direction == 'up':
canvas.move(projectile, 0, -20)
projectile_y -= 20
elif projectile_direction == 'down':
canvas.move(projectile, 0, 20)
projectile_y += 20
elif projectile_direction == 'left':
canvas.move(projectile, -20, 0)
projectile_x -= 20
elif projectile_direction == 'right':
canvas.move(projectile, 20, 0)
projectile_x += 20
# Move the projectile after 1/4 seconds
maze.after(250, fire_projectile)
# Bind the canvas to the aim_projectile function
canvas.bind("<Motion>", aim_projectile)
# Bind the left mouse button to the fire_projectile function
canvas.bind("<Button-1>",fire_projectile)
so this is my first time as a registered user here, so please bear with me if I was searching wrong,
but with the tags [python] or [pygame] I could not find an answer to this question of a random color.
So my problem is the following:
The Game
In this game I want the bricks to be random colors. To do so I made a function to generate a random color:
def brick_color():
color_var = random.randint(0,3)
if color_var == 0: #red
brick_color1 = pygame.Color(198,44,58)
elif color_var == 1:#blue
brick_color1 = pygame.Color(1,128,181)
elif color_var == 2:#yellow
brick_color1 = pygame.Color(255,211,92)
elif color_var == 3:#green
brick_color1 = pygame.Color(0,157,103)
return brick_color1
and implemented the function into this lines:
# brick init
brick = pygame.Surface([brick_width, brick_height]),brick_color # surface for a single brick
pygame.draw.rect(brick[0], brick[1], [0, 0, brick_width, brick_height])
bricks = [] # list of *coordinates* of the bricks
# initialize coordinates of bricks
for y in range(num_brick_rows):
brickY = (y * brick_row_height) + brick_offset_y
for x in range(num_bricks_in_row):
brickX = (x * brick_column_width) + brick_offset_x
color_of_brick = brick_color()
bricks.append((brickX, brickY),color_of_brick) # coordinates are in fact tuples (x,y)
But i keep getting this error:
File "C:\_____\unbenannt0.py", line 146, in <module>
color_of_brick = brick_color()
TypeError: 'pygame.Color' object is not callable
And this is the full code.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Jul 2 09:33:50 2020
#author:
"""
# example from the Sloan Kelly book
# very slightly modified
# imports (or makes usable) external packages that may be 3rd party (such as pygame)
import pygame, sys, random
# enable "short-hand" notation for imported stuff
from pygame.locals import QUIT, MOUSEBUTTONUP, MOUSEMOTION, KEYDOWN, K_ESCAPE
from pygame import display
#Functions
def draw_bricks():
for b in bricks:
windowSurfaceObj.blit(brick[0], b) # makes a copy of brick image acording to coordinates, where stuff is drawn!
def draw_bat():
windowSurfaceObj.blit(bat, batRect)
def draw_ball():
windowSurfaceObj.blit(ball, ballRect)
def brick_color():
color_var = random.randint(0,3)
if color_var == 0: #red
brick_color = pygame.Color(198,44,58)
elif color_var == 1:#blue
brick_color = pygame.Color(1,128,181)
elif color_var == 2:#yellow
brick_color = pygame.Color(255,211,92)
elif color_var == 3:#green
brick_color = pygame.Color(0,157,103)
return brick_color
pygame.init()
fpsClock = pygame.time.Clock()
# new and improved:
# create surface object aka. the main window
inf = display.Info()
screen_width = inf.current_w - 200 # make window sizea little bit smaller than actual screen
screen_height = inf.current_h - 200
# windowSurfaceObj = display.set_mode(size=(screen_width, screen_height), flags=pygame.FULLSCREEN) # initialize window
windowSurfaceObj = display.set_mode(size=(screen_width, screen_height)) # initialize window
display.set_caption('New and improved Bricks'); # set window title
# brick layout
brick_width = 50
brick_height = 20
num_bricks_in_row = 7
num_brick_rows = 5
brick_row_height = 2 * brick_height
brick_offset_y = 100
brick_column_width = 2 * brick_width
brick_offset_x = int(screen_width/2 - brick_column_width*num_bricks_in_row/2) # place it in the middle of the screen
brick_color = brick_color()
# ball related stuff
ball_radius = int(screen_height/200)
# more game constants!
fps = 60 # desired frames per second
background_colour = pygame.Color(0, 0, 0) # background is black
# used variables for bat dimensions
bat_width = 100
bat_height = 10
# ball related stuff
ball_start_x = 24 # somehwere near the left of the window
ball_start_y = 200 # initial ball position when new ball is released
ball_speed = int(fps*0.15) # speed of ball in pixel per frame! use fps var. here to make real ball speed independent of frame rate
# bat init
# replace bat with crude hand drawn one
batcolor = pygame.Color(0, 0, 255) # bat color: blue!
bat = pygame.Surface([bat_width, bat_height]) # this Surface is for drawing the bat upon
pygame.draw.rect(bat, batcolor, [0, 0, bat_width, bat_height]) # draw bat. It's a simple rectangle.
bat = bat.convert_alpha() # deal with transparency
# place the bat somewhere near the bottom of the screen/window
player_start_x = 0 # initial position is on left
player_start_y = screen_height - 6 * bat_height # this is used as Y coordinate for bat, near the bottom of the screen
batRect = bat.get_rect() # rectangle around bat, used to move it around later
mousex = player_start_x
mousey = player_start_y # mousex and mousey later used for moving the bat around, not actual mouse coordinates at this point
# ball init
ball_color = pygame.Color(255, 255, 255) # white
ball = pygame.Surface([ball_radius*2, ball_radius*2]) # Surface for drawing the ball upon
pygame.draw.circle(ball, ball_color, (ball_radius, ball_radius), ball_radius) # draw circle on ball surface
ballRect = ball.get_rect() # rectangle around ball, use to move it around later
ballServed = False
bx = ball_start_x # bx is actual ball postion
by = ball_start_y # by is actual (current) ball position
sx = ball_speed # current ball speed in horizontal direction
sy = ball_speed # current ball speed vertical
ballRect.topleft = (bx, by) # move ball rectangle to initial position
# brick init
brick = pygame.Surface([brick_width, brick_height]),brick_color # surface for a single brick
pygame.draw.rect(brick[0], brick[1], [0, 0, brick_width, brick_height])
bricks = [] # list of *coordinates* of the bricks
# initialize coordinates of bricks
for y in range(num_brick_rows):
brickY = (y * brick_row_height) + brick_offset_y
for x in range(num_bricks_in_row):
brickX = (x * brick_column_width) + brick_offset_x
color_of_brick = brick_color()
bricks.append((brickX, brickY),color_of_brick) # coordinates are in fact tuples (x,y)
while True: # main loop, run once per frame (i.e. fps times per second)
windowSurfaceObj.fill(background_colour) # clear the screen
# brick draw
# for b in bricks: # remember: bricks is a list of brick coordinates, not surfaces
# windowSurfaceObj.blit(brick, b) # make copy of brick image and place it on screen, b = brick coordinates
draw_bricks()
# bat and ball draw, rectangles around bat and ball are used for positioning
# windowSurfaceObj.blit(bat, batRect) # copy surface with image of bat to screen
# windowSurfaceObj.blit(ball, ballRect) # same for ball
draw_bat()
draw_ball()
# main event loop
# process user interaction
for event in pygame.event.get():
# quit the game if window is closed or escape key is pressed
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONUP and not ballServed:
ballServed = True # start game, when mouse is clicked
elif event.type == MOUSEMOTION: # mouse has moved
mousex, mousey = event.pos # set mouse coordinate variables to actual mouse coordinates
if mousex < screen_width - bat_width:
if mousex < 0: # may occur in full screen / large window mode
batRect.topleft = (0, player_start_y)
else:
batRect.topleft = (mousex, player_start_y)
else:
batRect.topleft = (screen_width - bat_width, player_start_y)
# main game logic
if ballServed: # if game is in progress
ballRect.topleft = (bx, by) # position the ball using its rectangle
bx += sx # sx = speed of the ball in X direction
by += sy # sy = speed of the ball in Y direction
if (by >= screen_height): # ball below bottom of screen
ballServed = False # game not in progess, ball lost!
bx, by = (ball_start_x, ball_start_y) # ball is reset to start position
ballRect.topleft = (bx, by) # move the rectangle around ball to correct position
if by <= 0: # ball hits top
by = 0
sy *= -1 # reflect
if bx <= 0: # ball hits left side of window
bx = 0
sx *= -1 # reflect
if bx >= screen_width - ball_radius*2: # ball hits right side of window
bx = screen_width - ball_radius*2
sx *= -1 # reflection
# collision detection
brickForRemoval = None
for b in bricks: # remember: bricks is list of coordinates of bricks; iterating all bricks and check each one for collision
briX, briY = b # tuple unwrapping: x and y coordinates of top left of brick
if bx + ball_radius*2 >= briX and bx <= briX + brick_width: # is x coordinate of ball inside brick (or touching brick)
if (by + ball_radius*2 >= briY and by <= briY + brick_height): # same for y coordinate
brickForRemoval = b # brick was hit and is scheduled for removal
if bx <= briX + ball_radius*2: # ball hit brick from left
sx *= -1 # reflect
elif bx >= briX + brick_width - ball_radius*2: # ball hit brick from right
sx *= -1 # reflect
if by <= briY + ball_radius * 2: # ball hit brick from top
sy *= -1 # reflect
elif by >= briY + brick_height - ball_radius*2: # ball hit brick from below
sy *= -1 # reflect
break # ball hit a brick and cannot hit another one at the same time
if brickForRemoval != None: # if a brick is scheduled for removal…
bricks.remove(brickForRemoval) # … remove it!
# collision check: does bat hit ball?
if (bx >= mousex and bx <= mousex + bat_width): # using bat_width variable
if(by >= player_start_y - bat_height and by <= player_start_y):
sy *= -1 # reflect
pygame.display.update() # show updated screen
fpsClock.tick(fps) # limit fps
Any help is highly appreciated!
As per #jasonharper's comment, when you assign a value to a variable name that is the same as the function name, that name becomes "re-assigned", in this case - making the function no longer accessible.
Using any other variable name inside your function would fix it:
def brick_color():
color_var = random.randint(0,3)
if color_var == 0: #red
new_color = pygame.Color(198,44,58)
elif color_var == 1:#blue
new_color = pygame.Color(1,128,181)
elif color_var == 2:#yellow
new_color = pygame.Color(255,211,92)
elif color_var == 3:#green
new_color = pygame.Color(0,157,103)
return new_color
Your function could also use a python list to be a bit more flexible and compact:
def getRandomColor():
# red blue yellow green
color_list = [ (198,44,58), (1,128,181), (255,211,92), (0,157,103) ]
random_choice = random.randrange( len( color_list ) )
return color_list[ random_choice ]
This form is more flexible because you can easily add more colours by appending them to the list (no more to do).
I have a list of 10 drawn rectangles (referenced as cubes in my script) that are affected by gravity. I made a simple collision system for them to stop when they hit the ground. How can I make it so when 2 cubes collide they stop falling like they do with the ground?
import pygame
import time
import random
pygame.init()
clock = pygame.time.Clock()
wnx = 800
wny = 600
black = (0,0,0)
grey = (75,75,75)
white = (255,255,255)
orange = (255,100,30)
wn = pygame.display.set_mode((wnx, wny))
wn.fill(white)
def cube(cx,cy,cw,ch):
pygame.draw.rect(wn, orange, [cx, cy, cw, ch])
def floor(fx,fy,fw,fh):
pygame.draw.rect(wn, grey, [fx, fy, fw, fh])
def main():
floory = 550
number = 30
cubex = [0] * number
cubey = [0] * number
cubew = 10
cubeh = 10
for i in range(len(cubex)):
cubex[i] = (random.randrange(0, 80)*10)
cubey[i] = (random.randrange(2, 5)*10)
gravity = -10
exit = False
while not exit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit = True
for i in range(len(cubex)): #i want to check here if it collides with an other cube
if not (cubey[i] + 10) >= floory:
cubey[i] -= gravity
wn.fill(white)
floor(0,floory,800,50)
for i in range(len(cubex)):
cube(cubex[i], cubey[i], cubew, cubeh)
pygame.display.update()
clock.tick(5)
main()
pygame.quit()
quit()
Use pygame.Rect.colliderect to check if to rectangles are intersecting.
Create an rectangle (pygame.Rect) which defines the next position (area) of the cube:
cubeR = pygame.Rect(cubex[i], cubey[i] + 10, cubew, cubeh)
Find all intersecting rectangles
cl = [j for j in range(len(cubey)) if j != i and cubeR.colliderect(pygame.Rect(cubex[j], cubey[j], cubew, cubeh))]
And don't move (let further "fall") the cube if there is any() collision:
if not any(cl):
# [...]
The check may look like this:
for i in range(len(cubex)):
cubeR = pygame.Rect(cubex[i], cubey[i] + 10, cubew, cubeh)
cisect = [j for j in range(len(cubey)) if j != i and cubeR.colliderect(pygame.Rect(cubex[j], cubey[j], cubew, cubeh))]
if not any(cisect) and not (cubey[i] + 10) >= floory:
cubey[i] -= gravity
Note, since all the cubes are aligned to an 10*10 raster, it is sufficient to check if the origins of the cubes are equal:
for i in range(len(cubex)):
cisect = [j for j in range(len(cubey)) if j != i and cubex[i] == cubex[j] and cubey[i]+10 == cubey[j]]
if not any(cisect) and not (cubey[i] + 10) >= floory:
cubey[i] -= gravity
I'm making a game.
The game's world is represented by a tilemap. The tiles correspond to values in a 2D array.
I would like to use a special "corner" wall sprite if any three wall tiles make an L shape. That is,
V
## # >##
# ## #
^
The tiles indicated with arrows should be corner tiles.
I have the code to search for the wall but I don't know how to identify which tiles are the corner tiles.
My code is:
import pygame, sys
import Sprites
import random
from pygame.locals import *
pygame.init()
fpsClock = pygame.time.Clock()
cloudx = -200
cloudy = 0
infoObject = pygame.display.Info()
DIRT = 0
GRASS = 1
WATER = 2
COAL = 3
CLOUD = 4
WALL = 5
CWALL = 6
controls = {
DIRT : 49,
GRASS: 50,
WATER: 51,
COAL : 52,
WALL : 53
}
infoObject = pygame.display.Info()
w = infoObject.current_w
h = infoObject.current_h
TILESIZE = 40
MAPWIDTH = 15
MAPHEIGHT = 15
WHITE = (255,255,255)
BLACK = (0,0,0)
resources = [DIRT, GRASS, WATER, COAL]
textures = {
DIRT : pygame.image.load('Sprites/Dirt.png'),
GRASS : pygame.image.load('Sprites/tile130.png'),
WATER : pygame.image.load('Sprites/Water.png'),
COAL : pygame.image.load('Sprites/Coal.png'),
CLOUD : pygame.image.load('Sprites/Cloud.png'),
WALL : pygame.image.load('Sprites/Swall.png'),
CWALL : pygame.image.load('Sprites/Swall.png')
}
playerPos = [0,0]
inventory = {
DIRT : 0,
GRASS : 0,
WATER : 0,
COAL : 0,
WALL : 10,
}
tilemap = [[DIRT for w in range(MAPWIDTH)] for h in range(MAPHEIGHT)]
DISPLAYSURF = pygame.display.set_mode((MAPWIDTH*TILESIZE,MAPHEIGHT*TILESIZE + 50))
pygame.display.set_caption('M I N E C R A F T -- 2D')
pygame.display.set_icon(pygame.image.load('Sprites/player.png'))
PLAYER = pygame.image.load('Sprites/Player.png').convert_alpha()
for rw in range(MAPHEIGHT):
for cl in range(MAPWIDTH):
randomNumber = random.randint(0,15)
if randomNumber == 0:
tile = COAL
elif randomNumber == 1 or randomNumber == 2:
tile = WATER
elif randomNumber >= 3 and randomNumber <=7:
tile = GRASS
else:
tile = DIRT
tilemap[rw][cl] = tile
INVFONT = pygame.font.Font('freesansbold.ttf', 18)
print(tilemap)
while True:
currentTile = tilemap[playerPos[1]][playerPos[0]]
DISPLAYSURF.fill(BLACK)
for event in pygame.event.get():
# print(event)
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
for key in controls:
if (event.key == controls[key]):
if inventory[key] > 0:
inventory[key] -=1
inventory[currentTile] += 1
tilemap[playerPos[1]][playerPos[0]] = key
if(event.key == K_RIGHT) and playerPos[0] < MAPWIDTH - 1:
playerPos[0]+=1
elif(event.key == K_LEFT) and playerPos[0] > 0:
playerPos[0]-=1
elif(event.key == K_DOWN) and playerPos[1] < MAPHEIGHT - 1:
playerPos[1]+=1
elif(event.key == K_UP) and playerPos[1] > 0:
playerPos[1]-=1
if event.key == K_SPACE:
currentTile = tilemap[playerPos[1]][playerPos[0]]
inventory[currentTile] += 1
tilemap[playerPos[1]][playerPos[0]] = DIRT
for row in range(MAPHEIGHT):
for column in range(MAPWIDTH):
DISPLAYSURF.blit(textures[tilemap[row][column]],(column*TILESIZE, row*TILESIZE, TILESIZE, TILESIZE))
DISPLAYSURF.blit(PLAYER,(playerPos[0]*TILESIZE,playerPos[1]*TILESIZE))
DISPLAYSURF.blit(textures[CLOUD].convert_alpha(),(cloudx,cloudy))
cloudx +=1
if cloudx > MAPWIDTH*TILESIZE:
cloudy = random.randint(0, MAPHEIGHT*TILESIZE)
cloudx = -200
placePosition = 10
for item in resources:
DISPLAYSURF.blit(textures[item],(placePosition, MAPHEIGHT*TILESIZE+10))
placePosition+=50
textObj = INVFONT.render(str(inventory[item]), True, WHITE, BLACK)
DISPLAYSURF.blit(textObj,(placePosition, MAPHEIGHT*TILESIZE+20))
placePosition += 50
pygame.display.update()
fpsClock.tick(24)
for x in range(MAPWIDTH):
for y in range(MAPHEIGHT):
if tilemap[x][y] == WALL:
go_left = x > 1
go_right = x < MAPWIDTH - 1
go_up = y > 1
go_down = y < MAPHEIGHT - 1
if go_left:
tilemap[x - 1][y] = CWALL
if go_up:
pass
if go_right:
tilemap[x + 1][y] = WALL
if go_up:
pass
if go_down:
pass
print('WALL')
pygame.display.update()
And here are the links to the sprites:
https://framadrop.org/r/fmReup_rTK#bMSywSUa7nxb1qL/a4FIbns+VaspgE0c/FU+9f1drHI=
https://framadrop.org/r/pBOfrnKcdT#eNEZQ9QjX5Cl6X4gH4UwdIg3eBPnY/L4WcSGYtUR5PE=
https://framadrop.org/r/ZFfTz_Lq9V#2Nd5ba1iE7miyFg8JpPFvwabAkdnHds/GfVkSAQeJaQ=
https://framadrop.org/r/gN9Y748L9G#Z552pPpgjTcSubt9tn74mZ0tT1COv7UCFdkUq2DorAU=
https://framadrop.org/r/d9k4hyCUni#OTza8UbsR8Am/R1PA9MAWkLDPRDBsT1rAHMgr61jusc=
https://framadrop.org/r/1mv777OR6d#pkqwaQrmVRElUPcdEV5K4UhmALsJSYX7z3WtrZXl4TE=
https://framadrop.org/r/CyF-tk7yUb#IFexcePe418JizElZzCJzDENTJPDfz7i1nr+lGns0rU=
https://framadrop.org/r/VzVfAz6bnL#oLHivyHPtTD8+IxliDD4yc+6LS9kpGyEp1HNFGUsBHo=
https://framadrop.org/r/0V0ShMH0uq#PZHdPSQNbgL7QqH2niwdS4HO34ZRMfIlhpvpRqbWwng=
From the comments, I'm not sure what the problem is... let me just show you what I would do so we can discuss further if needed:
for x in range(MAPWIDTH):
for y in range(MAPHEIGHT):
if tilemap[x][y] == WALL:
# there is a wall at indices x and y
# get neighbouring tiles (check for limits)
go_left = x > 1
go_right = x < MAPWIDTH - 1
go_up = y > 1
go_down = y < MAPHEIGHT - 1
if go_left:
# you can use x-1
tilemap[x-1][y] = WALL # set left tile to WALL
if go_up:
# do something in the diagonal with x-1 y-1?
pass
if go_right:
# you can use x+1
tilemap[x+1][y] = WALL # set right tile to WALL
if go_up:
pass # same story
if go_down:
pass # and again
EDIT here is a simple (hence hopefully easy to understand) way of doing this
In order to make the textures clean, I first had to rotate the wall and corner to get all possible configurations (vertical/horizontal for the wall, and all four possibilities for the corner)
VWALL = 5
HWALL = 6
CORNERLD = 7
CORNERRD = 8
CORNERLU = 9
CORNERRU = 10
controls = {
DIRT : 49,
GRASS : 50,
WATER : 51,
COAL : 52,
VWALL : 53,
HWALL : 54,
CORNERLD: 55,
CORNERRD: 56,
CORNERLU: 57,
CORNERRU: 58,
}
tex_wall = pygame.image.load('Sprites/Swall.png')
tex_corner = pygame.image.load('Sprites/Corner.png')
textures = {
DIRT : pygame.image.load('Sprites/Dirt.png'),
GRASS : pygame.image.load('Sprites/tile130.png'),
WATER : pygame.image.load('Sprites/Water.png'),
COAL : pygame.image.load('Sprites/Coal.png'),
CLOUD : pygame.image.load('Sprites/Cloud.png'),
HWALL : pygame.transform.rotate(tex_wall, 90),
VWALL : tex_wall,
CORNERRD: tex_corner,
CORNERLD: pygame.transform.flip(tex_corner, True, False),
CORNERLU: pygame.transform.flip(tex_corner, True, True),
CORNERRU: pygame.transform.flip(tex_corner, False, True),
}
I created a wall dict to quickly check all 6 possibilities for walls with in walls
walls = {
VWALL : None,
HWALL : None,
CORNERLD: None,
CORNERRD: None,
CORNERLU: None,
CORNERRU: None,
}
And then I check the map
for x in range(MAPWIDTH):
for y in range(MAPHEIGHT):
if tilemap[x][y] in walls:
# there is a wall at indices x and y
# get neighbouring tiles (check for limits)
go_left = x > 1
go_right = x < MAPWIDTH - 1
go_up = y > 1
go_down = y < MAPHEIGHT - 1
l_wall = False
r_wall = False
u_wall = False
d_wall = False
if go_left and tilemap[x-1][y] in walls:
# left tile is WALL
l_wall = True
if go_right and tilemap[x+1][y] in walls:
# right tile is WALL
r_wall = True
if go_up and tilemap[x][y-1] in walls:
u_wall = True
if go_down and tilemap[x][y+1] in walls:
d_wall = True
if l_wall and u_wall:
# upper left corner
tilemap[x][y] = CORNERLU
elif l_wall and d_wall:
# down left corner
tilemap[x][y] = CORNERRU
elif r_wall and u_wall:
# upper left corner
tilemap[x][y] = CORNERLD
elif r_wall and d_wall:
# down left corner
tilemap[x][y] = CORNERRD
elif (l_wall or r_wall) and not (u_wall or d_wall):
# tiles in a vertical wall, use VWALL
tilemap[x][y] = VWALL
elif (u_wall or d_wall) and not (l_wall or r_wall):
# tiles in a horizontal wall, use HWALL
tilemap[x][y] = HWALL
And we get
Note that there are random configurations of wall that will not look good, though (T-shapes...) but these would require additional sprites.
The full code I used can be found here
EDIT2 note that you will have to update a few more things to make everything work smoothly (e.g. wall uptake in the inventory)
Also running this check every loop is costly, so you should declare an env_changed boolean to make the test only when a change was made to the environment.
For the inventory, you will need
if currentTile in walls:
inventory[VWALL] += 1
this makes VWALL the default wall in the inventory and the loop takes care of switching it to a proper one for the display.
For the rest, well... it's your game, so I'll let you figure it out ;)
I'd start by generating a set offsets
// 1
//0 2 Neighbour directions
// 3
// 0 1 2 3 Corresponding offsets
dx = [-1, 0, 1, 0]
dy = [0, -1, 0, 1]
Nmax = 4
Now, I can get the n-values of the neighbouring walls, like so:
nwalls = []
for n in range(Nmax): #n is the direction of the neighbour
#Ensure that the neighbour tile, identified as (x+dx[n], y+dy[n])
#is in the map and check to see if it is a wall
if 1<x+dx[n]<WIDTH and 1<y+dy[n]<HEIGHT and tilemap[x+dx[n]][y+dy[n]]==WALL:
nwalls.append(n) #Neighbour was a wall, add its direction to this list
Now, I can convert my list into a tile name:
#nwalls is a list of all the directions pointing to a wall
nwalls.sort() #Sort list, e.g. 3,1,2 -> 1,2,3
nwalls = map(str,nwalls) #Convert from numbers to strings
nwalls = ''.join(nwalls) #Append strings together: ['1','2','3'] -> '123'
if nwalls: #If the string is not empty
nwalls = 'corner_tile_{0}.jpg'.format(nwalls) #Convert string to tile name
Now I just need a bunch of tiles named, e.g.:
corner_tile_01.jpg
corner_tile_013.jpg
Then I can say:
if nwalls: #Only true if this was a corner
Code to display the tile whose name is stored in `nwalls`
import random
from random import *
import math
from math import *
from pygame import *
import pygame, sys
from pygame.locals import *
import pygame.font
from pygame.font import *
bif= "grass.png"
mif= "character front.png"
mifB= "character back.png"
mifL= "character left.png"
mifR= "character right.png"
mifRS= "character right still.png"
mifLS= "character left still.png"
skel= "skeleton front.png"
skelB= "skeleton back.png"
skelL= "skeleton left.png"
skelR= "skeleton right.png"
swordsky="sword_sky.png"
sworddown="sword_down.png"
swordleft="sword_left.png"
swordright="sword_right.png"
swordblank="sword_blank.png"
healthpot="healthpot.png"
levelup = 1
pygame.init()
screen=pygame.display.set_mode((700,600),0,32)
#background create
r = 0
healthpotion=pygame.image.load(healthpot).convert_alpha()
sword_blank=pygame.image.load(swordblank).convert_alpha()
sword=pygame.image.load(sworddown).convert_alpha()
sword_down=pygame.image.load(sworddown).convert_alpha()
sword_sky=pygame.image.load(swordsky).convert_alpha()
sword_right=pygame.image.load(swordright).convert_alpha()
sword_left=pygame.image.load(swordleft).convert_alpha()
background=pygame.image.load(bif).convert()
character=pygame.image.load(mif).convert_alpha()
character_back=pygame.image.load(mifB).convert_alpha()
character_left=pygame.image.load(mifL).convert_alpha()
character_right=pygame.image.load(mifR).convert_alpha()
character_front=pygame.image.load(mif).convert_alpha()
character_right_still=pygame.image.load(mifRS).convert_alpha()
character_left_still=pygame.image.load(mifLS).convert_alpha()
skeleton=pygame.image.load(skel).convert_alpha()
skeleton_back=pygame.image.load(skelB).convert_alpha()
skeleton_left=pygame.image.load(skelL).convert_alpha()
skeleton_right=pygame.image.load(skelR).convert_alpha()
skeleton_front=pygame.image.load(skel).convert_alpha()
#convert image files to python useable files
x,y = 300, 250
movex,movey = 0,0
Ai_x, Ai_y = 0, 500
moveAi_x, moveAi_y=0,0
movesx, movesy=0,0
ix, iy = -500, -500
experience = 0
aihp = 15
health = 100
sx,sy = x+10, y+20
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#player movement
if event.type==KEYDOWN:
if event.key==K_a:
if character==character_right:
sx,sy= x+25, y+15
sword=sword_right
elif character==character_right_still:
sx,sy= x+25, y+15
sword=sword_right
elif character==character_left_still:
sx,sy= x-25, y+15
sword=sword_left
elif character==character_left:
sx,sy= x-25, y+15
sword=sword_left
elif character==character_front:
sx,sy= x, y+30
sword=sword_down
elif character==character_back:
sx,sy= x, y-20
sword=sword_sky
if event.key==K_RIGHT:
movex += .3
character=character_right
elif event.key==K_LEFT:
movex -= .3
character=character_left
elif event.key==K_UP:
movey -= .3
character=character_back
elif event.key==K_DOWN:
movey += .3
character=character_front
if event.type==KEYUP:
if event.key==K_RIGHT:
movex = 0
character=character_right_still
elif event.key==K_LEFT:
movex = 0
character=character_left_still
elif event.key==K_UP:
movey = 0
character=character_back
elif event.key==K_DOWN:
movey = 0
character=character_front
if event.key==K_a:
if character==character_right:
sx,sy= x+25, y+15
sword=sword_blank
elif character==character_right_still:
sx,sy= x+25, y+15
sword=sword_blank
elif character==character_left_still:
sx,sy= x-25, y+15
sword=sword_blank
elif character==character_left:
sx,sy= x-25, y+15
sword=sword_blank
elif character==character_front:
sx,sy= x, y+30
sword=sword_blank
elif character==character_back:
sx,sy= x, y-20
sword=sword_blank
x+=movex
y+=movey
#Creep movement
sdist = sqrt((sx - Ai_x)**2 + (sy - Ai_y)**2)
#damage
y2 = y - 20
if (sx, sy) != (x+40,y2):
if x > sx:
movesx = +.3
if x < sx:
movesx = -.3
if y > sy:
movesy = +.3
if y < sy:
movesy = -.3
sx+=movesx
sy+=movesy
#sword movement
if (Ai_x, Ai_y) != (x,y):
if x > Ai_x:
moveAi_x = .2
if x < Ai_x:
moveAi_x = -.2
if y > Ai_y:
moveAi_y = +.2
if y < Ai_y:
moveAi_y = -.2
Ai_x+=moveAi_x
Ai_y+=moveAi_y
#creep movement
#Controls for character
newallocatedstr=0
newallocatedend=0
newallocatedagi=0
newallocatedchr=0
newallocatedwis=0
#stats
if levelup == 2:
newallocatedstr = eval(input("strength? (0-5):" ))
newallocatedend = eval(input("endurance? (0-5):" ))
newallocatedagi= eval(input(" agility? (0-5):" ))
newallocatedchr = eval(input(" charisma? (0-5):" ))
newallocatedwis = eval(input("wisdom? (0-5):" ))
if ((newallocatedstr + newallocatedend)
+ (newallocatedagi + newallocatedchr) + newallocatedwis) > 5:
print("You filthy cheater.")
pygame.quit()
sys.exit()
levelup = 1
#levelup
strn = newallocatedstr
end = newallocatedend
agi = newallocatedagi
chra = newallocatedchr
wis = newallocatedwis
endurance= 5 + end
strength=5 + strn
wisdom=5 + wis
charisma=5 + chra
agility=5 + agi
#stats
# render health text
maxhealth = health
health = maxhealth
if sdist<12:
aihp = aihp - 1
dist = sqrt((Ai_x - x)**2 + (Ai_y - y)**2)
#damage
if dist<6:
health = health - 1
font = pygame.font.Font(None, 25)
mytext = font.render("Health:{0}".format(health), 1, (255,255,255))
exp = font.render("Experience:{0}".format(experience), 1, (255,255,255))
mytext = mytext.convert_alpha()
if health == 0:
print("Game over")
pygame.quit()
sys.exit()
mana = 50 + wisdom
font = pygame.font.Font(None, 25)
mana = font.render("Mana:"+str(mana), 1, (255,255,255))
font = pygame.font.Font(None, 20)
aihealth = font.render("Ai Health:{0}".format(aihp), 1, (255,255,255))
#create background
screen.blit(background, (0,0))
if aihp >= 0:
screen.blit(skeleton, (Ai_x, Ai_y))
screen.blit(aihealth, (Ai_x-25, Ai_y+40))
else:
experience = experience + 10
screen.blit(exp, (15, 5))
screen.blit(mytext, (15, 25))
screen.blit(mana, (15, 50))
screen.blit(aihealth, (Ai_x-25, Ai_y+40))
screen.blit(sword, (sx, sy))
screen.blit(healthpotion, (ix, iy))
screen.blit(character, (x,y))
pygame.display.flip()
pygame.display.update()
This is my current code, ive been working on it for a while.
Obviously the images won't work for anyone testing it, but my current problem is, when an enemy's hp goes below ``0, I don't know how to make the enemy completely get removed. I tried to do an
if AIhp <= 0:
Ai_x, Ai_y = -50, -50
But that only removes it from the screen, and since I also want to add drops it means when the Ai coordinates change so does the drop item coordinates since I only know how to make the drop coordinates equal to AI coordinates if I want it to appear in the place the AI died.
Also the exp, and health both go down fine, but when I try to add exp from monster kill it goes up by about 10 a millisecond indefinitely, and when I tried to make the maxhealth = health + endurance it was same issue with growing indefinitely.
I really need help, I have tried sorting my code into multiple functions but it only makes the entire thing stop working which pretty much exits out the option of just changing the sprites to objects...
Your code does not take into account, that an enemy cannot be dead. I recommend to use a list of enemies, and then do all the operations for all the enemies. On enemy death, you will add the xp only once.
It's better do make an Enemy class, that will do all the drawing, killing, etc, so you will not need to worry about it in your main code. Here are example calls:
for enemy in enemies:
if sdist<12:
enemy.hit()
if not enemy.isAlive():
enemies.remove(enemy)
#add Drops
enemy.move()
enemy.draw(screen)
Same goes for a player. If you divide this up, you will have a much easier time adding new functionality. The whole player movement could be a function in the class Player, since it does not interact with anything else.
Do not use eval, a player would be able to execute any code. You want to cast the str to int. Like this:
newallocatedwis = int(input("wisdom? (0-5):" ))
If changing the code is too difficult, you can always start over. You already have the code written, so you will know what goes where.