提问人:Amparo Walter 提问时间:11/17/2023 更新时间:11/17/2023 访问量:51
使用 MVVM 在 WPF 中阻止并行任务 UI 输入
Parallel task blocking UI input in WPF using MVVM
问:
我遇到了一个(可能不是这样)奇怪的问题,我正在更新物理(更新位置),但似乎无法再移动我的相机(或任何其他用户输入)。
当按下“播放”按钮时,主循环被调用,该按钮只是启动物理引擎。但是,当位置数据更新(通过引擎)时,我想更新场景中的所有模型。我读过一些关于 Dispacher 的文章,它可以更新模型,并且没有我尝试访问其他线程正在使用的错误。SetViewport()
模型会以应有的方式更新,并且 Dispatcher 运行良好(或不工作,idk)
问题是我似乎无法再注册用户输入了。
奇怪的是,在调试模式下(我使用的是Visual Studio),它有时会注册。我不知道为什么。
- 用于处理键盘输入的代码在 MazeView 中处理。
- 用于处理“play”命令的代码是 MazeModelView 中的处理程序(使用 ICommand) 该函数启动一个新任务,该任务启动无限游戏循环。(我打算有一个暂停按钮)
- 引擎逻辑在“逻辑”项目中处理。
这是我的代码:
MazeView.cs
public MazeView()
{
InitializeComponent();
//logic = MazeViewModel.Logic;
Viewport.PreviewKeyDown += new KeyEventHandler(OnKeyDownHandler);
Viewport.Focusable = true;
Viewport.Focus();
//subscribe to event when maze is updated.
//Update gets called when maze is generated or changed.
MazeViewModel.MazeUpdated += SetViewport;
if(MazeViewModel.CurrentInstance == null)
{
throw new NullReferenceException("Could not find viewmodel!");
}
model = MazeViewModel.CurrentInstance;
}
/*
* .... rest of code
*/
private void OnKeyDownHandler(object sender, KeyEventArgs e)
{
Key k = e.Key;
Vector3D cameraMoveDirection = new(0, 0, 0);
double yaw = 0, pich = 0, roll = 0;
double zoom = 0;
switch (k)
{
case Key.Z:
cameraMoveDirection.Z = 1;
break;
case Key.S:
cameraMoveDirection.Z = -1;
break;
case Key.Q:
cameraMoveDirection.X = -1;
break;
case Key.D:
cameraMoveDirection.X = 1;
break;
case Key.Space:
cameraMoveDirection.Y = 1;
break;
case Key.F:
cameraMoveDirection.Y = -1;
break;
case Key.A:
yaw = 1;
break;
case Key.E:
yaw = -1;
break;
case Key.W:
zoom = 1;
break;
case Key.X:
zoom = -1;
break;
}
model.MoveCamera(cameraMoveDirection);
model.RotateCamera(yaw, pich, roll);
model.ZoomCamera(zoom);
e.Handled = true;
}
private void SetViewport()
{
Viewport.Children.Clear();
//lighting
DirectionalLight light = new()
{
Color = Colors.White,
Direction = new Vector3D(-1, -1, -1)
};
Model3DGroup lightGroup = new();
lightGroup.Children.Add(light);
AmbientLight ambLight = new()
{
Color = new Color()
{
R = 88,
G = 88,
B = 88
}
};
lightGroup.Children.Add(ambLight);
Viewport.Children.Add(new ModelVisual3D() { Content = lightGroup });
Model3DGroup modelGroup = new();
//models
foreach (Model3D model in MazeViewModel.Models)
{
modelGroup.Children.Add(model);
}
Viewport.Children.Add(new ModelVisual3D { Content = modelGroup });
}
}
MazeViewModel.cs
/*
* .... rest of code
*/
public void OnMazeUpdate()
{
SetModels();
MazeUpdated?.Invoke(this, new EventArgs());
}
private IPhysicsEngine engine;
private CancellationTokenSource gameLoopCancellationTokenSource;
public void PlayGame()
{
if(Logic == null)
{
throw new NullReferenceException("Can't start game because logic is null!");
}
engine = Logic.GetEngine(ref _maze, ref _ball);
gameLoopCancellationTokenSource = new();
engine.Start();
while(!gameLoopCancellationTokenSource.Token.IsCancellationRequested)
{
engine.Update();
Application.Current.Dispatcher.Invoke(() =>
{
OnMazeUpdate();
});
}
}
PlayCommand.cs
internal class PlayGameCommand : CommandBase
{
private readonly MazeViewModel model;
public PlayGameCommand(MazeViewModel model)
{
this.model = model;
MazeViewModel.MazeUpdated += OnCanExecuteChanged;
}
private void OnCanExecuteChanged(object? sender, EventArgs e)
{
OnCanExecuteChanged();
}
public override void Execute(object? parameter)
{
Task.Run(() => model.PlayGame());
}
public override bool CanExecute(object? parameter)
{
return model.Maze != null;
}
}
答: 暂无答案
评论
engine.update()
Dispatcher.Invoke()