zoukankan      html  css  js  c++  java
  • 练手WPF(二)——2048游戏的简易实现(上)

    1、创建游戏界面
    编辑MainWindow.xaml,修改代码如下:

    <Window.Resources>
            <Style TargetType="Label">
                <Setter Property="Height" Value="105" />
                <Setter Property="Width" Value="105" />
                <Setter Property="HorizontalContentAlignment" Value="Center"/>
                <Setter Property="VerticalContentAlignment" Value="Center" />
                <Setter Property="FontWeight" Value="Bold"/>
                <Setter Property="Opacity" Value="0.7" />
            </Style>
            <Style TargetType="Rectangle">
                <Setter Property="Width" Value="105"/>
                <Setter Property="Height" Value="105"/>
                <Setter Property="Fill" Value="#ccc0b2"/>
            </Style>
        </Window.Resources>
        <Grid HorizontalAlignment="Left">
            <Grid.RowDefinitions>
                <RowDefinition Height="100"/>
                <RowDefinition Height="500"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20"/>
                <ColumnDefinition Width="500"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Image Grid.Row="0" Grid.Column="1" Source="/Images/title2048.png"/>
            <Canvas x:Name="myCanvas" Grid.Row="1" Grid.Column="1" Width="495" Height="495" Background="#b8af9e"/>
            <StackPanel Grid.Row="0" Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Center">
                <Button x:Name="btnNewGame" Content="新的起点" Width="80" Margin="8" Height="30" 
                        Click="BtnNewGame_Click"  Focusable="False" />
                <Button x:Name="btnOldGame" Content="旧的征程" Width="80" Margin="8" Height="30" Focusable="False"/>
            </StackPanel>
            <Grid Grid.Row="1" Grid.Column="2" Margin="10"  >
                <Grid.RowDefinitions>
                    <RowDefinition Height="0.5*"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="0.5*"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="0.5*"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <TextBlock Text="当前得分: " Width="210" FontSize="20" VerticalAlignment="Bottom"/>
                <Label x:Name="lblCurrScore" Grid.Row="1" Content="2048" Width="210" FontSize="40" 
                       Foreground="Maroon" Opacity="1" />
                <Label x:Name="lblAddScore" Grid.Row="1" Content="0" Width="210" FontSize="20" 
                       Foreground="Chocolate" Opacity="0" >
                    <Label.RenderTransform>
                        <TranslateTransform x:Name="tt" X="0" Y="0"/>
                    </Label.RenderTransform>
                </Label>
    
                <TextBlock Text="最高记录: " Grid.Row="2" Width="210" FontSize="20" VerticalAlignment="Bottom"/>
                <Label x:Name="lblBastScore" Grid.Row="3" Content="0" Width="210" FontSize="35" 
                       Foreground="DarkGray" Opacity="1" />
                <Button x:Name="btnShowTopScore" Grid.Row="4" Content="排行榜" Width="80" Margin="8" Height="25" HorizontalAlignment="Right"
                        Focusable="False"/>
                <Button x:Name="btnExitGame" Grid.Row="5" Content="退出游戏" Width="80" Margin="8" Height="30" 
                        HorizontalAlignment="Right" VerticalAlignment="Bottom"  Focusable="False" Click="BtnExitGame_Click"/>
            </Grid>
        </Grid>

    (1)在Window资源区分别定义了Label和Rectangle的样式,因为接下来游戏区的4宫格中使用的主要是对Lebel控件进行显示和移动操作的。
    (2)另外还添加一个命名为lblAddScore的Label控件(其初始透明度为0,即完全透明),用于增加成绩时的动画效果,其中定义了命名为tt的TranslateTransform变换效果。
    (3)游戏主区控件为Canvas,命名为myCanvas。
    (4)其他可以根据自己的喜好进行调整。

    2、定义几个字段变量
    毕竟是小游戏,直接从MainWindow.xaml.cs开始下手了。

    int lblWidth = 105;                     // 方块大小
    int lblPadding = 15;                    // 方块间隙
    
    int[,] gridData = null;                 // 游戏主数据数组
    Label[,] lblArray = null;               // 用于显示成方块的Label数组
    
    int currScore = 0;                      // 当前成绩
    
    bool isStarted = false;                 // 游戏是否已开始
    
    Random rnd = new Random();             // 随机数

    3、几个开始需要调用的方法

       3.1 游戏开始数字板
    界面中的16个游戏数字为0时,只显示空的16块小板面,其颜色比背景色稍浅。

    /// <summary>
    ///  显示背景矩形块
    /// </summary>
    private void ShowBackRect()
    {
        for (int y = 0; y < 4; y++)
        {
            for (int x = 0; x < 4; x++)
            {
                Rectangle rect = new Rectangle();
                rect.SetValue(Canvas.LeftProperty, (double)((x + 1) * lblPadding + x * lblWidth));
                rect.SetValue(Canvas.TopProperty, (double)((y + 1) * lblPadding + y * lblWidth));
                myCanvas.Children.Add(rect);
            }
        }
    }

      3.2 生成新数
    游戏开始后,需要随机生成值为2或4的两个新数字。之后,每上、下、左、右移动其中一次界面中的数字后,如果还有空位,又需要随机生成一个2或4的新数字。

    /// <summary>
    /// 重载生成新数
    /// </summary>
    /// <returns></returns>
    private bool NewNum()
    {
        int num = rnd.Next(0, 9) > 2 ? 2 : 4;
    
        int nullnum = 0;
        for (int y = 0; y < 4; y++)
        {
            for (int x = 0; x < 4; x++)
            {
                if (gridData[y, x] == 0)
                    nullnum++;
            }
        }
    
        if (nullnum < 1)
        {
            return false;
        }
    
        int index = rnd.Next(1, nullnum);
        nullnum = 0;
        for (int y = 0; y < 4; y++)
        {
            for (int x = 0; x < 4; x++)
            {
                if (gridData[y, x] == 0)
                {
                    nullnum++;
                    if (nullnum != index)
                        continue;
    
                    gridData[y, x] = num;
                }
            }
        }
    
        return true;
    }

    先统计出界面中剩余空格数,再从空格数中获取随机数作为新数位置,赋值2或4之一。

      3.3 设置Label板背景色和前景字体大小
    根据gridData元素值的高低,显示不同的背景色和字体大小

    /// <summary>
    /// 根据数值生成方块背景色值
    /// </summary>
    /// <param name="num"></param>
    /// <returns></returns>
    private Brush SetBackground(int num)
    {
        Brush backColor;
        switch (num)
        {
            case 2:
                backColor = new SolidColorBrush(Color.FromRgb(0xee, 0xe4, 0xda));
                break;
            case 4:
                backColor = new SolidColorBrush(Color.FromRgb(0xec, 0xe0, 0xc8));
                break;
            case 8:
                backColor = new SolidColorBrush(Color.FromRgb(0xf2, 0xb1, 0x79));
                break;
            case 16:
                backColor = new SolidColorBrush(Color.FromRgb(0xf5, 0x95, 0x63));
                break;
            case 32:
                backColor = new SolidColorBrush(Color.FromRgb(0xf5, 0x7c, 0x5f));
                break;
            case 64:
                backColor = new SolidColorBrush(Color.FromRgb(0xf6, 0x5d, 0x3b));
                break;
            case 128:
                backColor = new SolidColorBrush(Color.FromRgb(0xed, 0xce, 0x71));
                break;
            case 256:
                backColor = new SolidColorBrush(Color.FromRgb(0xed, 0xcc, 0x61));
                break;
    
            case 512:
                backColor = new SolidColorBrush(Color.FromRgb(0xec, 0xc8, 0x50));
                break;
            case 1024:
                backColor = new SolidColorBrush(Color.FromRgb(0xed, 0xc5, 0x3f));
                break;
            case 2048:
                backColor = new SolidColorBrush(Color.FromRgb(0xee, 0xc2, 0x2e));
                break;
            case 4096:
                backColor = new SolidColorBrush(Color.FromRgb(0xef, 0x85, 0x9c));
                break;
            default:
                backColor = new SolidColorBrush(Color.FromRgb(0xcc, 0xc0, 0xb2));
                break;
        }
        return backColor;
    }
    
    /// <summary>
    /// 根据数值设置方块字体大小
    /// </summary>
    /// <param name="num"></param>
    /// <returns></returns>
    private int SetFontSize(int num)
    {
        int iFontsize;
        switch (num)
        {
            case 2:
            case 4:
            case 8:
                iFontsize = 55;
                break;
    
            case 16:
            case 32:
            case 64:
                iFontsize = 50;
                break;
    
            case 128:
            case 256:
            case 512:
                iFontsize = 40;
                break;
    
            case 1024:
            case 2048:
            case 4096:
                iFontsize = 33;
                break;
    
            default:
                iFontsize = 30;
                break;
        }
        return iFontsize;
    }

      3.4 显示游戏区的所有Label控件
    如果gridData元素值不为0的话,生成Label控件并添加到游戏板中。

    /// <summary>
    /// 重新显示所有Label
    /// </summary>
    private void ShowAllLabel()
    {
        myCanvas.Children.Clear();
        ShowBackRect();
    
        for (int y = 0; y < 4; y++)
        {
            for (int x = 0; x < 4; x++)
            {
                if (gridData[y, x] != 0)
                {
                    lblArray[y, x] = new Label();
                    lblArray[y, x].SetValue(Canvas.LeftProperty, lblPadding * (x + 1) + (double)(x * lblWidth));
                    lblArray[y, x].SetValue(Canvas.TopProperty, lblPadding * (y + 1) + (double)(y * lblWidth));
                    lblArray[y, x].SetValue(Label.ContentProperty, gridData[y, x].ToString());
                    lblArray[y, x].SetValue(Label.BackgroundProperty, SetBackground(gridData[y, x]));
                    lblArray[y, x].SetValue(Label.FontSizeProperty, (double)SetFontSize(gridData[y, x]));
                    myCanvas.Children.Add(lblArray[y, x]);
                }
            }
        }
    }

    4、初始化游戏数据

    /// <summary>
    /// 初始化数据
    /// </summary>
    private void InitData()
    {
        if (gridData != null)                       // 初始化主数据数组
            gridData = null;
    
        gridData = new int[4, 4]
        {
            { 0, 0, 0, 0 },
            { 0, 0, 0, 0 },
            { 0, 0, 0, 0 },
            { 0, 0, 0, 0 }
        };
    
        if (lblArray != null)                       // 初始化显示用Label数组
            lblArray = null;
    
        lblArray = new Label[4, 4];
    
        if (myCanvas.Children.Count > 0)
            myCanvas.Children.Clear();              // 清除界面
    
        NewNum();                                   // 生成两个新数
        NewNum();
    
        ShowAllLabel();                             // 刷新游戏方块
    
        isStarted = true;
        currScore = 0;                              // 初始化当前成绩为0
    
        lblCurrScore.Content = currScore.ToString();// 更新当前成绩显示
    }

    将InitData()方法添加到BtnNewGame_Click按钮事件方法中。

    现在运行游戏,不断点击开始新游戏按钮,会在不同位置生成两个2或4的数字,并显示出来。

    5、判断游戏是否结束
    判断每一行是否存在空位,如果有则未结束。如果该行已满,则看看是否存在相邻是否有相同的数字,如果有则说明可以合并出新空位,即未结束。
    列判断方式相同。

    /// <summary>
    /// 游戏是否结束
    /// </summary>
    /// <returns></returns>
    private bool isGameOver()
    {
        for (int row = 0; row < 4; row++)
        {
            for (int i = 0; i < 4; i++)
            {
                if (gridData[row, i] == 0)  // 是否有空位
                {
                    return false;
                }
            }
            for (int i = 0; i < 3; i++)
            {
                if (gridData[row, i] == gridData[row, i + 1])
                {
                    return false;
                }
            }
        }
    
        for (int col = 0; col < 4; col++)
        {
            for (int i = 0; i < 4; i++)
            {
                if (gridData[i, col] == 0)
                {
                    return false;
                }
            }
            for (int i = 0; i < 3; i++)
            {
                if (gridData[i, col] == gridData[i + 1, col])
                {
                    return false;
                }
            }
        }
    
        return true;
    }
    
    /// <summary>
    /// 显示动画结束对话框
    /// </summary>
    private void ShowGameOver()
    {
        MessageBox.Show("游戏已经结束。", "Game Over", MessageBoxButton.OK, MessageBoxImage.Information);
        isStarted = false;
    }
  • 相关阅读:
    HTML5数据推送SSE原理及应用开发
    用Docker构建分布式Redis集群
    开发者必备的12个JavaScript库
    分享:我用一天时间开发的 新年送祝福 微信手机网站(可在线体验附图)(要代码的留下邮箱)
    祝福csdn回望2014,展望2015 大致可以这样总结和展望
    对 云寻觅贴吧(http://tieba.yunxunmi.com/)的简要分析
    开源前夕先给大家欣赏一下我用C语言开发的云贴吧 网站自动兼容-移动、手机、PC自动兼容云贴吧
    舞蹈模特欣欣(六)棚拍私房 大家看看像小龙女(李若彤)吗?
    终于解决了贴吧手机版的一个重大BUG
    比基尼美女_人像摄影吧主题
  • 原文地址:https://www.cnblogs.com/moonblogcore/p/10931768.html
Copyright © 2011-2022 走看看