pygame学习笔记04-贪吃蛇

发布时间 2023-06-21 15:31:45作者: 编程驴子

运行效果:

代码如下:


import collections
import copy
import random
import sys
import time

import pygame
from pygame import locals

class CSnake(object):
    #I_WIDTH = 600
    #I_HEIGHT = 480
    I_WIDTH = 300
    I_HEIGHT = 240
    I_STEP = 20
    I_LINE_WIDTH = 1
    F_MOVE_TIME_DELTA_ORG = 0.5

    I_MAX_X = I_WIDTH//I_STEP -1
    I_MAX_Y = I_HEIGHT//I_STEP -1

    T_COLOR_BG = (40, 40, 60) #背景色
    T_COLOR_BLACK = (0, 0, 0) #网格线颜色
    T_COLOR_DARK = (200, 200, 200)  # 蛇颜色
    T_COLOR_LIGHT = (150, 150, 150)  # 食物颜色
    T_COLOR_RED = (255, 0, 0)  # 红色

    LIST_ORG_FOOD      = [12, 8]
    LIST_ORG_DIRECTION = [1, 0]

    def __init__(self):

        self.s_game_state = 'waiting' #waiting, running, over, pause
        self.i_score = 0
        self.i_level = 0
        self.f_move_time_delta = self._gen_move_time_delta()

        self.list_snake = self._init_snake()
        self.list_food = self._init_food()
        self.list_direction = self._init_direction()
        self.list_direction_tmp = self._init_direction()
        self.o_last_time = time.time()

        self.o_surface = pygame.display.set_mode((self.I_WIDTH, self.I_HEIGHT))

        self._run()

    def _init_snake(self):
        _list = list()

        _list.append((9, 3))
        _list.append((8, 3))
        _list.append((7, 3))
        _list.append((6, 3))

        return _list

    def _init_food(self):
        _list = list()
        _list.extend(self.LIST_ORG_FOOD)
        return _list

    def _init_direction(self):
        _list = list()
        _list.extend(self.LIST_ORG_DIRECTION)
        return _list

    def _gen_new_list_direction_on_move_time(self):
        _list = list()

        #if new direction is reverse
        _i_delta_x = self.list_direction[0] + self.list_direction_tmp[0]
        _i_delta_y = self.list_direction[1] + self.list_direction_tmp[1]

        if _i_delta_x==0 and _i_delta_y==0:
            #reverse direction, not accept
            _list = copy.copy(self.list_direction)
        else:
            #accept new direciton
            _list = copy.copy(self.list_direction_tmp)

        return _list

    def _run(self):
        pygame.init()
        pygame.display.set_caption(f'Snake, score:{self.i_score}, level:{self.i_level}')

        while True:
            for event in pygame.event.get():
                if event.type == locals.QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == locals.KEYDOWN: #键盘按下
                    self.list_snake = self._gen_new_list_snake_on_key_down(event.key)
                    self.list_direction = self._gen_new_list_direction_on_key_down(event.key)
                    self.list_direction_tmp = self._gen_new_list_direction_tmp_on_key_down(event.key)
                    self.s_game_state = self._gen_new_game_state_on_key_down(event.key)
                else:
                    pass

            self._update_window()

            _o_curr_time = time.time()
            if self.s_game_state=='running' and _o_curr_time - self.o_last_time >= self.f_move_time_delta:
                self.o_last_time = _o_curr_time
                
                self.list_direction = self._gen_new_list_direction_on_move_time()
                #self.list_snake = self._gen_new_list_snake_on_move_time()
                self._update_snake_and_food()
                self._update_window()
            else:
                pass

    def _draw_nets(self):
        #画横网格线
        for y in range(self.I_STEP, self.I_HEIGHT, self.I_STEP):
            pygame.draw.line(self.o_surface, self.T_COLOR_BLACK, (0, y), (self.I_WIDTH, y), self.I_LINE_WIDTH)

        #画竖网格线
        for x in range(self.I_STEP, self.I_WIDTH, self.I_STEP):
            pygame.draw.line(self.o_surface, self.T_COLOR_BLACK, (x, 0), (x, self.I_HEIGHT), self.I_LINE_WIDTH)

    def _draw_snake(self):
        for i, _t_cell in enumerate(self.list_snake):

            x0 = _t_cell[0]*self.I_STEP + self.I_LINE_WIDTH
            y0 = _t_cell[1]*self.I_STEP + self.I_LINE_WIDTH
            x1 =            self.I_STEP - self.I_LINE_WIDTH
            y1 =            self.I_STEP - self.I_LINE_WIDTH

            _t_color = self.T_COLOR_RED if i==0 else self.T_COLOR_DARK

            pygame.draw.rect(
                self.o_surface,
                _t_color,
                (x0, y0, x1, y1),
                0,
            )

    def _draw_food(self):
        x0 = self.list_food[0]*self.I_STEP
        y0 = self.list_food[1]*self.I_STEP
        x1 = self.I_STEP
        y1 = self.I_STEP

        pygame.draw.rect(
            self.o_surface,
            self.T_COLOR_LIGHT,
            (x0, y0, x1, y1),
            0,
        )

    def _update_snake_and_food(self):
        #time.sleep(1)

        _t_cell_next = (
            self.list_snake[0][0]+self.list_direction[0],
            self.list_snake[0][1]+self.list_direction[1],
        )

        _b_hit_snake = self._is_hit_snake(_t_cell_next)
        _b_hit_edge = self._is_hit_edge(_t_cell_next)
        _b_eat_food = self._is_eat_food(_t_cell_next)

        if _b_hit_snake or _b_hit_edge:
            self.s_game_state = 'over'
        elif _b_eat_food:
            self.list_snake.insert(0, _t_cell_next)
            self.list_food = self._gen_new_food()

            self.i_score += 1
            self.i_level = self.i_score//10
            self.i_level = self.i_score//2
            self.f_move_time_delta = self._gen_move_time_delta()

            pygame.display.set_caption(f'Snake, score:{self.i_score}, level:{self.i_level}')
        else:
            self.list_snake.insert(0, _t_cell_next)
            self.list_snake.pop()
            pass

    def _gen_new_list_snake_on_move_time(self):
        _list = copy.copy(self.list_snake)

        _t_cell_next = (
            _list[0][0]+self.list_direction[0],
            _list[0][1]+self.list_direction[1],
        )

        _b_hit_snake = self._is_hit_snake(_t_cell_next)
        _b_hit_edge = self._is_hit_edge(_t_cell_next)
        _b_eat_food = self._is_eat_food(_t_cell_next)


        if _b_hit_snake or _b_hit_edge:
            pass
        elif _b_eat_food:
            print('eat food, snake grow big')
            _list.insert(0, _t_cell_next)
        else:
            _list.insert(0, _t_cell_next)
            _list.pop()

        return _list

    def _gen_new_food(self):
        
        _list_for_random = list()
        for i in range(0, self.I_MAX_X+1):
            for j in range(0, self.I_MAX_Y+1):
                if (i, j) not in self.list_snake:
                    _list_for_random.append((i, j))

        _t_food_next = random.choice(_list_for_random)

        return _t_food_next

    def _update_window(self):
        self.o_surface.fill(self.T_COLOR_BG)
        self._draw_nets()
        self._draw_snake()
        self._draw_food()

        if self.s_game_state == 'waiting':
            self._display_info('Press Enter to Start')
        elif self.s_game_state == 'over':
            self._display_info('GAME OVER\nPress Enter to Start')
        else:
            pass

        pygame.display.flip()

    def _is_eat_food(self, t_cell_next):
        _b = False
        print(f'{t_cell_next} {self.list_food}')

        if t_cell_next[0]==self.list_food[0] and t_cell_next[1]==self.list_food[1]:
            _b = True

        return _b

    def _is_hit_snake(self, t_cell_next):
        _b = False

        if t_cell_next in self.list_snake:
            _b = True

        return _b

    def _is_hit_edge(self, t_cell_next):
        _b = False

        _b_hit_x = t_cell_next[0] not in range(0, self.I_MAX_X+1)
        _b_hit_y = t_cell_next[1] not in range(0, self.I_MAX_Y+1)

        #print(f'{t_cell_next}, {_b_hit_x}, {_b_hit_y}')

        if _b_hit_x or _b_hit_y:
            _b = True

        return _b

    def _gen_move_time_delta(self):
        return self.F_MOVE_TIME_DELTA_ORG * pow(0.9, self.i_level)

    def _display_info(self, s_info):
        basicFont = pygame.font.SysFont('arial', 40)
        text = basicFont.render(s_info, True, (255, 255, 0), (0, 0, 255))
        textRect = text.get_rect()
        textRect.centerx = self.o_surface.get_rect().centerx
        textRect.centery = self.o_surface.get_rect().centery
        #textRect.center = (200, 200) #设置显示对象居中
        self.o_surface.blit(text, textRect)

    def _gen_new_list_snake_on_key_down(self, event_key):
        _list = list()

        if event_key == locals.K_RETURN and self.s_game_state in ['waiting', 'over']:
            _list = self._init_snake()
        else:
            _list = copy.copy(self.list_snake)

        return _list

    def _gen_new_list_direction_on_key_down(self, event_key):
        _list = list()

        if event_key == locals.K_RETURN and self.s_game_state in ['waiting', 'over']:
            _list = self._init_direction()
        else:
            _list = copy.copy(self.list_direction)

        return _list

    def _gen_new_list_direction_tmp_on_key_down(self, event_key):
        _list = list()

        if event_key in (locals.K_w, locals.K_UP): # w键, 向上键
            _list = [0, -1]
        elif event_key in (locals.K_s, locals.K_DOWN): # s键, 向下键
            _list = [0, 1]
        elif event_key in (locals.K_a, locals.K_LEFT): # a键, 向左键
            _list = [-1, 0]
        elif event_key in (locals.K_d, locals.K_RIGHT): # d键, 向右键
            _list = [1, 0]
        else:
            _list = copy.copy(self.list_direction_tmp)

        return _list

    def _gen_new_game_state_on_key_down(self, event_key):
        _s_game_state_new = self.s_game_state

        if event_key == locals.K_RETURN: # Enter键
            if self.s_game_state in ['waiting', 'over']:
                _s_game_state_new = 'running'
        elif event_key == locals.K_SPACE:
            if   self.s_game_state == 'over'   : _s_game_state_new = 'over'
            elif self.s_game_state == 'waiting': _s_game_state_new = 'waiting'
            elif self.s_game_state == 'running': _s_game_state_new = 'pause'
            elif self.s_game_state == 'pause'  : _s_game_state_new = 'running'
            else                               : _s_game_state_new = 'pause'
        else:
            pass

        return _s_game_state_new

if __name__ == '__main__':
    o_snake = CSnake()