C# WPF 实现五子棋&黑白棋小游戏

By jerryxjr1220 at 2023-09-27 • 0人收藏 • 303人看过

用WPF画的界面,双击实现落子(黑白翻转)的效果。

加了一个小白点,瞬间有反光的立体效果了

screenshots.gif

<hc:GlowWindow x:Class="WPFBlackWhite.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:model="clr-namespace:WPFBlackWhite.Models"
        xmlns:vm="clr-namespace:WPFBlackWhite.ViewModels"
        xmlns:view="clr-namespace:WPFBlackWhite.Views"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        xmlns:local="clr-namespace:WPFBlackWhite"
        xmlns:hc="https://handyorg.github.io/handycontrol"
        d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
        mc:Ignorable="d"
        Title="Black &amp; White Chess" Height="450" Width="400" ResizeMode="NoResize"
        FontFamily="JetBrains Mono" 
        FontSize="{StaticResource TextFontSize}">
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
        <vm:BorderValueConverter Offset="2" x:Key="BorderValueConverter"/>
    </Window.Resources>
    <hc:SimplePanel>
        <ScrollViewer x:Name="scrollViewer" VerticalScrollBarVisibility="Auto">
            <Grid Background="DarkGreen" Margin="0, 5">
                <ItemsControl ItemsSource="{Binding Chesses}" MouseDoubleClick="Control_OnMouseDoubleClick">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <Canvas />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemContainerStyle>
                        <Style TargetType="ContentPresenter">
                            <Setter Property="Canvas.Left" Value="{Binding XPosActual}" />
                            <Setter Property="Canvas.Top" Value="{Binding YPosActual}" />                            
                        </Style>
                    </ItemsControl.ItemContainerStyle>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Border BorderBrush="DarkGray" BorderThickness="1">
                                <Border x:Name="ChessCanvas" Width="{Binding BlockSize, Converter={StaticResource BorderValueConverter}}" Height="{Binding BlockSize, Converter={StaticResource BorderValueConverter}}"/>
                            </Border>
                            <DataTemplate.Triggers>
                                <DataTrigger Binding="{Binding ChessType}" Value="0">
                                    <Setter TargetName="ChessCanvas" Property="Background" Value="DarkGreen"></Setter>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding ChessType}" Value="1">
                                    <Setter TargetName="ChessCanvas" Property="Background" Value="Black"></Setter>
                                    <Setter TargetName="ChessCanvas" Property="CornerRadius" Value="20"></Setter>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding ChessType}" Value="2">
                                    <Setter TargetName="ChessCanvas" Property="Background" Value="White"></Setter>
                                    <Setter TargetName="ChessCanvas" Property="CornerRadius" Value="20"></Setter>
                                </DataTrigger>
                                
                            </DataTemplate.Triggers>
                        </DataTemplate>
                        
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Grid>
        </ScrollViewer>
    </hc:SimplePanel>
</hc:GlowWindow>

GameModel:

using System;
using System.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace WPFBlackWhite.Models;

public partial class GameModel: ObservableObject
{
    public enum ChessTypes
    {
        NullChess,
        BlackChess,
        WhiteChess,
    }
    
    [ObservableProperty] private ChessTypes _chessType;
    
    [NotifyPropertyChangedFor("XPosActual")]
    [ObservableProperty] 
    private int _xPos;
    [NotifyPropertyChangedFor("YPosActual")]
    [ObservableProperty] 
    private int _yPos;
    
    [ObservableProperty] private int _xPosActual;
    [ObservableProperty] private int _yPosActual;

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        base.OnPropertyChanged(e);
        switch(e.PropertyName)
        {
            case "XPos":
                XPosActual = XPos * BlockSize;
                break;
            case "YPos":
                YPosActual = YPos * BlockSize;
                break;
        }
    }

    [ObservableProperty] private int _blockSize;

    public GameModel()
    {
    }

    public GameModel(string _type, int _x, int _y, int _blockSize=40)
    {
        switch (_type)
        {
            case "N":
                ChessType = ChessTypes.NullChess;
                break;
            case "B":
                ChessType = ChessTypes.BlackChess;
                break;
            case "W":
                ChessType = ChessTypes.WhiteChess;
                break;
            default:
                throw new ArgumentException();
        }

        BlockSize = _blockSize;
        XPos = _x;
        YPos = _y;
        XPosActual = XPos * BlockSize;
        YPosActual = YPos * BlockSize;
    }
    
    public void ChangeToBlack()
    {
        ChessType = ChessTypes.BlackChess;
    }
    
    public void ChangeToWhite()
    {
        ChessType = ChessTypes.WhiteChess;
    }
}

ViewModel:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Threading;
using System.Windows.Data;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using WPFBlackWhite.Models;
namespace WPFBlackWhite.ViewModels;

public partial class MainViewModel: ObservableObject
{
    [ObservableProperty] private BindingList<GameModel> _chesses;
    [ObservableProperty] private int _blockSize;
    public MainViewModel()
    {
        BlockSize = 40;
        Chesses = new BindingList<GameModel>();
        for (int x = 0; x < 10; x++)
        {
            for (int y = 0; y < 10; y++)
            {
                var chess = new GameModel("N", x, y, BlockSize);
                if (x == 4 && y == 4 || x == 5 && y == 5)
                    chess = new GameModel("B", x, y, BlockSize);
                else if (x == 4 && y == 5 || x == 5 && y == 4)
                    chess = new GameModel("W", x, y, BlockSize);
                Chesses.Add(chess);
            }
        }
        
        //发送消息
        //WeakReferenceMessenger.Default.Send(new CustomizedMessage(new MessageModel()));
    }

    public void FlipChess(int x, int y)
    {
        foreach (var chess in Chesses)
        {
            if (chess.XPos == x && chess.YPos == y)
            {
                switch (chess.ChessType)
                {
                    case GameModel.ChessTypes.BlackChess:
                        chess.ChangeToWhite();
                        break;
                    default:
                        chess.ChangeToBlack();
                        break;
                }
                break;
            }
        }
    }
    
}

public class BorderValueConverter : IValueConverter
{
    public int Offset { get; set; } = 2;
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return int.Parse(value.ToString()) - Offset;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

//定义消息
// public class CustomizedMessage : ValueChangedMessage<MessageModel> // PropertyChangedMessage // RequestMessage ( Async + ..)
// {
//     public CustomizedMessage(MessageModel value) : base(value)
//     {
//     }
// }

主界面:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using HandyControl.Controls;
using WPFBlackWhite.Models;
using WPFBlackWhite.ViewModels;
using MessageBox = HandyControl.Controls.MessageBox;

namespace WPFBlackWhite.Views;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : GlowWindow
{
    private MainViewModel mainViewModel;
    public MainWindow()
    {
        InitializeComponent();
        

        #region 注册并接受消息

        //WeakReferenceMessenger.Default.Register<CustomizedMessage>(this, (o, m) => { MessageBox.Show("Received!"); });

        #endregion
        
        
        mainViewModel = new MainViewModel();
        this.DataContext = mainViewModel;
        
    }


    private void Control_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        var pos = e.GetPosition(sender as UIElement);
        //MessageBox.Show(pos.X.ToString() + " " + pos.Y.ToString());
        var x = (int)(pos.X / mainViewModel.BlockSize);
        var y = (int)(pos.Y / mainViewModel.BlockSize);
        mainViewModel.FlipChess(x, y);
    }
}

MVVM架构思维导图:

Mind.pdf


3 个回复 | 最后更新于 2023-09-27
2023-09-27   #1

这个可以作为棋类游戏的通用底层模型,只要改一下行数、列数、调整一下颜色或者棋子的种类,就可以适合其他棋类游戏了,拓展性不错。


其实应该说不光是棋类游戏,2D的平面走格子的游戏应该都可以兼容,比如贪吃蛇、吃豆人等等,哪怕超级玛丽这种改造一下也可以实现

2023-09-27   #2

上面示例中的黑白棋(五子棋)我是直接在WPF中用Border画的,但如果要用图像渲染的话,可以参考ChatGPT提供的方法:

// 加载图片到内存中
Bitmap bmp = new Bitmap("image_path");

// 定义切割后的小图片大小
int width = 100;
int height = 100;

// 遍历图片并切割为小图片
for (int x = 0; x < bmp.Width; x += width)
{
    for (int y = 0; y < bmp.Height; y += height)
    {
        Rectangle cropArea = new Rectangle(x, y, width, height);
        Bitmap cropBmp = bmp.Clone(cropArea, bmp.PixelFormat);

        // 将小图片显示在WPF中的Image控件中
        Image img = new Image();
        img.Source = BitmapToImageSource(cropBmp);
        // 在Canvas中添加小图片
        Canvas.SetLeft(img, x);
        Canvas.SetTop(img, y);
        canvas.Children.Add(img);
    }
}

// 将Bitmap对象转换为WPF中的ImageSource对象
private ImageSource BitmapToImageSource(Bitmap bmp)
{
    MemoryStream ms = new MemoryStream();
    bmp.Save(ms, ImageFormat.Bmp);
    BitmapImage bitmapImage = new BitmapImage();
    bitmapImage.BeginInit();
    bitmapImage.StreamSource = new MemoryStream(ms.ToArray());
    bitmapImage.EndInit();
    return bitmapImage;
}


2023-09-27   #3

黑白棋逻辑

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Windows.Data;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using WPFBlackWhite.Models;
namespace WPFBlackWhite.ViewModels;

public partial class MainViewModel: ObservableObject
{
    [ObservableProperty] private BindingList<GameModel> _chesses;
    [ObservableProperty] private int _blockSize;
    [ObservableProperty] private int _countBlack = 2;
    [ObservableProperty] private int _countWhite = 2;

    public enum Who
    {
        User,
        System
    }
    public MainViewModel()
    {
        BlockSize = 40;
        Chesses = new BindingList<GameModel>();
        for (int x = 0; x < 10; x++)
        {
            for (int y = 0; y < 10; y++)
            {
                var chess = new GameModel("N", x, y, BlockSize);
                if (x == 4 && y == 4 || x == 5 && y == 5)
                    chess = new GameModel("B", x, y, BlockSize);
                else if (x == 4 && y == 5 || x == 5 && y == 4)
                    chess = new GameModel("W", x, y, BlockSize);
                Chesses.Add(chess);
            }
        }
        
        //发送消息
        //WeakReferenceMessenger.Default.Send(new CustomizedMessage(new MessageModel()));
    }

    public (int, int) CountChesses(BindingList<GameModel> Chesses)
    {
        var tCountBlack = 0;
        var tCountWhite = 0;
        foreach (var chess in Chesses)
        {
            if (chess.ChessType == GameModel.ChessTypes.BlackChess) tCountBlack += 1;
            if (chess.ChessType == GameModel.ChessTypes.WhiteChess) tCountWhite += 1;
        }

        return (tCountBlack, tCountWhite);
    }

    

    public void FlipChess(BindingList<GameModel> Chesses, int x, int y)
    {
        /*foreach (var chess in Chesses)
        {
            if (chess.XPos == x && chess.YPos == y)
            {
                switch (chess.ChessType)
                {
                    case GameModel.ChessTypes.BlackChess:
                        chess.ChangeToWhite();
                        break;
                    default:
                        chess.ChangeToBlack();
                        break;
                }
                break;
            }
        }*/
        foreach (var chess in Chesses)
        {
            if (chess.XPos == x && chess.YPos == y)
            {
                chess.ChessType = chess.ChessType == GameModel.ChessTypes.NullChess ? GameModel.ChessTypes.BlackChess : (chess.ChessType == GameModel.ChessTypes.BlackChess ? GameModel.ChessTypes.WhiteChess :  GameModel.ChessTypes.BlackChess);
                # region <- x
                // <- x
                var a = x;
                var b = y;
                bool flag = false;
                while (a > 1)
                {
                    a--;
                    var t = Chesses.ToList().First(c => c.XPos == a && c.YPos == y);
                    if (t.ChessType == chess.ChessType)
                    {
                        flag = true;
                    }
                    if (t.ChessType == GameModel.ChessTypes.NullChess)
                    {
                        break;
                    }
                    if (flag)
                    {
                        DoFlipXY(a+1, x-1, y, y);
                        break;
                    }
                }
                #endregion

                #region x ->
                // x->
                a = x;
                b = y;
                flag = false;
                while (a < 9)
                {
                    a++;
                    var t = Chesses.ToList().First(c => c.XPos == a && c.YPos == y);
                    if (t.ChessType == chess.ChessType)
                    {
                        flag = true;
                    }
                    if (t.ChessType == GameModel.ChessTypes.NullChess)
                    {
                        break;
                    }
                    if (flag)
                    {
                        DoFlipXY(x+1, a-1, y, y);
                        break;
                    }
                }
                #endregion

                #region <- y

                // <-y
                a = x;
                b = y;
                flag = false;
                while (b > 1)
                {
                    b--;
                    var t = Chesses.ToList().First(c => c.YPos == b && c.XPos == x);
                    if (t.ChessType == chess.ChessType)
                    {
                        flag = true;
                    }
                    if (t.ChessType == GameModel.ChessTypes.NullChess)
                    {
                        break;
                    }
                    if (flag)
                    {
                        DoFlipXY(x, x, b+1, y-1);
                        break;
                    }
                }

                #endregion

                #region y ->
                // y->
                a = x;
                b = y;
                flag = false;
                while (b < 9)
                {
                    b++;
                    var t = Chesses.ToList().First(c => c.YPos == b && c.XPos == x);
                    if (t.ChessType == chess.ChessType)
                    {
                        flag = true;
                    }
                    if (t.ChessType == GameModel.ChessTypes.NullChess)
                    {
                        break;
                    }
                    if (flag)
                    {
                        DoFlipXY(x,x, y + 1, b - 1);
                        break;
                    }
                }

                #endregion

                #region <- x <- y

                // <- x <- y
                a = x;
                b = y;
                flag = false;
                while (a > 1 && b > 1)
                {
                    a--;
                    b--;
                    var t = Chesses.ToList().First(c => c.XPos == a && c.YPos == b);
                    if (t.ChessType == chess.ChessType)
                    {
                        flag = true;
                    }
                    if (t.ChessType == GameModel.ChessTypes.NullChess)
                    {
                        break;
                    }
                    if (flag)
                    {
                        DoFlipXY(a+1, x-1, b+1, y-1);
                        break;
                    }
                }

                #endregion

                #region x -> y ->

                // x -> y ->
                a = x;
                b = y;
                flag = false;
                while (a <9 && b < 9)
                {
                    a++;
                    b++;
                    var t = Chesses.ToList().First(c => c.XPos == a && c.YPos == b);
                    if (t.ChessType == chess.ChessType)
                    {
                        flag = true;
                    }
                    if (t.ChessType == GameModel.ChessTypes.NullChess)
                    {
                        break;
                    }
                    if (flag)
                    {
                        DoFlipXY(x+1, a-1, y+1, b-1);
                        break;
                    }
                }

                #endregion
                
                #region <- x y ->

                // <- x y ->
                a = x;
                b = y;
                flag = false;
                while (a > 1 && b < 9)
                {
                    a--;
                    b++;
                    var t = Chesses.ToList().First(c => c.XPos == a && c.YPos == b);
                    if (t.ChessType == chess.ChessType)
                    {
                        flag = true;
                    }
                    if (t.ChessType == GameModel.ChessTypes.NullChess)
                    {
                        break;
                    }
                    if (flag)
                    {
                        DoFlipXY(a+1, x-1, y+1, b-1);
                        break;
                    }
                }

                #endregion
                
                #region x -> y <-

                // x -> y <-
                a = x;
                b = y;
                flag = false;
                while (a < 9 && b > 1)
                {
                    a++;
                    b--;
                    var t = Chesses.ToList().First(c => c.XPos == a && c.YPos == b);
                    if (t.ChessType == chess.ChessType)
                    {
                        flag = true;
                    }

                    if (t.ChessType == GameModel.ChessTypes.NullChess)
                    {
                        break;
                    }
                    if (flag)
                    {
                        DoFlipXY(x+1, a-1, b+1, y-1);
                        break;
                    }
                }

                #endregion
            }
            
            
        }
        
    }

    public void DoFlipXY(int xStart, int xEnd, int yStart, int yEnd)
    {
        for (int i = xStart; i <= xEnd; i++)
        {
            for (int j = yStart; j <= yEnd; j++)
            {
                foreach (var a in Chesses.ToList().Where(c=> c.XPos == i && c.YPos == j))
                {
                    if(a.ChessType==GameModel.ChessTypes.NullChess) continue;
                    a.ChessType = a.ChessType == GameModel.ChessTypes.BlackChess ? GameModel.ChessTypes.WhiteChess : GameModel.ChessTypes.BlackChess;
                }
            }
        }
    }
    
       

    public bool IsGameOver
    {
        get
        {
            foreach (var chess in Chesses)
            {
                if (chess.ChessType == GameModel.ChessTypes.NullChess)
                    return false;
            }
            return true;
        }
    }
}

public class BorderValueConverter : IValueConverter
{
    public int Offset { get; set; } = 2;
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return int.Parse(value.ToString()) - Offset;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

//定义消息
// public class CustomizedMessage : ValueChangedMessage<MessageModel> // PropertyChangedMessage // RequestMessage ( Async + ..)
// {
//     public CustomizedMessage(MessageModel value) : base(value)
//     {
//     }
// }


登录后方可回帖

登 录
信息栏
 私人小站

本站域名

ChengXu.XYZ

投诉联系:  popdes@126.com



快速上位机开发学习,本站主要记录了学习过程中遇到的问题和解决办法及上位机代码分享

这里主要专注于学习交流和经验分享.
纯私人站,当笔记本用的,学到哪写到哪.
如果侵权,联系 Popdes@126.com

友情链接
Aardio官方
Aardio资源网


才仁机械


网站地图SiteMap

Loading...