这一步在你选择的编码语言中实现游戏的规则。
Slint的总体理念是你在 Slint中实现用户界面,并在你喜欢的编程语言中实现业务逻辑。
游戏规则强制要求最多两个图块打开其幕布。如果图块匹配,则游戏认为它们已解决并保持打开状态。否则,游戏会短暂等待,以便玩家记住图标的位置,然后再次关闭幕布。
在 MainWindow组件内添加以下代码,以便在用户单击图块时向 C++代码发出信号。
export component MainWindow inherits Window { width:326px; height:326px;
callback check_if_pair_solved(); // 已添加 in property <bool> disable_tiles; // 已添加
in-out property <[TileData]> memory_tiles: [ { image: @image-url("icons/at.png") },此更改添加了一种让 MainWindow调用 C++代码的方式,告知它应检查玩家是否已解决一对图块。Rust代码需要一个额外的属性来切换以禁用进一步的图块交互,以防止玩家打开超过允许数量的图块。不允许作弊!
代码的最后一项更改是当 MemoryTile发出玩家单击它的信号时执行操作。
在 MainWindow for循环 clicked处理程序中添加以下处理程序:
for tile[i] in memory_tiles : MemoryTile { x: mod(i,4) *74px; y: floor(i /4) *74px; width:64px; height:64px; icon: tile.image; open_curtain: tile.image_visible || tile.solved; //将 solved状态从模型传播到图块 solved: tile.solved; clicked => { //旧:tile.image_visible = !tile.image_visible; //新: if (!root.disable_tiles) { tile.image_visible = true; root.check_if_pair_solved(); } } }在 C++端,你现在可以为 check_if_pair_solved回调添加一个处理程序,该处理程序检查玩家是否打开了两个图块。 如果它们匹配,则代码将模型中的 solved 属性设置为 true。如果它们不匹配,则启动一个计时器,该计时器在一秒后关闭图块。在计时器运行时,禁用每个图块,以便玩家在此期间无法单击任何内容。
在 main_window->run() 调用之前插入以下代码:
main_window->on_check_if_pair_solved( [main_window_weak = slint::ComponentWeakHandle(main_window)] { auto main_window = *main_window_weak.lock(); auto tiles_model = main_window->get_memory_tiles(); int first_visible_index =-1; TileData first_visible_tile; for (int i =0; i < tiles_model->row_count(); ++i) { auto tile = *tiles_model->row_data(i); if (!tile.image_visible || tile.solved) continue; if (first_visible_index ==-1) { first_visible_index = i; first_visible_tile = tile; continue; } bool is_pair_solved = tile == first_visible_tile; if (is_pair_solved) { first_visible_tile.solved = true; tiles_model->set_row_data(first_visible_index, first_visible_tile); tile.solved = true; tiles_model->set_row_data(i, tile); return; } main_window->set_disable_tiles(true);
slint::Timer::single_shot(std::chrono::seconds(1), [=]() mutable { main_window->set_disable_tiles(false); first_visible_tile.image_visible = false; tiles_model->set_row_data(first_visible_index, first_visible_tile); tile.image_visible = false; tiles_model->set_row_data(i, tile); }); } });代码使用 main_window 的 ComponentWeakHandle指针。这很重要,因为在回调处理程序中捕获 main_window本身的副本会导致循环所有权。 MainWindow拥有回调处理程序,回调处理程序本身又拥有对 MainWindow 的引用,该引用必须是弱引用而不是强引用,以避免内存泄漏。
更改 memory.slint 的内容,以便在用户单击图块时向 JavaScript 代码发出信号。
export component MainWindow inherits Window { width:326px; height:326px;
callback check_if_pair_solved(); // 已添加 in property <bool> disable_tiles; // 已添加
in-out property <[TileData]> memory_tiles: [ { image: @image-url("icons/at.png") },此更改添加了一种让 MainWindow调用 JavaScript 代码的方式,告知它应检查玩家是否已解决一对图块。Rust代码需要一个额外的属性来切换以禁用进一步的图块交互,以防止玩家打开超过允许数量的图块。不允许作弊!
代码的最后一项更改是当 MemoryTile发出玩家单击它的信号时执行操作。
在 MainWindow for循环 clicked处理程序中添加以下处理程序:
for tile[i] in memory_tiles : MemoryTile { x: mod(i,4) *74px; y: floor(i /4) *74px; width:64px; height:64px; icon: tile.image; open_curtain: tile.image_visible || tile.solved; //将 solved状态从模型传播到图块 solved: tile.solved; clicked => { //旧:tile.image_visible = !tile.image_visible; //新: if (!root.disable_tiles) { tile.image_visible = true; root.check_if_pair_solved(); } } }在 JavaScript端,现在为 check_if_pair_solved回调添加一个处理程序,该处理程序检查玩家是否打开了两个图块。如果它们匹配,则代码将模型中的 solved 属性设置为 true。如果它们不匹配,则启动一个计时器,该计时器在一秒后关闭图块。在计时器运行时,禁用每个图块,以便玩家在此期间无法单击任何内容。
在 mainWindow.run() 调用之前插入以下代码:
mainWindow.check_if_pair_solved = function () { const flipped_tiles = []; tiles.forEach((tile, index) => { if (tile.image_visible && !tile.solved) { flipped_tiles.push({ index, tile, }); } });
if (flipped_tiles.length ===2) { const { tile: tile1, index: tile1_index } = flipped_tiles[0];
const { tile: tile2, index: tile2_index } = flipped_tiles[1];
const is_pair_solved = tile1.image.path === tile2.image.path; if (is_pair_solved) { tile1.solved = true; model.setRowData(tile1_index, tile1); tile2.solved = true; model.setRowData(tile2_index, tile2); } else { mainWindow.disable_tiles = true; setTimeout(() => { mainWindow.disable_tiles = false; tile1.image_visible = false; model.setRowData(tile1_index, tile1); tile2.image_visible = false; model.setRowData(tile2_index, tile2); },1000); } }};在 MainWindow组件内添加以下代码,以便在用户单击图块时向 Rust 代码发出信号。
export component MainWindow inherits Window { width:326px; height:326px;
callback check_if_pair_solved(); // 已添加 in property <bool> disable_tiles; // 已添加
in-out property <[TileData]> memory_tiles: [ { image: @image-url("icons/at.png") },此更改添加了一种让 MainWindow调用 Rust 代码的方式,告知它应检查玩家是否已解决一对图块。Rust代码需要一个额外的属性来切换以禁用进一步的图块交互,以防止玩家打开超过允许数量的图块。不允许作弊!
代码的最后一项更改是当 MemoryTile发出玩家单击它的信号时执行操作。
在 MainWindow for循环 clicked处理程序中添加以下处理程序:
for tile[i] in memory_tiles : MemoryTile { x: mod(i,4) *74px; y: floor(i /4) *74px; width:64px; height:64px; icon: tile.image; open_curtain: tile.image_visible || tile.solved; //将 solved状态从模型传播到图块 solved: tile.solved; clicked => { //旧:tile.image_visible = !tile.image_visible; //新: if (!root.disable_tiles) { tile.image_visible = true; root.check_if_pair_solved(); } } }在 Rust端,你现在可以为 check_if_pair_solved回调添加一个处理程序,该处理程序检查玩家是否打开了两个图块。 如果它们匹配,则代码将模型中的 solved 属性设置为 true。如果它们不匹配,则启动一个计时器,该计时器在一秒后关闭图块。在计时器运行时,禁用每个图块,以便玩家在此期间无法单击任何内容。
在 main_window.run().unwrap(); 调用之前添加以下代码:
let main_window_weak = main_window.as_weak(); main_window.on_check_if_pair_solved(move || { let mut flipped_tiles = tiles_model.iter().enumerate().filter(|(_, tile)| tile.image_visible && !tile.solved);
if let (Some((t1_idx, mut t1)), Some((t2_idx, mut t2))) = (flipped_tiles.next(), flipped_tiles.next()) { let is_pair_solved = t1 == t2; if is_pair_solved { t1.solved = true; tiles_model.set_row_data(t1_idx, t1); t2.solved = true; tiles_model.set_row_data(t2_idx, t2); } else { let main_window = main_window_weak.unwrap(); main_window.set_disable_tiles(true); let tiles_model = tiles_model.clone(); slint::Timer::single_shot(std::time::Duration::from_secs(1), move || { main_window.set_disable_tiles(false); t1.image_visible = false; tiles_model.set_row_data(t1_idx, t1); t2.image_visible = false; tiles_model.set_row_data(t2_idx, t2); }); } } });代码使用 main_window 的 Weak指针。这很重要,因为在回调处理程序中捕获 main_window本身的副本会导致循环所有权。 MainWindow拥有回调处理程序,回调处理程序本身又拥有对 MainWindow 的引用,该引用必须是弱引用而不是强引用,以避免内存泄漏。
更改 memory.slint 的内容,以便在用户单击图块时向 Python 代码发出信号。
export component MainWindow inherits Window { width:326px; height:326px;
callback check_if_pair_solved(); // 已添加 in property <bool> disable_tiles; // 已添加
in-out property <[TileData]> memory_tiles: [ { image: @image-url("icons/at.png") },此更改添加了一种让 MainWindow调用 Python 代码的方式,告知它应检查玩家是否已解决一对图块。Rust代码需要一个额外的属性来切换以禁用进一步的图块交互,以防止玩家打开超过允许数量的图块。不允许作弊!
代码的最后一项更改是当 MemoryTile发出玩家单击它的信号时执行操作。
在 MainWindow for循环 clicked处理程序中添加以下处理程序:
for tile[i] in memory_tiles : MemoryTile { x: mod(i,4) *74px; y: floor(i /4) *74px; width:64px; height:64px; icon: tile.image; open_curtain: tile.image_visible || tile.solved; //将 solved状态从模型传播到图块 solved: tile.solved; clicked => { //旧:tile.image_visible = !tile.image_visible; //新: if (!root.disable_tiles) { tile.image_visible = true; root.check_if_pair_solved(); } } }在 Python端,现在为 check_if_pair_solved回调添加一个处理程序,该处理程序检查玩家是否打开了两个图块。如果它们匹配,则代码将模型中的 solved 属性设置为 true。如果它们不匹配,则启动一个计时器,该计时器在一秒后关闭图块。在计时器运行时,禁用每个图块,以便玩家在此期间无法单击任何内容。
在 MainWindow 类中插入此函数,并使用 @slint.callback 进行注解,以将其与 check_if_pair_solved关联:
class MainWindow(slint.loader.ui.app_window.MainWindow): def __init__(self): super().__init__() initial_tiles = self.memory_tiles tiles = slint.ListModel( itertools.chain( map(copy.copy, initial_tiles), map(copy.copy, initial_tiles) ) ) random.shuffle(tiles) self.memory_tiles = tiles
@slint.callback
def check_if_pair_solved(self): flipped_tiles = [ (index, copy.copy(tile)) for index, tile in enumerate(self.memory_tiles) if tile.image_visible and not tile.solved ]
if len(flipped_tiles) ==2: tile1_index, tile1 = flipped_tiles[0] tile2_index, tile2 = flipped_tiles[1] is_pair_solved = tile1.image.path == tile2.image.path if is_pair_solved: tile1.solved = True self.memory_tiles[tile1_index] = tile1 tile2.solved = True self.memory_tiles[tile2_index] = tile2 else: self.disable_tiles = True
def reenable_tiles(): self.disable_tiles = False tile1.image_visible = False self.memory_tiles[tile1_index] = tile1 tile2.image_visible = False self.memory_tiles[tile2_index] = tile2
slint.Timer.single_shot(datetime.timedelta(seconds=1), reenable_tiles)这些是最后的更改,运行代码将打开一个窗口,允许玩家按照规则玩游戏。