본문 바로가기

내일배움캠프/1 ~ 4 주차

[내일배움캠프] - 8일차 : 개인 프로젝트 | 파이썬으로 게임만들기

 시작하는 글 

 

오늘은 2022.04.25 내일배움캠프 스파르타코딩클럽 실무형 AI 웹개발자 양성과정의 8일차다.

 

이번주는 2개의 개인 프로젝트를 진행할 예정이다.

그중, 오늘 진행할 프로젝트는

 

"파이썬으로 게임 만들기"

 

4/25 ~ 4/27 오후 1시까지 프로젝트를 완료해야하지만,

흥미로운 과제라고 생각했고 생각보다 쉽게 할 수 있을 것 같았다.

 

캠프 개강 전 사전과제로, 유튜브 강의를 보면서 2개의 파이썬 게임을 만들어 보았기 때문에

아주 적은 기본 개념과 로직은 알고 있었고

다시 한번 말하지만, 생각보다 쉽게 할 수 있을 것 같.았.다.

 

 

그렇게 내가 만들 게임은 이렇게 결정했다.

 

어쩌구저쩌구... 그래 슈팅게임!

 

 

그렇게 결정했다.

 

 


 

 훈련 내용 

 

사실 슈팅게임을 선택한 이유는 큰 건 없다.

일단 사전과제에서 슈팅게임을 만들어봤기 때문에 내 입장에선 접근성이 좋았고

평소 관심사인 마블 히어로 액션 게임을 만들어볼까라는 생각에 큰 고민없이 바로 진행했다.

 

 

1부 : 응, 난 텐가이 만들거야

 

 

내가 생각했던 게임 스타일은 유명한 고전게임 '텐가이'

 

 

아직 1탄도 못 깨본 '텐가이'

 

세로 방향보다는 가로 방향으로 이동하는 슈팅게임을 선택했다.

 

이유 : 세로 방향은 정수리만 봐야되니까

 


 

2부 : UI 구상 / 기능 구현 기획
그리고 내 '텐가이'

 

전체적인 화면 모습과 기능을 기획해보았다.

 

 

참, 사용 언어는 Python

라이브러리는 사전과제 때 사용 했던 pygame이다.

(에디터는 pyCham ^^)

 

 

- UI

  • 진행 방향 : 가로 우측 (주인공: → / 적: ←)
  • 화면 크기 : 800 * 480
  • 배경 컨셉 : 하늘 또는 우주

 

- 기능

 

  주인공

  • 주인공 무기 : 무언가(필살기/총알) 발사 (발사 이미지 변환)
  • 주인공 이동반경 : 스크린 내 동서남북 자유 이동 (단, 공격은 우측으로)

 

  적

  • 적 무기 : 무언가(총알) 발사 (발사 이미지 변환)
  • 적 이동반경 : 좌측으로 고정 (히든 적은 자유자재)

 

일단, 기본적인 기획은 했고

프로젝트 기간이 짧다보니, 위 기본 기능 구현이 가능하면 추가적으로 진행하려고 했다.

 


 

그렇게 반나절 정도의 시간을 들여 만든 내 '텐가이'

 

 

 

 

 

이거.

 

ㅋㅋㅋㅋㅋㅋ

(바위 이미지 한번 넣어봄)

 

먼저 기본 세팅을 깔아 놓고

 

(사전과제 때 배움)

import pygame

pygame.init()

screenWidth = 800
screenHeight = 480
screen = pygame.display.set_mode((screenWidth, screenHeight)) # 화면 설정

pygame.display.set_caption("텐가이") # 게임 타이틀 설정

running = True
while running: # 게임 구동
    
    for event in pygame.event.get(): # 이벤트 처리
        if event.type == pygame.QUIT: # 창이 닫히는 이벤트
            running = False

    pygame.display.update() # 게임화면 계속 그리기

pygame.quit() # 게임 종료

 


 

그 다음 대표적인 기능을 몇가지 꼽자면,

 

# 적 컨텐츠 구현

(※ 실제 코드 순서는 아니며, 설명을 위해 순서 재배열  선별적 복붙, 주석 재작성을 허가합니다.)  

enemy1 = pygame.image.load('img/enemy1.png') # 까만 벽돌놈
enemy1Size = enemy1.get_rect().size
enemy1Width = enemy1Size[0] # 벽돌놈 어깨넓이
enemy1Height = enemy1Size[1] # 벽돌놈 키
enemy1PosX = screenWidth - enemy1Width 
enemy1PosY = random.randint(0, screenHeight - enemy1Height) # 'ramdom'을 이용해서 랜덤 위치 지정

(중간생략)

enemy1PosX -= enemy1Speed * dt  # 벽돌놈이 프레임마다 움직이는 거리(실제로 이동하게 하는 부분)

if enemy1PosX < -enemy1Width: # 벽돌놈이 집나갔을 겨우
	enemy1PosX = screenWidth - enemy1Width # 잡아다가 처음 장소에 다시 세워둠
	enemy1PosY = random.randint(0, screenHeight - enemy1Height) # 위치는 랜덤하게

(중간생략)

screen.blit(enemy1, (enemy1PosX, enemy1PosY))  # 나와라 벽돌놈

 

 

# 컨텐츠 그리기

(※ 실제 코드 순서는 아니며, 설명을 위해 순서 재배열  선별적 복붙, 주석 재작성을 허가합니다.)  

(상단생략)

if event.key == pygame.K_SPACE: # 스페이스바를 누르면
    bulletPosX = characterPosX + characterWidth # X염색체
    bulletPosY = characterPosY # Y염색체
    bullets.append([bulletPosX, bulletPosY]) # 총알 탄생(탄창에 넣어둠)

bullets = [ [b[0] + bulletSpeed, b[1]] for b in bullets ] # 총알 관리(bulletSpeed으로 걸음마 시킴)
bulletsRem = -1 # 일 잘하는 총알 선출을 위한 변수

enemy1Rect = enemy1.get_rect() # 벽돌놈 위치 파악
enemy1Rect.left = enemy1PosX
enemy1Rect.top = enemy1PosY

for bulletiIdx, bulletVal in enumerate(bullets): # 벽돌놈 잡으러 나간 총알들 위치 파악
    bulletPosX = bulletVal[0]
    bulletPosY = bulletVal[1]

    bulletRect = bullet.get_rect() # 벽돌놈 잡으러 나간 총알들 위치 파악2
    bulletRect.left = bulletPosX
    bulletRect.top = bulletPosY

    if bulletRect.colliderect(enemy1Rect): # 총알이 벽돌놈을 잡으면
        bulletsRem = bulletiIdx # 잡은 총알이 누군지 확인
        enemy1PosX = screenWidth - enemy1Width # 범죄자 벽돌놈 또 생성 (세상의 이치)
        enemy1PosY = random.randint(0, screenHeight - enemy1Height) # 어디서 나타나는지 모름 (이 또한 세상의 이치)

    if bulletsRem > -1: # 일 잘하는 총알 선출 (벽돌놈 잡은 총알은 리스트의 idx값을 부여하기에 0 이상의 값을 가짐)
        del bullets[bulletsRem] # 총알 좋은 곳으로 보냄
        bulletsRem = -1 # 다시 총알 일주기

(뭐이리 길어)

 

 

그 다음은 뭐냐면,

 

 

 

 

 

 

 

(그외 생략)

 


 

3부 : 정리고 뭐고 일단 구현이나 해보자

 

지금까지 했던건 사전과제에서 배웠던 내용들이고

이제부터는 배운적 없는 막무가내 기능구현이다. (난 욕심은 많으니까)

 

난 아직 'class'의 개념이나 for문 등 기본적인 문법은 잘 모르기 때문에

일단 구현해 놓고 본다. (욕심 많음)

 

 

그렇게 한참을 기록 없이 하드코딩을 뚜드렸다.

 

그러고 나서 shift + F10 (비범함)

.

.

.

.

.

.

 

 

?

달라진게 뭐임

(내가 좀더 찌들었음)

 

일단 겉으로는 크게 달라진게 없지만,

속으론 좀더 견고해졌다 (내적성장)

 

 

달라진 기능은 다음과 같다.

 

  • 스코어 구현 (매 프레임마다 증가 + 적 사냥 시 증가)
  • 총알 지연 발사 / 최대 화면 표시 기능 구현
  • 목숨 구현 / 삭감 시 일시적 무적 효과 기능 구현
  • 손가락 기능 저하

 


 

하나씩 보자면, 하나만 보자면,

 

# 목숨 구현 / 삭감 시 일시적 무적 효과 기능 구현

(※ 실제 코드 순서는 아니며, 설명을 위해 순ㅅ 어쩌구)

life = 3 # 초기 목숨 설정 (난 목숨 3개^^)

collidedTime = 0 # 사고 시간 (처음엔 사고이력 없음)

(중간생략 : 사고 당사자 위치 어쩌구)
    
if characterRect.colliderect(enemy1Rect) or characterRect.colliderect(rockRect): # 적에게 피살 혹은 낙하물 사고 시
    lifeDown() # 목숨 깍기 판단 ㄱ

currentTime = pygame.time.get_ticks() # 지금 몇시

def lifeDown(): # 목숨 깍기 판단
    global life, collidedTime
    if currentTime - collidedTime < 2000: # 사고난지 얼마 안 됐으면 (최대 2초)
        life -= 0 # 봐줌
    else: # 아니면
        life -= 1 # 넥슬라이스
        collidedTime = currentTime # 사고이력 기록

if life <= 0: # 목숨이 없으면
    running = False # 사망

lifeShow = gameFont.render("life: {}".format(life), True, (255, 255, 255))
    screen.blit(lifeShow, (10, 50))  # 목숨 보여줘

 

+ 덤

 

# 총알 지연 발사 / 최대 화면 표시 기능 구현

(※ 실제 코드 순서는 아 어쩌구)

bulletSpeed = 5 # 총알 속도
maxBullets = 5 # 최대 총알 발사 수
nextBulletTime = 0 # 다음 총알 발사 시간
bulletDeltaTime = 200 # 총알 발사 딜레이 시간

if event.key == pygame.K_SPACE: # 스페이스바 누르면
    if len(bullets) < maxBullets and currentTime >= nextBulletTime: # 탄창(최대 4개)에 자리가 있거나 현재시간이 '다음 발사 시간'보다 이르면
        nextBulletTime = currentTime + bulletDeltaTime # '다음 발사 시간'을 현재시간 + 딜레이시간(200 멈칫)로 늘리고
        bulletPosX = characterPosX + characterWidth # 총알 X염색체 생성 
        bulletPosY = characterPosY # 총알 Y염색체 생성
        bullets.append([bulletPosX, bulletPosY]) # 총알 한발 장전

 

이렇게 발사 간격에 딜레이와 최대 화면 표시개수를 지정해줌 (안 그러면 무한 발사 때문에 스코어 1억 가능)

 

어떻게 알아냈냐면,

한참 방법을 찾다가 아래 착한 외국인 발견했다.

출처 : https://stackoverflow.com/questions/60122492/how-do-i-stop-more-than-1-bullet-firing-at-once/60125448#60125448

 

 

그렇게 저녁 9시 캠프 퇴근 시간이 되었고,,,

 

난,,,

 

 

 

 

(2 years later)

 

 

 

 

ㅠㅠ

같은 게임 맞노?

(배경음악도 넣음)

 

이제 '텐가이'와 상당히 가까워졌다. (남은거리 1광년 정도)

 

 

추가된 기능은,

 

  • 이미지 넣어서 멋있어보이는 기능
  • 숫자 표시였던 목숨 개수를 아이언맨 머가리 개수로 보여주는 기능
  • 리펄서 발사 혹은 아이언맨 피격 시 이미지 변환 기능 (핵심)
  • 수면부족 기능

 

어차피 내일 할 일이 이미지 더 넣기라서 내일 얘기하고 수면부족 기능 채우러 간다.

 

 


 

 

 끝내는 글 

 

다 쓰는데 이렇게 오래걸릴줄 알았으면 그냥 마지막 완성 모습만 설명할걸...

 

오늘의 교훈 : 이래서 깃을 해야함.