WPF游戏:如何改进游戏地图的动画?

时间:2022-06-19 03:49:26

I am making a game in WPF. I chose WPF because I wanted to use the layout containers and userControls in my game. The game has two parts: 1.) The map that a character walks around on, and 2.) a UserControl composed of many containers, controls, and other userControls that acts as another screen. I need to be able to transition from the map to the UserControl screen.

我在WPF做一个游戏。我之所以选择WPF,是因为我想在游戏中使用布局容器和userControls。游戏有两个部分:1。)角色四处走动的地图,以及2.)由许多容器,控件和其他用作另一个屏幕的用户控件组成的UserControl。我需要能够从地图转换到UserControl屏幕。

The UserControl part works great, but I am having difficulty animating the map which is an 8x5 grid of tiles of roughly 100x100 pixels. The player is centered in the screen as the map moves:

UserControl部分工作得很好,但我很难动画地图,这是一个大约100x100像素的8x5网格。当地图移动时,播放器在屏幕中居中:

ScreenShot of test map

ScreenShot的测试图

I have tried several approaches to render the map. I have used a timer, the CompositionTarget.Rendering event, and now I have overriden the OnRender method to draw to an Image with DrawingVisuals. The best I can do so far is move the map 1 pixel at a time to smooth out the animation, but the motion is too slow. In all cases, the frame rate will periodically be cut in half. The RenderTime method gives a pattern for update times that looks something like: 16, 17, 16, 17, 16, 17, 33, 16, 17, 16, 0, 16, 17, ...(milliseconds). I could compensate by changing how much I move the map if I knew when frames would be dropped ahead of time.

我尝试了几种渲染地图的方法。我使用了一个计时器,CompositionTarget.Rendering事件,现在我已经覆盖了OnRender方法以绘制到带有DrawingVisuals的图像。到目前为止我能做的最好的事情是一次将地图移动1个像素以平滑动画,但动作太慢了。在所有情况下,帧速率将定期减半。 RenderTime方法给出了更新时间的模式,类似于:16,17,16,17,16,17,33,16,17,16,0,16,17 ......(毫秒)。如果我知道什么时候提前丢帧,我可以通过改变移动地图的数量来弥补。

Is there a way to do this or is there another method that might get the job done? I am open to using any approach that allows 1.) and 2.) to happen, but the userControl must be a WPF Control(as I have spent a lot of time on that part.) Thanks!

有没有办法做到这一点,还是有另一种方法可以完成工作?我愿意使用允许1.)和2.)发生的任何方法,但userControl必须是WPF控件(因为我在这部分上花了很多时间。)谢谢!

UPDATED : The following WPF project is my latest attempt at getting smooth rending of the 8x5 map that I mentioned.

更新:以下WPF项目是我最近尝试顺利完成我提到的8x5地图。

The MainWindow.xaml file:

MainWindow.xaml文件:

<Window x:Class="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:local="clr-namespace:RenderingBitmapTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525" WindowState="Minimized">
    <Grid>
        <Image Name="Background"/>
        <Button Name="MoveDownButton" Height="35" Width="100" Content="Press Me!" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
        <Ellipse HorizontalAlignment="Center" VerticalAlignment="Center" Stroke="Black" StrokeThickness="3" Fill="Blue" Height="50" Width="50"/>
    </Grid>
</Window>

Here is the MainWindow.xaml.vb file:

这是MainWindow.xaml.vb文件:

Imports System.Security.Permissions
Imports System.Windows.Threading

Class MainWindow
    Private buffer As RenderTargetBitmap
    Private drawingVisual As New DrawingVisual

    ' FPS counter '
    Dim tSec As Integer = TimeOfDay.Second
    Dim tTicks As Integer = 0
    Dim MaxTicks As Integer = 0

    ' Motion Testing '
    Dim moveX As Double = 0
    Dim moveY As Double = 0

    Dim IsRunning As Boolean = True

    Dim grass As New BitmapImage(New Uri("pack://application:,,,/Resources/GrassTile2.png")) 'Image is 100 x 100 '
    Dim tree As New BitmapImage(New Uri("pack://application:,,,/Resources/Tree2.png"))

    Protected Overrides Sub OnRender(ByVal drawingContext As DrawingContext)
        MyBase.OnRender(drawingContext)
        buffer = New RenderTargetBitmap(CInt(Me.ActualWidth), CInt(Me.ActualHeight), 96, 96, PixelFormats.Pbgra32)
        Background.Source = buffer
    End Sub

    Private Sub Drawing()
        If buffer Is Nothing Then
            Return
        End If

        buffer.Clear()

        Using dc As DrawingContext = drawingVisual.RenderOpen()

            For X = 0 To 7
                For Y = 0 To 4
                    dc.DrawImage(grass, New Rect(moveX + Me.Width / 7 * X, moveY + Me.Height / 5 * Y, Me.Width / 6.9, Me.Height / 4.9))
                    dc.DrawText(New FormattedText(MaxTicks, Globalization.CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight, New Typeface("Verdana"), 32, Brushes.Black), New Point(0, 0))
                Next
            Next
            'Trees added. '
            dc.DrawImage(tree, New Rect(moveX + 700, moveY + 400, Me.Width / 8, Me.Height / 3))
            dc.DrawImage(tree, New Rect(moveX + 1000, moveY + 300, Me.Width / 8, Me.Height / 3))
        End Using

        buffer.Render(drawingVisual)
    End Sub

    Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
        Me.WindowState = WindowState.Maximized

    End Sub

    Private Sub Background_Loaded(sender As Object, e As RoutedEventArgs) Handles Background.Loaded
        While IsRunning
            TickCounter()

            If Keyboard.IsKeyDown(Key.Up) Then
                moveY += 5 '1'
            ElseIf Keyboard.IsKeyDown(Key.Down) Then
                moveY -= 5 '1'
            ElseIf Keyboard.IsKeyDown(Key.Left) Then
                moveX += 5 '1'
            ElseIf Keyboard.IsKeyDown(Key.Right) Then
                moveX -= 5 '1'
            End If

            DoEvents()
            Drawing()

        End While

    End Sub

    Private Sub TickCounter()
        If tSec = TimeOfDay.Second And IsRunning = True Then
            tTicks = tTicks + 1
        Else
            MaxTicks = tTicks
            tTicks = 0
            tSec = TimeOfDay.Second
        End If

    End Sub

    '<Do Events>'
    <SecurityPermissionAttribute(SecurityAction.Demand, Flags:=SecurityPermissionFlag.UnmanagedCode)>
    Public Sub DoEvents()
        Dim frame As New DispatcherFrame()
        Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, New DispatcherOperationCallback(AddressOf ExitFrame), frame)
        Dispatcher.PushFrame(frame)
    End Sub

    Public Function ExitFrame(ByVal f As Object) As Object
        CType(f, DispatcherFrame).Continue = False

        Return Nothing
    End Function
    '</Do Events>'

    Private Sub MainWindow_Closed(sender As Object, e As EventArgs) Handles Me.Closed
        IsRunning = False
        Me.Close()
    End Sub

    Private Sub MoveDownButton_Click(sender As Object, e As RoutedEventArgs) Handles MoveDownButton.Click
        moveY -= 5
    End Sub

End Class

If anyone can get a working smooth animated game map on keypress with another method, please let me know. I am currently working on a 4 year old toshiba laptop, but I have ran this code on newer computers and the same problems are present. I would like this game to run on any computer capable of running WPF applications.

如果有人可以用另一种方法在按键上获得工作流畅的动画游戏地图,请告诉我。我目前正在研发一台4岁的东芝笔记本电脑,但我已经在新电脑上运行了这个代码并且存在同样的问题。我希望这个游戏能够在任何能够运行WPF应用程序的计算机上运行。

1 个解决方案

#1


0  

I'm just spitballing here as there's a plethora of potential issues and trying to solve a problem sight-unseen is guess work at best of the time, but having said that I have a few areas that you may want to dig into for further thought:

我只是在这里喋喋不休,因为有太多的潜在问题并试图解决一个问题 - 看不见的是在最好的时候猜测工作,但是说我有一些你可能想深入研究的领域以供进一步思考:

  1. If framerates are not consistent, can you identify specific points in time when this inconsistency occurs? I.e. When a large number of animations are running at once, or when a specific brush is used?

    如果帧速率不一致,您是否可以识别出现此不一致的特定时间点?即当一次运行大量动画时,或者使用特定画笔时?

    • Have you used any profiling tools in order to find 'hot' parts of your code? This may help in identifying specific scenarios where these inconsistencies occur.
    • 您是否使用过任何分析工具来查找代码的“热门”部分?这可能有助于识别出现这些不一致的特定情况。
  2. What is the DesiredFrameRate of the animation in question, and other animations which may be running at the same time? By default this is 60fps, and this can get quite taxing, particularly when where are a lot of objects on screen or transitions being polled.

    什么是相关动画的DesiredFrameRate,以及可能同时运行的其他动画?默认情况下,这是60fps,这可能会非常繁重,特别是当屏幕上有很多对象或轮询转换时。

    • First Try setting Timeline.DesiredFrameRate=60 on your hitching animation.
    • 首先尝试在搭便车动画上设置Timeline.DesiredFrameRate = 60。
    • Secondly try setting the DesiredFrameRate of other Timelines to say 30 in order to observe if this increases performance for your map- this would elude to the quantity or quality of other elements causing the performance impact.
    • 然后尝试将其他时间轴的DesiredFrameRate设置为30,以便观察这是否会提高地图的性能 - 这将导致影响性能的其他元素的数量或质量。
  3. Have you looked into the RenderQuality of the brushes you're using for the map and your visual objects?

    您是否查看了用于地图和视觉对象的画笔的RenderQuality?

    • Try reducing the RenderQuality of your brushes: RenderOptions.SetBitmapScalingMode(myBrush, BitmapScalingMode.LowQuality);
    • 尝试减少画笔的RenderQuality:RenderOptions.SetBitmapScalingMode(myBrush,BitmapScalingMode.LowQuality);
    • Ensure your re-usable brushes are being cached: RenderOptions.SetCachingHint(myBrush, CachingHint.Cache);
    • 确保缓存可重用的画笔:RenderOptions.SetCachingHint(myBrush,CachingHint.Cache);

There are a lot of potential performance bottlenecks and without seeing code its difficult to provide you with more than these loose suggestions. As a quick first-stop for baseline optimisations, there is a fairly comprehensive post by Kevin Pfister which may provide some further useful insight in lieu of any code or specific identified problem areas.

有很多潜在的性能瓶颈,没有看到代码,很难为您提供超过这些松散的建议。作为基线优化的快速第一站,Kevin Pfister有一篇相当全面的文章,它可以提供一些进一步有用的见解,代替任何代码或特定的已识别问题区域。

If there were more information provided, such as:

如果提供了更多信息,例如:

  • What your hardware setup consists of, and if this issue persists on other (more powerful) machines.
  • 您的硬件设置包含哪些内容,以及其他(更强大的)计算机上是否存在此问题。
  • If you're deferring rendering to GPU, and indeed what the GPU is.
  • 如果你要将渲染推迟到GPU,那么GPU确实如此。
  • What architecture(s) and platforms you are targetting. I.e. is this a Xamarin project, Win32WPF or something else?
  • 您要定位的体系结构和平台。即这是一个Xamarin项目,Win32WPF还是其他什么?
  • Wheter or not you are experiencing this only with debug symbols enabled.
  • 是否启用了调试符号,您是否遇到此问题。

I might have been able to give you some more suggestions.

我或许可以给你一些建议。

#1


0  

I'm just spitballing here as there's a plethora of potential issues and trying to solve a problem sight-unseen is guess work at best of the time, but having said that I have a few areas that you may want to dig into for further thought:

我只是在这里喋喋不休,因为有太多的潜在问题并试图解决一个问题 - 看不见的是在最好的时候猜测工作,但是说我有一些你可能想深入研究的领域以供进一步思考:

  1. If framerates are not consistent, can you identify specific points in time when this inconsistency occurs? I.e. When a large number of animations are running at once, or when a specific brush is used?

    如果帧速率不一致,您是否可以识别出现此不一致的特定时间点?即当一次运行大量动画时,或者使用特定画笔时?

    • Have you used any profiling tools in order to find 'hot' parts of your code? This may help in identifying specific scenarios where these inconsistencies occur.
    • 您是否使用过任何分析工具来查找代码的“热门”部分?这可能有助于识别出现这些不一致的特定情况。
  2. What is the DesiredFrameRate of the animation in question, and other animations which may be running at the same time? By default this is 60fps, and this can get quite taxing, particularly when where are a lot of objects on screen or transitions being polled.

    什么是相关动画的DesiredFrameRate,以及可能同时运行的其他动画?默认情况下,这是60fps,这可能会非常繁重,特别是当屏幕上有很多对象或轮询转换时。

    • First Try setting Timeline.DesiredFrameRate=60 on your hitching animation.
    • 首先尝试在搭便车动画上设置Timeline.DesiredFrameRate = 60。
    • Secondly try setting the DesiredFrameRate of other Timelines to say 30 in order to observe if this increases performance for your map- this would elude to the quantity or quality of other elements causing the performance impact.
    • 然后尝试将其他时间轴的DesiredFrameRate设置为30,以便观察这是否会提高地图的性能 - 这将导致影响性能的其他元素的数量或质量。
  3. Have you looked into the RenderQuality of the brushes you're using for the map and your visual objects?

    您是否查看了用于地图和视觉对象的画笔的RenderQuality?

    • Try reducing the RenderQuality of your brushes: RenderOptions.SetBitmapScalingMode(myBrush, BitmapScalingMode.LowQuality);
    • 尝试减少画笔的RenderQuality:RenderOptions.SetBitmapScalingMode(myBrush,BitmapScalingMode.LowQuality);
    • Ensure your re-usable brushes are being cached: RenderOptions.SetCachingHint(myBrush, CachingHint.Cache);
    • 确保缓存可重用的画笔:RenderOptions.SetCachingHint(myBrush,CachingHint.Cache);

There are a lot of potential performance bottlenecks and without seeing code its difficult to provide you with more than these loose suggestions. As a quick first-stop for baseline optimisations, there is a fairly comprehensive post by Kevin Pfister which may provide some further useful insight in lieu of any code or specific identified problem areas.

有很多潜在的性能瓶颈,没有看到代码,很难为您提供超过这些松散的建议。作为基线优化的快速第一站,Kevin Pfister有一篇相当全面的文章,它可以提供一些进一步有用的见解,代替任何代码或特定的已识别问题区域。

If there were more information provided, such as:

如果提供了更多信息,例如:

  • What your hardware setup consists of, and if this issue persists on other (more powerful) machines.
  • 您的硬件设置包含哪些内容,以及其他(更强大的)计算机上是否存在此问题。
  • If you're deferring rendering to GPU, and indeed what the GPU is.
  • 如果你要将渲染推迟到GPU,那么GPU确实如此。
  • What architecture(s) and platforms you are targetting. I.e. is this a Xamarin project, Win32WPF or something else?
  • 您要定位的体系结构和平台。即这是一个Xamarin项目,Win32WPF还是其他什么?
  • Wheter or not you are experiencing this only with debug symbols enabled.
  • 是否启用了调试符号,您是否遇到此问题。

I might have been able to give you some more suggestions.

我或许可以给你一些建议。