使用 Kivy 开发跨平台贪吃蛇游戏

下面我将详细介绍如何使用 Python 的 Kivy 框架开发一个适配电脑和手机的贪吃蛇游戏。Kivy 是一个强大的开源 Python 库,专门用于开发多平台应用,支持 Windows、macOS、Linux、Android 和 iOS。

游戏设计思路

贪吃蛇游戏的核心组件包括:

  • 蛇:由多个方块组成的移动实体
  • 食物:随机出现在游戏区域内的目标
  • 游戏区域:一个有边界的矩形区域
  • 得分系统:记录玩家吃到的食物数量

完整实现代码

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ListProperty, BooleanProperty
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.graphics import Color, Rectangle
import random

class SnakePart(Widget):
    pass

class Food(Widget):
    pass

class SnakeGame(Widget):
    # 游戏属性
    grid_size = NumericProperty(20)  # 网格大小
    snake_direction = ListProperty([1, 0])  # 初始向右移动
    snake_parts = []  # 蛇的身体部分
    food = None  # 食物对象
    score = NumericProperty(0)  # 得分
    game_over = BooleanProperty(False)  # 游戏状态
    
    def __init__(self, **kwargs):
        super(SnakeGame, self).__init__(**kwargs)
        self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
        self._keyboard.bind(on_key_down=self._on_keyboard_down)
        self.reset_game()
    
    def reset_game(self):
        # 重置游戏状态
        self.game_over = False
        self.score = 0
        self.snake_direction = [1, 0]
        
        # 清除现有蛇身
        for part in self.snake_parts:
            self.remove_widget(part)
        self.snake_parts = []
        
        # 创建初始蛇身(3个部分)
        start_x = self.width / 2
        start_y = self.height / 2
        for i in range(3):
            part = SnakePart()
            part.size = (self.grid_size, self.grid_size)
            part.pos = (start_x - i * self.grid_size, start_y)
            self.snake_parts.append(part)
            self.add_widget(part)
        
        # 生成食物
        self.spawn_food()
        
        # 开始游戏循环
        Clock.schedule_interval(self.update, 0.1)
    
    def spawn_food(self):
        # 生成食物在随机位置
        if self.food:
            self.remove_widget(self.food)
        
        self.food = Food()
        self.food.size = (self.grid_size, self.grid_size)
        
        # 确保食物不会出现在蛇身上
        valid_pos = False
        while not valid_pos:
            x = random.randint(0, int(self.width - self.grid_size))
            y = random.randint(0, int(self.height - self.grid_size))
            self.food.pos = (x, y)
            
            # 对齐到网格
            self.food.pos = (
                (x // self.grid_size) * self.grid_size,
                (y // self.grid_size) * self.grid_size
            )
            
            # 检查是否与蛇身重叠
            valid_pos = True
            for part in self.snake_parts:
                if part.pos == self.food.pos:
                    valid_pos = False
                    break
        
        self.add_widget(self.food)
    
    def update(self, dt):
        if self.game_over:
            return
        
        # 移动蛇
        head = self.snake_parts[0]
        new_x = head.x + self.snake_direction[0] * self.grid_size
        new_y = head.y + self.snake_direction[1] * self.grid_size
        
        # 检查边界碰撞
        if (new_x < 0 or new_x >= self.width or 
            new_y < 0 or new_y >= self.height):
            self.game_over = True
            return
        
        # 检查自身碰撞
        for part in self.snake_parts[1:]:
            if part.pos == (new_x, new_y):
                self.game_over = True
                return
        
        # 检查是否吃到食物
        if self.food.pos == (new_x, new_y):
            # 增加蛇身
            new_part = SnakePart()
            new_part.size = (self.grid_size, self.grid_size)
            new_part.pos = self.food.pos
            self.snake_parts.append(new_part)
            self.add_widget(new_part)
            
            # 更新分数
            self.score += 1
            
            # 生成新食物
            self.spawn_food()
        else:
            # 移动蛇身
            for i in range(len(self.snake_parts)-1, 0, -1):
                self.snake_parts[i].pos = self.snake_parts[i-1].pos
            
            # 移动蛇头
            head.pos = (new_x, new_y)
    
    def _keyboard_closed(self):
        self._keyboard.unbind(on_key_down=self._on_keyboard_down)
        self._keyboard = None
    
    def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
        # 处理方向键输入
        key = keycode[1]
        if key == 'up' and self.snake_direction != [0, -1]:
            self.snake_direction = [0, 1]  # 向上
        elif key == 'down' and self.snake_direction != [0, 1]:
            self.snake_direction = [0, -1]  # 向下
        elif key == 'left' and self.snake_direction != [1, 0]:
            self.snake_direction = [-1, 0]  # 向左
        elif key == 'right' and self.snake_direction != [-1, 0]:
            self.snake_direction = [1, 0]  # 向右
        elif key == 'r' and self.game_over:  # 游戏结束后按R重新开始
            self.reset_game()
        return True

class SnakeApp(App):
    def build(self):
        game = SnakeGame()
        # 设置游戏区域大小
        Window.size = (400, 400)
        return game

if __name__ == '__main__':
    SnakeApp().run()

适配手机触摸屏

为了让游戏在手机上也能完美运行,我们需要添加触摸控制支持:

# 在SnakeGame类中添加以下方法
def on_touch_down(self, touch):
    if self.game_over:
        return super().on_touch_down(touch)
    
    # 计算触摸点相对于游戏区域中心的偏移
    center_x = self.center_x
    center_y = self.center_y
    dx = touch.x - center_x
    dy = touch.y - center_y
    
    # 根据偏移方向改变蛇的移动方向
    if abs(dx) > abs(dy):
        # 水平方向
        if dx > 0 and self.snake_direction != [-1, 0]:
            self.snake_direction = [1, 0]  # 右
        elif dx < 0 and self.snake_direction != [1, 0]:
            self.snake_direction = [-1, 0]  # 左
    else:
        # 垂直方向
        if dy > 0 and self.snake_direction != [0, -1]:
            self.snake_direction = [0, 1]  # 上
        elif dy < 0 and self.snake_direction != [0, 1]:
            self.snake_direction = [0, -1]  # 下
    
    return super().on_touch_down(touch)

游戏界面美化

使用 Kivy 的图形功能美化游戏界面:

# 在SnakePart类中添加
def __init__(self, **kwargs):
    super(SnakePart, self).__init__(**kwargs)
    with self.canvas:
        Color(0, 1, 0)  # 绿色
        self.rect = Rectangle(pos=self.pos, size=self.size)
    self.bind(pos=self.update_rect, size=self.update_rect)

def update_rect(self, *args):
    self.rect.pos = self.pos
    self.rect.size = self.size

# 在Food类中添加
def __init__(self, **kwargs):
    super(Food, self).__init__(**kwargs)
    with self.canvas:
        Color(1, 0, 0)  # 红色
        self.rect = Rectangle(pos=self.pos, size=self.size)
    self.bind(pos=self.update_rect, size=self.update_rect)

def update_rect(self, *args):
    self.rect.pos = self.pos
    self.rect.size = self.size

游戏打包为移动应用

要将游戏打包为 Android 应用:

  1. 安装 Buildozer:

    pip install buildozer
    

  2. 创建 buildozer.spec 文件:

    buildozer init
    

  3. 修改 buildozer.spec 文件:

    • 设置 title = Snake Game
    • 设置 package.name = snakegame
    • 设置 requirements = python3,kivy
  4. 打包应用:

    buildozer android debug
    

游戏功能扩展建议

  1. 难度级别:增加游戏速度随分数提高
  2. 障碍物:在游戏区域添加固定障碍物
  3. 多人模式:添加双人同屏对战模式
  4. 高分记录:保存本地最高分记录
  5. 游戏暂停:添加暂停功能

这个贪吃蛇游戏实现了所有核心功能,并适配了电脑键盘操作和手机触摸屏操作。通过 Kivy 的跨平台特性,你可以在多种设备上运行这个游戏,无需修改代码。

Logo

助力合肥开发者学习交流的技术社区,不定期举办线上线下活动,欢迎大家的加入

更多推荐