2D WPF游戏渲染困难 [英] 2D WPF Game Rendering Difficulties

查看:74
本文介绍了2D WPF游戏渲染困难的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

嗨.我正在创建WPF游戏.它具有两个主要部分:玩家在其中移动的游戏世界,以及包含其他用户控件和布局控件的用户控件.  在游戏中的某些时候,玩家可以触发UserControl 出现并开始从地图屏幕到用户控制屏幕的不透明度动画.  游戏的这一部分工作正常.我的问题是渲染地图的运动.  在此游戏中,地图移动时,玩家位于屏幕中央.  将地图绘制到自定义画布上(以下称为"Surface"),然后在其中添加绘图效果.  我正在使用绘图上下文DrawImage方法来绘制图块.我目前正在绘制7 x 7的瓷砖网格.

Hi.  I am creating a WPF game.  It has two main parts: the game world where the player moves about, and a user control that contains other user controls and layout controls.  At certain points in the game, the player can trigger the UserControl to appear and begins an opacity animation from the map screen to the user control screen.  That part of the game works fine.  My problem is rendering motion of the map.  In this game, the player is centered in the screen while the map moves.  The map is drawn to a custom canvas (called Surface below) and I add drawing visuals to it.   I am using the drawing context DrawImage method to draw tiles.  I am currently drawing a 7 by 7 grid of tiles.

我尝试了几种方法来修复地图在移动过程中的周期性变化.我已经使用CompositionTarget.Rendering事件来更新地图.   rendering events参数具有一个我用来尝试使运动平滑的renderTime方法. 我列出了与renderTime值之间的差异,以确定渲染发生的速率.模式类似于:16,17,17,16,17,0,16,17,16,33,16,17,16 .....毫秒.我怀疑这个动作很蠢 在差值是平均值的两倍或零的点处发生.我做了几次尝试来调整慢帧速率或快帧速率,方法是随着帧速率的变化来调整地图移动量.  但是我没有尝试过 有所不同.我也尝试过使用DispatcherTimer的tick事件来运行绘图代码,但我认为它有所帮助,但我不确定.  计时器间隔设置为10毫秒,这似乎会增加CPU使用率,但可能 效果最好,但仍然很经常出现这种混蛋.

I have tried several methods to fix the periodic jerk in the map as it moves.  I have used the CompositionTarget.Rendering event to update the map.  The rendering events argument has a renderingTime method that I used to try to smooth out the motion.  I made a list of the differences from the values of renderingTime to determine the rate the rendering occurs. There is a pattern that is something like: 16,17,17,16,17,0, 16,17,16, 33, 16, 17, 16..... milliseconds.  I suspect that the jerk in motion is occurring at points where the difference is double the average or zero.  I made several attempts to correct for the slow or fast frame rate by adjusting how much the map moves with the changing frame rate.  But nothing I have tried seems to make a difference.  I have also tried using a DispatcherTimer's tick event to run the drawing code, and I think that it helped a little but I am not certain.  The timer interval is set to 10 milliseconds, and this seemed to increase CPU usage but may have given the best result, but still the jerk appeared too often. 

我创建了一个地图编辑器来构建地图并保存要在游戏中加载的地图对象.我曾尝试使用bmp或png格式的50x50到150x150像素的不同大小的图块.我还尝试了不同的网格尺寸和非常大的网格 映射只是为了查看性能.  如果您对如何平滑动画有任何想法,请告诉我.大约一周以来,我一直在尝试解决此问题的方法.我知道它的存在,但我没有意识到有多糟 看起来,我真的很想使用WPF,因为布局控件对游戏的其他部分至关重要.

I created a map editor to build the maps and save the map objects to be loaded in the game.  I have tried different sized tiles that range from 50x50 to 150x150 pixels in bmp or png format.  I have also tried different grid sizes and very large maps just to see the performance.   If you have any ideas about how to smooth out the animation, please let me know.  I have been trying to figure a way around this problem for about a week now.  I knew it existed, but I didn't realize how bad it would look and I really would like to use WPF as the layout controls are critical to the other parts of the game.

这里是一些代码.  渲染事件标记为大约一半:

Here is a bit of the code.  The rendering event is marked about half way down:

'<Events> Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded Dim aFormatter As New BinaryFormatter Dim aFileStream As FileStream Dim result As New Nullable(Of Boolean) Dim fileName As String = String.Empty Dim OpenDialog As New Microsoft.Win32.OpenFileDialog() result = OpenDialog.ShowDialog() fileName = OpenDialog.FileName 'If result = true then ok was selected... If result Then aFileStream = New FileStream(fileName, FileMode.Open, FileAccess.Read) GameMap = CType(aFormatter.Deserialize(aFileStream), Map) aFileStream.Close() End If 'Initialization of objects and variables... TileSize = New Rect(0, 0, 200, 175) MapSize = 100 GameMap.PopulateInGrid(TileSize, GameMap.BoundsInGrid, GameMap.BoundsLayer) GameMap.PopulateInGrid(TileSize, GameMap.DetailInGrid, GameMap.DetailLayer) GameScreen = New Rect GameScreen.Height = 5 * TileSize.Height GameScreen.Width = 7 * TileSize.Width GameScreen.X = 0 * TileSize.Width GameScreen.Y = 0 * TileSize.Height GameLoaded = True GamePlayer = New Player GamePlayer.Speed = 0.1 GamePlayer.StaticSpeed = GamePlayer.Speed GamePlayer.X = GameScreen.X + GameScreen.Width / 2 GamePlayer.Y = GameScreen.Y + GameScreen.Height / 2 'Starting grid position of GameScreen... StartingPosition = New Point StartingPosition.X = 0 StartingPosition.Y = 0 GameScreen.X = StartingPosition.X * TileSize.Width GameScreen.Y = StartingPosition.Y * TileSize.Height UpKeyPressed = False DownKeyPressed = False LeftKeyPressed = False RightKeyPressed = False SelectionKeyPressed = False AllKeysUp = True MapShiftX = 0 MapShiftY = 0 KeyMap = New KeyMap KeyMap.UpKey = Key.Up KeyMap.DownKey = Key.Down KeyMap.LeftKey = Key.Left KeyMap.RightKey = Key.Right KeyMap.SelectionKey = Key.Space 'Rendering loop for timing purposes or animations... AddHandler CompositionTarget.Rendering, AddressOf GameModeMainLoop RenderingLoopFlag = True 'Timer initialization... GameTimer.Interval = New TimeSpan(10) GameTimer.IsEnabled = True 'enable timer 'GameTimer.Start() 'Draw map... For i = 0 To 7 For j = 0 To 7 If GameMap.GridLayer1(i, j).FileName IsNot Nothing Then Dim aVisual As New DrawingVisual() DrawTile(aVisual, GameMap.GridLayer1(i, j).TileRect, New BitmapImage(GameMap.GridLayer1(i, j).FileName)) Surface.AddVisual(aVisual) End If Next Next For i = 0 To 7 For j = 0 To 7 If GameMap.GridLayer2(i, j).FileName IsNot Nothing Then Dim aVisual As New DrawingVisual() DrawTile(aVisual, GameMap.GridLayer2(i, j).TileRect, New BitmapImage(GameMap.GridLayer2(i, j).FileName)) Surface.AddVisual(aVisual) End If Next Next 'Detail InGrid Layer... For i = 0 To 7 For j = 0 To 7 If GameMap.DetailInGrid(i, j).Count > 0 Then For k = GameMap.DetailInGrid(i, j).Count - 1 To 0 Step -1 Dim aVisual As New DrawingVisual() DrawTile(aVisual, GameMap.DetailInGrid(i, j)(k).TileRect, New BitmapImage(GameMap.DetailInGrid(i, j)(k).FileName)) Surface.AddVisual(aVisual) Next End If Next Next For i = 0 To GameMap.BoundsLayer.Count - 1 Dim aVisual As New DrawingVisual() DrawLine(aVisual, GameMap.BoundsLayer(i).LineStartPt, GameMap.BoundsLayer(i).LineEndPt) Surface.AddVisual(aVisual) Next End Sub 'HERE IS THE RENDERING EVENT... Public Sub GameModeMainLoop(sender As Object, e As RenderingEventArgs) lblCount.Foreground = Brushes.Blue lblCount.Content = GamePlayer.X & ", " & GamePlayer.Y 'Surface.visuals.Count.ToString 'Map Drawing... Dim StartX As Integer = 0 Dim StartY As Integer = 0 Dim EndX As Integer = 0 Dim EndY As Integer = 0 StartX = GameScreen.X / TileSize.Width StartY = GameScreen.Y / TileSize.Height EndX = StartX + GameScreen.Width / TileSize.Width EndY = StartY + GameScreen.Height / TileSize.Height

如果GamePlayer.Speed< GamePlayer.StaticSpeed然后 GamePlayer.Speed + = 0.25 万一 '将GamePlayer的最后位置设置为... GamePlayer.LastX = GamePlayer.X GamePlayer.LastY = GamePlayer.Y '密钥检查... 如果Keyboard.IsKeyDown(KeyMap.LeftKey)然后 MapShiftY + = GamePlayer.Speed Surface.Margin =新厚度(Surface.Margin.Left,Surface.Margin.Top + GamePlayer.Speed,Surface.Margin.Right,Surface.Margin.Bottom-GamePlayer.Speed) 万一 如果Keyboard.IsKeyDown(KeyMap.RightKey)然后 MapShiftY + = -GamePlayer.Speed Surface.Margin =新厚度(Surface.Margin.Left,Surface.Margin.Top-GamePlayer.Speed,Surface.Margin.Right,Surface.Margin.Bottom + GamePlayer.Speed) 万一 如果Keyboard.IsKeyDown(KeyMap.UpKey)然后 MapShiftX + = GamePlayer.Speed Surface.Margin =新的厚度(Surface.Margin.Left + GamePlayer.Speed,Surface.Margin.Top,Surface.Margin.Right-GamePlayer.Speed,Surface.Margin.Bottom) 万一 如果Keyboard.IsKeyDown(KeyMap.DownKey)然后 MapShiftX + = -GamePlayer.Speed Surface.Margin =新的厚度(Surface.Margin.Left-GamePlayer.Speed,Surface.Margin.Top,Surface.Margin.Right + GamePlayer.Speed,Surface.Margin.Bottom) 万一 GamePlayer.X = GameScreen.X + GameScreen.Width/2-MapShiftX GamePlayer.Y = GameScreen.Y + GameScreen.Height/2-MapShiftY 结束子 私有Sub btnDesignWindow_Click(发送者作为对象,e作为RoutedEventArgs)处理btnDesignWindow.Click 将wndDesign设为新的DesignWindow wndDesign.Show() 结束子 '</事件> '<程序> 私人子DrawTile(ByVal视觉为DrawingVisual,ByVal myRect为Rect,ByVal myBitmap为BitmapImage) 将上下文用作DrawingContext = visual.RenderOpen() myRect.X + = MapShiftX myRect.Y + = MapShiftY context.DrawImage(myBitmap,myRect) 最终使用 结束子 专用子DrawLine(ByVal视觉作为DrawingVisual,ByVal point1作为点,ByVal point2作为点) point1.X + = MapShiftX point1.Y + = MapShiftY point2.X + = MapShiftX point2.Y + = MapShiftY 将上下文用作DrawingContext = visual.RenderOpen() 昏暗的绘图笔作为新笔(Brushes.Black,5) 暗淡DrawingBrush作为画笔= Brushes.Black context.DrawLine(DrawingPen,point1,point2) 最终使用 结束子 Private Sub GameTimer_Tick(发送者为对象,e作为EventArgs)处理GameTimer.Tick 要进行测试,请调用GameTimer.Start() 结束子 '</过程>

If GamePlayer.Speed < GamePlayer.StaticSpeed Then GamePlayer.Speed += 0.25 End If 'Set GamePlayer last position... GamePlayer.LastX = GamePlayer.X GamePlayer.LastY = GamePlayer.Y 'Key Checking... If Keyboard.IsKeyDown(KeyMap.LeftKey) Then MapShiftY += GamePlayer.Speed Surface.Margin = New Thickness(Surface.Margin.Left, Surface.Margin.Top + GamePlayer.Speed, Surface.Margin.Right, Surface.Margin.Bottom - GamePlayer.Speed) End If If Keyboard.IsKeyDown(KeyMap.RightKey) Then MapShiftY += -GamePlayer.Speed Surface.Margin = New Thickness(Surface.Margin.Left, Surface.Margin.Top - GamePlayer.Speed, Surface.Margin.Right, Surface.Margin.Bottom + GamePlayer.Speed) End If If Keyboard.IsKeyDown(KeyMap.UpKey) Then MapShiftX += GamePlayer.Speed Surface.Margin = New Thickness(Surface.Margin.Left + GamePlayer.Speed, Surface.Margin.Top, Surface.Margin.Right - GamePlayer.Speed, Surface.Margin.Bottom) End If If Keyboard.IsKeyDown(KeyMap.DownKey) Then MapShiftX += -GamePlayer.Speed Surface.Margin = New Thickness(Surface.Margin.Left - GamePlayer.Speed, Surface.Margin.Top, Surface.Margin.Right + GamePlayer.Speed, Surface.Margin.Bottom) End If GamePlayer.X = GameScreen.X + GameScreen.Width / 2 - MapShiftX GamePlayer.Y = GameScreen.Y + GameScreen.Height / 2 - MapShiftY End Sub Private Sub btnDesignWindow_Click(sender As Object, e As RoutedEventArgs) Handles btnDesignWindow.Click Dim wndDesign As New DesignWindow wndDesign.Show() End Sub '</Events> '<Procedures> Private Sub DrawTile(ByVal visual As DrawingVisual, ByVal myRect As Rect, ByVal myBitmap As BitmapImage) Using context As DrawingContext = visual.RenderOpen() myRect.X += MapShiftX myRect.Y += MapShiftY context.DrawImage(myBitmap, myRect) End Using End Sub Private Sub DrawLine(ByVal visual As DrawingVisual, ByVal point1 As Point, ByVal point2 As Point) point1.X += MapShiftX point1.Y += MapShiftY point2.X += MapShiftX point2.Y += MapShiftY Using context As DrawingContext = visual.RenderOpen() Dim DrawingPen As New Pen(Brushes.Black, 5) Dim DrawingBrush As Brush = Brushes.Black context.DrawLine(DrawingPen, point1, point2) End Using End Sub Private Sub GameTimer_Tick(sender As Object, e As EventArgs) Handles GameTimer.Tick 'For testing call GameTimer.Start() End Sub '</Procedures>

           

           

推荐答案

嗨t0mr,

感谢您的信息.我发现了一篇名为的文章 StackOverflow上的Wpf动画最佳做法.我 t包含许多 使用 动画制作的体验. 我希望这篇文章对您有用.

Thank you for your post. I found an article named Wpf Animation Best Practices on StackOverflow. It contains many experiences of using animation. I hope the article is useful for you.

最好的问候,
王丽

Best Regards,
Li Wang


这篇关于2D WPF游戏渲染困难的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆