Python: Creating a slot based inventory system - python

I am trying to create an inventory system in Python using the module pygame. The inventory system is similar to Minecraft. It is a slot-based inventory system, meaning that each item is attached to a slot, and can be moved around the inventory.
This is the code:
#Module that allows me to create a screen and add images to the screen
import pygame
pygame.init()
win = pygame.display.set_mode((800,600))
#Variable that will keep track of the index of what slot the player is
#selecting
selectedSlot = None
#Function that checks whether there is collision or not
def collision(x,y,x2,y2,w):
if x + w > x2 > x and y+w > y2 > y:
return True
else:
return False
#Slot Class
class slotClass:
def __init__(self,x,y):
self.x = x
self.y = y
def draw(self,win):
#Draws the slots using the x and y values
pygame.draw.rect(win,(255,0,0),(self.x,self.y,50,50))
#Uses a function to test for collision with the mouse's x and y values
if collision(self.x,self.y,mx,my,66):
global selectedSlot
pygame.draw.rect(win,(128,0,0),(self.x,self.y,50,50))
#This will set an integer value to a varaible, dependent on what the index of the element the player selecting is
selectedSlot = slotArray.index(self)
#Problem with code:
#When the following 2 lines are uncommmented, the variable selectedSlot is set to "None", regardless of whether there is collision or not
#else:
#selectedSlot = None
#Slot array
slotArray = []
#Slot information
slotCount = 9
#Populates the slotArray with the desired slotCount
while len(slotArray) != slotCount:
slotArray.append(slotClass(100+len(slotArray)*70,50))
#main loop
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
print(selectedSlot)
#Mouse x and y value to set to the vars mx and my
mx,my = pygame.mouse.get_pos()
win.fill((0,0,0))
#For every element in the slotArray, the function draw is called from the slotClass
for i in slotArray:
i.draw(win)
pygame.display.update()
pygame.quit()
How it works is I have a class, which will hold the information for each slot. Such as the x value, y value and what item is in every slot.
Then I have an array, which will contain every slot instance. I also defined how many slots I want entirely.
To populate this array with the slots, I first started by constantly appending to this array of slots until it equals the desired amount of slots in each row. I multiply 55 by the number of elements in the array to spread apart the slots.
The problem I am having comes when trying to create the ability for the player to mouse over/select each slot. What I want is for the player to simply be able to hover over a slot, that slot will turn a different colour, and then the player can select an item out of said slot.
I created a collision function for that and I'm calling that function within the draw function inside the slotClass. I also have a variable called slotSelected, which keeps track of the index of the slot that the player is mousing over with/hovering over.
The problem I am experiencing is, that whenever the player hovers over a slot and then stops hovering over any slots, the slot index that I am setting still remains to be the index of the slot the player was just on. When I add an else statement to check if there is no collision with a slot, and set the var slotSelected to something like None for example (to show that the player isn't colliding with any slot) the var slotSelected is constantly set to None, regardless of whether there is collision or not. I have a print statement that prints the value of slotSelected. You'll notice that it prints the index of the slot the player is colliding with. However, when you uncomment the else statement I have contained in the slotClass, the var slotSelected will always be set to None.
Any suggestions on how to fix this?

Perhaps you could try testing for another collision with the background as well. I'm not an expert at pygame, but this is the pseudo-code I would use:
if collision("with game window"): # If mouse is within your game screen
global selectedSlot
if collision(self.x, self.y, mx, my, 66): # mouse is on screen AND on a box
selectedSlot = slotArray.index(self)
else: # mouse on screen, but not on box -> return None
selectedSlot = None
So that you can assign None when the mouse is in your game window but not on an item slot.
EDIT
I figured out why it is responding as such. You have the lines:
for i in slotArray:
i.draw(win)
Which will go to slot 0, see it is selected -> set selectedSlot = 0, then go to slot 1, see it is unselected -> set selectedSlot = None OVERWRITING the value you had previously set. You will need to break your loop if selectedSlot is not None!! Here is code to solve that:
#Module that allows me to create a screen and add images to the screen
import pygame
pygame.init()
win = pygame.display.set_mode((800,600))
#Variable that will keep track of the index of what slot the player is
#selecting
selectedSlot = None
#Function that checks whether there is collision or not
def collision(x,y,x2,y2,w):
if x + w > x2 > x and y+w > y2 > y:
return True
else:
return False
#Slot Class
class slotClass:
def __init__(self,x,y):
self.x = x
self.y = y
def draw(self, win):
#Draws the slots using the x and y values
pygame.draw.rect(win, (255, 0, 0), (self.x, self.y, 50, 50))
#Uses a function to test for collision with the mouse's x and y values
if collision(self.x, self.y, mx, my, 66):
global selectedSlot
pygame.draw.rect(win, (128, 0, 0), (self.x, self.y, 50, 50))
#This will set an integer value to a varaible, dependent on what the index of the element the player selecting is
selectedSlot = slotArray.index(self)
#Problem with code:
#When the following 2 lines are uncommmented, the variable selectedSlot is set to "None", regardless of whether there is collision or not
else:
selectedSlot = None
#Slot array
slotArray = []
#Slot information
slotCount = 9
#Populates the slotArray with the desired slotCount
while len(slotArray) != slotCount:
slotArray.append(slotClass(100+len(slotArray)*70,50))
#main loop
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#Mouse x and y value to set to the vars mx and my
mx,my = pygame.mouse.get_pos()
win.fill((0,0,0))
#For every element in the slotArray, the function draw is called from the slotClass
selectedSlot = None
for i in slotArray:
i.draw(win)
if selectedSlot is not None:
break
print(selectedSlot)
pygame.display.update()
pygame.quit()
The problem is you've combined drawing the items and selecting them into one function (very bad). So now when the loop is broken the rest of the boxes are not drawn!!

Related

Memory Game in Pygame [duplicate]

This question already has answers here:
how to handle time for different components in pygame
(1 answer)
Adding a particle effect to my clicker game
(1 answer)
How can I show explosion image when collision happens?
(2 answers)
Closed 2 years ago.
I am making a memory game for an assignment. When clicking on two tiles that are not the same, only the first tile shows but the second tile does not show and immediately both the tiles are flipped. Is there a way to slow it down so that both tiles are shown before they are flipped again? I have tried using pygame.time.delay() and time.sleep() but nothing seems to work.
class Game:
def handle_mouse_up(self, position):
# handles the events that take place when the mouse button is clicked
# - self is the Game
# - position is the position where the mouse button is clicked
if len(self.flipped) < 2:
for row in self.board:
for tile in row:
if tile.select(position) and not tile.covered:
self.flipped.append(tile)
if len(self.flipped) == 2:
self.check_matching()
def check_matching(self):
# checks whether both the tiles which are flipped are matching or not
# - self is the Game
if self.flipped[0].same_tiles(self.flipped[1]):
self.flipped[0].show_tile()
self.flipped[1].show_tile()
self.flipped.clear()
else:
self.flipped[0].hide_tile()
self.flipped[1].hide_tile()
self.flipped.clear()
class Tile:
def show_tile(self):
# shows the tile which is clicked
# - self is the Tile
self.covered = False
def hide_tile(self):
# hides the tile which is not clicked
# - self is the Tile
self.covered = True
def select(self, mouse_position):
# checks whether a tile has been clicked on or not
# - self is the Tile
# - mouse_position is the position where the mouse is clicked
mouse_click = False
if self.rect.collidepoint(mouse_position):
if self.covered:
mouse_click = True
self.show_tile()
else:
mouse_click = False
return mouse_click
def same_tiles(self, other_tile):
# checks whether both the tiles are same
# - self is the Tile
# - other_tile is the second tile which is flipped
return self.image == other_tile.image
Change the method hide tiles. Don't hide the tiles, but compute the time when the tile has to be hidden. Use pygame.time.get_ticks() to return the number of milliseconds since pygame.init() was called. Calculate the point in time after that the image has to be hidden. Add a method that evaluates if the time has exceeded and the tile has to be hidden
class Tile:
def __init__(self):
self.hide_time = None
# [...]
def hide_tile(self):
self.hide_time = pygame.time.get_ticks() + 1000 # 1000 milliseconds == 1 second
def hide_timed(self):
if self.hide_time != None and self.hide_time < pygame.time.get_ticks()
self.covered = True
self.hide_time = None
Add a method to Game that evaluate whether the tiles need to be hidden:
class Game:
# [...]
def hide_timed(self):
for row in self.board:
for tile in row:
tile.hide_timed()
Call hide_timed in the application loop in every frame.

The pong ball is just stopped in the initial point by unknown issue

I am new to Pygame, and doing a assigned homework. I tried to make an program called Pong by python3. The ball was moving correctly, but after I write something new in the program, the ball just stopped at the initial point. I already check many times of my code, but I can't find what's wrong with it.
Here is my code below:
# Pong Version 2
# Display the ball and both paddles, with the ball bouncing from the window edges.
# The ball bounce off the paddles. It go through the back of paddles.
# The paddles do not move, but score is updated.
# The game ends when a score is 11 or until the player closes the window.
import pygame, random
def main():
# initialize all pygame modules (some need initialization)
pygame.init()
# create a pygame display window
pygame.display.set_mode((1000, 800))
# set the title of the display window
pygame.display.set_caption('Pong')
# get the display surface
w_surface = pygame.display.get_surface()
# create a game object
game = Game(w_surface)
#init text
pygame.font.init()
# start the main game loop by calling the play method on the game object
game.play()
# quit pygame and clean up the pygame window
pygame.quit()
class Game:
# An object in this class represents a complete game.
def __init__(self, surface):
# Initialize a Game.
# - self is the Game to initialize
# - surface is the display window surface object
# === objects that are part of every game that we will discuss
self.surface = surface
self.bg_color = pygame.Color('black')
self.FPS = 60
self.game_Clock = pygame.time.Clock()
self.close_clicked = False
self.continue_game = True
self.num_board1 = '0'
self.num_board2 = '0'
# === game specific objects
#ball elements
#(ball_color,ball_radius,ball_center,ball_velocity,surface)
self.ball = Ball('white', 10, [500, 400], [10,20], self.surface)
#rectangle elements
#(rect_left_top,rect_width_height,rect_surface,rect_color)
self.paddle1 = Rect((150,350),(10,100),self.surface,'white')
self.paddle2 = Rect((840,350),(10,100),self.surface,'white')
#board elements
#(name,size,content,color,center,screen)
#board size is (57,104)
self.board1 = Board("",150,self.num_board1,(100,100,100),(10,10),self.surface)
self.board2 = Board("",150,self.num_board2,(100,100,100),(933,10),self.surface)
def play(self):
# Play the game until the player presses the close box.
# - self is the Game that should be continued or not.
while not self.close_clicked: # until player clicks close box
# play frame
self.handle_events()
self.draw()
if self.continue_game:
self.update()
self.decide_continue()
self.game_Clock.tick(self.FPS) # run at most with FPS Frames Per Second
def handle_events(self):
# Handle each user event by changing the game state appropriately.
# - self is the Game whose events will be handled
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
self.close_clicked = True
def draw(self):
# Draw all game objects.
# - self is the Game to draw
self.surface.fill(self.bg_color) # clear the display surface first
# Draw game elements ball
self.ball.draw()
# Draw game elements paddles
self.paddle1.draw()
self.paddle2.draw()
# Draw game elements boards
self.board1.draw()
self.board2.draw()
pygame.display.update() # make the updated surface appear on the display
def update(self):
# Update the game objects.
# - self is the Game to update
self.ball.move()
def decide_continue(self):
# Check and remember if the game should continue
# - self is the Game to check
pass
if self.num_board1 or self.num_board2 == '11':
self.continue_game = False
class Ball:
# An object in this class represents a ball that moves
def __init__(self, ball_color, ball_radius, ball_center, ball_velocity, surface):
# Initialize a ball.
# - self is the ball to initialize
# - color is the pygame.Color of the ball
# - center is a list containing the x and y int
# coords of the center of the ball
# - radius is the int pixel radius of the ball
# - velocity is a list containing the x and y components
# - surface is the window's pygame.Surface object
self.color = pygame.Color(ball_color)
self.radius = ball_radius
self.center = ball_center
self.velocity = ball_velocity
self.surface = surface
def move(self):
# Change the location of the ball by adding the corresponding
# speed values to the x and y coordinate of its center
# - self is the ball
size = self.surface.get_size() # size is a tuple (width,height)
for index in range(0,2):
self.center[index] = self.center[index] + self.velocity[index]
if self.center[index] < self.radius: # left or top
self.velocity[index] = -self.velocity[index] # bounce the ball
if self.center[index]+ self.radius > size[index]:# right of bottom
self.velocity[index] = -self.velocity[index] # bounce the ball
def draw(self):
# Draw the ball on the surface
# - self is the ball
pygame.draw.circle(self.surface, self.color, self.center, self.radius)
class Rect:
def __init__(self,rect_left_top,rect_width_height,rect_surface,rect_color):
#set elements
# - rect_left_top is the distance from edge
# - rect_width_height is width and height of the rectangle
# - rect_surface is the surface of rectangle
# - rect_color is color of the rectangle
self.left_top = rect_left_top
self.width_height = rect_width_height
self.surface = rect_surface
self.color = pygame.Color(rect_color)
self.rect = (self.left_top,self.width_height)
def draw(self):
# draw the rectangle
pygame.draw.rect(self.surface,self.color,self.rect)
class Board:
def __init__(self,name,size,content,color,text_left_right,surface):
# - name is the typeface of the text
# - size is the size of the text
# - content is the content fo the text
# - color is the color of text, it looks like (x,x,x), which is combined by three basic color)
# - text_left_right is a the left and right distance from edge
# - surface is a the display.set_mode, used to blit text
self.name = name
self.size = size
self.content = content
self.color = color
self.text_left_right = text_left_right
self.surface = surface
def draw(self):
#to show the text
font = pygame.font.SysFont(self.name,self.size)
text = font.render(self.content,True,self.color)
self.surface.blit(text,self.text_left_right)
main()
The problem with decide_continue method.
When I remove call of decide_continue from play method ball start moving:
def play(self):
# Play the game until the player presses the close box.
# - self is the Game that should be continued or not.
while not self.close_clicked: # until player clicks close box
# play frame
self.handle_events()
self.draw()
if self.continue_game:
self.update()
self.game_Clock.tick(self.FPS) # run at most with FPS Frames Per Second
So the problem with decide_continue method. You should review it.
Going by the test done by Yurii (see other answer), since you didn't mention which change you had made before the program didn't work properly anymore.
decide_continue(self) has the following if-clause:
if self.num_board1 or self.num_board2 == '11':
This means it evaluates whether self.num_board1 is equivalent to True (i.e., not False, 0, empty string and the like), which is probably always the case, or it compares self.num_board2 to the string '11'.
Important note: the string '0', the initial value for the boards, does not evaluate to False: it's not an empty string, nor an integer 0.
Because of the first condition, this if-statement will practically always evaluate in being true.
You probably want:
if self.num_board1 == '11' or self.num_board2 == '11':
or something like that. You should also consider whether you want a '11' string, or that this should be a number (integer) instead, given that you call the variable num_board.
Further, since no manipulations with num_board1/2 appear to be done, it's value will likely never change from the initial value '0', in which case it'll, at this moment, never be equal to '11'.

Python pygame function not defined

I've been asked to find a problem with my cousin's Pygame code. I'm not big on Python, using other languages more and I haven't been able to find the issue by googling or debugging. Basically he's getting a "playGame is not defined" error, playGame being a function. Other questions about this are usually because:
The function is called before it is declared
The function is declared inside a different scope from which it is called
Neither of these seems to be the issue so I'm hoping someone more versed in Python can spot it. I've copied his code below with a lot of what (I hope) is irrelevant to the question removed to simplify.
The function playGame is not working and is called by a button click under
def button(msg, x, y, action = None):
. Interestingly the exit function is working fine which is called and declared exactly the same as playGame as far as I can tell.
# --------------- SETUP ---------------
# Importing necessary modules
import pygame
import math
import random
# --------------- DISPLAY ---------------
# Setting up the display
pygame.init()
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption(title)
# --------------- GLOBALS ---------------
#removed globals from stackoverflow version
# --------------- FUNCTIONS ---------------
# Blitting the text
def blitText(angle, power):
#code
# Drawing the tank model
def drawTank():
#code
# Creating the buttons
def button(msg, x, y, action = None):
mousePos = pygame.mouse.get_pos() # Gets the mouse position
mouseClick = pygame.mouse.get_pressed()
(buttonWidth, buttonHeight) = (175, 45) # Sets the button width and height
if x + (buttonWidth / 2) > mousePos[0] > x - (buttonWidth / 2) and y + buttonHeight > mousePos[1] > y: # Checks if the mouse is over the button
pygame.draw.rect(screen, darkGrey, [x - (buttonWidth / 2), y, buttonWidth, buttonHeight]) # Draws a dark grey button
if mouseClick[0] == 1 and action != None: # Checks if the button is clicked
if action == "play":
playGame()
elif action == "exit":
exit()
else:
pygame.draw.rect(screen, grey, [x - (buttonWidth / 2), y, buttonWidth, buttonHeight]) # Draws a light grey button if not
screen.blit(msg, [x - (buttonWidth / 2), y]) # Writes the text over the button
# Defining the shell
class shell(pygame.sprite.Sprite): # Creates the shell() class
def __init__(self): # Defines an initiation fuction for this class
super().__init__() # Call the parent class constructor
self.image = pygame.Surface([2, 2]) # Defines the bullet as a 2x4 surface
self.image.fill(black) # Paints the bullet black
self.rect = self.image.get_rect() # Gets the area size of the bullet
def update(self): # Defines a function as update for this class
(bulletChangeX, bulletChangeY) = (((maxAngle - angle) / maxAngle) * (bulletSpeed * power), (angle / maxAngle) * (bulletSpeed * power)) # Ccalculates the changes in x and y
bulletChangeY -= vert # Changes the trajectory of the bullet
self.rect.y -= bulletChangeY # Moves the bullet in the y axis
self.rect.x += bulletChangeX # Moves the bullet in the x axis
# --------------- TITLE SCREEN ---------------
# Creating the main menu
menu = True
while menu: # Starts the loop
for event in pygame.event.get():
if event.type == pygame.QUIT: # Checks if pygame has been closed
exit() # Exits python
screen.fill(white) # Fills the screen white
screen.blit(titleText, [0, 0]) # Writes the title text
button(startButton, width / 2, (height / 3) * 2, "play") # Calls the button function for the start button
button(exitButton, width / 2, ((height / 3) * 2) + 70, "exit") # Calls the button function for the exit button
# Updating the display
pygame.display.update() # Updates the display
clock.tick(fps)
# --------------- MAIN LOOP ---------------
# Running the program
def playGame():
#code. This function has no return value.
# --------------- EXIT ---------------
# Exits PyGame and Python
def exit():
pygame.quit()
quit()
Hopefully the mistake is obvious here to someone and I haven't removed any key code that is causing the problems (I removed start variable declarations and the contents of function code) I can provide the full code if people need it.
Yes, themistake is obvious - as you put it:
The cod e is trying to call the function before it is defined -
the while menu code which draws the menu screen and draws the button is placed before the playGame function - which name is undeclared at that point.
While Python does run code on the module top-level, the best pratice is to leave only some constant and variable declarations on the toplevel, and putting code like the block while menu: ... inside a function. (Which may be called main - but there is no top language requirement on its name)
Then, at the very bottom of the file, make a call to that function, with a call placed - this time correctly, at the module body -
So - something along:
def main():
# Creating the main menu
menu = True
while menu: # Starts the loop
for event in pygame.event.get():
if event.type == pygame.QUIT: # Checks if pygame has been closed
exit() # Exits python
screen.fill(white) # Fills the screen white
screen.blit(titleText, [0, 0]) # Writes the title text
button(startButton, width / 2, (height / 3) * 2, "play") # Calls the button function for the start button
button(exitButton, width / 2, ((height / 3) * 2) + 70, "exit") # Calls the button function for the exit button
# Updating the display
pygame.display.update() # Updates the display
clock.tick(fps)
And at the very bottom, place a single main() call would make that particular error go away.

Pygame: .set_clip returns first value in list, instead of requested value

I am trying to make walking animations for images returned from a sprite sheet.
I am using .set_clip to return a portion of the sprite sheet. Everything looks like it should work
But the .set_clip is returning the first Rect value in the list that I provide, rather than the item in the list that I call for.
My Code
import pygame, sys
from pygame.locals import *
xList = (6, 27,48,69)
yList = (24,24,24,24)
wList = (21,21,21,21)
hList = (32,32,32,32)
ani_pos = list(zip(xList, yList, wList, hList))
sprite_num = 0
class Player:
def __init__(self):
playerSheet = pygame.image.load('MainChar1.png').convert() # load sprite sheet
playerSheet.set_clip(pygame.Rect(ani_pos[sprite_num]))
self.player = playerSheet.subsurface(playerSheet.get_clip())
self.x = 320
self.y = 240
def draw(self, DISPLAYSURF):
DISPLAYSURF.blit(self.player, (self.x, self.y))
self.player.set_colorkey(255, 0, 255)# set sprite background invisible
class Background:
def __init__(self):
dirtTile = pygame.image.load('DirtBackground.png').convert()
dirtTile.set_clip(pygame.Rect(0, 0, 863, 1103))
self.background = dirtTile.subsurface(dirtTile.get_clip())
self.x = -111
self.y = -311
def draw(self, DISPLAYSURF):
DISPLAYSURF.blit(self.background, (self.x, self.y))
pygame.init()
FPS = 30
FPSCLOCK = pygame.time.Clock()
# set up the window
WINDOWWIDTH = 640 # size of windows' width in pixels
WINDOWHEIGHT = 480 # size of windows' height in pixels
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption('farm game')
player = Player()
background = Background()
pygame.key.set_repeat(1,0) # (mSec to begin repeat, mSec between repeats)
running = True
while running: # the main game loop
DISPLAYSURF.fill (255,255,255)
background.draw(DISPLAYSURF)
player.draw(DISPLAYSURF)
pygame.display.flip()
FPSCLOCK.tick(FPS)
for event in pygame.event.get(): # event handling loop
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
running = False
elif event.type == KEYDOWN:
if event.key == K_d:
background.x -= 4
sprite_num = 2
I am using .set_clip to return a portion of the sprite sheet.
No, you don't. You use subsurface to return a portion of the sprite sheet.
I don't know what you think what set_clip does, but it's probably not what it actually does:
This is a rectangle that represents the only pixels on the Surface that can be modified.
Better explained at the SDL docs:
Sets the clipping rectangle for a surface. When this surface is the destination of a blit, only the area within the clip rectangle will be drawn into.
But the .set_clip is returning the first Rect value in the list that I provide, rather than the item in the list that I call for.
set_clip does not return any Rect value of any list at all.
When you call pygame.Rect(ani_pos[sprite_num]), you're creating a Rect with the values in the list ani_pos at index sprite_num, and sprite_num is 0 when the __init__ method is called.
You store your Surface that you create in the __init__ method in the player field, but you never actually change it. If you want to create some kind of animation, you should change it every n frames. You change sprite_num to 2, but that does not do anything since you never use that value again.

Different colors for shapes through iterations in Python / Pygame?

I'm new to stackoverflow, but was hoping for a little insight from more advanced programmers. I am switching majors to Computer Science next semester and am taking an intro class learning some beginner's Python programming. I have already finished the program below (the assignment was to make a program that draws ovals on the window surface by filling in some of the professor's code, not too bad at all) but I wanted to add a little something extra: As you can see, I have the color of the ovals set to be random, but it stays the same until the program is restarted entirely i.e. all of the ovals are that particular color for the length of the program. With the code written the way it is, I can't figure out a way to get the color to change for each oval. Keep in mind, this is all for kicks, but if anyone's feeling especially helpful or creative, I'm curious to see what you have to say. Let me know if I can expound on anything. Thanks!
import pygame, random, sys
WINDOWWIDTH = 700
WINDOWHEIGHT = 700
BACKGROUNDCOLOR = (150,160,100)
#A different color every run
OVAL_COLOR = (random.randint (0,255),random.randint (0,255),
random.randint (0,255))
pygame.init()
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption("Mobile Ovals")
#The draw variable is used later to indicate the mouse is still pressed
ovals = []
completedOvals = []
finished = False
draw = False
startXY = (-1, -1)
while not finished:
for event in pygame.event.get():
if event.type == pygame.QUIT or (event.type == pygame.KEYUP and
event.key == pygame.K_ESCAPE):
finished = True
elif event.type == pygame.KEYDOWN:
pressed = pygame.key.get_pressed()
if pressed[pygame.K_F4] and (pressed[pygame.K_LALT] or
pressed[pygame.K_RALT]):
finished = True
elif event.type == pygame.MOUSEBUTTONDOWN:
startXY = event.pos
draw = True
elif event.type == pygame.MOUSEBUTTONUP:
draw = False
for oval in ovals:
completedOvals.append (oval)
if draw == True:
del ovals [:]
#The above function ensures only one oval is onscreen at any given time
endXY = event.pos
width = (abs(endXY[0]-startXY[0]))
height = (abs(endXY[1]-startXY[1]))
#The code below allows the user to drag any direction
if endXY[0] < startXY[0]:
left = endXY[0]
else:
left = startXY[0]
if endXY[1] < startXY[1]:
top = endXY[1]
else:
top = startXY[1]
ovals.append (pygame.Rect (left, top, width, height))
windowSurface.fill(BACKGROUNDCOLOR)
for oval in ovals:
pygame.draw.ellipse(windowSurface, OVAL_COLOR, oval)
for completedOval in completedOvals:
pygame.draw.ellipse(windowSurface, OVAL_COLOR, completedOval)
pygame.display.update()
pygame.quit()
Your problem is quite simple. You set OVAL_COLOR once. But every time you make reference to the variable OVAL_COLOR, you're not creating a new random color, you're re-using the RGB color that was randomly generated when you created the variable.
Now, the way your program is structured, you maintain a list of all complete ovals that you're re-drawing every time the draw variable is set to true. If you place the OVAL_COLOR variable inside the for loop, you will update the color with every mouse movement, changing the color of the oval being drawn, as well as the color of all the old ovals being re-drawn.
The solution to have a new random oval color is to set the variable OVAL_COLOR when the mouse button goes down. That way, the oval color won't change as you drag the mouse to adjust the oval. But, given the current structure of the program, you'll need to save the oval colors assigned to completed ovals, or you'll still have the oval color change each time.
When the mouse button is pressed down, we want a new random color for our circle. Generate a random value, which will be used every time the circle is re-drawn.
elif event.type == pygame.MOUSEBUTTONDOWN:
startXY = event.pos
OVAL_COLOR = (random.randint (0,255),random.randint (0,255),
random.randint (0,255))
draw = True
When the mouse button is released, save the coordinates for the oval, along with the color that it was drawn with.
elif event.type == pygame.MOUSEBUTTONUP:
draw = False
# print len(ovals) # (always ==1)
completedOvals.append ((ovals[-1], OVAL_COLOR))
When we iterate through these completed ovals, draw them with the same color each time.
for (completedOval, color) in completedOvals:
pygame.draw.ellipse(windowSurface, color, completedOval)
Create a simple Oval() class, that contains it's color, and size.
import pygame
from pygame.locals import *
class Oval(object):
"""handle, and draw basic ovals. stores Rect() and Color()"""
def __init__(self, startXY, endXY):
self.color = Color(random.randint(0,255), random.randint(0,255), random.randint(0,255))
self.rect = Rect(0,0,1,1)
self.coord_to_oval(startXY, endXY)
def draw(self):
pygame.draw.ellipse(windowSurface, self.color, self.rect)
def coord_to_oval(self, startXY, endXY):
width = (abs(endXY[0]-startXY[0]))
height = (abs(endXY[1]-startXY[1]))
#The code below allows the user to drag any direction
if endXY[0] < startXY[0]:
left = endXY[0]
else:
left = startXY[0]
if endXY[1] < startXY[1]:
top = endXY[1]
else:
top = startXY[1]
self.rect = Rect(left, top, width, height)
# main loop
while not finished:
for event in pygame.event.get():
# events, and creation:
# ... your other events here ...
elif event.type == MOUSEBUTTONDOWN:
startXY = event.pos
draw = True
elif event.type ==MOUSEBUTTONUP:
# on mouseup, create instance.
endXY = event.pos
oval_new = Oval(startXY, endXY)
completedOvals.append(oval_new)
# draw them:
for oval in ovals:
oval.draw()
for oval in completedOvals:
oval.draw()
I mostly left out your non-completed ovals. Was that to show the size before clicking?

Categories

Resources