使用Xamarin開發移動應用示例——數獨遊戲(二)建立遊戲介面

尋找無名的特質發表於2022-01-24

在本系列第一部分,我們建立了程式框架,現在我們建立遊戲的介面,專案程式碼可以從Github下載:https://github.com/zhenl/ZL.Shudu 。程式碼隨專案進度更新。

首先在Views目錄下新增一個內容頁面,名稱為Game.xaml:

然後,在AppShell.xaml中增加這個頁面導航:

    <TabBar>
        <ShellContent Title="遊戲" Icon="icon_about.png" Route="Game"  ContentTemplate="{DataTemplate local:Game}" />  
        <ShellContent Title="關於" Icon="icon_about.png" Route="AboutPage" ContentTemplate="{DataTemplate local:AboutPage}" />
        <ShellContent Title="Browse" Icon="icon_feed.png" ContentTemplate="{DataTemplate local:ItemsPage}" />
    </TabBar>

如果這時執行程式,會發現頁面底部增加了一個“遊戲”分頁,並且初始頁面也改為這個頁面。現在我們修改這個頁面,改造為數獨遊戲介面。數獨的介面是9X9的格子,這些格子組成九個九宮格,每個格子中是1-9的數字,玩家需要將所有的格子填滿,並且行、列和九宮格中的資料不能重複。我們可以使用Grid進行佈局,構造九行九列,每個格子中放置一個按鈕(Button),使用者按下按鈕,彈出數字輸入框,輸入數字後,數字顯示在按鈕上,完成一個輸入。
下面是頁面的佈局XAML:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ZL.Shudu.Views.Game">
    <ContentPage.Content>
        <StackLayout x:Name="outerStack"  Orientation="Vertical">
            <!-- Place new controls here -->
            <Grid x:Name="myGrid" IsVisible="True" >
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>

                    <RowDefinition Height="25"  />
                    <RowDefinition Height="25" />
                    <RowDefinition Height="25" />
                    <RowDefinition Height="25" />
                    <RowDefinition Height="25" />
                    <RowDefinition Height="25" />
                    <RowDefinition Height="25" />
                    <RowDefinition Height="25" />
                    <RowDefinition Height="25" />
                    <RowDefinition Height="40" x:Name="rowButton" />
                    <RowDefinition Height="40" x:Name="rowResult" />
                </Grid.RowDefinitions>
                <Label x:Name="lbFinish" Text="完成" IsVisible="false" Grid.Row="10" Grid.Column="0"  Grid.ColumnSpan="2" />
                <Label x:Name="lbTime" Grid.Row="10" Grid.Column="3" Grid.ColumnSpan="2" Text="" IsVisible="False"></Label>
                <Label x:Name="lbMessage" Grid.Row="10" Grid.Column="5" Grid.ColumnSpan="4" Text="" IsVisible="False"></Label>
            </Grid>

            <Grid x:Name="grdNumber" IsVisible="false">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="*" />

                </Grid.RowDefinitions>
            </Grid>

        </StackLayout>
    </ContentPage.Content>
</ContentPage>

在頁面初始化時,生成數字按鈕、遊戲介面中每個單元格按鈕,並根據初始資料確定按鈕的文字和使能。

        public Game()
        {
            try
            {
                InitializeComponent();

                SetNumButtons();
                SetLayout();
                SetNewGame();
            }
            catch (Exception ex)
            {
                lbMessage.IsVisible = true;
                rowResult.Height = 40;
                lbMessage.Text = ex.Message;
                //throw;
            }
        }

設定數字按鈕的函式程式碼如下:

      private void SetNumButtons()
        {
            var num = 1;
            for (var i = 0; i < 2; i++)
            {
                for (var j = 0; j < 5; j++)
                {
                    var btn = new Button();
                    if (num == 10)
                    {
                        btn.Text = "清除";
                        btn.Clicked += btn_Clear_Clicked;
                        btn.FontSize = 15;
                    }
                    else
                    {
                        btn.Text = num.ToString();
                        btn.Clicked += btn_Num_Clicked;
                        btn.FontSize = 16;
                    }
                    btn.Padding = 0;
                    grdNumber.Children.Add(btn, j, i);
                    numbuttons[i, j] = btn;
                    num++;
                }
            }
        }

設定數獨介面按鈕的程式碼如下:

       private void SetLayout()
        {
            for (var i = 0; i < 9; i++)
            {
                for (var j = 0; j < 9; j++)
                {
                    int m = i / 3;
                    int n = j / 3;
                    var btn = new Button();
                    var c = new Color(0.9, 0.9, 0.9);
                    if ((m + n) % 2 == 0)
                    {
                        c = new Color(0.5,0.5, 0.5);
                    }
                    btn.BackgroundColor = c;
                    btn.Padding = 0;
                    btn.Margin = 0;
                    btn.FontSize = 20;
                    myGrid.Children.Add(btn, i, j);
                    btn.Clicked += Btn_Clicked;
                    buttons[i, j] = btn;
                }
            }
        }

根據初始化資料,設定按鈕狀態的程式碼如下:

      private void SetGame(int[,] inp)
        {

            for (var i = 0; i < 9; i++)
            {
                for (var j = 0; j < 9; j++)
                {
                    chess[i, j] = inp[i, j];
                }
            }

            for (var i = 0; i < 9; i++)
            {
                for (var j = 0; j < 9; j++)
                {
                    var btn = buttons[i, j];
                    if (chess[i, j] > 0)
                    {
                        btn.Text = chess[i, j].ToString();
                        btn.IsEnabled = false;
                    }
                    else
                    {
                        btn.Text = "";
                        btn.IsEnabled = true;
                    }
                }
            }
            this.lbFinish.IsVisible = false;
            this.lbTime.IsVisible = false;
            this.lbMessage.IsVisible = false;
            this.rowResult.Height = 1;
            lbTime.Text = "";
            lbMessage.Text = "";

        }

按鈕的響應事件如下:

     private void Btn_Clicked(object sender, EventArgs e)
        {
            currentButton = sender as Button;
            rowResult.Height = 1;
            rowButton.Height = 1;
            grdNumber.IsVisible = true;
        }

        private void btn_Clear_Clicked(object sender, EventArgs e)
        {
            if (currentButton == null) return;
            currentButton.Text = "";
            grdNumber.IsVisible = false;
            myGrid.IsVisible = true;
            rowResult.Height = 40;
            rowButton.Height = 40;
        }

        private void btn_Num_Clicked(object sender, EventArgs e)
        {
            currentNumBtn = sender as Button;
            
            int x = -1, y = -1;
            for (var i = 0; i < 9; i++)
            {
                for (var j = 0; j < 9; j++)
                {
                    if (buttons[i, j] == currentButton)
                    {
                        x = i;
                        y = j;
                        break;
                    }

                }
            }
            var num = int.Parse(currentNumBtn.Text);

            if (!checkval(x, y, num))
            {
                return;
            }
            currentButton.Text = currentNumBtn.Text;
            myGrid.IsVisible = true;
            grdNumber.IsVisible = false;
            rowResult.Height = 40;
            rowButton.Height = 40;

            if (IsFinish())
            {
                lbFinish.IsVisible = true;
                rowResult.Height = 40;
            }
        }

判斷輸入狀態和遊戲結束的函式如下:


        private bool checkval(int x, int y, int num)
        {
            for (var i = 0; i < 9; i++)
            {
                var buttonnum = string.IsNullOrEmpty(buttons[x, i].Text) ? 0 : int.Parse(buttons[x, i].Text);
                if (i != y && buttonnum == num) return false;
            }

            for (var i = 0; i < 9; i++)
            {
                var buttonnum = string.IsNullOrEmpty(buttons[i, y].Text) ? 0 : int.Parse(buttons[i, y].Text);
                if (i != x && buttonnum == num) return false;
            }

            int m = x / 3;
            int n = y / 3;
            for (int i = m * 3; i < (m + 1) * 3; i++)
            {
                for (int j = n * 3; j < (n + 1) * 3; j++)
                {
                    var buttonnum = string.IsNullOrEmpty(buttons[i, j].Text) ? 0 : int.Parse(buttons[i, j].Text);
                    if (i != x && j != y && buttonnum == num) return false;
                }
            }

            return true;
        }

        private bool IsFinish()
        {
            for (var i = 0; i < 9; i++)
            {
                for (var j = 0; j < 9; j++)
                {
                    if (string.IsNullOrEmpty(buttons[i, j].Text)) return false;
                }
            }
            return true;
        }
    }

執行效果如下:

使用Xamarin開發移動應用示例——數獨遊戲(二)建立遊戲介面

現在基本介面已經搭建完成,後續需要增加允許回退、歷史記錄、退出儲存以及自動完成等功能。專案程式碼可以從Github下載:https://github.com/zhenl/ZL.Shudu 。程式碼隨專案進度更新。

相關文章