Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pygame Scroll Bar To Play Volume Low OR High.?

Tags:

python

pygame

so I have a scroll bar in my game what Im trying to do is make it so if my mouse is over the bar1 button and we are on the moving_spot of the bar1 button then we can move it up and down on its y axis

how can I move the bar up and down and if its colliding with with any of the volume buttons I can change the volume of my background music either 0.1 or 0.2 or 0.3 so it controls my game volumepygame.mixer.music.set_volume(0.3) enter image description here

my problem is im not sure how I could get this started I have everything in place but not sure where to start ***how can I move the bar with my mouse on the moving_spot on its y values only and if the bar1 is over and of the volume1 2 or 3 4 buttons then it should play the volume at defferent level im not sure how to approach this problem any help is appreciated I just need a way to adjust my music of my game if the player moves the bar up or down

while run:
    # Making game run with fps
    clock.tick(fps)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False


        if event.type == pygame.MOUSEBUTTONDOWN:
            pos = pygame.mouse.get_pos()
        
              # this is our bar1 the gray part that we will be able to move
            if bar1.isOver(pos):
                bar1.y = pos
                print("WORKING{")
                

here are my buttons and positions places the move_spot is where the bar1 can only move up and down the bar1 is the bar that the player can control to control the volume and also the volume 1 2 3 4 are where the defferent volume of our background music will be set

move_spot = button((colors),720,125,25,260, '')

bar1 = button((colors),715,125,30,60, '')
volume1 = button((colors2),715,145,30,60, '')
volume2 = button((colors2),715,210,30,60, '')
volume3 = button((colors2),715,280,30,60, '')
volume4 = button((colors2),715,350,30,60, '')


buttons = [bar1,move_spot,volume1,volume2,volume3,volume4]

this is my buttons class

# our buttons
class button():
    def __init__(self, color, x,y,width,height, text=''):
        self.color = color
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.text = text
        self.over = False

    def draw(self,window,outline=None):
                #Call this method to draw the button on the screen
        if outline:
            pygame.draw.rect(window, outline, (self.x-2,self.y-2,self.width+4,self.height+4),0)
                    
        pygame.draw.rect(window, self.color, (self.x,self.y,self.width,self.height),0)
                
        if self.text != '':
            font = pygame.font.SysFont('image/abya.ttf', 60)
            text = font.render(self.text, 1, (255,255,255))
            window.blit(text, (self.x + (self.width/2 - text.get_width()/2), self.y + (self.height/2 - text.get_height()/2)))

    def isOver(self, pos):
                #Pos is the mouse position or a tuple of (x,y) coordinates
        if pos[0] > self.x and pos[0] < self.x + self.width:
            if pos[1] > self.y and pos[1] < self.y + self.height:
                return True
                    
        return False

    def playSoundIfMouseIsOver(self, pos, sound):
        if self.isOver(pos):            
            if not self.over:
                click.play()
                self.over = True
        else:
            self.over = False



here a minimal code you can run and test with this bar imageenter image description here

heres the background music music

import pygame
pygame.init()

window = pygame.display.set_mode((800,800))



# our buttons
class button():
    def __init__(self, color, x,y,width,height, text=''):
        self.color = color
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.text = text
        self.over = False

    def draw(self,window,outline=None):
                #Call this method to draw the button on the screen
        if outline:
            pygame.draw.rect(window, outline, (self.x-2,self.y-2,self.width+4,self.height+4),0)
                    
        pygame.draw.rect(window, self.color, (self.x,self.y,self.width,self.height),0)
                
        if self.text != '':
            font = pygame.font.SysFont('freesansbold.ttf', 60)
            text = font.render(self.text, 1, (255,255,255))
            window.blit(text, (self.x + (self.width/2 - text.get_width()/2), self.y + (self.height/2 - text.get_height()/2)))

    def isOver(self, pos):
                #Pos is the mouse position or a tuple of (x,y) coordinates
        if pos[0] > self.x and pos[0] < self.x + self.width:
            if pos[1] > self.y and pos[1] < self.y + self.height:
                return True
                    
        return False

    def playSoundIfMouseIsOver(self, pos, sound):
        if self.isOver(pos):            
            if not self.over:
                click.play()
                self.over = True
        else:
            self.over = False


colors = 0,23,56
colors2 = 0,123,56

bar11 = pygame.image.load("bar.png").convert_alpha()


move_spot = button((colors),720,125,25,260, '')

bar1 = button((colors),715,125,30,60, '')
volume1 = button((colors2),715,145,30,60, '')
volume2 = button((colors2),715,210,30,60, '')
volume3 = button((colors2),715,280,30,60, '')
volume4 = button((colors2),715,350,30,60, '')


buttons = [bar1,move_spot,volume1,volume2,volume3,volume4]



# fos
fps = 60
clock = pygame.time.Clock()


# redraw
def redraw():
    window.fill((40,100,200))
    for button in buttons:
        button.draw(window)
    window.blit(bar11,(bar1.x,bar1.y))

# main loop
run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False



    redraw()

    pygame.display.update()

pygame.quit()

like image 768
Habib Ismail Avatar asked Oct 23 '25 08:10

Habib Ismail


1 Answers

As a general rule of thumb, you want to delegate all the heavy lifting to classes, and leave the "good" stuff to the mainloop of your program. I have created a simple class here, which takes some inputs (width, height, number of slider options), and will take care of all the drawing, positioning, etc. for you. It also has an option of self.chosen, which tells you which option is picked. I then used this to set the volume outputted by the mixer, based on which option is chosen, in the update() function:

class VolumeBar(pygame.sprite.Sprite):
    def __init__(self, options, width, height):

        # Store these useful variables to the class
        self.options = options
        self.width = width
        self.height = height

        # The slider
        self.slider = pygame.image.load('slider.png')
        self.slider_rect = self.slider.get_rect()
        
        # The background "green" rectangles, mostly for decoration
        self.back = []
        objectHeight = (height-options*6)/(options-1)
        self.backHeight = objectHeight
        
        for index in range(options-1):
            self.back.append(pygame.Rect((0, rint((6*index+6)+index*objectHeight)), (width, rint(objectHeight))))

        # The foreground "blue" rectangles, mostly for decoration
        self.fore = []

        for index in range(options):
            self.fore.append(pygame.Rect((0, rint(index*(objectHeight+6))), (width, 6)))

        # Get list of possible "snaps", which the slider can be dragged to
        self.snaps = []
        
        for index in range(options):
            self.snaps.append((width/2, 3+(index*(objectHeight+6))))
            
        # A simple variable to tell you which option has been chosen
        self.chosen = 0

        # Generate the image surface, then render the bar to it
        self.image = pygame.Surface((width, height))
        self.rect = self.image.get_rect()
        self.render()

        self.focus = False

    def render(self):
        self.image.fill([255, 255, 255])
        
        for back in self.back:
            pygame.draw.rect(self.image, [0, 192, 0], back)

        for fore in self.fore:
            pygame.draw.rect(self.image, [0, 0, 0], fore)

        self.image.blit(self.slider, (rint((self.width-self.slider_rect.width)/2),
                        rint(self.snaps[self.chosen][1]-(self.slider_rect.height/2))))

    def draw(self, surface):
        surface.blit(self.image, self.rect.topleft)

    def mouseDown(self, pos):
        if self.rect.collidepoint(pos):
            self.focus = True
            return True
        return False

    def mouseUp(self, pos):
        if not self.focus:
            return

        self.focus = False

        if not self.rect.collidepoint(pos):
            return

        pos = list(pos)

        # We've made sure the mouse started in our widget (self.focus), and ended in our widget (collidepoint)
        # So there is no reason to care about the exact position of the mouse, only where it is relative
        # to this widget
        pos[0] -= self.rect.x
        pos[1] -= self.rect.y

        # Calculating max distance from a given selection, then comparing that to mouse pos
        dis = self.backHeight/2 + 3

        for index, snap in enumerate(self.snaps):
            if abs(snap[1]-pos[1]) <= dis:
                self.chosen = index
                break

        self.render()

    def update(self):
        pygame.mixer.music.set_volume((self.options-self.chosen)*0.1)

Most of the __init__ function is spent calculating positions for each of the green and black rectangles, which are drawn in render() and displayed in draw(). The other functions are there for the mouse input, the first checks if the mouseDown happened on said button, and if it did, it sets self.focus to True, so that the mouseUp handler knows that it should change the slider position.

All of this works together to make a working VolumeBar. Below is an example of how it would be used in a mainloop:

import pygame
pygame.init()

rint = lambda x: int(round(x, 0)) # Rounds to the nearest integer. Very useful.

class VolumeBar(pygame.sprite.Sprite):
    def __init__(self, options, width, height):

        # Store these useful variables to the class
        self.options = options
        self.width = width
        self.height = height

        # The slider
        self.slider = pygame.image.load('slider.png')
        self.slider_rect = self.slider.get_rect()
        
        # The background "green" rectangles, mostly for decoration
        self.back = []
        objectHeight = (height-options*6)/(options-1)
        self.backHeight = objectHeight
        
        for index in range(options-1):
            self.back.append(pygame.Rect((0, rint((6*index+6)+index*objectHeight)), (width, rint(objectHeight))))

        # The foreground "blue" rectangles, mostly for decoration
        self.fore = []

        for index in range(options):
            self.fore.append(pygame.Rect((0, rint(index*(objectHeight+6))), (width, 6)))

        # Get list of possible "snaps", which the slider can be dragged to
        self.snaps = []
        
        for index in range(options):
            self.snaps.append((width/2, 3+(index*(objectHeight+6))))
            
        # A simple variable to tell you which option has been chosen
        self.chosen = 0

        # Generate the image surface, then render the bar to it
        self.image = pygame.Surface((width, height))
        self.rect = self.image.get_rect()
        self.render()

        self.focus = False

    def render(self):
        self.image.fill([255, 255, 255])
        
        for back in self.back:
            pygame.draw.rect(self.image, [0, 192, 0], back)

        for fore in self.fore:
            pygame.draw.rect(self.image, [0, 0, 0], fore)

        self.image.blit(self.slider, (rint((self.width-self.slider_rect.width)/2),
                        rint(self.snaps[self.chosen][1]-(self.slider_rect.height/2))))

    def draw(self, surface):
        surface.blit(self.image, self.rect.topleft)

    def mouseDown(self, pos):
        if self.rect.collidepoint(pos):
            self.focus = True
            return True
        return False

    def mouseUp(self, pos):
        if not self.focus:
            return

        self.focus = False

        if not self.rect.collidepoint(pos):
            return

        pos = list(pos)

        # We've made sure the mouse started in our widget (self.focus), and ended in our widget (collidepoint)
        # So there is no reason to care about the exact position of the mouse, only where it is relative
        # to this widget
        pos[0] -= self.rect.x
        pos[1] -= self.rect.y

        # Calculating max distance from a given selection, then comparing that to mouse pos
        dis = self.backHeight/2 + 3

        for index, snap in enumerate(self.snaps):
            if abs(snap[1]-pos[1]) <= dis:
                self.chosen = index
                break

        self.render()

    def update(self):
        pygame.mixer.music.set_volume((self.options-self.chosen)*0.1)

screen = pygame.display.set_mode([500, 500])

test = VolumeBar(10, 30, 300)
test.rect.x = 50
test.rect.y = 50

clock = pygame.time.Clock()

game = True
while game:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            game = False

        if event.type == pygame.MOUSEBUTTONDOWN:
            test.mouseDown(pygame.mouse.get_pos())

        if event.type == pygame.MOUSEBUTTONUP:
            test.mouseUp(pygame.mouse.get_pos())

    if not game:
        break

    screen.fill([255, 255, 255])

    test.update()
    test.draw(screen)
    
    pygame.display.update()

    clock.tick(60)

The final product:

https://i.gyazo.com/f6c2b5ede828f7715e5fd53a65c32c13.mp4

As long as your mouseDown happened on this widget, mouseUp will determine where the slider ends up. Thusly, you can click, drag, or tap the slider anywhere on it, and the slider will go to the correct position.

Accessing the position of the slider is quite simple, just look at self.chosen. It defaults to 0 (Because it was set to that in the __init__) function, which is at the very top.

like image 71
User 12692182 Avatar answered Oct 24 '25 22:10

User 12692182