Kivy使用篇12

Kivy使用篇之翻牌游戏

  1. RecycleView RecycleGridLayout RecycleDataViewBehavior 使用示例
  2. Popup 的使用示例
  3. FocusBehavior 使用示例
  4. canvas 使用
  5. random.shuffle 打乱列表

翻牌游戏
有一个卡牌矩阵,依次翻开2个卡牌,如果2卡牌相同,则消除改2处的卡牌。依次类推,直到所有卡牌都被消除。
可翻转卡牌实现

:
    # 背景色来指示卡牌类型
    canvas.before:
        Color:
            rgba: 
                (1, 0, 0, 1) if self.mode == 0 \
                else (0, 1, 0, 1) if self.mode == 1 \
                else (0, 0, 1, 1) if self.mode == 2 \
                else (1, 0, 1, 1)
        Ellipse:
            size: self.size
            pos: self.pos
    # 前景色 选中时透明
    canvas.after:
        Color:
            rgba: 
                (0, 0, 0, 1) if not self.selectable \
                else (1, 1, 1, .1) if self.selected \
                else (.1, .8, .5, 1)
        Rectangle:
            pos: self.pos
            size: self.size
    font_size: sp(30)

每个卡牌有三种颜色,红、绿、蓝。当被选中时,前景色透明,可显示背景图颜色和文字信息。当无法被选中时,即not self.selectable时,前景为全黑且不透明。
pythonSelectableCard的定义如下

class SelectableCard(RecycleDataViewBehavior, Label):
    """ 可翻转的卡牌"""
    index = None
    mode = NumericProperty(0)
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)

    def refresh_view_attrs(self, rv, index, data):
        """视图改变"""
        self.index = index
        return super(SelectableCard, self).refresh_view_attrs(rv, index, data)

    def on_touch_down(self, touch):
        """ 触摸按下事件"""
        if super(SelectableCard, self).on_touch_down(touch):
            return True
        if self.collide_point(*touch.pos) and self.selectable:  # 按下坐标是否在卡牌坐标内且改卡牌可被选中
            return self.parent.select_with_touch(self.index, touch)

    def apply_selection(self, rv, index, is_selected):
        """卡牌被选中"""
        self.selected = is_selected

卡牌视图列表布局
使用RecycleView来显示卡牌

:
    layout: gridlayout
    viewclass: 'SelectableCard'  # 用来处理数据的显示
    SelectableRecycleGridLayout:
        id: gridlayout
        default_size: dp(60), dp(60)  # 默认大小
        default_size_hint: None, None
        size_hint_y: None
        height: self.minimum_height
        cols: 6  # 列表列数
        spacing: 10
        multiselect: True  #  支持多选
        touch_multiselect: True

其中布局定义如下:

class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior, RecycleGridLayout):
    """卡牌被选中"""
    is_checking = False  # 是否正在检测卡牌
    count = NumericProperty(0)  # 翻拍次数

    def select_with_touch(self, index, touch):
        if self.is_checking:
            return
        if len(self.selected_nodes) != 0:  # 获取选中卡牌数量
            pre_index = self.selected_nodes[0]
            if pre_index == index:  # 两次点击相同卡牌
                return
        self.count += 1
        super(SelectableRecycleGridLayout, self).select_with_touch(index, touch)
        if len(self.selected_nodes) > 1:  # 当选中卡牌为2个时 检测选中卡牌是否相同
            self.is_checking = True
            _thread.start_new_thread(self.check_selected_cards, ())

    def check_selected_cards(self):
        """检测选中卡牌是否相同"""
        pre_index = self.selected_nodes[0]
        index = self.selected_nodes[1]
        if (self.parent.data[pre_index]['mode'] == self.parent.data[index]['mode']) and (
                self.parent.data[pre_index]['text'] == self.parent.data[index]['text']):  # 卡牌相同
            # 使两张卡牌无效
            self.parent.data[index]['mode'] = self.parent.data[pre_index]['mode'] = 0
            self.parent.data[index]['selectable'] = self.parent.data[pre_index]['selectable'] = False
            # 更新视图
            self.parent.refresh_from_data()
        time.sleep(.3)  # 延时0.3s 清空选择
        self.clear_selection()
        # 检查是否已全部消除
        for card in self.parent.data:
            if card['selectable']:
                self.is_checking = False
                return
        # 已全部消除显示弹窗
        popup = FinishedPopup(title='Score: {}'.format(self.count), size_hint=(.8, .2))
        popup.content = Label(text='Click another location to continue')
        popup.open()
        self.count = 0
        self.parent.new_game()

def select_with_touch(self, index, touch): 用来处理触摸选中事件
当选择卡牌数量为2个时,启动一个线程def check_selected_cards(self):来检测选中的两卡牌是否相同(颜色和文字都一样),如果相同,则设置两卡牌不可选即:

            self.parent.data[index]['mode'] = self.parent.data[pre_index]['mode'] = 0
            self.parent.data[index]['selectable'] = self.parent.data[pre_index]['selectable'] = False

当所有卡牌都不可选,则游戏结束
主界面
主界面kv源码如下:

:
    orientation: 'vertical'
    BoxLayout:
        orientation: 'horizontal' 
        size_hint_y: None
        height: sp(52)
        Button:  # 重新游戏
            text: 'New'
            size_hint_x: None
            width: sp(52)
            on_press: 
                rv.layout.count = 0
                rv.new_game()
        Label:  # 翻牌次数计数
            text: '{}'.format(rv.layout.count)
        Button:  # 帮助按钮,打开所有牌
            size_hint_x: None
            width: sp(52)
            text: 'Help'
            on_press: rv.open_all()
    BoxLayout:
        orientation: 'horizontal' 
        Label:  # 保持RV居中
        RV:
            id: rv
            size_hint_x: None
            width: sp(430)
        Label: # 保持RV居中
    Label:   # 显示使用时间
        size_hint_y: None
        height: sp(52)
        text: '{}:{}'.format((rv.layout.timer // 60), (rv.layout.timer % 60))

新游戏:

    def new_game(self):
        """新游戏"""
        self.data = [{'text': str(x), 'mode': x % 3, 'selectable': True, 'selected': True} for x in range(24)] * 2
        random.shuffle(self.data)  # 随机打乱顺序
        self.refresh_from_data()

打开所有牌:

    def select_all(self):
        for x in range(48):
            self.layout.select_node(x)
        time.sleep(1)
        self.layout.clear_selection()

程序运行截图
Kivy使用篇12_第1张图片
Kivy使用篇12_第2张图片

源码地址:https://github.com/babylco0/python3_examples/tree/master/TurnOverGame

你可能感兴趣的:(kivy)