【发布时间】:2016-10-09 03:25:16
【问题描述】:
我正在用 WPF 制作游戏。我选择 WPF 是因为我想在我的游戏中使用布局容器和用户控件。游戏有两个部分:1.) 一个角色四处走动的地图,以及 2.) 一个 UserControl,它由许多容器、控件和其他充当另一个屏幕的 userControls 组成。我需要能够从地图转换到 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 应用程序的计算机上运行。
【问题讨论】:
-
WPF 已经提供了动画(例如通过故事板)。用那个。让图书馆担心帧率。您可以通过结合使用情节提要与视图模型中的属性绑定来连接到该系统。
-
感谢马特的建议。我曾在游戏的不同部分使用故事板制作短动画或周期性动画。我假设任何故事板动画都容易出现 CompositionTarget.Rendering 事件的问题。我将尝试使用另一个测试项目中的 keydown 事件处理程序触发的情节提要动画来更新地图位置。
标签: wpf