Skip to content

Feature Request: Mobile Compatibility and Engine Enhancements #2

@Pac-Dessert1436

Description

@Pac-Dessert1436

As a hobbyist developer using vbPixelGameEngine, I'd like to propose these improvements to make the engine more versatile, preserving the charm of VB.NET:

Feature Benefit Major Use Case
Mobile Platform Support Enables Android/iOS deployments 2D pixel-style games for mobile devices
SpriteSheet Implementation Reduces draw calls by approx. 70% Character animations and tile-maps
Dedicated Editor for Existing ResourcePack Cuts asset prep time by approx. 40% Texture bundles and bitmap fonts

To implement the above features, we could think into these factors:

  • Conditional compilation for platform-specific code, such as using the SixLabors.ImageSharp package for image rendering on mobile devices.
  • Existing Sprite class for texture references, and even bitmap fonts contained in sprite sheets.
  • A WPF-based or WinForms-based standalone tool for editing resource packs, similar to MonoGame Content Builder.

The following is my own implementation of the SpriteSheet class, containing a rudimental but full-fledged system for animations and tile-maps. As is intended, one SpriteSheet instance is only for one character's animation.

Option Strict On
Option Infer On
Imports VbPixelGameEngine

Public Class SpriteSheet
    Private ReadOnly sheet As Sprite, frameW As Integer, frameH As Integer

    Public ReadOnly Property Rows As Integer
    Public ReadOnly Property Columns As Integer
    Public Property CurrFrame As Sprite

    Private ReadOnly Property AnimFrames As New Dictionary(Of String, List(Of Sprite))
    Private ReadOnly Property AllFrameIndices As (rowIdx As Integer, colIdx As Integer)()

    Private Shared _pauseAllAnim As Boolean
    Public Shared Property PauseAllAnimations As Boolean
        Private Get
            Return _pauseAllAnim
        End Get
        Set(value As Boolean)
            _pauseAllAnim = value
        End Set
    End Property

    Public Sub New(sprite As Sprite, frameScale As Vi2d)
        sheet = sprite
        frameW = frameScale.x
        frameH = frameScale.y

        Columns = sheet.Width \ frameW
        Rows = sheet.Height \ frameH

        ReDim AllFrameIndices(Rows * Columns - 1)
        Dim i As Integer = 0
        For row As Integer = 0 To Rows - 1
            For col As Integer = 0 To Columns - 1
                AllFrameIndices(i) = (row, col)
                i += 1
            Next col
        Next row
    End Sub

    Public Sub New(imgPath As String, frameScale As Vi2d)
        Me.New(New Sprite(imgPath), frameScale)
    End Sub

    Default Public ReadOnly Property Frame(rowIdx As Integer, colIdx As Integer) As Sprite
        Get
            ArgumentNullException.ThrowIfNull(Me)
            If rowIdx >= Rows OrElse colIdx >= Columns Then
                Throw New ArgumentException("Row index or column index is out of range.")
            End If
            Dim output As New Sprite(frameW, frameH)
            For i As Integer = 0 To frameW - 1
                For j As Integer = 0 To frameH - 1
                    Dim xPos As Integer = colIdx * frameW + i
                    Dim yPos As Integer = rowIdx * frameH + j
                    output.SetPixel(i, j, sheet.GetPixel(xPos, yPos))
                Next j
            Next i
            Return output
        End Get
    End Property

    Public Function CreateTileMap(start As (row As Integer, col As Integer),
            [end] As (row As Integer, col As Integer)) As List(Of Sprite)

        Dim outputTileMap As New List(Of Sprite)

        Dim tileMapStartIdx = Array.IndexOf(AllFrameIndices, (start.row, start.col))
        Dim tileMapEndIdx = Array.IndexOf(AllFrameIndices, ([end].row, [end].col))

        For idx As Integer = tileMapStartIdx To tileMapEndIdx
            Dim row = AllFrameIndices(idx).rowIdx, col = AllFrameIndices(idx).colIdx
            outputTileMap.Add(Frame(row, col))
        Next idx
        Return outputTileMap
    End Function

    Public Sub DefineAnimation(name As String, start As (row As Integer, col As Integer),
                               [end] As (row As Integer, col As Integer))
        AnimFrames(name) = CreateTileMap(start, [end])
    End Sub

    Public Sub PlayAnimation(name As String, frameDuration As Single, dt As Single,
                             Optional isLooping As Boolean = True,
                             Optional isAnimPaused As Boolean = False)
        Static framePointer As IEnumerator(Of Sprite), prevName As String
        If name <> prevName Then
            If framePointer IsNot Nothing Then framePointer.Reset()
            framePointer = AnimFrames(name).GetEnumerator()
            prevName = name
        End If

        Static frameTimer As Single = 0F
        If AnimFrames(name).Count = 0 Then
            Throw New InvalidOperationException("Animation frames cannot be empty.")
        End If
        CurrFrame = framePointer.Current
        If isAnimPaused OrElse PauseAllAnimations Then Exit Sub
        frameTimer += dt
        If frameTimer < frameDuration Then Exit Sub

        If framePointer.MoveNext() Then
            frameTimer = 0
        ElseIf isLooping Then
            framePointer.Reset()
            framePointer.MoveNext()  ' Move to the first frame.
        End If
    End Sub
End Class

The following example code shows how to animate my demo character (named "PacManAnim.png", as is shown in the sprite sheet below), which works smoothly in the engine.

Image

Imports VbPixelGameEngine

Public NotInheritable Class Program
    Inherits PixelGameEngine

    Friend Const VIEWPORT_W As Integer = 800, VIEWPORT_H As Integer = 600

    Friend Shared Sub Main()
        With New Program
            If .Construct(VIEWPORT_W, VIEWPORT_H, fullScreen:=True) Then .Start()
        End With
    End Sub

    Public Sub New()
        AppName = "VBPGE Animation Test"
    End Sub

    ReadOnly ss As New SpriteSheet("PacManAnim.png", New Vi2d(15, 15))

    Protected Overrides Function OnUserCreate() As Boolean
        SetPixelMode(Pixel.Mode.Mask)
        ss.DefineAnimation("yellow", (0, 0), (0, 6))
        ss.DefineAnimation("blue", (1, 0), (1, 6))
        Return True
    End Function

    Protected Overrides Function OnUserUpdate(elapsedTime As Single) As Boolean
        Clear()

        ss.PlayAnimation(If(GetKey(Key.SPACE).Held, "yellow", "blue"), 0.1, elapsedTime)
        Dim pos As New Vi2d(10, 10), scale As Integer = 3
        DrawSprite(pos, ss.CurrFrame, scale)

        Return Not GetKey(Key.ESCAPE).Pressed
    End Function
End Class

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions