pygame網絡遊戲_4_2:人物行走_角色移動效果

1.添加網格線

爲了讓大家能更直觀的感受到地圖是一個個小格子,我在GameMap類中又新增了一個繪製網格線的方法:

    def draw_grid(self, screen_surf):
        """
        畫網格
        """
        for x in range(self.w):
            for y in range(self.h):
                if self[x][y] == 0:  # 不是障礙,畫空心的矩形
                    pygame.draw.rect(screen_surf, (255, 255, 255), (self.x + x * 32, self.y + y * 32, 32, 32), 1)
                else:  # 是障礙,畫黑色實心的矩形
                    pygame.draw.rect(screen_surf, (0, 0, 0), (self.x + x * 32 + 1, self.y + y * 32 + 1, 30, 30), 0)

功能很簡單:不是障礙就繪製空心矩形,是障礙就繪製實心矩形。我們在繪圖函數中調用:

    def update(self):
        while True:
            self.clock.tick(self.fps)
            # TODO:邏輯更新
            self.event_handler()
            # TODO:畫面更新
            self.game_map.draw_bottom(self.screen_surf)
            Sprite.draw(self.screen_surf, self.hero, 100, 100, 0, 0)
            Sprite.draw(self.screen_surf, self.hero, 210, 120, 1, 1)
            Sprite.draw(self.screen_surf, self.hero, 300, 100, 2, 2)
            self.game_map.draw_top(self.screen_surf)
            self.game_map.draw_grid(self.screen_surf)
            pygame.display.update()

畫風突然鬼畜起來了,哈哈~

其中黑色的格子就是障礙啦。

2.人物行走類

在尋路之前,先得把我們的行走功能(動畫效果、人物面向)搞定。人物行走的邏輯相對之前的代碼是要複雜一些的,所以動手寫代碼之前,得好好分析幾個問題。

1.角色id:我們使用的是一張集成了所有角色的精靈圖,但是不同的角色在圖片中的位置都各不相同。這時,我們就需要一個角色id來確定每個角色在圖片中的位置。如何優雅的設計人物id呢?我們先看一張圖:

我將每一幀都進行了編號0~95。那麼第一個角色的id就是0,第二個角色id是3,第三個是6,第四個是9,第五個是48...

可以發現,我把每個角色第一幀的編號當作了它的id,而不是直接讓角色id按照1,2,3,4,5...的順序排下去。

這樣做的好處是什麼呢?

我們來回憶一下之前封裝的Sprite.draw:

draw(dest, source, x, y, cell_x, cell_y, cell_w=32, cell_h=32)

cell_x和cell_y代表精靈圖中的列和行

所以,我們就可以直接通過角色id計算出角色第一幀的cell_x和cell_y。

cell_x=角色id%12 ,因爲每行有12列,所以對12取餘是列數

cell_y=角色id//12,每行12個,所以除以12就是行數

 

2.只算出來第一幀可不行,每個角色一共4*3=12幀,怎麼確定角色當前是12幀的哪一幀呢?

我們可以用一個方向變量dir記錄角色當前的方向,取值範圍是0~3(一共4個方向),再用一個當前幀變量frame記錄角色在當前方向的第幾幀,取值範圍是0~2(每個方向有3幀)。

所以角色在移動過程中的列和行就是:

cell_x=角色id%12+frame

cell_y=角色id//12+dir

 

3.人物移動邏輯

假設我們已知:1.角色在地圖中的格子的行cur_my和列cur_mx  2.角色下一步將要去的格子的行next_my和列next_mx

那麼角色當前的繪圖座標是:cur_x=cur_mx*32,cur_y=cur_my*32。

角色的下一步格子的繪圖座標就是:dest_x=next_mx*32,dest_y=next_my*32。

因爲一個格子是32*32的,所以實際的繪圖座標需要*32。

 

然後,我們需要在遊戲主循環裏不斷的去執行以下邏輯:

如果cur_x大於dest_x,那麼cur_x-=2,其中這個2代表每次主循環角色移動的像素。

如果cur_x小於dest_x,那麼cur_x+=2。

cur_y也是同理

當cur_x==dest_x並且cur_y==dest_y的時候,就代表角色已經移動到目標位置了。

 

理解了上面說的三個問題之後(沒理解就配合下面的代碼再思考一遍),我們就可以開始編寫人物行走類了,在core.py中增加:


class CharWalk:
    """
    人物行走類 char是character的縮寫
    """
    DIR_DOWN = 0
    DIR_LEFT = 1
    DIR_RIGHT = 2
    DIR_UP = 3

    def __init__(self, hero_surf, char_id, dir, mx, my):
        """
        :param hero_surf: 精靈圖的surface
        :param char_id: 角色id
        :param dir: 角色方向
        :param mx: 角色所在的小格子座標
        :param my: 角色所在的小格子座標
        """
        self.hero_surf = hero_surf
        self.char_id = char_id
        self.dir = dir
        self.mx = mx
        self.my = my

        self.is_walking = False  # 角色是否正在移動
        self.frame = 1  # 角色當前幀
        self.x = mx * 32  # 角色相對於地圖的座標
        self.y = my * 32
        # 角色下一步需要去的格子
        self.next_mx = 0
        self.next_my = 0
        # 步長
        self.step = 2  # 每幀移動的像素

    def draw(self, screen_surf, map_x, map_y):
        cell_x = self.char_id % 12 + int(self.frame)
        cell_y = self.char_id // 12 + self.dir
        Sprite.draw(screen_surf, self.hero_surf, map_x + self.x, map_y + self.y, cell_x, cell_y)

    def goto(self, x, y):
        """
        :param x: 目標點
        :param y: 目標點
        """
        self.next_mx = x
        self.next_my = y

        # 設置人物面向
        if self.next_mx > self.mx:
            self.dir = CharWalk.DIR_RIGHT
        elif self.next_mx < self.mx:
            self.dir = CharWalk.DIR_LEFT

        if self.next_my > self.my:
            self.dir = CharWalk.DIR_DOWN
        elif self.next_my < self.my:
            self.dir = CharWalk.DIR_UP

        self.is_walking = True

    def move(self):
        if not self.is_walking:
            return
        dest_x = self.next_mx * 32
        dest_y = self.next_my * 32

        # 向目標位置靠近
        if self.x < dest_x:
            self.x += self.step
            if self.x >= dest_x:
                self.x = dest_x
        elif self.x > dest_x:
            self.x -= self.step
            if self.x <= dest_x:
                self.x = dest_x

        if self.y < dest_y:
            self.y += self.step
            if self.y >= dest_y:
                self.y = dest_y
        elif self.y > dest_y:
            self.y -= self.step
            if self.y <= dest_y:
                self.y = dest_y

        # 改變當前幀
        self.frame = (self.frame + 0.1) % 3

        # 角色當前位置
        self.mx = int(self.x / 32)
        self.my = int(self.y / 32)

        # 到達了目標點
        if self.x == dest_x and self.y == dest_y:
            self.frame = 1
            self.is_walking = False

其中,self.x和self.y就是問題分析中的cur_x,cur_y。

在draw函數中,傳入了兩個參數map_x和map_y,這是地圖的繪圖座標,我們的self.x,self.y都是相對於地圖的,所以要加上地圖繪圖座標纔是實際的角色繪圖座標。

這個類的代碼也不是很多,大家一定要理解透徹哦~

 

最後我們來看一下效果,讓我們的0號角色從(5,10)走到(14,10):

在__init_game中創建我們的角色:

    def __init_game(self):
        """
        我們遊戲的一些初始化操作
        """
        self.hero = pygame.image.load('./img/character/hero.png').convert_alpha()
        self.map_bottom = pygame.image.load('./img/map/0.png').convert_alpha()
        self.map_top = pygame.image.load('./img/map/0_top.png').convert_alpha()
        self.game_map = GameMap(self.map_bottom, self.map_top, 0, 0)
        self.game_map.load_walk_file('./img/map/0.map')
        self.role = CharWalk(self.hero, 0, CharWalk.DIR_DOWN, 5, 10)
        self.role.goto(14, 10)

別忘了,在繪圖函數中顯示角色:

    def update(self):
        while True:
            self.clock.tick(self.fps)
            # 邏輯更新
            self.role.move()
            self.event_handler()
            # 畫面更新
            self.game_map.draw_bottom(self.screen_surf)
            self.role.draw(self.screen_surf, self.game_map.x, self.game_map.y)
            self.game_map.draw_top(self.screen_surf)
            pygame.display.update()

運行效果:

本章完,請完全理解本章內容後再繼續閱讀後續章節喔~

有問題可以直接在評論中留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章