Skip to content

Latest commit

 

History

History
483 lines (360 loc) · 14.5 KB

File metadata and controls

483 lines (360 loc) · 14.5 KB

PingPong Game

Pygame 을 사용하여 만들어진 핑퐁게임입니다. AI를 상대로 게임이 진행되며 AI가 10점을 달성시 종료됩니다. 종료시 플레이어의 이름,점수,날짜가 랭킹에 기록됩니다.

Requirements

  • python 2 or 3
  • pygame
  • numpy

Getting started

Pygame 모듈 설치가 필요합니다.

Python 2 또는 3의 환경에서 사용 가능합니다.

Original game source

back = pygame.Surface((640,480))
background = back.convert()
background.fill((0,0,0))
bar = pygame.Surface((10,50))
bar1 = bar.convert()
bar1.fill((255,255,255))
bar2 = bar.convert()
bar2.fill((255,255,255))
circ_sur = pygame.Surface((15,15))
circ = pygame.draw.circle(circ_sur,(255,255,255),(int(15/2),int(15/2)),int(15/2))
circle = circ_sur.convert()
circle.set_colorkey((0,0,0))
  1. 공을 받아칠 하얀색 직사각형 바 2개(bar1, bar2)를 생성하고, 하얀색 공(circle)을 만듭니다.
bar1_x, bar2_x = 10. , 620.
bar1_y, bar2_y = 215. , 215.
circle_x, circle_y = 307.5, 232.5
bar1_move, bar2_move = 0. , 0.
speed_x, speed_y, speed_circ = 250., 250., 250.
bar1_score, bar2_score = 0,0

#clock and font objects
clock = pygame.time.Clock()
font = pygame.font.SysFont("calibri",40)

done = False
while done==False:       
    for event in pygame.event.get(): # User did something
        if event.type == pygame.QUIT: # If user clicked close
            done = True # Flag that we are done so we exit this loop
        if event.type == KEYDOWN:
            if event.key == K_UP:
                bar1_move = -ai_speed
            elif event.key == K_DOWN:
                bar1_move = ai_speed
        elif event.type == KEYUP:
            if event.key == K_UP:
                bar1_move = 0.
            elif event.key == K_DOWN:
                bar1_move = 0.
            
    score1 = font.render(str(bar1_score), True,(255,255,255))
    score2 = font.render(str(bar2_score), True,(255,255,255))

    screen.blit(background,(0,0))
    frame = pygame.draw.rect(screen,(255,255,255),Rect((5,5),(630,470)),2)
    middle_line = pygame.draw.aaline(screen,(255,255,255),(330,5),(330,475))
    screen.blit(bar1,(bar1_x,bar1_y))
    screen.blit(bar2,(bar2_x,bar2_y))
    screen.blit(circle,(circle_x,circle_y))
    screen.blit(score1,(250.,210.))
    screen.blit(score2,(380.,210.))

    bar1_y += bar1_move
        
    # movement of circle
    time_passed = clock.tick(30)
    time_sec = time_passed / 1000.0
        
    circle_x += speed_x * time_sec
    circle_y += speed_y * time_sec
    ai_speed = speed_circ * time_sec
  1. 공이 바에서 튕기면 반대쪽 방향의 랜덤위치로 이동하는 코드입니다. 여기서, 플레이어가 우측 상단의 X 키를 눌러 종료하면 게임이 꺼집니다. 플레이어는 키보드의 위아래 방향키를 이용하여 바를 움직일 수 있습니다.
if circle_x >= 305.:
        if not bar2_y == circle_y + 7.5:
            if bar2_y < circle_y + 7.5:
                bar2_y += ai_speed
            if  bar2_y > circle_y - 42.5:
                bar2_y -= ai_speed
        else:
            bar2_y == circle_y + 7.5
    
    if bar1_y >= 420.: bar1_y = 420.
    elif bar1_y <= 10. : bar1_y = 10.
    if bar2_y >= 420.: bar2_y = 420.
    elif bar2_y <= 10.: bar2_y = 10.
    #since i don't know anything about collision, ball hitting bars goes like this.
    if circle_x <= bar1_x + 10.:
        if circle_y >= bar1_y - 7.5 and circle_y <= bar1_y + 42.5:
            circle_x = 20.
            speed_x = -speed_x
    if circle_x >= bar2_x - 15.:
        if circle_y >= bar2_y - 7.5 and circle_y <= bar2_y + 42.5:
            circle_x = 605.
            speed_x = -speed_x
    if circle_x < 5.:
        bar2_score += 1
        circle_x, circle_y = 320., 232.5
        bar1_y,bar_2_y = 215., 215.
    elif circle_x > 620.:
        bar1_score += 1
        circle_x, circle_y = 307.5, 232.5
        bar1_y, bar2_y = 215., 215.
    if circle_y <= 10.:
        speed_y = -speed_y
        circle_y = 10.
    elif circle_y >= 457.5:
        speed_y = -speed_y
        circle_y = 457.5

    pygame.display.update()
  1. AI의 움직임을 설정한 코드입니다. AI의 바가 공을 튕기지 못하고 벽을 지나치면 플레이어가 점수 1점을 획득합니다. 이후 공의 위치가 초기화 되어 다시 움직입니다. 마찬가지로 플레이어가 공을 튕기지 못하면 AI가 점수를 획득하고, 공 위치가 초기화됩니다.

Modified game source

음향 추가

music = os.path.join('bgm','bubble_bobble.wav')
bgm = pygame.mixer.Sound(music)
bgm.play(-1) 
bgm.set_volume(0.3) 

music2 = os.path.join('bgm',"game_over.wav")
game_over = pygame.mixer.Sound(music2)

music3  = os.path.join('bgm',"pingpongbat.wav")
pingpong = pygame.mixer.Sound(music3)

music4  = os.path.join('bgm', "click.wav")
click = pygame.mixer.Sound(music4)
  1. 'bgm' 폴더안에서 상황에 맞는 음악을 가져와 적절하게 설정해줍니다.

<pingpong.play()>

    if circle_x <= bar1_x + 10.:
        if circle_y >= bar1_y - 7.5 and circle_y <= bar1_y + 42.5:
            circle_x = 20.
            speed_x = -speed_x
            pingpong.play()
  1. 공이 바에 닿을때, 공을 받아내지 못했을때 등 각 상황에 맞게 소리가 나게 브금을 추가해 줍니다.
 if bar2_score == 3:  # ai가 10점 달성시 종료 bgm
        c.execute("INSERT INTO users('username', 'score', 'regdate') VALUES(?,?,?)", \
            ('playern', bar1_score, nowDatetime))
        bgm.stop()
        game_over.play()
        game_over.set_volume(0.3)
        # 게임 오버 메시지
        msg = font.render("Game Over", True, (255, 255, 0)) 
        screen.blit(msg, (230,260))
        pygame.display.update()
  1. if bar2_score == 3: 해당 라인을 통해, 특정 조건이 되었을때 게임이 종료되고 <bgm.stop()> 원래 나오던 브금을 종료, <game_over.play()> 새로운 종료 브금이 나오도록 추가해 줍니다. AI가 특정 점수를 획득시 게임이 종료되는 문구가 표시되고, 4초후 프로그램이 종료되도록 설정합니다.

랭킹 제도 추가

now=datetime.datetime.now()
nowDatetime=now.strftime('%Y-%m-%d %H:%M:%S')
  1. datetime 을 이용하여 게임 시간을 기록하는 변수를 설정합니다.
conn=sqlite3.connect('rank.db', isolation_level=None)
c=conn.cursor()
c.execute("CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY AUTOINCREMENT, \
    username text, score text, regdate text)")
  1. db 경로와 커서를 연결하고 테이블을 생성합니다. 점수가 기록되면 따로 insert 해주지 않아도 1씩 증가 되게 합니다.
def pg_rank():
    rankwindow()
    loop=1

    while loop:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                loop = 0
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    loop = 0
                    pygame.quit()
        pygame.display.update()
        clock.tick(60)
  1. 시작화면의 Ranking 버튼을 클릭하면 해당 함수가 실행되어 rankwindow() 함수를 통해 랭킹 화면이 나오게 합니다.
def rankwindow():
    screen.fill(white)
    BLACK = (0,0,0)
    RED = (255,0,0)
    BLUE = (0,0,255)
    
    myFont = pygame.font.SysFont("arial",30,True,False)
    myFont2 = pygame.font.SysFont("arial", 14, True, False)
    myFont3 = pygame.font.SysFont("arial", 25, True, False)

    text_rank = myFont.render("RANKING", True, RED)
    text_back = myFont2.render("Press space to go back", True,RED )
    text_row=myFont.render("NAME    SCORE   REGDATE", True, (0,0,0))
    screen.blit(text_rank, (240,30))
    screen.blit(text_row, (140, 90))
  
  1. 랭킹 화면이 나타나는 함수입니다. <screen.fill(white)> 하얀색 배경으로 된 창에 "RANKING", "NAME SCORE REGDATE", "Press space to go back" 글씨가 나타나게 합니다.
def wait_key():
    waiting = True
    while waiting:
        clock.tick(600)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                waiting = False
                running = False
            if event.type == pygame.K_SPACE:
                waiting = False
                running = False        
  1. clock.tick(600)을 이용해 시간을 600초로 임의로 설정한뒤, <if event.type == pygame.K_SPACE:> Space 키를 누르면 다시 돌아가게 설정합니다.
  for row, length in zip(c.execute("SELECT username, score, regdate FROM users ORDER BY score desc LIMIT 5"), range(140,610,30)):
        row=(','.join(row)).split(',')
        for s, width in zip(row, [140, 270, 320]):
            rank=myFont3.render(s, True, BLACK)
            screen.blit(rank, (width, length))

    screen.blit(text_back, (240,380))
  1. 함수 rankwindow() 부분중, 랭킹을 클릭하면 그동안 저장된 데이터를 ORDER BY score desc LIMIT 5 score 기준 내림차순 정렬하여 5개 값을 출력합니다.
 if bar2_score == 3:  # ai가 10점 달성시 종료 bgm
        c.execute("INSERT INTO users('username', 'score', 'regdate') VALUES(?,?,?)", \
            ('playern', bar1_score, nowDatetime))
  1. AI 가 특정 점수를 획득하면, execute 를 이용하여 각 데이터를 db에 저장합니다.

시작화면 추가

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
    
    def draw(self, win, outline=None):
        if outline:
            pygame.draw.rect(win, outline, (self.x-2, self.y-2, self.width+4, self.height+4), 0)
        
        pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.height), 0)

        if self.text != '':
            font = pygame.font.SysFont('comicsans', 60)
            text = font.render(self.text, 1, (0,0,0))
            win.blit(text, (self.x + (self.width/2 - text.get_width()/2), self.y + (self.height/2 - text.get_height()/2)))
        
    def isOver(self, pos):
        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
  1. Button 클래스 안에 색, 좌표, 크기, 텍스트 를 추가합니다. <def draw(self, win, outline=None):> 해당 함수에서 외곽선 및 버튼을 그립니다. <def isOver(self, pos):> 함수에서 버튼의 크기를 정해줍니다.
def intro():
   i=True
   StartButton = Button((0, 255, 0), 450, 100, 50, 50, 'Click')
   RankingButton = Button((0, 255, 0), 450, 200, 50, 50, 'Click')
   QuitButton = Button((0, 255, 0), 450, 300, 50, 50, 'Click')

   Start = font.render('Start', True,(255,255,255))
   Ranking = font.render('Ranking', True,(255,255,255))
   Quit = font.render('Quit', True,(255,255,255))
   while i:
       
       for event in pygame.event.get():
           pos = pygame.mouse.get_pos()
           keyp = action()
           if event.type == pygame.QUIT:
               pygame.quit()
               quit()
           if keyp!=None or event.type==pygame.KEYDOWN :
               try:
                   if keyp=='c' or event.key==pygame.K_c :
                       i=False
               except:
                   continue
           if event.type == pygame.MOUSEBUTTONDOWN:
               if StartButton.isOver(pos):
                   click.play()
                   i=False
               elif RankingButton.isOver(pos):
                   click.play()
                   i=False
                   pg_rank()
               elif QuitButton.isOver(pos):
                   click.play()
                   pygame.quit()
                   quit()
       image = pygame.image.load(pong_title_path)
       screen.blit(image,(0,0))
       screen.blit(Start,(450.,100.))
       screen.blit(Ranking,(450.,200.))
       screen.blit(Quit,(450.,300.))
       pygame.display.update()
       clock.tick(15)
  1. <image = pygame.image.load(pong_title_path)> 를 통해 배경화면을 설정합니다. 위치에 맞게 Start, Ranking, Quit 버튼을 클래스를 활용해 추가해줍니다. < if event.type == pygame.MOUSEBUTTONDOWN:> 마우스를 클릭했을때의 이벤트를 추가하여 Ranking 버튼을 눌렀을때는 랭킹 화면이 나타나도록 pg_rank() 해당함수를 추가합니다. 마찬가지로 Quit 버튼을 눌렀을때는 게임이 종료되도록 pygame.quit() 해당 라인을 추가합니다.
def action():
    global blist
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()
    for tup in blist:
        if tup[0] + tup[2] > mouse[0] > tup[0] and tup[1] + tup[3] > mouse[1] > tup[1]:
            if click[0] == 1:
                return tup[4]
    return None
intro()    
  1. 현재 마우스 클릭 좌표를 기준으로 버튼을 구현합니다.

정지기능 추가

def paused():
    loop =1
    BLACK = (0,0,0)
    RED = (255,0,0)
    BLUE = (0,0,255)
    
    myFont = pygame.font.SysFont("arial",30,True,False)
    myFont2 = pygame.font.SysFont("arial", 20, True, False)

    text_pause = myFont.render("PAUSE", True, RED)
    text_continue = myFont2.render("Press space to continue", True,BLUE )
    text_quit = myFont2.render("Press esc to quit", True, BLUE)

    screen.blit(text_pause, (280,150))
    screen.blit(text_continue, (230,260))
    screen.blit(text_quit, (230, 320))
  1. 사용할 색을 지정해주고, 글씨 폰트와 크기를 설정해 "PAUSE", "Press space to continue", "Press esc to quit" 문구를 위치에 맞게 추가해줍니다.

    while loop:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                loop = 0
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    loop = 0
                    pygame.quit()
                if event.key == pygame.K_SPACE:
                    screen.fill((0,0,0))
                    loop = 0
        pygame.display.update()
        clock.tick(60)   
  1. < if event.type == pygame.KEYDOWN:> 를 이용해 키가 눌러졌을때의 이벤트를 살핍니다. if event.key == pygame.K_ESCAPE: Esc키를 누르면 게임 프로그램 종료, if event.key == pygame.K_SPACE: Space 키를 누르면 게임이 재시작 되게 합니다.

Games