我在WPF中制作游戏。我之所以选择WPF,是因为我想在游戏中使用布局容器和userControls。游戏有两个部分:1。)角色四处走动的地图,以及2.)由许多容器,控件和其他用作另一个屏幕的userControl组成的UserControl。我需要能够从地图转换到UserControl屏幕。
UserControl部分工作得很好,但我很难动画地图,这是一个大约100x100像素的8x5网格。当地图移动时,玩家在屏幕中居中:
我尝试了几种渲染地图的方法。我使用了一个计时器,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应用程序的计算机上运行。
答案 0 :(得分:0)
我只是在这里喋喋不休,因为有太多的潜在问题并试图解决问题 - 看不见的是猜测最好的时间工作,但是说我有几个你可能想深入研究的领域进一步思考:
如果帧速率不一致,您是否可以确定发生此不一致的具体时间点?即当一次运行大量动画时,或者使用特定画笔时?
- 您是否使用过任何分析工具来查找代码的“热门”部分?这有助于识别出现这些不一致的特定情况。
相关动画的DesiredFrameRate和可能同时运行的其他动画是什么?默认情况下,这是60fps,这可能会非常繁重,特别是当屏幕上有很多对象或轮询的过渡时。
- 首先尝试在搭便车动画上设置
Timeline.DesiredFrameRate=60
。- 然后尝试将其他时间轴的
DesiredFrameRate
设置为30,以便观察这是否会提高地图的效果 - 这会影响导致性能影响的其他元素的数量或质量。- 醇>
您是否研究过用于地图和视觉对象的画笔的RenderQuality?
- 尝试降低画笔的RenderQuality:
RenderOptions.SetBitmapScalingMode(myBrush, BitmapScalingMode.LowQuality);
- 确保缓存可重复使用的画笔:
RenderOptions.SetCachingHint(myBrush, CachingHint.Cache);
存在许多潜在的性能瓶颈,如果没有看到代码,则很难为您提供超过这些松散的建议。作为基线优化的快速第一站,有一个相当comprehensive post by Kevin Pfister可以提供一些进一步有用的见解代替任何代码或特定的已识别问题区域。
如果提供了更多信息,例如:
我或许可以给你更多建议。