C# WPF 实现躲避球小游戏
By
jerryxjr1220
at 2023-09-28 • 0人收藏 • 656人看过
其实用的游戏底层与前面的五子棋是同一个,渲染不同而已,要是贴上不同图像可以模拟更多平面游戏,坦克大战,超级玛丽这种都不是问题。

竖过来,就变成《是男人就挑战100层》

//Model
using System;
using System.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
namespace WPFFlight.Models;
public partial class GameObject: ObservableObject
{
public enum ObjectTypes
{
Space,
Rock,
Bullet,
Enemy,
Player,
}
[ObservableProperty] private ObjectTypes _blockType;
[ObservableProperty] private int _xPos;
[ObservableProperty] private int _yPos;
[ObservableProperty] private int _xPosActual;
[ObservableProperty] private int _yPosActual;
[ObservableProperty] private int _blockSize;
[ObservableProperty] private int _speed;
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
switch(e.PropertyName)
{
case "XPos":
XPosActual = XPos * BlockSize;
break;
case "YPos":
YPosActual = YPos * BlockSize;
break;
}
}
public GameObject(string _type, int _x, int _y, int _speed=1, int _blockSize=20)
{
switch (_type)
{
case "S":
BlockType = ObjectTypes.Space;
break;
case "R":
BlockType = ObjectTypes.Rock;
break;
case "B":
BlockType = ObjectTypes.Bullet;
break;
case "E":
BlockType = ObjectTypes.Enemy;
break;
case "P":
BlockType = ObjectTypes.Player;
break;
default:
throw new ArgumentException();
}
Speed = _speed;
BlockSize = _blockSize;
XPos = _x;
YPos = _y;
}
public GameObject()
{
}
}//ViewModel
using System;
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 WPFFlight.Models;
namespace WPFFlight.ViewModels;
public partial class MainViewModel: ObservableObject
{
[ObservableProperty] private BindingList<GameObject> _uiLayout;
[ObservableProperty] private int _blockSize;
[ObservableProperty] private int _speed;
[ObservableProperty] private int _level;
public string[,] GameLayout { get; set; }
public MainViewModel()
{
GameLayout = new string[10, 20]
{
{"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"},
{"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"},
{"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "R", "R", "S"},
{"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"},
{"S", "P", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "R", "S", "S", "S"},
{"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "R", "S", "S", "S"},
{"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"},
{"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "R", "S"},
{"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "R", "S"},
{"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"},
};
BlockSize = 20;
Speed = 1;
Level = 1;
UiLayout = new BindingList<GameObject>();
RenderLayout();
//发送消息
//WeakReferenceMessenger.Default.Send(new CustomizedMessage(new MessageModel()));
}
public void RenderLayout()
{
UiLayout.Clear();
for (int x = 0; x < 10; x++)
{
for (int y = 0; y < 20; y++)
{
var current = GameLayout[x, y];
UiLayout.Add(new GameObject(current, y, x, Speed, BlockSize));
}
}
}
public void Move()
{
int px = 0;
int py = 0;
for (int j = 0; j < 10; j++)
{
for (int i = 1; i < 20; i++)
{
if (GameLayout[j, i] == "P")
{
px = j;
py = i;
GameLayout[j, i - 1] = "S";
continue;
}
GameLayout[j, i-1] = GameLayout[j, i];
}
var rand = new Random();
var r = rand.Next(0, 21-Level);
GameLayout[j, 19] = r < 19-Level ? "S" : "R";
}
GameLayout[px, py] = "P";
RenderLayout();
}
public void MoveDown()
{
int px = 0;
for (int j = 0; j < 10; j++)
{
if (GameLayout[j, 1] == "P")
{
px = j;
GameLayout[j, 1] = "S";
}
}
if (px >= 9)
GameLayout[px, 1] = "P";
else
GameLayout[px+1, 1] = "P";
RenderLayout();
}
public void MoveUp()
{
int px = 0;
for (int j = 0; j < 10; j++)
{
if (GameLayout[j, 1] == "P")
{
px = j;
GameLayout[j, 1] = "S";
}
}
if (px <= 0)
GameLayout[px, 1] = "P";
else
GameLayout[px-1, 1] = "P";
RenderLayout();
}
}
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)
// {
// }
// }<hc:GlowWindow x:Class="WPFFlight.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:WPFFlight.Models"
xmlns:vm="clr-namespace:WPFFlight.ViewModels"
xmlns:view="clr-namespace:WPFFlight.Views"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:WPFFlight"
xmlns:hc="https://handyorg.github.io/handycontrol"
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
mc:Ignorable="d"
Title="Flight Fight" Height="240" Width="400" ResizeMode="NoResize"
FontFamily="JetBrains Mono"
FontSize="{StaticResource TextFontSize}">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<!--<vm:BorderValueConverter Offset="0" x:Key="BorderValueConverter" />-->
</Window.Resources>
<hc:SimplePanel>
<ScrollViewer x:Name="scrollViewer" VerticalScrollBarVisibility="Auto">
<Grid Background="Black" Margin="0, 5">
<ItemsControl ItemsSource="{Binding UiLayout}" MouseWheel="MainWindow_OnMouseWheel" >
<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="0">
<Border x:Name="UiCanvas"
Width="{Binding BlockSize}"
Height="{Binding BlockSize}" />
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding BlockType}" Value="0">
<Setter TargetName="UiCanvas" Property="Background" Value="Black"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding BlockType}" Value="1">
<Setter TargetName="UiCanvas" Property="Background" Value="Chocolate"></Setter>
<Setter TargetName="UiCanvas" Property="CornerRadius" Value="8"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding BlockType}" Value="2">
<Setter TargetName="UiCanvas" Property="Background" Value="Red"></Setter>
<Setter TargetName="UiCanvas" Property="CornerRadius" Value="10"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding BlockType}" Value="3">
<Setter TargetName="UiCanvas" Property="Background" Value="Cyan"></Setter>
<Setter TargetName="UiCanvas" Property="CornerRadius" Value="9"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding BlockType}" Value="4">
<Setter TargetName="UiCanvas" Property="Background" Value="White"></Setter>
<Setter TargetName="UiCanvas" Property="CornerRadius" Value="9"></Setter>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</ScrollViewer>
</hc:SimplePanel>
</hc:GlowWindow>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 WPFFlight.Models;
using WPFFlight.ViewModels;
using MessageBox = HandyControl.Controls.MessageBox;
namespace WPFFlight.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 MainWindow_OnMouseWheel(object sender, MouseWheelEventArgs e)
{
if(e.Delta>0) mainViewModel.MoveUp();
else mainViewModel.MoveDown();
mainViewModel.Move();
}
}
3 个回复 | 最后更新于 2023-10-07
登录后方可回帖
优化后版本《是男人就上100层》
using System; using System.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel; namespace WPFFly.Models; public partial class GameObject: ObservableObject { public enum ObjectTypes { Space, Rock, Bullet, Enemy, Player, } [ObservableProperty] private ObjectTypes _blockType; [ObservableProperty] private int _xPos; [ObservableProperty] private int _yPos; [ObservableProperty] private int _xPosActual; [ObservableProperty] private int _yPosActual; [ObservableProperty] private int _blockSize; [ObservableProperty] private int _speed; protected override void OnPropertyChanged(PropertyChangedEventArgs e) { base.OnPropertyChanged(e); switch(e.PropertyName) { case "XPos": XPosActual = XPos * BlockSize; break; case "YPos": YPosActual = YPos * BlockSize; break; } } public GameObject(string _type, int _x, int _y, int _speed=1, int _blockSize=20) { switch (_type) { case "S": BlockType = ObjectTypes.Space; break; case "R": BlockType = ObjectTypes.Rock; break; case "B": BlockType = ObjectTypes.Bullet; break; case "E": BlockType = ObjectTypes.Enemy; break; case "P": BlockType = ObjectTypes.Player; break; default: throw new ArgumentException(); } Speed = _speed; BlockSize = _blockSize; XPos = _x; YPos = _y; } public GameObject() { } }using System; using System.ComponentModel; using System.Globalization; using System.Threading.Tasks; using System.Timers; using System.Windows; using System.Windows.Data; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging.Messages; using Microsoft.Xaml.Behaviors.Core; using WPFFly.Models; namespace WPFFly.ViewModels; public partial class MainViewModel: ObservableObject { [ObservableProperty] private BindingList<GameObject> _uiLayout; [ObservableProperty] private int _blockSize; [ObservableProperty] private int _speed; [ObservableProperty] private int _level; [ObservableProperty] private int _steps; public string[,] GameLayout { get; set; } public MainViewModel() { UiLayout = new BindingList<GameObject>(); GameLayout = new string[10, 20] { {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "R", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, {"S", "P", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "R", "S", "S", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "R", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, }; GameInit(); //发送消息 //WeakReferenceMessenger.Default.Send(new CustomizedMessage(new MessageModel())); } public void GameInit() { GameLayout = new string[10, 20] { {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "R", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, {"S", "P", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "R", "S", "S", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "R", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, {"S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S", "S"}, }; Steps = 0; BlockSize = 20; Speed = 1; Level = 1; UiLayout = new BindingList<GameObject>(); RenderLayout(); } public void RenderLayout() { UiLayout.Clear(); for (int x = 0; x < 10; x++) { for (int y = 0; y < 20; y++) { var current = GameLayout[x, y]; UiLayout.Add(new GameObject(current, x, 19-y, Speed, BlockSize)); } } } public void Move() { int px = 0; int py = 0; for (int j = 0; j < 10; j++) { for (int i = 1; i < 20; i++) { // 判断是否游戏结束 if (GameLayout[j, i] == "R" && GameLayout[j, i - 1] == "P") { MessageBox.Show($"Game Over! \r\n Level: {Level}", "Game Over", MessageBoxButton.OK, MessageBoxImage.Stop); GameInit(); return; } // 记录玩家坐标 if (GameLayout[j, i] == "P") { px = j; py = i; GameLayout[j, i - 1] = "S"; continue; } GameLayout[j, i-1] = GameLayout[j, i]; } // 随机生成新一排的地图, 难度由level计算得到。 level越高,生成的石头越多。 var rand = new Random(); var r = rand.Next(0, 51-Level); GameLayout[j, 19] = r < 49-Level ? "S" : "R"; } //把玩家坐标写回到界面中 GameLayout[px, py] = "P"; RenderLayout(); //步数增加 Steps += 1; // level随步数增加而增加 Level = Steps / 10 + 1; } //滚轮移动后向左右移动 public void MoveDown() { //Move(); int px = 0; for (int j = 0; j < 10; j++) { if (GameLayout[j, 1] == "P") { px = j; GameLayout[j, 1] = "S"; } } if (px >= 9) GameLayout[px, 1] = "P"; else GameLayout[px+1, 1] = "P"; RenderLayout(); } //滚轮移动后向左右移动 public void MoveUp() { //Move(); int px = 0; for (int j = 0; j < 10; j++) { if (GameLayout[j, 1] == "P") { px = j; GameLayout[j, 1] = "S"; } } if (px <= 0) GameLayout[px, 1] = "P"; else GameLayout[px-1, 1] = "P"; RenderLayout(); } } 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 System.Windows.Threading; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging.Messages; using HandyControl.Controls; using WPFFly.Models; using WPFFly.ViewModels; using MessageBox = HandyControl.Controls.MessageBox; namespace WPFFly.Views; /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : GlowWindow { private DispatcherTimer timer; private MainViewModel mainViewModel; public MainWindow() { InitializeComponent(); #region 注册并接受消息 //WeakReferenceMessenger.Default.Register<CustomizedMessage>(this, (o, m) => { MessageBox.Show("Received!"); }); #endregion mainViewModel = new MainViewModel(); this.DataContext = mainViewModel; timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromMilliseconds(500); timer.Tick += (sender, args) => { mainViewModel.Move(); }; timer.Start(); } private void MainWindow_OnMouseWheel(object sender, MouseWheelEventArgs e) { if(e.Delta>0) mainViewModel.MoveUp(); else mainViewModel.MoveDown(); //mainViewModel.Move(); } }<hc:GlowWindow x:Class="WPFFly.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:WPFFly.Models" xmlns:vm="clr-namespace:WPFFly.ViewModels" xmlns:view="clr-namespace:WPFFly.Views" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:local="clr-namespace:WPFFly" xmlns:hc="https://handyorg.github.io/handycontrol" d:DataContext="{d:DesignInstance Type=vm:MainViewModel}" mc:Ignorable="d" Title="{Binding Level, StringFormat=Level: {0}}" Height="440" Width="200" ResizeMode="NoResize" FontFamily="JetBrains Mono" FontSize="{StaticResource TextFontSize}"> <Window.Resources> <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> <!--<vm:BorderValueConverter Offset="0" x:Key="BorderValueConverter" />--> </Window.Resources> <hc:SimplePanel> <ScrollViewer x:Name="scrollViewer" VerticalScrollBarVisibility="Auto"> <Grid Background="Black" Margin="0, 5"> <ItemsControl ItemsSource="{Binding UiLayout}" MouseWheel="MainWindow_OnMouseWheel" > <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="0"> <Border x:Name="UiCanvas" Width="{Binding BlockSize}" Height="{Binding BlockSize}" /> </Border> <DataTemplate.Triggers> <DataTrigger Binding="{Binding BlockType}" Value="0"> <Setter TargetName="UiCanvas" Property="Background" Value="Black"></Setter> </DataTrigger> <DataTrigger Binding="{Binding BlockType}" Value="1"> <Setter TargetName="UiCanvas" Property="Background" Value="Chocolate"></Setter> <Setter TargetName="UiCanvas" Property="CornerRadius" Value="8"></Setter> </DataTrigger> <DataTrigger Binding="{Binding BlockType}" Value="2"> <Setter TargetName="UiCanvas" Property="Background" Value="Red"></Setter> <Setter TargetName="UiCanvas" Property="CornerRadius" Value="10"></Setter> </DataTrigger> <DataTrigger Binding="{Binding BlockType}" Value="3"> <Setter TargetName="UiCanvas" Property="Background" Value="Cyan"></Setter> <Setter TargetName="UiCanvas" Property="CornerRadius" Value="9"></Setter> </DataTrigger> <DataTrigger Binding="{Binding BlockType}" Value="4"> <Setter TargetName="UiCanvas" Property="Background" Value="White"></Setter> <Setter TargetName="UiCanvas" Property="CornerRadius" Value="9"></Setter> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> </ScrollViewer> </hc:SimplePanel> </hc:GlowWindow>