数字記憶ゲーム
ゲームのキャラクタを作るのが苦手という人のために、キャラクタを使わなくて済むゲームを考えてみます。
表示は〇や四角形などと文字の描画のみとする。これで楽になります。
ゲームらしくするため動きがあること。
その例として数字記憶ゲームを作りました。
背景は写真です。だれでも手軽に用意できますね。

S キーを押せばスタートします。

黄色の〇の中に乱数で発生させた数字が右から左に移動します。 数字の表示が終了すると、次は数字入力になります。

全部の数字を入力すると採点が表示されます。

それではソースを見ていきましょう。

import pygame
from pygame.constants import KEYDOWN
import pygame.key
import pygame.event
import pygame.display
import pygame.draw as dw
import sys
import random

IDLE=0
SHOWING=1
ANSERING=3
RESULT=4

DEGITS = 4

RADIUS=30
CICLE_COL = (255, 216, 0)
TEXT_COL = (0, 0, 255)

KEY2STR={pygame.K_0:'0', pygame.K_KP0:'0',
    pygame.K_1:'1', pygame.K_KP1:'1',
    pygame.K_2:'2', pygame.K_KP1:'2',
    pygame.K_3:'3', pygame.K_KP3:'3',
    pygame.K_4:'4', pygame.K_KP4:'4',
    pygame.K_5:'5', pygame.K_KP5:'5',
    pygame.K_6:'6', pygame.K_KP6:'6',
    pygame.K_7:'7', pygame.K_KP7:'7',
    pygame.K_8:'8', pygame.K_KP8:'8',
    pygame.K_9:'9', pygame.K_KP9:'9'
}



def makeNum(degits):
    min = 10**(degits - 1)
    max = 10**(degits) -1
    num = random.randint(min, max)
    strNum = str(num)
    return strNum

def getKeyCode(key):
    l = len(key)
    for k in range(0, l):
        if key[k] == True:
            return k
    return None
    

def showNum(screen, x, question, numCount):
    dw.circle(screen, CICLE_COL, [x, 200], RADIUS, 0)
    font = pygame.font.Font(None, 40)
    txt = font.render(question[numCount], True, TEXT_COL)
    screen.blit(txt, [x - 40 / 4 , 200 - 40 / 4])

    x -= 8
    if x < -RADIUS * 2:
        numCount += 1
        x = screen.get_width()
    return x, numCount

def showAnser(screen ,anser, numCount):
    x = RADIUS + 10
    y = 400
    font = pygame.font.Font(None, 40)

    for i in range(0, numCount):
        dw.circle(screen, (255,255,255), [x, y], RADIUS, 0)
        txt = font.render(anser[i], True, TEXT_COL)
        screen.blit(txt, [x - 40 / 4 , y - 40 / 4])
        x += RADIUS * 2 + 8

def showQuestion(screen ,question, numCount):
    x = RADIUS + 10
    y = 300
    font = pygame.font.Font(None, 40)

    for i in range(0, numCount):
        dw.circle(screen, CICLE_COL, [x, y], RADIUS, 0)
        txt = font.render(question[i], True, (0,0,0))
        screen.blit(txt, [x - 40 / 4 , y - 40 / 4])
        x += RADIUS * 2 + 8

def compare(screen, question, anser, numCount):
    x = RADIUS + 10
    y = 400

    for i in range(0,numCount):
        if question[i] == anser[i]:
            dw.circle(screen, (255,0,0), [x, y], RADIUS, 4)
        else:
            dw.line(screen, (255,0,0), [x - RADIUS, y - RADIUS], [x + RADIUS, y + RADIUS], 4)
            dw.line(screen, (255,0,0), [x + RADIUS, y - RADIUS], [x - RADIUS, y + RADIUS], 4)
        x += RADIUS * 2 + 8


def shoResult(screen, question, anser, numCount):
    showQuestion(screen ,question, numCount)
    showAnser(screen ,anser, numCount)
    compare(screen, question, anser, numCount)

def main():

    pygame.init()
    pygame.display.set_caption("Pygame 数字の記憶ゲーム")
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    img_back = pygame.image.load('back.png')
    x = screen.get_width()
    font = pygame.font.Font(None, 120)
    mode = IDLE
    numCount = 0
    anser = []
    while True:
        key = None
        ky = None
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                # 終了ボタンで終了
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                ky = event.key
                name = pygame.key.name(event.key)
        
        screen.fill((0,0,0))
        screen.blit(img_back, [0,0])

        #キーの取得
        key = pygame.key.get_pressed()
        if mode == IDLE:
            if key[pygame.K_s]:
                mode = SHOWING
                numCount = 0
                question = makeNum(DEGITS)
                x = screen.get_width()
            txt = font.render('S key:Start', True, (0,255, 0))
            screen.blit(txt, [100, 100])

        elif mode == SHOWING:
            x, numCount = showNum(screen, x, question, numCount) 
            if numCount >= len(question):
                anser.clear()
                mode = ANSERING
                numCount = 0
                showAnser(screen, anser, numCount)

        elif mode == ANSERING:
            txt = font.render('Input Number', True, (0,255,0))
            screen.blit(txt, [60, 100])
            if ky != None:
                strNum = KEY2STR.get(ky)
                if strNum != None:
                    #数字が入力された
                    anser.insert(numCount, strNum)
                    numCount += 1
            showAnser(screen, anser, numCount)
            if numCount >= len(question):
                mode = RESULT

        elif mode == RESULT:
            if key[pygame.K_s]:
                mode = SHOWING
                numCount = 0
                question = makeNum(DEGITS)
                x = screen.get_width()
            txt = font.render('S key:Start', True, (0,255,0))
            screen.blit(txt, [100, 100])
            shoResult(screen, question, anser, numCount)
        
        pygame.display.update()
        clock.tick(10)#1秒間のフレーム数 100msec

if __name__ == '__main__':
    main()
まず問題の作成です。
makeNum(degits)
degitsは桁数です。4桁の数字なら最少が1000、最大が9999です。10の4乗が1000、10の5乗からマイナス1すると9999です。 この結果で乱数を発生させます。そして採点をしやすいようにstrに変換しています。

次にキー入力です。 これは面倒です。何故ならpygame.key.get_pressed()で取得した数値データは 文字に関連付けされていません。
pygame.K_1 1のキー
pygame.K_KP1:'テンキーの1
これを関連付けするため辞書KEY2STRを使います。
ここで問題なのはpygame.key.get_pressed()を使うと1回5と押したのに何回も5が入力されてしまいます。 これはゲームの特性上こうでなければなりません。これを回避するにはイベントで pygame.KEYDOWNがあります。 これを使って問題を回避しました。

pygame.KEYDOWを使うともっと簡単にできそうなのでやってみました。

import pygame
from pygame.constants import KEYDOWN
import pygame.key
import pygame.event
import pygame.display
import pygame.draw as dw
import sys
import random

IDLE=0
SHOWING=1
ANSERING=3
RESULT=4

DEGITS = 7  #問題の桁数

RADIUS=30
CICLE_COL = (255, 216, 0)
TEXT_COL = (0, 0, 255)

def makeNum(degits):
    min = 10**(degits - 1)
    max = 10**(degits) -1
    num = random.randint(min, max)
    strNum = str(num)
    return strNum


def showNum(screen, x, question, numCount):
    dw.circle(screen, CICLE_COL, [x, 200], RADIUS, 0)
    font = pygame.font.Font(None, 40)
    txt = font.render(question[numCount], True, TEXT_COL)
    screen.blit(txt, [x - 40 / 4 , 200 - 40 / 4])

    x -= 8
    if x < -RADIUS * 2:
        numCount += 1
        x = screen.get_width()
    return x, numCount

def showAnser(screen ,anser, numCount):
    x = RADIUS + 10
    y = 400
    font = pygame.font.Font(None, 40)

    for i in range(0, numCount):
        dw.circle(screen, (255,255,255), [x, y], RADIUS, 0)
        txt = font.render(anser[i], True, TEXT_COL)
        screen.blit(txt, [x - 40 / 4 , y - 40 / 4])
        x += RADIUS * 2 + 8

def showQuestion(screen ,question, numCount):
    x = RADIUS + 10
    y = 300
    font = pygame.font.Font(None, 40)

    for i in range(0, numCount):
        dw.circle(screen, CICLE_COL, [x, y], RADIUS, 0)
        txt = font.render(question[i], True, (0,0,0))
        screen.blit(txt, [x - 40 / 4 , y - 40 / 4])
        x += RADIUS * 2 + 8

def compare(screen, question, anser, numCount):
    x = RADIUS + 10
    y = 400

    for i in range(0,numCount):
        if question[i] == anser[i]:
            dw.circle(screen, (255,0,0), [x, y], RADIUS, 4)
        else:
            dw.line(screen, (255,0,0), [x - RADIUS, y - RADIUS], [x + RADIUS, y + RADIUS], 4)
            dw.line(screen, (255,0,0), [x + RADIUS, y - RADIUS], [x - RADIUS, y + RADIUS], 4)
        x += RADIUS * 2 + 8


def shoResult(screen, question, anser, numCount):
    showQuestion(screen ,question, numCount)
    showAnser(screen ,anser, numCount)
    compare(screen, question, anser, numCount)

def main():

    pygame.init()
    pygame.display.set_caption("Pygame 数字の記憶ゲーム")
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    img_back = pygame.image.load('back.png')
    x = screen.get_width()
    font = pygame.font.Font(None, 120)
    mode = IDLE
    numCount = 0
    anser = []


    while True:
        keyName = ''
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                # 終了ボタンで終了
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                keyName = pygame.key.name(event.key)
                keyName = keyName.replace('[', '')
                keyName = keyName.replace(']', '')
        
        screen.fill((0,0,0))
        screen.blit(img_back, [0,0])

        if mode == IDLE:
            if keyName == 's':
                mode = SHOWING
                numCount = 0
                question = makeNum(DEGITS)
                x = screen.get_width()
            txt = font.render('S key:Start', True, (0,255, 0))
            screen.blit(txt, [100, 100])

        elif mode == SHOWING:
            x, numCount = showNum(screen, x, question, numCount) 
            if numCount >= len(question):
                anser.clear()
                mode = ANSERING
                numCount = 0
                showAnser(screen, anser, numCount)

        elif mode == ANSERING:
            txt = font.render('Input Number', True, (0,255,0))
            screen.blit(txt, [60, 100])
            if keyName >= '0' and keyName <= '9':
                #数字が入力された
                anser.insert(numCount, keyName)
                numCount += 1
            showAnser(screen, anser, numCount)
            if numCount >= len(question):
                mode = RESULT

        elif mode == RESULT:
            if keyName == 's':
                mode = SHOWING
                numCount = 0
                question = makeNum(DEGITS)
                x = screen.get_width()
            txt = font.render('S key:Start', True, (0,255,0))
            screen.blit(txt, [100, 100])
            shoResult(screen, question, anser, numCount)
        
        pygame.display.update()
        clock.tick(10)#1秒間のフレーム数 100msec

if __name__ == '__main__':
    main()
またまた、テンキーは曲者です。例えばテンキーの2は[2]という名前になります。[]を削除しなければなりません。

            if event.type == pygame.KEYDOWN:
                keyName = pygame.key.name(event.key)
                keyName = keyName.replace('[', '')
                keyName = keyName.replace(']', '')
これでソースがスッキリしました。