[Pygame] 2D게임 만들기 - New, Init, Update(충돌), Run

ParkJuneWoo·2019년 12월 12일
0

Pygame

목록 보기
2/3

Start.py

  • 소스코드 중 가장 중요한 게임을 실행하는 run, init, update, new 부분을 먼저 보겠다
  • 세부적인 구현부는 전체 소스를 참고하고, 개발 시 필요한 부분들만 설명하겠다

initialize

class Game:
    def __init__(self):
        # initialize game window, etc
        pg.init()
        pg.mixer.init()
        self.screen = pg.display.set_mode((WIDTH, HEIGHT))
        pg.display.set_caption(TITLE)
        self.clock = pg.time.Clock()
        self.running = True #게임 실행 Boolean 값
        ...
        ...

pg.init() : pygame initializer 입니다. pygame 라이브러리를 사용하기 위해 가장먼저 사용
pg.mixer.init() : music 사용을 위한 initializer
self.screen : display.set_mode() 를 사용하여 게임 창의 크기를 지정
(WIDTH, HEIGHT 값은 setting.py 에 정의되어 있다)
pg.display.set_caption(TITLE) : 게임 창의 제목표시줄에 쓰여질 문구
self.clock : time.Clock() 객체를 사용하여 이후 초당 프레임 설정시 사용한다
self.running : 게임이 끝난지 아닌지를 판단하기 위한 변수입니다(이름은 상관없다)

run

def run(self):
        #game loop
        pg.mixer.music.play(loops=-1) #배경음 플레이 (loops 값 false = 반복, true = 한번)
        self.playing = True
        while self.playing:
            self.clock.tick(FPS)
            self.events()
            self.update()
            self.draw()
        pg.mixer.music.fadeout(500) #배경음이 갑자기 꺼지지 않고 점점 꺼지게 함

정말 간단하다 😅

self.playing : 값이 True 이면 현재 게임중 임을 나타낸다
self.clock.tick(FPS) : 초당 그려질 프레임을 설정합니다

FPS값이 커질수록 부드러워지나 CPU에 부하가 많이 간다
20~30이 적당하다

events : 주로 버튼클릭에 대한 정의가 있다 (총 쏘기, 점프, Exit, ... 등)
update : 매 프레임마다 update 될 항목들 이다 (충돌, 적 생성, 게임 오버,.. 등)
(중요한 부분이므로 따로 자세하게 다루겠다)
draw : 게임을 프레임단위로 계속 그려주는 부분이다
(사실 게임은 매 프레임마다 새로운 화면을 그려주는 것의 반복이다)

New

 	def new(self):
        # start a new game
        self.score = 0
        self.head_count = 0
        self.enemy_level = 0
        self.speed_x = 4
        self.speed_y = 5
        self.speed_x_min = -2
        self.speed_y_min = 2
        
        ## sprite group ##
        self.all_sprites = pg.sprite.Group()
        self.explo = pg.sprite.Group()
        self.enemys = pg.sprite.Group() #적 sprite 그룹 생성
        self.bullets = pg.sprite.Group() # 총알 sprite 그룹 생성
        self.platforms = pg.sprite.Group() #platforms(블록) sprites 그룹 생성
        self.items = pg.sprite.Group()
        self.heads = pg.sprite.Group()
        
        self.player = Player(self) #self.player, Player객체 생성
        self.start_tick = pg.time.get_ticks()
        #PLATFORM_LIST에서 각 value값을 받아와 객체 생성
        for plat in PLATFORM_LIST:
            Platform(self, *plat) 
        pg.mixer.music.load(os.path.join(self.snd_dir, 'old city theme.mp3')) #배경음 로드
        with open(os.path.join(self.dir, SCORE), 'r') as f:
            try :
                self.highscore = int(f.read())
            except:
                self.highscore = 0
        self.run()
  • 여기 있는 값들의 정체는 본인 밖에 모를것이라 생각한다 😅
  • 너무 어렵게 생각하지 말자 사실상 초기값들을 설정한 것뿐이다

초기화라면 init에다가 하면 되지 않을까?

초기화라고 해서 init 과 혼란이 될 수 있지만 간단하게 생각하면 쉽다

  • init 에서는 게임 프로그램을 시작하면서 설정되어야 할 값들 이 존재한다
    (ex 창 크기, 타이틀 제목, 프레임 값, 게임 폰트)
  • new 에서는 게임을 진행하기 위해 설정되어야 할 값들이 존재한다

쉽게 말하면
💥 게임을 클릭하여 실행하는 행위init 이고
💥 게임 시작화면에서 start 를 눌러 게임을 플레이 하는 행위는 new 이다

이 점을 잘 생각해서 코딩을 할 수 있도록 하자

  • 중간에 sprite.Group()을 사용하여 그룹화 하는 부분은 이후 sprite 부분에서 다시 설명하겠다
  • 먼저 짧게 말하자면 pygame은 sprite(게임의 등장 객체)들을 그룹화하여 사용한다

Update

    def update(self):
        #game loop - update
        self.all_sprites.update()
        self.second = ((pg.time.get_ticks() - self.start_tick)/1000)
        if self.player.vel.y > 0:
            #hits -> spritecollide 메서드를 이용(x,y, default boolean)충돌 체크
            hits = pg.sprite.spritecollide(self.player, self.platforms, False)
            if hits:
                lowest = hits[0]
                for hit in hits:
                    if hit.rect.bottom > lowest.rect.bottom:
                        lowest = hit
                if self.player.pos.x < lowest.rect.right + 15 and \
                   self.player.pos.x > lowest.rect.left - 15:
                    if self.player.pos.y < lowest.rect.bottom:
                        self.player.pos.y = lowest.rect.top+1 #충돌시 player의 Y축 위치값이 충돌한 블록의 TOP값으로
                                                                #즉, 블록위에 있는 것처럼 보이게함
                        self.player.vel.y = 0
                        self.player.jumping = False

       #Game Level 처리, 최대 3번 난이도가 증가.
        if self.score == 1000:
            self.score += 10
            self.level_up.play()
            self.leveup_text()
            sleep(0.4)
            self.enemy_level += 1
            self.levelup(self.enemy_level)
        elif self.score == 2500:
            self.score += 10
            self.level_up.play()
            self.leveup_text()
            sleep(0.4)
            self.levelup(self.enemy_level)
        elif self.score == 4000:
            self.score += 10
            self.level_up.play()
            self.leveup_text()
            sleep(0.4)
            self.levelup(self.enemy_level)

        #게임 클리어 조건
        if self.head_count == 15:
            self.clear_text()
            self.ending = True
            self.playing = False
            self.head_count = 0
            sleep(1)
            ```
- 사실상 여기에 있는 값들도 본인밖에 모를거라 생각한다.. 
- 전부다 이해하지 말고 큰 그림으로 어떠한 로직인지만 이해하자
- 필요한 부분만 캐치하자!

`self.all_sprites.update()` : 모든 sprite(객체) 들을 update한다. 모든 객체들은 **오버라이드 된 update()메서드**를 갖는다 
_(sprite 부분에서 따로 설명하겠다)_
`self.score` : 게임에서 스코어 값을 이용하여 대한 난이도 조절을 한다
`self.head_count` : 게임 클리어 조건을 만족하는지 확인한다

🌟다음은 충돌처리 부분인데, 모든 게임에서 매우 중요한 부분이기 떄문에 따로 설명하겠다

## 충돌 처리
```python
	if self.player.vel.y > 0:
            #hits -> spritecollide 메서드를 이용(x,y, default boolean)충돌 체크
            hits = pg.sprite.spritecollide(self.player, self.platforms, False)
            if hits:
                lowest = hits[0]
                for hit in hits:
                    if hit.rect.bottom > lowest.rect.bottom:
                        lowest = hit
                if self.player.pos.x < lowest.rect.right + 15 and \
                   self.player.pos.x > lowest.rect.left - 15:
                    if self.player.pos.y < lowest.rect.bottom:
                        self.player.pos.y = lowest.rect.top+1 #충돌시 player의 Y축 위치값이 충돌한 블록의 TOP값으로
                                                                #즉, 블록위에 있는 것처럼 보이게함
                        self.player.vel.y = 0
                        self.player.jumping = False
                # pos 는 플레이어의 position(위치)를 의미한다
                # vel 은 플레이어의 이동방향에 대한 속도를 의미한다
                # jumping 은 플레이어가 현재 점프 중인지 아닌지를 나타낸다
                        ```
> 충돌 감지가 필요한 부분은 게임마다 다르므로 여기서 어떤 경우 충돌을 체크하는지 보겠다

` self.player.vel.y` : 이 변수는 `player`객체가 `vel.y > 0` y 값이 0보다 커진 경우, 즉 점프한 경우를 말한다
`hits = pg.sprite.spritecollide(self.player, self.platforms, False)` 
: pygame에서 지원하는 충돌체크 메서드인 spritecollide()를 사용한다. 이 메서드는
- 단일 객체와 충돌한 **group spirte 내의 객체 리스트를 반환**한다
- parameter **첫 번째로 single sprite** 객체를 받는다
- parameter **두 번째로 group sprite** 객체를 받는다
- 세 번째 parameter 는 True/False 이며 값을 True로하면 충돌즉시 group에서 충돌한 객제를 삭제한다

코드를 쉽게 설명하면, 
1. 가장먼저 플레이어와 벽돌(platforms)이 충돌했는지 확인한다
2. 충돌한 벽돌이 여러개라면 **가장 밑에 있는 것을 타겟**으로 잡는다 

```python
                lowest = hits[0]
                for hit in hits:
                    if hit.rect.bottom > lowest.rect.bottom:
                        lowest = hit
  1. 플레이어를 충돌한 벽돌 위로 옮긴다
				# 플레이어의 위치가 벽돌의 왼쪽/오른쪽 끝부분 안에 있고
                if self.player.pos.x < lowest.rect.right + 15 and \
                   self.player.pos.x > lowest.rect.left - 15:
      			  # 플레이어의 윗 부분 위치(머리부분)가 벽돌의 밑부분에 닿으면
        			# 벽돌 위로 올린다
                    if self.player.pos.y < lowest.rect.bottom:
                        self.player.pos.y = lowest.rect.top+1 
                               
  1. 플레이어의 점프 상태를 서있는 상태로 바꾼다

플레이어가 점프 했을 때 올라갈 수 있는 벽돌에 닿았을때의 충돌처리를 구현한 부분이다. 다소 복잡해 보일 수 있지만
순서대로 일어나는 행위 전체를 본다면 이해가 쉬울 것이다. 😅
충돌 부분은 실제로 직접 구현하면 어렵지만 pygame 에서 제공하는 메서드를 이용하면 보다 쉽게 구현할 수 있다

여기 까지 start.py 에서 중요한 부분들을 살펴봤으며, 다음에는 게임에 등장하는 객체들(sprite)을 설명하겠다
마지막으로 게임을 실행하는 부분 을 보여주지만 이해는 각자 해보도록 하자 (귀찮아서가 아님)

g = Game()
while g.start:
    while g.running:
        g.new()
        if g.ending == True:
            g.ending_screen() # ending 
        else:
            g.show_over_screen() # game over
pg.quit()

Source Code

profile
안녕하세요.

0개의 댓글