본문 바로가기

TIL 및 WIL/TIL (Today I Learned)

[TIL] 2022.04.27 (Python 문법 연습, pygame 개인 프로젝트)

오늘은 파이썬 문법에 대해 추가로 공부하였다.

조건문(if)과 함수(def)를 사용하여 추가적인 공부를 하였는데, 꽤 유익한 시간이었다.

 

1. 조건문(if)

print("="*24)
print("주택용 전기요금(저압) 계산기")
print("="*24)

# 만약 전기 사용량이 100이하라면?
if (value <= 100): # value 값이 100보다 작거나 같을경우
    # 전기량에 해당 값을 곱해준다
    price = value*60.7
# 만약 전기 사용량이 100을 넘어갔다면?
elif (value > 100):
    # 100 이하에 대한 연산 + 100 초과에 대한 연산
    price = (100*60.7)+(value-100)*125.9

print(value, "kWh의 전기 요금은", price, "원 입니다.")

 

해당 내용은 조건문 중 하나이다.

해당 value의 값이 100보다 작거나 같을 경우, 혹은 100보다 클 경우에 대한 조건문이다.

이 상태로 진행하면 당연히 value 변수가 선언이 되지 않았기 때문에 오류가 발생하게 된다.

 

# 첫번째 사용한 전기량
value = 99

# 두번째 사용한 전기량
value = 150

 

해당 코드를 사용하기 위하여 2개의 value 값을 준비했다.

하지만 이것들도 변수의 이름이 중복되어 사용되기 때문에 2번째 value 변수에 담긴 값이 출력되는 것을 볼 수 있다.

 

두번째 value 값이 적용되어 조건문이 돌아가는 모습

 

해당 내용을 함수를 사용하여 비슷한 형태로 만들어보도록 하자.

 

2. 함수(def)

def watt(value):
    print("="*24)
    print("주택용 전기요금(저압) 계산기")
    print("="*24)

    # 만약 전기 사용량이 100이하라면?
    if (value <= 100): # value 값이 100보다 작거나 같을경우
        # 전기량에 해당 값을 곱해준다
        price = value*60.7
    # 만약 전기 사용량이 100을 넘어갔다면?
    elif (value > 100):
        # 100 이하에 대한 연산 + 100 초과에 대한 연산
        price = (100*60.7)+(value-100)*125.9

    print(value, "kWh의 전기 요금은", price, "원 입니다.")
watt(value=90)

하나의 함수 출력

watt의 함수에 value라는 매개변수를 지정하고, 해당 함수 내에 코드를 추가해줬다.

위의 조건문(if) 형식과 마찬가지로 똑같은 형태를 갖추었기 때문에 결과값을 똑같이 출력한다.

 

하지만 해당 내용에서 다른 점을 찾아보자면, 함수의 사용 부분에 있어서 다르다는 점인데

해당 함수의 마지막에 watt(value=90)을 통하여 watt()라는 함수를 호출하였고, 매개변수 value에 90의 값을 넣어서 해당 조건문이 실행되는 것을 볼 수 있다.

 

만약, 호출되는 함수가 두 개가 되는 경우에는 어떻게 될까?

 

1) 조건문(if)에서 보면 알 수 있다시피 똑같은 이름의 변수 value 값 중 후에 선언된 두 번째 value = 150의 값이 조건문에서 사용되는 것을 볼 수 있다.

하지만 함수는 다르다.

 

watt(value=150)

 

해당 내용을 watt(value=90) 밑에 추가해보면 어떻게 될까?

1)에서 본 것과 같이 후에 입력된 watt(value=150)의 값만 담겨 실행되는 것이라고 생각할 수 있다.

 

하지만 결과를 보면 다르다.

 

위(watt(value=90)), 아래(watt(value=150)

 

해당 사진처럼 결과가 출력되는 것을 알 수 있다.

 

왜 저렇게 출력되는 것일까?

밑에서 중복되는 것 없이 watt() 함수를 각각 따로 총 2번 호출했기 때문이다.

그래서 코드의 중복이 일어나지 않았고 그때 사용한 value 값이 watt함수의 매개변수에 담겨 조건문이 실행되는 것이었다.

그렇기에 먼저 선언된 watt(value=90)에서 한번 실행되고, 후에 선언된 watt(value=150)에서 한번 더 중복 없이 실행되어 해당 결과를 출력받을 수 있는 것이었다.

 

이번엔 다른 코드들을 살펴보자.

def opcode(value):
    # 만약 전기 사용량이 100이하라면?
    if (value <= 100):  # value 값이 100보다 작거나 같을경우
        # 전기량에 해당 값을 곱해준다
        price = value * 60.7
    # 만약 전기 사용량이 100을 넘어갔다면?
    elif (value > 100):
        # 100 이하에 대한 연산 + 100 초과에 대한 연산
        price = (100 * 60.7) + (value - 100) * 125.9

def watt_text(value, price_input):
    print("=" * 24)
    print("주택용 전기요금(저압) 계산기")
    print("=" * 24)
    print(value, "kWh의 전기 요금은", price_input, "원 입니다.")


# 해당 변수로 함수들을 불러와 들어가는 값을 지정하거나
price = opcode(value = 99)
watt_text(value = 99, price_input = price)

 

해당 함수들을 보면 각각 조건문과 연산을 담아 준 opcode() 함수와 출력되는 텍스트를 담아 준 watt_text() 함수를 선언해주었다.

그리고 맨 밑에 price = opcode(value = 99)와 watt_text(value = 99, price_input = price)를 적어주어 해당 함수들을 호출하고 opcode() 함수의 값을 price에, watt_text() 함수는 그대로 호출하여 value는 99를 price_input에는 변수 price를 넣어주었다.

 

하지만 이렇게 실행하게 된다면 실행은 되겠지만 어딘가 이상하게 실행이 될 것이다.

사진을 한번 살펴보자.

 

price = None

 

변수로 선언된 price의 값을 가져오지 못해 출력에 있어 None으로 표시되는 것을 볼 수 있다.

이러한 이유는 바로 opcode에서 해당 value 값을 받아 실행된 price의 값을 반환하지 못해서인데, opcode 안에 반환해주는 코드를 넣어주면 해결이 된다.

 

return price를 통해 정상적으로 나오는 모습

 

opcode 안에 해당 값을 반환해주는 return을 적어주고 조건문에 맞게 실행된 price의 값을 반환하여 변수 price에 담을 수 있게 된 것이다.

결과를 보면 알 수 있듯이 조건문에 맞게 실행된 값을 반환받아 연산된 값이 출력되는 것을 볼 수 있었다.

 

조금 더 나아가서, 밑에 쓰였던 변수들을 사용할 때 따로 함수로 선언하여 사용해줄 수도 있다.

 

def total_vp(value):
    price = opcode(value)
    watt_text(value, price_input = price)
total_vp(value = 150)

def total_vp(value0: 의 실행 결과

 

def total_vp() 함수를 통해 변수와 함수 호출을 하나의 함수 안에 집어넣어 사용이 가능하다.

 

해당 코드의 결과를 보면 알 수 있듯이 opcode() 함수의 결과를 호출하여 가져왔고, watt_text() 함수의 결과도 호출하여 가져온 것을 볼 수 있다.

그리고 해당 total_vp() 함수에 매개변수 값 150을 집어넣어 해당 변수값에 맞는 코드가 실행되는 것을 볼 수 있다.

 

-

 

밑으로는 개인 프로젝트로 진행하였던 pygame을 이용하여 파이썬 게임 만들기를 얘기해보겠다.

 

4월 25일부터 4월 27일까지 진행한 3일간의 개인 미니 프로젝트이다.

 

: 4월 25일 (1일 차) :

 

1일 차, 4월 25일에 정한 목록들이다.

0) 파이썬 기초 문법 공부(1/2)

1) 게임 선정

2) 대략적인 게임의 틀

3) 게임 Title 이름과 아이콘 변경

4) 게임 시작 시 음악 재생

5) 해당 게임의 틀이 무엇인가에 대해 공부(주석처리)

 

 

우선 프로젝트를 만들기 앞서 생각을 해보았다.

구체적으로 무엇을 만들고 싶은가? 무슨 기능들을 넣고 싶은가? 전체적인 모양은 어떻게 만들 것인가? 등

이러한 것들을 우선적으로 생각한 것이 아닌 "우선 파이썬에 대한 기초 지식을 바탕으로 지식들을 모아 보는 것이 좋겠다."라는 생각이 먼저 들었다.

그래서 첫날에는 파이썬 기초 문법에 대해 공부하는 시간을 가졌던 것이다.

 

파이썬 기초 문법을 어느 정도 공부했을 때, 내가 만들고자 하는 것이 무엇인지 생각해보았다.

다음은 맨 처음 후보로 정해졌던 것들이다.

1. 횡스크롤 RPG
2. 리듬 게임
3. 벽돌깨기
4. 같은 그림 찾기

총 4가지의 게임을 우선적으로 선정하였고, 무엇을 해보고 싶은지 생각해보았다.

 

곰곰이 생각해 보던 도중 너구리가 머릿속에 스쳐 지나갔다.

내가 가지고 있는 많은 너구리 사진들을 뭔가 한 게임 안에 담아내고 싶었기에 간단하게 생각하여 같은 그림 찾기를 만들어보고 싶다고 생각하였다.

 

주제를 정한 뒤 게임의 틀을 어떻게 만들 것인가를 고민해보았다.

여태까지 배운 파이썬의 기초 문법들을 가지고 게임이라는 큰 프로젝트를 혼자서 진행한다는 것은 내겐 아직 무리라는 생각이 들었다.

 

우선 구글링을 통해 다른 좋은 사례들이 있는가에 대해 찾아보았고, 그렇게 찾은 블로그를 토대로 기본적인 틀을 구성하였다.

 

https://m.blog.naver.com/scyan2011/222037978957

 

[python]파이썬/게임/pygame/퍼즐/그림 맞추기/게임 오버/gameWonAnimation

이제 모든 퍼즐을 다 맞추면 게임 오버를 알리는 배경색의 변화를 준 후 게임을 다시 시작합니다. 퍼즐이 ...

blog.naver.com

 

추가로 게임 Title의 이름과 아이콘을 변경해주었고, 게임 시작 시 음악을 재생하도록 추가적인 코드를 덧붙였다.

 

# 게임 타이틀 제목
pygame.display.set_caption('A racoon trick')
# pygame 아이콘 이미지 변경
racoon_image = pygame.image.load('image/icon_racoon.png')
pygame.display.set_icon(racoon_image)
# 게임 시작 시 음악 재생
pygame.mixer.init()
pygame.mixer.music.load("sound/easy.mp3")
pygame.mixer.music.play(-1, 0.0)

 

해당 코드를 보면 알 수 있듯이 각 필요한 요소들마다 주석도 추가해주기도 하고, 블로그를 참고하여 어디에 무엇이 들어가고 해당 코드는 무엇을 하게 만들어주는지 주석 처리를 겸하며 만들었다.

 

: 4월 26일 (2일 차) :

 

2일 차, 4월 26일에 정한 목록들이다.

0) 파이썬 기초 문법 공부(2/2)

1) 그림(도형, 색깔) > 그림(이미지) 변경

2) 성공(Success), 실패(Fail) 텍스트 추가 및 값 저장

3) 백그라운드 이미지 변경

4) 게임 종료 시 백그라운드 이미지 전환

 

25일~26일 이어서 빠르게 파이썬 기초 문법에 대해 공부하려다 보니 뭔가 부족한 느낌이 들었지만 27일 저녁에 추가로 공부하는 마음가짐으로 우선은 파이썬 기초 문법에 대해 마무리지었다.

 

# 박스 이미지 그리기
def drawIcon(shape, color, boxx, boxy):  # 결정된 모양과 색상으로 이미지를 그리는 함수
    quarter = int(BOXSIZE * 0.25)
    half = int(BOXSIZE * 0.5)

    # leftTopCoordsOfBox의 boxx, boxy에 left, top 순서로 값을 집어넣음
    left, top = leftTopCoordsOfBox(boxx, boxy)

    # 순서 : pygame.draw.모양이름(Surface, color, Rect, Width)
    # Surface : pygame 실행 시 전체적으로 화면을 선언한 변수 값
    # color : 해당 모양의 색깔
    # Rect : 해당 모양의 축
    # Width : 기본적으로 0, 테두리의 굵기

    if shape == DONUT:  # 원 (1,2,Pos,Radius,4)
        # Pos : 원의 위치 x,y값
        # Radius : 원의 반지름 길이 값
        pygame.draw.circle(DISPLAY, color, (left + half, top + half), half - 5)
        pygame.draw.circle(DISPLAY, BGCOLOR, (left + half, top + half), quarter - 5)
    elif shape == SQUARE:  # 사각형 (x축,y축,가로,세로)
        pygame.draw.rect(DISPLAY, color, (left + quarter, top + quarter, BOXSIZE - half, BOXSIZE - half))
    elif shape == DIAMOND:  # 삼각형 ((x축,y축),(x축,y축),(x축,y축))
        pygame.draw.polygon(DISPLAY, color, (
            (left + half, top), (left + BOXSIZE - 1, top + half), (left + half, top + BOXSIZE - 1), (left, top + half)))
    elif shape == LINES:  # 선 (1,2,Start_pos,End_pos,3)
        # Start_pos : 선이 시작하는 점, x,y값
        # End_pos : 선이 끝나는 점, x,y값
        for i in range(0, BOXSIZE, 4):
            pygame.draw.line(DISPLAY, color, (left, top + i), (left + i, top))
            pygame.draw.line(DISPLAY, color, (left + i, top + BOXSIZE - 1), (left + BOXSIZE - 1, top + i))
    elif shape == OVAL:  # 타원 (x축,y축,가로,세로)
        pygame.draw.ellipse(DISPLAY, color, (left, top + quarter, BOXSIZE, half))

 

해당 코드는 원래의 게임 틀에서 사용된 그림을 그릴 때 사용하는 함수이다.

하지만 원하고자 하는 이미지를 넣어서 같은 그림을 찾는 게임을 만들고 싶었기에 이 코드를 수정하는 방향을 정했다.

 

다음과 같은 하드 코딩의 형태로 그림들에 이미지를 넣어주어 해당 코드를 변경하였다.

 

def draw_Card(pic, num, boxx, boxy):  # 결정된 이미지를 그리는 함수
    eight = int(BOXSIZE * 0.01)

    # left_Top_Coords_Of_Box의 boxx, boxy에 left, top 순서로 값을 집어넣음
    left, top = left_Top_Coords_Of_Box(boxx, boxy)

    racoon1Img = pygame.image.load('image/racoon1.png')
    racoon2Img = pygame.image.load('image/racoon2.png')
    racoon3Img = pygame.image.load('image/racoon3.png')
    racoon4Img = pygame.image.load('image/racoon4.png')
    racoon5Img = pygame.image.load('image/racoon5.png')

    if pic == 'racoon1':
        DISPLAY.blit(racoon1Img, (left + eight, top + eight))
    elif pic == 'racoon2':
        DISPLAY.blit(racoon2Img, (left + eight, top + eight))
    elif pic == 'racoon3':
        DISPLAY.blit(racoon3Img, (left + eight, top + eight))
    elif pic == 'racoon4':
        DISPLAY.blit(racoon4Img, (left + eight, top + eight))
    elif pic == 'racoon5':
        DISPLAY.blit(racoon5Img, (left + eight, top + eight))

 

 

message_Success = FONT.render("Success : {} / 10".format(Success), True, BLACK)
message_Fail = FONT.render("Fail : {} / 15".format(Fail), True, BLACK)

DISPLAY.blit(message_Success, (5, 2))
DISPLAY.blit(message_Fail, (5, 32))

#################################################################################################################

		# 만약 이미지가 서로 다르다면? 서로 다르니 둘 다 닫기
                    if icon1shape != icon2shape or icon1color != icon2color:
                        pygame.time.wait(500)  # 0.5초 후
                        # 박스를 덮는다.
                        cover_Boxes_Animation(mainBoard, [(firstSelection[0], firstSelection[1]), (boxx, boxy)])
                        # 선택된 첫번째와 두번째 박스를 닫는다.
                        revealedBoxes[firstSelection[0]][firstSelection[1]] = False
                        revealedBoxes[boxx][boxy] = False

                        # Fail 점수 추가
                        Fail += 1
                            
#################################################################################################################

		# 만약 이미지가 모두 같을 경우? Success 점수 추가
                    # or로 설정 시 이미지1.1, 이미지2.1이 같아도 점수가 추가됐었음 > and로 바꾸니 해결
                    if icon1shape == icon2shape and icon1color == icon2color:
                        pygame.time.wait(500)  # 0.5초 후
                        Success += 1

 

성공(Success)과 실패(Fail)를 적어서 각 텍스트들의 위치를 정해주었다.

그리고 해당 그림들에 대해 성공(Success)과 실패(Fail)를 이루면 해당 값들이 저장될 수 있도록 조건을 지정해주었다.

 

여기서 문제가 하나 발생하였는데

이미지 1.1과 이미지 1.2가 같아야 점수가 올라가는 형태가 되어야 정상인데 이미지 1.1과 이미지 2.1이 같아도 점수가 올라가는 기이한 문제가 발생하였다.

그래서 해당 조건문(if)에 있는 논리 연산자 or를 and형태로 바꿔주니 문제가 해결되는 것을 확인할 수 있었다.

 

BGIMAGE = pygame.image.load("image/easy_racoon.png")
WONIMAGE1 = pygame.image.load("image/won_racoon1.png")
WONIMAGE2 = pygame.image.load("image/won_racoon2.png")

##############################################################

def game_Won_Animation(board):
    wimage1 = WONIMAGE1
    wimage2 = WONIMAGE2

    for i in range(9):
        wimage1, wimage2 = wimage2, wimage1
        DISPLAY.blit(wimage1, (0, 0))
        pygame.display.update()
        pygame.time.wait(500)

##############################################################

DISPLAY = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
DISPLAY.blit(BGIMAGE, (0, 0))

 

이번엔 기존 백그라운드 이미지를 설정해주는 것과 게임 종료가 되는 애니메이션이 나오면 화면이 전환되면서 다른 백그라운드 이미지가 반복되는 형식의 함수를 만들어주었다.

 

: 4월 27일 (마지막 날) :

 

개인 프로젝트의 마지막 날인 4월 27일에 최종적으로 정한 목록들이다.

1) 성공(Success), 실패(Fail) 값에 대한 Clear 및 Over 지정

2) Game Clear 및 Game Over 화면과 이미지, 텍스트 추가

3) Game Clear 및 Game Over 이후 게임 종료

4) 최종 점검

 

어제 최종적으로 마무리 단계까지 가지 못한 성공(Success), 실패(Fail)에 대한 값들이 일정 수치에 도달하면 Game Clear를 할 것인지 Game Over를 할 것인지 지정을 해주었다.

 

# 만약 이미지가 서로 다르다면? 서로 다르니 둘 다 닫기
if icon1shape != icon2shape or icon1color != icon2color:
    pygame.time.wait(500)  # 0.5초 후
    # 박스를 덮는다.
    cover_Boxes_Animation(mainBoard, [(firstSelection[0], firstSelection[1]), (boxx, boxy)])
    # 선택된 첫번째와 두번째 박스를 닫는다.
    revealedBoxes[firstSelection[0]][firstSelection[1]] = False
    revealedBoxes[boxx][boxy] = False

    # Fail 점수 추가
    Fail += 1
    # Fail의 값이 10일 경우
    if Fail == 15:
        game_Lose_Animation(mainBoard)
        game_over()

# 만약 이미지가 모두 같을 경우? Success 점수 추가
# or로 설정 시 이미지1.1, 이미지2.1이 같아도 점수가 추가됐었음 > and로 바꾸니 해결
if icon1shape == icon2shape and icon1color == icon2color:
    pygame.time.wait(500)  # 0.5초 후
    Success += 1
    # Success의 값이 10일 경우
    if Success == 10:
        game_Won_Animation(mainBoard)
        game_clear()

 

마지막에 추가적으로 조건문(if)을 달아주고 비교 연산자(==)를 통해 해당 값이 맞을 경우 해당 조건문(if)이 실행되는 형식으로 지정해주었다.

 

아직 Game Clear와 Game Over에 대한 명확한 내용이 존재하지 않았기에 해당 내용을 적어주기 위해 함수로 묶어서 사용하였고, 해당 함수들 내에 화면 전환과 이미지, 텍스트를 추가하였다.

그리고 마지막 화면 전환이 끝나고 난 뒤 Game Clear와 Game Over를 보여주고 게임을 종료해주기 위해 각각 함수로 지정하여 다음과 같이 만들어주었다.

 

# 게임에서 이겼을 때 화면 전환
def game_Won_Animation(board):
    wimage1 = WONIMAGE1
    wimage2 = WONIMAGE2

    for i in range(9):
        wimage1, wimage2 = wimage2, wimage1
        DISPLAY.blit(wimage1, (0, 0))
        pygame.display.update()
        pygame.time.wait(500)


# 게임에서 졌을 때 화면 전환
def game_Lose_Animation(board):
    limage1 = LOSEIMAGE1
    limage2 = LOSEIMAGE2

    for i in range(9):
        limage1, limage2 = limage2, limage1
        DISPLAY.blit(limage1, (0, 0))
        pygame.display.update()
        pygame.time.wait(500)


# 게임 Clear 함수
def game_clear():
    DISPLAY = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
    font_clear = pygame.font.Font('font/LeeSeoyun.ttf', 70)
    font_after = pygame.font.Font('font/LeeSeoyun.ttf', 50)

    clear_message = font_clear.render("Game Clear!!", True, YELLOW)
    next_message = font_after.render("End after 5 seconds.", True, YELLOW)

    DISPLAY.blit(WONIMAGE2, (0, 0))
    DISPLAY.blit(clear_message, (120, 140))
    DISPLAY.blit(next_message, (75, 220))

    pygame.display.update()
    sleep(5)

    pygame.quit()
    sys.exit()

# 게임 Over 함수
def game_over():
    DISPLAY = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
    font_clear = pygame.font.Font('font/LeeSeoyun.ttf', 70)
    font_after = pygame.font.Font('font/LeeSeoyun.ttf', 50)

    clear_message = font_clear.render("Game Over.", True, RED)
    next_message = font_after.render("End after 5 seconds.", True, RED)

    DISPLAY.blit(LOSEIMAGE2, (0, 0))
    DISPLAY.blit(clear_message, (140, 140))
    DISPLAY.blit(next_message, (75, 220))

    pygame.display.update()
    sleep(5)

    pygame.quit()
    sys.exit()

 

그리고 이제 마지막 최종 점검에 돌입하여 문제가 없는 것을 확인하였고, 파이썬 게임 만들기라는 개인 프로젝트를 영상과 exe 파일로 남겨놓았다.

 

A racoon trick 게임 화면

 

-

 

3일이라는 짧은 시간에 공부를 겸하며 만든 게임 하나가 비록 누군가에겐 우습고 작게 느낄지도 모른다.

 

하지만 모든 코드들을 훑어보았고, 이해하려고 노력했으며, 누군가가 만들어준 틀을 가지고 내 것으로 한번 바꿔보기 위해 많은 노력과 최선을 다하였다.

나만의 것으로 한번 바꿔보는 것이 새로운 시도였으며, 파이썬 공부에 있어 큰 도움이 되었다고 생각했다.

 

나는 만족하였고, 이러한 시도로 인해 성장해나가는 과정을 보니 기쁜 마음이 안들 수가 없었다.

 

:)