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

时间:2016-06-08 17:56:09

标签: wpf

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

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

ScreenShot of test map

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

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

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

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>

这是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

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

1 个答案:

答案 0 :(得分:0)

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

  
      
  1. 如果帧速率不一致,您是否可以确定发生此不一致的具体时间点?即当一次运行大量动画时,或者使用特定画笔时?

         
        
          
    • 您是否使用过任何分析工具来查找代码的“热门”部分?这有助于识别出现这些不一致的特定情况。
    •     
      
  2.   
  3. 相关动画的DesiredFrameRate和可能同时运行的其他动画是什么?默认情况下,这是60fps,这可能会非常繁重,特别是当屏幕上有很多对象或轮询的过渡时。

         
        
          
    • 首先尝试在搭便车动画上设置Timeline.DesiredFrameRate=60
    •     
    • 然后尝试将其他时间轴的DesiredFrameRate设置为30,以便观察这是否会提高地图的效果 - 这会影响导致性能影响的其他元素的数量或质量。
    •     
      
  4.   
  5. 您是否研究过用于地图和视觉对象的画笔的RenderQuality?

         
        
    • 尝试降低画笔的RenderQuality:RenderOptions.SetBitmapScalingMode(myBrush, BitmapScalingMode.LowQuality);
    •   
    • 确保缓存可重复使用的画笔:RenderOptions.SetCachingHint(myBrush, CachingHint.Cache);
    •   
  6.   

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

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

  • 您的硬件设置包含哪些内容,以及其他(更强大的)计算机上是否存在此问题。
  • 如果您要将渲染推迟到GPU,那么确实是GPU。
  • 您要定位的体系结构和平台。即这是一个Xamarin项目,Win32WPF还是别的什么?
  • 只有在启用调试符号的情况下才会遇到此问题。

我或许可以给你更多建议。