練手WPF(三)——掃雷小遊戲的簡易實現(上)

1、建立項目
1.建立WPF項目,設置初始化窗口大小(初級難度):高x寬爲430x350。
2.添加文件夾Images,並添加相關圖片。數組


3.xaml中引入圖片資源。dom

<Window.Resources>
    <BitmapImage x:Key="ImgSpace" UriSource="/Images/space.bmp"/>
    <BitmapImage x:Key="ImgMine" UriSource="/Images/mine.png"/>
    <BitmapImage x:Key="ImgNum1_8" UriSource="/Images/num1_8.png"/>
    <BitmapImage x:Key="ImgForeground" UriSource="/Images/foreImg.png"/>
    <BitmapImage x:Key="ImgMineOrTimer" UriSource="/Images/mineAndTimer.png"/>
    <BitmapImage x:Key="ImgBomb" UriSource="/Images/bomb.png"/>
</Window.Resources>

4.添加窗口元素
(1)菜單ide

<DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="遊戲(_G)">
                <MenuItem x:Name="MenuGameStart" Header="開始遊戲(_S)" Click="MenuGameStart_Click"/>
                <Separator/>
                <MenuItem x:Name="MenuGameExit" Header="退出遊戲(_x)" Click="MenuGameExit_Click"/>
            </MenuItem>
            <MenuItem Header="級別(_L)">
                <MenuItem Header="初級(_L)" x:Name="MenuLowLevel" IsChecked="True" Click="MenuLowLevel_Click"/>
                <MenuItem Header="中級(_M)" x:Name="MenuMiddleLevel" IsChecked="False" Click="MenuMiddleLevel_Click"/>
                <MenuItem Header="高級(_H)" x:Name="MenuHighLevel" IsChecked="False" Click="MenuHighLevel_Click"/>
            </MenuItem>
            <MenuItem Header="選項(_O)">
                <MenuItem  x:Name="MenuOptionsMusic" Header="音樂音效(_S)" IsCheckable="True" IsChecked="True"
                          Click="MenuOptionsMusic_Click"/>
                <Separator/>
                <MenuItem x:Name="MenuShowAllMine" Header="顯示地雷(_O)" Click="MenuShowAllMine_Click"/>
                <MenuItem x:Name="MenuRestoreMap" Header="繼續遊戲(_F)" Click="MenuRestoreMap_Click"/>
                <Separator />
                <MenuItem x:Name="MenuResetData" Header="重置記錄(_R)" Click="MenuResetData_Click" />
            </MenuItem>
            <MenuItem Header="幫助(_H)">
                <MenuItem x:Name="MenuHelpAbout" Header="關於(_A)..." Click="MenuHelpAbout_Click"/>
            </MenuItem>
        </Menu>


</DockPanel>

(2)在菜單以後,</DockPanel>以前添加其餘界面元素函數

<Grid Margin="3" DockPanel.Dock="Top">
            <Grid.RowDefinitions>
                <RowDefinition Height="40"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <StackPanel Grid.Row="0" Background="SkyBlue" VerticalAlignment="Center">
                <Grid Margin="2 2 2 2">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <StackPanel Grid.Column="0" HorizontalAlignment="Right" Margin="0 0 60 0" Orientation="Horizontal">
                        <Image x:Name="ImgClock" Width="35" Height="35" />
                        <Border Height="30" Width="60" CornerRadius="6" BorderThickness="1" VerticalAlignment="Center" Background="#333333">
                            <TextBlock x:Name="textBlockTime" Text="" VerticalAlignment="Center" HorizontalAlignment="Center" 
                                   FontSize="14" Foreground="AliceBlue"/>
                        </Border>
                    </StackPanel>

                    <StackPanel Grid.Column="1" HorizontalAlignment="Left" Orientation="Horizontal"  Margin="40 0 0 0">
                        <Image x:Name="ImgMineNum" Width="35" Height="35" />
                        
                        <Border Height="30" Width="60" CornerRadius="6" BorderThickness="1" VerticalAlignment="Center" Background="#333333">
                            <TextBlock x:Name="textBlockMineNum" Text="" VerticalAlignment="Center" HorizontalAlignment="Center" 
                               FontSize="14" Foreground="AliceBlue"/>
                        </Border>
                    </StackPanel>
                </Grid>
            </StackPanel>
            <StackPanel Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top">
                <Canvas x:Name="BackCanvas" Width="315" Height="315" Background="LightCyan" Margin="10" />
            </StackPanel>
            <StackPanel Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top">
                <Canvas x:Name="ForeCanvas" Width="315" Height="315"  Margin="10"
                        MouseLeftButtonDown="ForeCanvas_MouseLeftButtonDown"
                        MouseRightButtonDown="ForeCanvas_MouseRightButtonDown"/>
            </StackPanel>
        </Grid>

其中兩個Image用於顯示時鐘和地雷數圖例,其後兩個TextBlock分別用於顯示讀秒數和剩餘地雷數。
兩個重疊的Canvas,分別顯示底層圖塊和前景圖塊。底層圖塊遊戲開始後是固定的,其中會顯示隨機分佈的地雷及其周邊地雷數值,而前景圖塊能夠經過鼠標點擊進行移除或標記爲問號或小紅旗。

動畫

2、載入圖片資源this

一、添加一個靜態圖片幫助類ImageHelper,方便之後的圖片切片操做。spa

public static class ImageHelper
{
    /// <summary>
    /// BitmapSource圖片源剪切操做函數
    /// </summary>
    /// <param name="bmpSource">等待剪切的源</param>
    /// <param name="cut">剪切矩形</param>
    /// <returns>已剪切的圖片源</returns>
    public static BitmapSource CutImage(BitmapSource bmpSource, Int32Rect cut)
    {
        return new CroppedBitmap(bmpSource, cut);
    }

    /// <summary>
    /// 將BitmapImage轉換爲BitmapSource
    /// </summary>
    /// <param name="bitmapImage">待轉換的BitmapImage</param>
    /// <returns>BitmapSource</returns>
    public static BitmapSource BitmapImageToBitmapSource(BitmapImage bitmapImage)
    {
        ImageSource imageSource = bitmapImage;
        Bitmap bitmap = ImageSourceToBitmap(imageSource);
        BitmapSource bitmapSource = BitmapToBitmapImage(bitmap);
     bitmap.Dispose();
return bitmapSource; } /// <summary> /// 將ImageSource轉爲Bitmap /// </summary> /// <param name="imageSource"></param> /// <returns></returns> public static System.Drawing.Bitmap ImageSourceToBitmap(ImageSource imageSource) { BitmapSource m = (BitmapSource)imageSource; Bitmap bmp = new Bitmap(m.PixelWidth, m.PixelHeight, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); System.Drawing.Imaging.BitmapData data = bmp.LockBits(new Rectangle(System.Drawing.Point.Empty, bmp.Size), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); m.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride); bmp.UnlockBits(data); return bmp; } /// <summary> /// 將Bitmap轉爲BitmapImage /// </summary> /// <param name="bitmap"></param> /// <returns></returns> public static BitmapImage BitmapToBitmapImage(Bitmap bitmap) { using (MemoryStream stream = new MemoryStream()) { bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png); stream.Position = 0; BitmapImage result = new BitmapImage(); result.BeginInit(); result.CacheOption = BitmapCacheOption.OnLoad; result.StreamSource = stream; result.EndInit(); result.Freeze(); return result; } } }

二、添加幾個字段變量pwa

主程序MainWindow.xaml.cs中添加:code

private BitmapSource _bmpSpace, _bmpMine, _bmpNum1_8, _bmpForeground, _bmpMineOrTimer, _bmpBomb;
private System.Drawing.Size _cellSize = new System.Drawing.Size(35, 35);

三、獲取圖片資源orm

private void GetImageResource()
{
    BitmapImage bmpSpace = (BitmapImage)TryFindResource("ImgSpace");
    BitmapImage bmpMine = (BitmapImage)TryFindResource("ImgMine");
    BitmapImage bmpNum1_8 = (BitmapImage)TryFindResource("ImgNum1_8");
    BitmapImage bmpForeground = (BitmapImage)TryFindResource("ImgForeground");
    BitmapImage bmpMineOrTimer = (BitmapImage)TryFindResource("ImgMineOrTimer");
    BitmapImage bmpBomb = (BitmapImage)TryFindResource("ImgBomb");

    _bmpSpace = ImageHelper.BitmapImageToBitmapSource(bmpSpace);
    _bmpMine = ImageHelper.BitmapImageToBitmapSource(bmpMine);
    _bmpNum1_8 = ImageHelper.BitmapImageToBitmapSource(bmpNum1_8);
    _bmpForeground = ImageHelper.BitmapImageToBitmapSource(bmpForeground);
    _bmpMineOrTimer = ImageHelper.BitmapImageToBitmapSource(bmpMineOrTimer);
    _bmpBomb = ImageHelper.BitmapImageToBitmapSource(bmpBomb);
}

 

四、將界面中用到的時鐘和地圖數這兩張圖片設置到位。

private void SetTimerAndMineImage()
{
    ImgClock.Source = ImageHelper.CutImage(_bmpMineOrTimer, new Int32Rect(0, 0, _cellSize.Width, _cellSize.Height));
    ImgMineNum.Source = ImageHelper.CutImage(_bmpMineOrTimer, new Int32Rect(1 * _cellSize.Width, 0, _cellSize.Width, _cellSize.Height));
}

 

五、將上述兩個方法添加到構造方法中。

運行程序,如圖1。

 

3、設置狀態枚舉

添加一個MyEnum.cs類文件,讓主cs文件看起來簡練一點。內容以下:

// 遊戲級別
public enum Level
{
    SIMPLE,
    NORMAL,
    HARD
};

// 前景狀態
public enum ForeState
{
    NONE,           //
    NORMAL,         // 正常覆蓋
    FLAG,           // 紅旗
    QUESTION        // 問號
};

// 底層狀態
public enum BackState
{
    MINE = -1,      // 地雷
    BLANK = 0,      //
};

// 遊戲狀態
public enum GameState
{
    NONE,           // 未開始
    STOP,           // 已中止
    START,          // 已開始
    PAUSE           // 已暫停
};

 

4、添加GameLevel類,定義遊戲級別相關參數

public class GameLevel
{
    public int _mineNum { get; set; }      // 地雷數
    public int _rowGrid { get; set; }      // 橫格子數
    public int _colGrid { get; set; }      // 縱格子數

    public GameLevel(int currLevel, int mineNum, int rowGrid, int colGrid)
    {
        _mineNum = mineNum;
        _rowGrid = rowGrid;
        _colGrid = colGrid;
    }
}

 

5、定義主遊戲數據變量

private int[,] _backData = null;            // 底部背景數據(雷-1,0爲空白,數值(1-8)周圍雷數)
private int[,] _foreData = null;            // 前景數據(1:正常蓋住;2:紅旗;3:問號)
private Image[,] _backImage = null;         // 底圖圖片數組
private Image[,] _foreImage = null;         // 上方圖片數組

private GameState _gameState = GameState.NONE;

private Random rnd = new Random();          // 隨機數

private GameLevel _gameLevel;
private Level _level = Level.Simple;


 

另外還用到幾個計時器,看後面的註釋

// 計時
private System.Diagnostics.Stopwatch _stopWatchGame = new System.Diagnostics.Stopwatch(); // 遊戲過程讀秒
private DispatcherTimer _timerSetTimeText = new DispatcherTimer();      // 更新讀秒文本框
private DispatcherTimer _timerWinAnim = new DispatcherTimer();     // 用於勝利時動畫
private DispatcherTimer _timerBomb = new DispatcherTimer();            // 用於踩雷動畫

將計時器初始化放在構造方法中

// 計時器
_timerSetTimeText.Interval = new TimeSpan(0, 0, 1);
_timerSetTimeText.Tick += _timerSetTimeText_Tick;

// 動畫計時器
_timerWinAnim.Interval = new TimeSpan(0, 0, 0, 0, 300);
_timerWinAnim.Tick += _timerWinAnim_Tick;

// 用戶踩雷後的動畫
_timerBomb.Interval = new TimeSpan(0, 0, 0, 0, 200);
_timerBomb.Tick += _timerBomb_Tick;

 

6、初始化遊戲狀態

private void InitialGameState()
{
    _timerSetTimeText.Stop();
    _timerWinAnim.Stop();

    _stopWatchGame.Reset();
    _stopWatchGame.Stop();
    _gameState = GameState.NONE;

    MenuGamePauseOrContinue.Header = "暫停(_P)";

}

private void _timerSetTimeText_Tick(object sender, EventArgs e)
{
    textBlockTime.Text = ((int)_stopWatchGame.Elapsed.TotalSeconds).ToString();
}

private void _timerWinAnim_Tick(object sender, EventArgs e)
{
}

private void _timerBomb_Tick(object sender, EventArgs e)
{
}

後面動畫計時事件方法之後再加入,這裏先空着。

 

7、初始化遊戲主數據

private void InitGameData(Level level)
{
    switch (level)
    {
        case  Level.SIMPLE:
            _gameLevel = new GameLevel(9, 9, 10);
            break;

        case Level.NORMAL:
            _gameLevel = new GameLevel(16, 16, 40);
            break;

        case Level.HARD:
            _gameLevel = new GameLevel(30, 16, 99);
            break;
    }

    // 設置窗口大小
    this.Width = _cellSize.Width * _gameLevel._rowGrid + 40;
    this.Height = _cellSize.Height * _gameLevel._colGrid + 20 + 100;

    // 獲取屏幕大小
    double screenWidth = SystemParameters.WorkArea.Width;
    double screenHeight = SystemParameters.WorkArea.Height;

    // 使窗口居中
    this.Left = (screenWidth - this.Width) / 2;
    this.Top = (screenHeight - this.Height) / 2;

    // 設置繪圖控件大小
    BackCanvas.Width = _gameLevel._colGrid * _cellSize.Width;
    BackCanvas.Height = _gameLevel._rowGrid * _cellSize.Height;
    ForeCanvas.Width = BackCanvas.Width;
    ForeCanvas.Height = BackCanvas.Height;

    // 初始化前景和背景數據
    _backData = new int[_gameLevel._colGrid, _gameLevel._rowGrid];
    _foreData = new int[_gameLevel._colGrid, _gameLevel._rowGrid];

    for (int y = 0; y<_gameLevel._colGrid; y++)
    {
        for (int x=0; x<_gameLevel._rowGrid; x++)
        {
            _backData[y, x] = (int)BackState.BLANK;
            _foreData[y, x] = (int)ForeState.NORMAL;
        }
    }

    // 初始化前景和背景圖片數組
    _backImage = new Image[_gameLevel._colGrid, _gameLevel._rowGrid];
    _foreImage = new Image[_gameLevel._colGrid, _gameLevel._rowGrid];

    // 清理繪製區
    BackCanvas.Children.Clear();
    ForeCanvas.Children.Clear();
}
相關文章
相關標籤/搜索