zoukankan      html  css  js  c++  java
  • 使用win2d实现萤火虫粒子效果

    这几天我在做游戏的标题画面,需要实现随机飞舞的萤火虫。萤火虫会闪烁黄绿色的光,远近不同,并且飞出屏幕边界不久会自动飞回来。

    我前一阵子用win2d写了个简单的游戏引擎(现在还是alpha阶段),用它可以减少代码量。

    https://github.com/Nukepayload2/Nukepayload2.Graphics.N2Engine

    那么,大概设计一下。

    萤火虫用粒子系统实现比较方便,所以Models有两个类,一个是FireflyParticle,另一个是FireflyParticleSystem。

    游戏标题画面有一个ViewModel。

    萤火虫呈现类是FireflyParticleSystemView,自定义的游戏视图一个类。所以有2个Views的类

    资源文件夹里面要添加xxxResourceManager,尽管这次我们不用任何图片素材。

    开始代码。首先拿FireflyParticle开刀。

    Public Class FireflyParticle
        Inherits Particle
    
        Public Property Radius As Single
        Public Property CenterColor As Color
        Public Property Opacity As Single = 1
        Public Property Parent As FireFlyParticleSystem
        Public Sub New(acceleration As Vector2, lifeTime As Integer, location As Vector2, velocity As Vector2, radius As Single, centerColor As Color, parent As FireFlyParticleSystem)
            MyBase.New(acceleration, lifeTime, location, velocity)
            Me.Radius = radius
            Me.Parent = parent
            Me.CenterColor = centerColor
            delta = 0.3 + 0.12 * Rndf()
        End Sub
    
        Dim delta!
    
        Public Overrides Sub Update()
            MyBase.Update()
            Opacity -= delta!
            Dim ub = Math.Max(0, 1 - Age / LifeTime)
            If Opacity < 0 OrElse Opacity > ub Then
                delta = -delta
                If Opacity < 0 Then
                    Opacity = 0
                ElseIf Opacity > ub Then
                    Opacity = ub
                End If
            End If
            Radius -= Radius / LifeTime
            Velocity += New Vector2(Rndf() - 0.5, Rndf() - 0.5).WithLength(0.5)
            Const bound = 20
            Dim lx = Location.X, ly = Location.Y, vx = Velocity.X, vy = Velocity.Y
            If lx < -bound Then
                lx = -bound
                vx = -vx
            ElseIf lx - bound > Parent.Width Then
                lx = Parent.Width + bound
                vx = -vx
            End If
            If ly < -bound Then
                ly = -bound
                vy = -vy
            ElseIf ly - bound > Parent.Height Then
                ly = Parent.Height + bound
                vy = -vy
            End If
            Location = New Vector2(lx, ly)
            Velocity = New Vector2(vx, vy).WithLength(Parent.MaxSpeed)
        End Sub
    End Class

     FireflyParticle类实现的是单独的萤火虫的飞舞,闪烁产生的数据变动。大多数的变量名很规范,所以不多解释了。

    接下来的是粒子系统。实现了自适应窗口大小的一半功能,还关联了一个呈现用的类(这里为了利用Intellisense的自动创建类代码的功能减少工作量才这样设计,粒子系统类不访问呈现类的任何成员)。

    Public Class FireFlyParticleSystem
        Inherits ParticleSystem(Of FireflyParticle)
    
        Public Sub New(spawnInterval As Integer, initialCount As Integer, width As Integer, height As Integer, baseColor As Color)
            Me.InitialCount = initialCount
            Me.SpawnInterval = spawnInterval
            Me.Width = width
            Me.Height = height
            Me.BaseColor = baseColor
            For i = 0 To initialCount - 1
                Dim part = CreateParticle()
                part.Age = part.LifeTime - i / initialCount * part.LifeTime
                Particles.Enqueue(part)
            Next
        End Sub
        Protected Overrides Sub OnParticleRemoved()
            MyBase.OnParticleRemoved()
            Particles.Enqueue(CreateParticle())
        End Sub
        Public Overrides Property Particles As New Queue(Of FireflyParticle)
        Public Overrides ReadOnly Property Presenter As GameVisualView = New FireFlyParticleSystemView(Me)
        Public Overrides Property SpawnCount As Integer = 1
        Public Overrides Property SpawnDuration As Integer = Integer.MaxValue
        Public Property InitialCount As Integer
        Public Overrides Property SpawnInterval As Integer = Integer.MaxValue
        Public Property Width As Integer
        Public Property Height As Integer
        Public Property BaseColor As Color
        Public Property MaxSpeed As Double = 1.4
        Protected Overrides Function CreateParticle() As FireflyParticle
            Return New FireflyParticle(New Vector2, (SpawnInterval * (0.6 + 0.4 * Rndf())), New Vector2(Rndf() * Width, Height * Rndf()), New Vector2(Rndf() - 0.5, Rndf() - 0.5).WithLength(MaxSpeed), 5 + Rndf(), BaseColor, Me)
        End Function
    End Class

    下面是ViewModel。向AnimObjects集合绑定了上面的萤火虫粒子系统,并且通过事件更新粒子系统的宽度和高度(绑定方式略原始,我正考虑怎样改进这个过程)。

    Public Class ImTheBossTitleScreenN2ViewModel
        Inherits GamePanel
    
        Public Sub New(SpaceSize As Size)
            MyBase.New(SpaceSize)
        End Sub
    
        Dim FireflySys As FireFlyParticleSystem
        Protected Overrides Sub InitializeGameVisuals()
            FireflySys = New FireFlyParticleSystem(10000, 20, SpaceSize.Width, SpaceSize.Height, Colors.YellowGreen)
            AnimObjects.Add(FireflySys)
        End Sub
    
        Private Sub ImTheBossTitleScreenN2ViewModel_SizeChanged(NewSize As Size) Handles Me.SizeChanged
            FireflySys.Width = NewSize.Width
            FireflySys.Height = NewSize.Height
        End Sub
    End Class

    View代码只发呈现萤火虫。

    萤火虫的光可以用对实心圆进行扩大组织变换再进行高斯模糊模拟。

    Friend Class FireFlyParticleSystemView
        Inherits TypedGameVisualPresenter(Of FireFlyParticleSystem)
    
        Public Sub New(Target As FireFlyParticleSystem)
            MyBase.New(Target)
        End Sub
    
        Public Overrides Sub OnDraw(sender As GamePanelView, DrawingSession As CanvasDrawingSession, Canvas As ICanvasResourceCreator)
            For Each fireFly In Target.Particles
                Using cl As New CanvasCommandList(DrawingSession), ds = cl.CreateDrawingSession
                    Using glow As New GlowEffectGraph
                        Dim color = fireFly.CenterColor
                        color.A = CByte(fireFly.Opacity * 255)
                        ds.FillCircle(fireFly.Location + Target.Location, fireFly.Radius, color)
                        glow.Setup(cl, fireFly.Radius)
                        DrawingSession.DrawImage(glow.Output)
                    End Using
                End Using
            Next
        End Sub
    
        Public Overrides Sub OnGlobalQualityChanged(Quality As GraphicQualityManager)
    
        End Sub
    End Class
    
    Class GlowEffectGraph
        Implements IDisposable
    
        Public ReadOnly Property Output() As ICanvasImage
            Get
                Return blur
            End Get
        End Property
    
        Private morphology As New MorphologyEffect() With {
            .Mode = MorphologyEffectMode.Dilate,
            .Width = 1,
            .Height = 1
        }
    
        Private blur As New GaussianBlurEffect() With {
            .BlurAmount = 0,
            .BorderMode = EffectBorderMode.Soft
        }
    
        Public Sub New()
            blur.Source = morphology
        End Sub
    
        Public Sub Setup(source As ICanvasImage, amount As Single)
            morphology.Source = source
            Dim halfAmount = Math.Min(amount / 2, 100)
            morphology.Width = CInt(Math.Truncate(Math.Ceiling(halfAmount)))
            morphology.Height = CInt(Math.Truncate(Math.Ceiling(halfAmount)))
            blur.BlurAmount = halfAmount
        End Sub
    
    #Region "IDisposable Support"
        Private disposedValue As Boolean ' 要检测冗余调用
    
        ' IDisposable
        Protected Overridable Sub Dispose(disposing As Boolean)
            If Not disposedValue Then
                If disposing Then
                    ' TODO: 释放托管状态(托管对象)。
                    Output?.Dispose()
                    morphology.Dispose()
                    blur.Dispose()
                End If
    
                ' TODO: 释放未托管资源(未托管对象)并在以下内容中替代 Finalize()。
                ' TODO: 将大型字段设置为 null。
            End If
            disposedValue = True
        End Sub
    
        ' TODO: 仅当以上 Dispose(disposing As Boolean)拥有用于释放未托管资源的代码时才替代 Finalize()。
        'Protected Overrides Sub Finalize()
        '    ' 请勿更改此代码。将清理代码放入以上 Dispose(disposing As Boolean)中。
        '    Dispose(False)
        '    MyBase.Finalize()
        'End Sub
    
        ' Visual Basic 添加此代码以正确实现可释放模式。
        Public Sub Dispose() Implements IDisposable.Dispose
            ' 请勿更改此代码。将清理代码放入以上 Dispose(disposing As Boolean)中。
            Dispose(True)
            ' TODO: 如果在以上内容中替代了 Finalize(),则取消注释以下行。
            ' GC.SuppressFinalize(Me)
        End Sub
    #End Region
    End Class

    这里我纠正了win2d gallery示例代码的错误:GlowEffectGraph类最好实现IDisposable,这样可以避免内存释放不及时带来应用闪退,或者产生性能问题。

    效果图(注意看那些绿色光斑):

    效果视频:

    http://v.youku.com/v_show/id_XMTU3MzA1ODg3Ng==.html

  • 相关阅读:
    iOS开发——UI基础-自定义构造方法,layoutSubviews,Xib文件,利用Xib自定义View
    iOS开发——UI基础-懒加载,plist文件,字典转模型,自定义view
    iOS开发——UI基础-按钮的创建和设置
    iOS开发——UI基础-Xcode资源拷贝
    机器学习中的数学基础_七月算法4月机器学习班第1次课程笔记
    leetcode--Merge Sorted Array
    leetcode--First Missing Positive
    leetcode--Divide two integers
    leetcode--Implement strStr()
    leetcode--Remove Element
  • 原文地址:https://www.cnblogs.com/Nukepayload2/p/uwp_fireflyparticlesys_vbnet.html
Copyright © 2011-2022 走看看