How to ask 20 multiple choice questions on pygame?

微笑、不失礼 提交于 2021-02-17 05:07:37

问题


I am coding a very basic game on pygame for a project and the main function of the game is to ask 20 questions based on which operator(s) and level the user has picked to play the game at.

I am really struggling with two things:
the first being that the code which I have written does produce 20 questions with the correct operator and level of difficulty however I don't know how to blit these to the screen one at a time each time the user has answered the last one. Currently, the code only shows the last question on the screen.

The second problem I am having is making the multiple-choice buttons for each question. I have a 'Button' class in my game which is the same as the 'Text' class which you can see in the code however it also tracks when the button has been clicked on.
For each question, I need to have 4 instances of the button class on the screen one of which is the correct answer for each question and the other three and random numbers and I need the code to randomize which button is the answer for each question so the answer isn't always the same button.

I also have a stopwatch in my game and none of these can interfere with it.

    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    pygame.quit()
        gameBG=pygame.image.load('gameBG.bmp').convert()
        screen.blit(gameBG,(0,0))
        questiontxt= Text((325,45),(question), (white), 80)
        questiontxt.draw(screen)
        ticks=pygame.time.get_ticks()
        if timing == False:
            timing=True
            seconds=0
        seconds=int((ticks/1000%60))
        minutes=int((ticks/60000%24))
        out='{minutes:02d}:{seconds:02d}'.format(minutes=minutes, seconds=seconds)
        timefont.render_to(screen,(855,50),out, green)
        pygame.display.update()
        clock.tick(60)

        while j < 38:
            qnum1=int(numlist[j])
            qnum2=int(numlist[j+1])
            if section == 'mixed':
                mixedno=random.randrange(0,4)
                operators=['addition','subtraction','division','multiplication']
                qsection=operators[mixedno]
            else:
                qsection=section

            if qsection == 'addition':
                question=str(qnum1)+ '+'+ str(qnum2)+' = ?'
                answer= qnum1+qnum2
            elif qsection == 'subtraction':
                question=str(qnum1)+ '-'+ str(qnum2)+' = ?'
                answer= qnum1-qnum2
            elif qsection == 'multiplication':
                question=str(qnum1)+ 'x'+ str(qnum2)+' = ?'
                answer= qnum1*qnum2
            else:
                question=str(qnum1)+'÷'+str(qnum2)+' = ?'
                answer= qnum1/qnum2

            print(question)
            questiontxt= Text((325,45),(question), (white), 80)
            questiontxt.draw(screen)
            j=j+2

回答1:


Let's start with a new, basic pygame program. I usually start like this:

import pygame

def main():
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    dt = 0
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
        screen.fill(pygame.Color('lightgrey'))
        pygame.display.flip()
        dt = clock.tick(60)

if __name__ == '__main__':
    main()

Not much to see here. We create a window, paint it grey, handle events and keep track of the delta time (the time each frame takes).


Let's also think a moment of how the game is supposed to work. First, we have a title screen, then we select a difficulty, then we show some questions, and finally, we display the result. We call each of those parts a scene, and we jump from one to another.

Here's a way we could implement these:

import pygame
import pygame.freetype

class SimpleScene:

    FONT = None

    def __init__(self, text, next_scene):
        self.background = pygame.Surface((640, 480))
        self.background.fill(pygame.Color('lightgrey'))

        if text:
            if SimpleScene.FONT == None:
                SimpleScene.FONT = pygame.freetype.SysFont(None, 32)
            SimpleScene.FONT.render_to(self.background, (120, 180), text, pygame.Color('black'))
            SimpleScene.FONT.render_to(self.background, (119, 179), text, pygame.Color('white'))

        self.next_scene = next_scene

    def start(self):
        pass

    def draw(self, screen):
        screen.blit(self.background, (0, 0))

    def update(self, events, dt):
        for event in events:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    return self.next_scene

def main():
    pygame.init()
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    dt = 0
    scenes = {
        'TITLE': SimpleScene('PRESS SPACE TO START', 'GAME'),
        'GAME': SimpleScene('Can you press [SPACE]', 'RESULT'),
        'RESULT': SimpleScene('You win! 100 points!', 'TITLE'),
    }
    scene = scenes['TITLE']
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return

        next_scene = scene.update(events, dt)
        if next_scene:
            scene = scenes[next_scene]
            scene.start()

        scene.draw(screen)

        pygame.display.flip()
        dt = clock.tick(60)

if __name__ == '__main__':
    main()

We can now transition between the scenes by pressing space. As you can see, each scene is like a miniature game itself; of course we only have the SimpleScene that does nothing but draw a simple text, so let's change that and implement the real game. We also have a game state, so we need to keep track if it, too.

Here's how it could look like:

import pygame
import pygame.freetype
import random

class SimpleScene:

    FONT = None

    def __init__(self, next_scene, *text):
        self.background = pygame.Surface((640, 480))
        self.background.fill(pygame.Color('lightgrey'))

        y = 80
        if text:
            if SimpleScene.FONT == None:
                SimpleScene.FONT = pygame.freetype.SysFont(None, 32)
            for line in text:
                SimpleScene.FONT.render_to(self.background, (120, y), line, pygame.Color('black'))
                SimpleScene.FONT.render_to(self.background, (119, y-1), line, pygame.Color('white'))
                y += 50

        self.next_scene = next_scene
        self.additional_text = None

    def start(self, text):
        self.additional_text = text

    def draw(self, screen):
        screen.blit(self.background, (0, 0))
        if self.additional_text:
            y = 180
            for line in self.additional_text:
                SimpleScene.FONT.render_to(screen, (120, y), line, pygame.Color('black'))
                SimpleScene.FONT.render_to(screen, (119, y-1), line, pygame.Color('white'))
                y += 50

    def update(self, events, dt):
        for event in events:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    return (self.next_scene, None)

class GameState:
    def __init__(self, difficulty):
        self.difficulty = difficulty
        self.questions = [
            ('How many legs has a cow?', 4),
            ('How many legs has a bird?', 2),
            ('What is 1 x 1 ?', 1)
        ]
        self.current_question = None
        self.right = 0
        self.wrong = 0

    def pop_question(self):
        q = random.choice(self.questions)
        self.questions.remove(q)
        self.current_question = q
        return q

    def answer(self, answer):
        if answer == self.current_question[1]:
            self.right += 1
        else:
            self.wrong += 1

    def get_result(self):
        return f'{self.right} answers correct', f'{self.wrong} answers wrong', '', 'Good!' if self.right > self.wrong else 'You can do better!'

class SettingScene:

    def __init__(self):
        self.background = pygame.Surface((640, 480))
        self.background.fill(pygame.Color('lightgrey'))

        if SimpleScene.FONT == None:
            SimpleScene.FONT = pygame.freetype.SysFont(None, 32)

        SimpleScene.FONT.render_to(self.background, (120, 50), 'Select your difficulty level', pygame.Color('black'))
        SimpleScene.FONT.render_to(self.background, (119, 49), 'Select your difficulty level', pygame.Color('white'))

        self.rects = []
        x = 120
        y = 120
        for n in range(4):
            rect = pygame.Rect(x, y, 80, 80)
            self.rects.append(rect)
            x += 100

    def start(self, *args):
        pass

    def draw(self, screen):
        screen.blit(self.background, (0, 0))
        n = 1
        for rect in self.rects:
            if rect.collidepoint(pygame.mouse.get_pos()):
                pygame.draw.rect(screen, pygame.Color('darkgrey'), rect)
            pygame.draw.rect(screen, pygame.Color('darkgrey'), rect, 5)                
            SimpleScene.FONT.render_to(screen, (rect.x+30, rect.y+30), str(n), pygame.Color('black'))
            SimpleScene.FONT.render_to(screen, (rect.x+29, rect.y+29), str(n), pygame.Color('white'))
            n+=1

    def update(self, events, dt):
        for event in events:
            if event.type == pygame.MOUSEBUTTONDOWN:
                n = 1
                for rect in self.rects:
                    if rect.collidepoint(event.pos):
                        return ('GAME', GameState(n))
                    n += 1

class GameScene:
    def __init__(self):
        if SimpleScene.FONT == None:
            SimpleScene.FONT = pygame.freetype.SysFont(None, 32)

        self.rects = []
        x = 120
        y = 120
        for n in range(4):
            rect = pygame.Rect(x, y, 80, 80)
            self.rects.append(rect)
            x += 100

    def start(self, gamestate):
        self.background = pygame.Surface((640, 480))
        self.background.fill(pygame.Color('lightgrey'))
        self.gamestate = gamestate
        question, answer = gamestate.pop_question()
        SimpleScene.FONT.render_to(self.background, (120, 50), question, pygame.Color('black'))
        SimpleScene.FONT.render_to(self.background, (119, 49), question, pygame.Color('white'))


    def draw(self, screen):
        screen.blit(self.background, (0, 0))
        n = 1
        for rect in self.rects:
            if rect.collidepoint(pygame.mouse.get_pos()):
                pygame.draw.rect(screen, pygame.Color('darkgrey'), rect)
            pygame.draw.rect(screen, pygame.Color('darkgrey'), rect, 5)
            SimpleScene.FONT.render_to(screen, (rect.x+30, rect.y+30), str(n), pygame.Color('black'))
            SimpleScene.FONT.render_to(screen, (rect.x+29, rect.y+29), str(n), pygame.Color('white'))
            n+=1

    def update(self, events, dt):
        for event in events:
            if event.type == pygame.MOUSEBUTTONDOWN:
                n = 1
                for rect in self.rects:
                    if rect.collidepoint(event.pos):
                        self.gamestate.answer(n)
                        if self.gamestate.questions:
                            return ('GAME', self.gamestate)
                        else:
                            return ('RESULT', self.gamestate.get_result())
                    n += 1

def main():
    pygame.init()
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    dt = 0
    scenes = {
        'TITLE':    SimpleScene('SETTING', 'Welcome to the quiz', '', '', '', 'press [SPACE] to start'),
        'SETTING':  SettingScene(),
        'GAME':     GameScene(),
        'RESULT':   SimpleScene('TITLE', 'Here is your result:'),
    }
    scene = scenes['TITLE']
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return

        result = scene.update(events, dt)
        if result:
            next_scene, state = result
            if next_scene:
                scene = scenes[next_scene]
                scene.start(state)

        scene.draw(screen)

        pygame.display.flip()
        dt = clock.tick(60)

if __name__ == '__main__':
    main()

Of course it's not 100% finished, but it should give you an idea on how to structure your game to get what you want.

As you can see, each scene handles its own aspect of the game, and the game scene jumps to itself while passing the game state. To add a timer, you can simply store the current game time (e.g. pygame.time.get_ticks()) in the game state the first time its start function is called, and calculate the remaining time in the update function of the scene. If the timer runs out, jump to a scene that displays the result.



来源:https://stackoverflow.com/questions/59726334/how-to-ask-20-multiple-choice-questions-on-pygame

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!