시작하는 글
오늘은 2022.04.25 내일배움캠프 스파르타코딩클럽 실무형 AI 웹개발자 양성과정의 8일차다.
이번주는 2개의 개인 프로젝트를 진행할 예정이다.
그중, 오늘 진행할 프로젝트는
"파이썬으로 게임 만들기"
4/25 ~ 4/27 오후 1시까지 프로젝트를 완료해야하지만,
흥미로운 과제라고 생각했고 생각보다 쉽게 할 수 있을 것 같았다.
캠프 개강 전 사전과제로, 유튜브 강의를 보면서 2개의 파이썬 게임을 만들어 보았기 때문에
아주 적은 기본 개념과 로직은 알고 있었고
다시 한번 말하지만, 생각보다 쉽게 할 수 있을 것 같.았.다.
그렇게 내가 만들 게임은 이렇게 결정했다.
어쩌구저쩌구... 그래 슈팅게임!
그렇게 결정했다.
훈련 내용
사실 슈팅게임을 선택한 이유는 큰 건 없다.
일단 사전과제에서 슈팅게임을 만들어봤기 때문에 내 입장에선 접근성이 좋았고
평소 관심사인 마블 히어로 액션 게임을 만들어볼까라는 생각에 큰 고민없이 바로 진행했다.
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억 가능)
어떻게 알아냈냐면,
한참 방법을 찾다가 아래 착한 외국인 발견했다.
그렇게 저녁 9시 캠프 퇴근 시간이 되었고,,,
난,,,
(2 years later)
ㅠㅠ
(배경음악도 넣음)
이제 '텐가이'와 상당히 가까워졌다. (남은거리 1광년 정도)
추가된 기능은,
- 이미지 넣어서 멋있어보이는 기능
- 숫자 표시였던 목숨 개수를 아이언맨 머가리 개수로 보여주는 기능
- 리펄서 발사 혹은 아이언맨 피격 시 이미지 변환 기능 (핵심)
- 수면부족 기능
어차피 내일 할 일이 이미지 더 넣기라서 내일 얘기하고 수면부족 기능 채우러 간다.
끝내는 글
다 쓰는데 이렇게 오래걸릴줄 알았으면 그냥 마지막 완성 모습만 설명할걸...
오늘의 교훈 : 이래서 깃을 해야함.
'내일배움캠프 > 1 ~ 4 주차' 카테고리의 다른 글
[내일배움캠프] - 10일차 : 개인 프로젝트 | 파이썬으로 게임만들기 쓰리 | 제출 (0) | 2022.04.27 |
---|---|
[내일배움캠프] - 9일차 : 개인 프로젝트 | 파이썬으로 게임만들기 투 (0) | 2022.04.26 |
[내일배움캠프] - 1주차 : 웹프로그래밍 학습부터 팀프로젝트까지 | 전부 다 처음 (0) | 2022.04.24 |
[내일배움캠프] - 6~7일차 : 휴일 (0) | 2022.04.24 |
[내일배움캠프] - 5일차 : 웹 프로그래밍 A-Z 기초 미니 팀 프로젝트 쓰리 | 발표 (0) | 2022.04.22 |