functions/utility/New-MiniGameObjectUtility.ps1

function New-MiniGameObjectUtility {
    <#
    .SYNOPSIS
    This return a utility class for managing gameobjects.
    - Use New-MiniGame for creating a game loop.
    - The object utility is used to create and manage objects in the game.
 
    .DESCRIPTION
    This return a utility class for managing gameobjects.
 
    An object:
    - has a position and optional velocity
    - supports Draw, Redraw, Undraw, Remove and Move
    - can have multple canvases (sides of a character) and switch between
 
    Can be used by itself or via New-MiniGame
     
 
    .OUTPUTS
 
     
    .EXAMPLE
 
    Creating two stickmans and moving the left one towards the right stickman:
 
    PS> $object = New-MiniGameObjectUtility
 
        $blueprint = $object.AddBlueprint('Stickman')
        $blueprint.Rightmost()
        $blueprint.Foreground.SetColor('White')
        $blueprint.AddCanvas(
            'normal',
            @(
                " Ö ",
                " /|\ ",
                " | ",
                " / \ "
            )
        )
        $blueprint.AddCanvas(
            'happy',
            @(
                " \ Ö / ",
                " | ",
                " | ",
                " / \ "
            )
        )
 
        $blueprint.CollisionGroup()
        $blueprint.OnCollision({
            param($self, $colliders)
            Write-Host "`nI'm $($self.id) colliding with $($colliders[0].id)"
        })
 
 
 
        $stickman1 = $object.FromBlueprint($blueprint, 'Stickman 1')
        $stickman1.Move(-23, 5)
 
        $stickman2 = $object.FromBlueprint('Stickman', 'Stickman 2')
        $stickman2.Move(-17, 5)
 
 
        $child = $stickman2.AddChild('Stickman Child')
        # Moves relative to the parent position
        $child.Move(3, 0)
        $child.Foreground.SetColor('Red')
        $child.AddCanvas(@(
            " Ö ",
            " ´|`` "
        ))
 
        $stickman2
        $stickman1
 
        clear
        $stickman1.Redraw()
        $stickman2.Redraw()
        $stickman2.Collisions()
        $stickman1.Collisions()
 
         
        Start-Sleep -Seconds 2
        $stickman2.setCanvas('happy')
        $stickman2.Redraw()
        Start-Sleep -Seconds 2
        $stickman2.Move(-1, 0)
        $stickman2.Redraw()
        Start-Sleep -Seconds 2
        $stickman2.setCanvas('normal')
        $stickman2.Move(-1, 0)
        $stickman2.Redraw()
        Start-Sleep -Seconds 2
 
        $stickman2.Move(-2, 0)
        $stickman2.Redraw()
        $stickman2.Collisions()
        $stickman1.Collisions()
        Start-Sleep -Seconds 2
 
        $stickman1.Remove()
        Start-Sleep -Seconds 2
        $stickman2.Remove()
        Start-Sleep -Seconds 2
 
    .LINK
     
    #>

    
    [CmdletBinding()]
    param (
        [Parameter(
            Position = 0,
            Mandatory = $false
        )]
        [System.Int32]
        $MaximumX = -1,
    
        [Parameter(
            Position = 1,
            Mandatory = $false
        )]
        [System.Int32]
        $MaximumY = -1,
    
        [Parameter(
            Position = 2,
            Mandatory = $false
        )]
        [System.Int32]
        $MinimumX = 0,
    
        [Parameter(
            Position = 3,
            Mandatory = $false
        )]
        [System.Int32]
        $MinimumY = 0,
    
    
        # Inverts drawing on the Y-Axis. Normally Y=0 points to the top of the screen.
        # This is usefull for having elements fly from the bottom upwards.
        [Parameter()]
        [switch]
        $InvertY,

        # Inverts drawing on the X-Axis. Normally X=0 points to the left side of the screen.
        # Might not be useful but complement to InvertY
        [Parameter()]
        [switch]
        $InvertX,


        <#
            The method of calculating positions.
            - Radius: Draws a radius around each object and calculates its distance (more performant with many objects)
            - Position: Compares all occupied positions of each object
 
            Use radius if Position causes performance issues.
        #>

        [Parameter()]
        [ValidateSet('Position', 'Radius')]
        [System.String]
        $CollisionMethod = 'Position',
    
        # This automatically trims empty spaces from inputet strings.
        [Parameter(
            Mandatory = $false
        )]
        [System.Boolean]
        $TrimSpaces = $true
    )


    <#
 
        ///////////////////////////////////////////////////////////////////////
        Every object has this for foreground and background.
        It gives color information when calling the draw utility.
    #>

    class MiniGameObjectUtilityObjectGraphics {

        [System.String] $_color = $null
        [System.Byte[]]$_bytes = $null
        [void] Reset() {
            $this._color = $null
            $this._bytes = $null
        }

        # There are some not so nice code here like this.
        # It receives the MiniGameDrawUtilityGraphics from the draw utility,
        # to set the color information for the next draw method call.
        # It should only be called internally.
        [void] _draw([System.Object] $drawUtilityGraphics) {
            if (-NOT [System.String]::IsNullOrEmpty($this._color)) {
                $drawUtilityGraphics.OnceColor($this._color)
            }
            elseif ($null -NE $this._bytes -AND $this._bytes.Count -EQ 1) {
                $drawUtilityGraphics.Once8Bit($this._bytes[0])
            }
            elseif ($null -NE $this._bytes -AND $this._bytes.Count -EQ 3) {
                $drawUtilityGraphics.OnceRGB($this._bytes)
            }
        }
        
        [void] setColor([System.String] $color) {
            $this.Reset()
            $this._color = $color
        }
        [void] Set8Bit([System.Byte] $byte) {
            $this.Reset()
            $this._byte = $byte
        }
        [void] SetRGB(            
            [System.Byte] $red,
            [System.Byte] $green,
            [System.Byte] $blue
        ) {
            $this.Reset()
            $this._bytes = @($red, $green, $blue)
        }
        [void] SetRGB([System.Byte[]] $rgb) {
            $this.SetRGB($rgb[0], $rgb[1], $rgb[2])
        }
    }


    <#
 
        ///////////////////////////////////////////////////////////////////////
        This is an object that can be drawn on the screen.
        - Blueprint: This is a template for creating many similar objects.
        - Group: This is to easily group objects together. like 'obstacles', 'stars', etc.
        - Tag: Additional tags for objects. like 'enemy', 'player', etc.
                     TODO: Method to retrieve objects by tag.
 
        - Variables: Variables for each object. like 'health', 'score', etc.
        - Position: The position of the object on the screen.
        - Velocity: (Optional) The velocity of the object. This is used for moving the object.
 
        - Offset: Automatically set on childs and used for moving the object relative to its parent.
 
    #>

    class MiniGameObjectUtilityObject {
        static [MiniGameObjectUtilityObject] FromBlueprint(
            [MiniGameObjectUtilityObject] $blueprint,
            [System.String] $id
        ) {
            $object = [MiniGameObjectUtilityObject]@{
                id       = $id
                _manager = $blueprint._manager
            }

            $object.canvas = $blueprint.canvas
            $object.SetPos($blueprint.position)
            $object.SetVel($blueprint.velocity)
            foreach ($entry in $blueprint.canvasStates.Keys) {
                $object.AddCanvas($entry, $blueprint.canvasStates[$entry])
            }

            foreach ($tag in $blueprint.tag.Keys) {
                $object.Tag($tag)
            }
        
            foreach ($var in $blueprint.var.Keys) {
                $object.Set($var, $blueprint.var[$var])
            }

            if ($blueprint.parent) {
                $object.SetParent($blueprint.parent)
            }

            if ($null -NE $blueprint._group) {
                $object.Group($blueprint._group)
            }

            if ($null -NE $blueprint._collisionObject) {
                $object.CollisionGroup($blueprint._group)
                foreach ($handler in $blueprint._collisionObject._onCollision.Values) {
                    $object.OnCollision($handler)
                }
            }

            if ($null -NE $blueprint.Foreground._bytes) {
                $object.Foreground._bytes = $blueprint.Foreground._bytes
            }
            if ($blueprint.Background._bytes) {
                $object.Background._bytes = $blueprint.Background._bytes
            }
            if ($blueprint.Foreground._color) {
                $object.Foreground.SetColor($blueprint.Foreground._color)
            }
            if ($blueprint.Background._color) {
                $object.Background.SetColor($blueprint.Background._color)
            }

            if ($null -NE $blueprint._blueprintData) {
                $initData = $blueprint._blueprintData.initData
                $blueprint._blueprintData.onInit.Invoke($object, $initData)
            }

            return $object
        }

        [System.Boolean] $_isBlueprint = $false
        [System.Object] $_blueprintData = $null
        [void] OnBlueprintInit([System.Object] $initData, [scriptblock] $action) {
            $this._blueprintData = @{
                onInit   = $action
                initData = $initData
            }
        }

        [System.String] $_group
        [System.Object] $_manager
        [System.Object] $_collisionObject

        # Parent and child relations
        [MiniGameObjectUtilityObject] $parent = $null
        [System.Collections.Hashtable] $children = @{}
        
        # Tag and variable storage
        [System.Collections.Hashtable] $tag = @{}
        [System.Collections.Hashtable] $var = @{}

        # Information to draw the object
        [System.String] $id
        [System.Object] $canvas = @{ name = $null; data = @() }
        [System.Collections.Hashtable] $canvasStates = @{}
        [MiniGameObjectUtilityObjectGraphics] $Foreground = [MiniGameObjectUtilityObjectGraphics]@{}
        [MiniGameObjectUtilityObjectGraphics] $Background = [MiniGameObjectUtilityObjectGraphics]@{}

        [System.Numerics.Vector2] $velocity = [System.Numerics.Vector2]::Zero
        [System.Numerics.Vector2] $position = [System.Numerics.Vector2]::Zero
        [System.Numerics.Vector2] $offset = [System.Numerics.Vector2]::Zero

        <#
             
            Helper properties to correclty draw and undraw the object.
            - $_wasDrawn: Prevent undrawing with no valid data to undraw.
            - $_drawnCanvas: Keep track of the drawn state to correctly undraw again, when the current state has changed.
        #>
 
        [System.Boolean] $_wasDrawn = $false
        [System.String[]] $_drawnCanvas
        [System.Numerics.Vector2] $_drawnPosition


        <#
         
            Methods on the object.
        #>

        [void] Remove() {
            $this._manager.Remove($this.id)
        }
        [void] Group([System.String] $group) {
            $this._manager.Group($group, $this)
        }
        [void] Tag([System.String] $tag) {
            $this.tag.Remove($tag)
            $this.tag.Add($tag, $null)
        }
        [void] Untag([System.String] $tag) {
            $this.tag.Remove($tag)
        }
        [System.Boolean] HasTag([System.String[]] $tags) {
            foreach ($tag in $tags) {
                if (-NOT $this.tag.ContainsKey($tag)) {
                    return $false
                }
            }
            return $true
        }



        <#
 
            Parent Child relations
            Child are drawn relative to its parents position
        #>

        [void] SetParent(
            [MiniGameObjectUtilityObject] $parent
        ) {
            $parent.children.Remove($this.id)
            $parent.children.Add($this.id, $this)
            $this.parent = $parent

            # This recalculates the position in offset to the parent.
            $this.SetPos($this.offset)
        }
        [void] UnsetParent() {
            if ($null -EQ $this.parent) {
                return 
            }
            $this.parent.children.Remove($this.id)
            $this.parent = $null
        }

        [void] SetChild(
            [MiniGameObjectUtilityObject] $child
        ) {
            # Unset potential old parent, before setting a new one.
            $child.UnsetParent()
            $child.SetParent($this)
        }
        [void] UnsetChild(
            [MiniGameObjectUtilityObject] $child
        ) {
            $child.UnsetParent()
        }

        # Create a new object with automatic parent relation.
        [MiniGameObjectUtilityObject] AddChild(
            [System.String] $id
        ) {
            $object = $this._manager.Add($id)
            $object.SetParent($this)
            return $object
        }
        [MiniGameObjectUtilityObject] AddChild() {
            $randomId = $this._manager.RandomId()
            return $this.AddChild("$($this.id)-$randomId")
        }



        <#
 
            Drawing / Undrawing
 
        #>

        [void] Draw([System.Boolean] $force) {
            if (-NOT $this._wasDrawn -OR $force) {
                $this._wasDrawn = $true
                $this._drawnCanvas = $this.canvas.data
                $this._drawnPosition = $this.position

                $drawUtility = $this._manager.canvas
                $this.Foreground._draw($drawUtility.ForeGround)
                $this.Background._draw($drawUtility.Background)
                $drawUtility.Draw($this.position, $this.canvas.data)
            }
            foreach ($child in $this.children.Values) {
                $child.Draw($force)
            }
        }
        [void] Draw() {
            $this.Draw($false)
        }
        [void] Undraw() {
            if ($this._wasDrawn) {
                $this._wasDrawn = $false
                $this._manager.canvas.Undraw(
                    $this._drawnPosition, 
                    $this._drawnCanvas
                )
            }
            foreach ($child in $this.children.Values) {
                $child.Undraw()
            }
        }
        [void] Redraw() {
            $this.Undraw()
            $this.Draw()
        }


        <#
 
            Variables
 
        #>

        [System.Object] Get([System.String] $name) {
            return $this.var[$name]
        }
        [void] Set(
            [System.String] $name,
            [System.Object] $value
        ) {
            $this.var.Remove($name)
            $this.var.Add($name, $value)
        }
        [void] Add(
            [System.String] $name,
            [System.Object] $value
        ) {
            $this.var[$name] += $value
        }
        [void] Sub(
            [System.String] $name,
            [System.Object] $value
        ) {
            $this.var[$name] -= $value
        }



        <#
 
            Drawable Canvases
 
        #>

        [System.String] GetCanvas() {
            return $this.canvas.name
        }
        [void] SetCanvas([System.String] $name) {
            $this.canvas = @{
                Name = $name
                Data = $this.canvasStates[$name]
            }

            if ($null -NE $this._collisionObject) {
                $this._collisionObject.SetCanvas($this.canvas.data)
            }
        }
        [void] SetCanvas() {
            $this.SetCanvas('__default__')
        }
        [void] AddCanvas(
            [System.String] $name, 
            [System.String[]] $canvas
        ) {
            $this.canvasStates.Remove($name)
            $this.canvasStates.Add($name, $canvas)
            if ([System.String]::IsNullOrEmpty($this.canvas.name)) {
                $this.SetCanvas($name)
            }
        }
        [void] AddCanvas([System.String[]] $canvas) {
            $this.AddCanvas('__default__', $canvas)
        }    


        <#
            Position and offset calculation
 
            offset: This is the position of the object in its 'reference-frame'
            position: This is the actual position of the object in the terminal.
 
            In Parent-Child relations the offset is relative to the parents actual position.
        #>

        [void] SetPos([System.Single] $x, [System.Single] $y) {
            $this.offset = [System.Numerics.Vector2]@{X = $x; Y = $y }

            if ($null -NE $this.parent) {
                $x = $this.parent.X + $x
                $y = $this.parent.Y + $y
            }

            $this.position = [System.Numerics.Vector2]@{X = $x; Y = $y }
            if ($null -NE $this._collisionObject) {
                $this._collisionObject.SetPos($x, $y)
            }

            foreach ($child in $this.children.Values) {
                $child.Position = [System.Numerics.Vector2]@{
                    X = $child.offset.X + $x
                    Y = $child.offset.Y + $y
                }
            }
        }
        [void] SetPos([System.Numerics.Vector2] $pos) {
            $this.SetPos($pos.X, $pos.Y)
        }
        [void] Rightmost() {
            $this.SetPos($this._manager.canvas.Maximum.X, $this.position.Y)
        }
        [void] Leftmost() {
            $this.SetPos($this._manager.canvas.Minimum.X, $this.position.Y)
        }
        [void] Topmost() {
            $this.SetPos($this.position.X, $this._manager.canvas.Maximum.Y)
        }
        [void] Botmost() {
            $this.SetPos($this.position.X, $this._manager.canvas.Minimum.Y)
        }


        [void] SetVel($x, $y) {
            $this.velocity = [System.Numerics.Vector2]@{X = $x; Y = $y }
        }        
        [void] SetVel([System.Numerics.Vector2] $vel) {
            $this.SetVel($vel.X, $vel.Y)
        }


        [void] Move() {
            if (0 -EQ $this.velocity.X -AND 0 -EQ $this.velocity.Y) {
                return
            }

            $this.Move($this.velocity.X, $this.velocity.Y)
        }
        [void] Move(
            [System.Single] $x,
            [System.Single] $y
        ) {
            if (0 -EQ $x -AND 0 -EQ $y) {
                return
            }

            $this.SetPos(
                $this.offset.X + $X,
                $this.offset.Y + $Y
            )
        }
        [void] AddPos(
            [System.Single] $x,
            [System.Single] $y
        ) {
            $this.Move($x, $y)
        }
        [void] AddPos(
            [System.Numerics.Vector2] $pos
        ) {
            $this.Move($pos.X, $pos.Y)
        }
        [void] SubPos(
            [System.Single] $x,
            [System.Single] $y
        ) {
            $this.Move(-$x, - $y)
        }
        [void] SubPos(
            [System.Numerics.Vector2] $pos
        ) {
            $this.Move(-$pos.X, - $pos.Y)
        }
        [void] MulPos(
            [System.Single] $x,
            [System.Single] $y
        ) {
            $this.SetPos(
                $this.offset.X * $x,
                $this.offset.Y * $y
            )
        }
        [void] MulPos(
            [System.Numerics.Vector2] $pos
        ) {
            $this.MulPos($pos.X, $pos.Y)
        }


        <#
 
            Collision groups and collision handlers
 
        #>

        [void] CollisionGroup([System.String] $group) {
            $this._collisionObject = $this._manager._collision.Add($this.id, $group)
            $this._collisionObject.SetPos($this.X, $this.Y)
            $this._collisionObject.SetCanvas($this.canvas)
            $this._collisionObject.SetRef($this)
        }
        [void] CollisionGroup() {
            $this._collisionObject = $this._manager._collision.Add($this.id)
            $this._collisionObject.SetPos($this.X, $this.Y)
            $this._collisionObject.SetCanvas($this.canvas)
            $this._collisionObject.SetRef($this)
        } 
        [void] RemoveCollisionGroup() {
            if ($null -EQ $this._collisionObject) {
                return
            }
            $this._collisionObject.Remove()
            $this._collisionObject = $null
        }


        [void] OnCollision([scriptblock] $action) {
            $this._collisionObject.OnCollision($action)
        }
        [void] Collisions() {
            $this._collisionObject.Collisions()
        }
    }

    if (
        $null -EQ (Get-TypeData -TypeName  'MiniGameObjectUtilityObject').Members.X
    ) {
        Update-TypeData -TypeName  'MiniGameObjectUtilityObject' -MemberName  'X'  -Value { return $this.position.X } -MemberType  ScriptProperty
    }
    if (
        $null -EQ (Get-TypeData -TypeName  'MiniGameObjectUtilityObject').Members.Y
    ) {
        Update-TypeData -TypeName  'MiniGameObjectUtilityObject' -MemberName  'Y'  -Value { return $this.position.Y } -MemberType  ScriptProperty
    }



    <#
     
        ///////////////////////////////////////////////////////////////////////
        The Manager class for all objects.
        - Keeps track of all objects and ability to Add or Remove them.
        - Adds a blueprint-object for creating many similar objects.
        - Groups objects together. 'obstacles', 'stars', etc.
        - Perform collision calculation for all or a specific group.
     
    #>

    class MiniGameObjectUtility {
        [System.Collections.Hashtable] $_blueprints = @{
            __default__ = [MiniGameObjectUtilityObject]@{
                id           = "blueprint-$id"
                _manager     = $this
                _isBlueprint = $true
            }
        }
        <#
 
            Adds a blueprint-object for creating many similar objects.
 
        #>

        [MiniGameObjectUtilityObject] AddBlueprint([System.String] $id) {
            $object = [MiniGameObjectUtilityObject]@{
                id           = $id
                _manager     = $this
                _isBlueprint = $true
            }
            $this._blueprints.Add($id, $object)
            return $object
        }



        <#
 
            Keeps track of all objects and ability to Add or Remove them.
 
        #>

        [System.Collections.Hashtable] $_allObjects = @{}
        [System.String] RandomId() {
            $bytes = (1..8 | ForEach-Object { [byte](Get-Random -Max 256) })
            $randomId = [System.Convert]::ToHexString($bytes)
            if ($this._allObjects.ContainsKey($randomId)) {
                return $this.RandomId()
            }
            else {
                return $randomId
            }
        }        

        [MiniGameObjectUtilityObject] Get([System.String] $id) {
            return $this._allObjects[$id]
        }
        [MiniGameObjectUtilityObject[]] Get() {
            return $this._allObjects.Values
        }




        <#
 
            Groups objects together. 'obstacles', 'stars', etc.
 
            object will be simultaneously stored in all objects and its group.
 
        #>

        [System.Collections.Hashtable] $_groups = @{}
        [void] Group(
            [System.String] $group,
            [MiniGameObjectUtilityObject] $object
        ) {
            # Remove from old group if it exists.
            if ($null -NE $object._group) {
                $this._groups[$object._group].Remove($object.id)
            }

            # Create new group if it does not exist.
            if (-NOT $this._groups.ContainsKey($group)) {
                $this._groups.Add($group, @{})
            }

            $object._group = $group

            # Blueprint objects are not added to the collision objects,
            # but we save the group, for instances of the blueprint being added.
            if (-NOT $object._isBlueprint) {
                $this._groups[$group].Add($object.id, $object)
            }
        }
        [MiniGameObjectUtilityObject[]] GetGroup([System.String] $group) {
            return $this._groups[$group].Values
        }




        <#
 
            Adding a new blank object.
 
        #>

        [MiniGameObjectUtilityObject] Add(
            [System.String] $id, 
            [MiniGameObjectUtilityObject] $blueprint
        ) {
            $object = [MiniGameObjectUtilityObject]::FromBlueprint($blueprint, $id)
            $this._allObjects.Add($id, $object)
            return $object
        }
        [MiniGameObjectUtilityObject] Add([System.String] $id) {
            return $this.Add($id, $this._blueprints.__default__)
        }
        [MiniGameObjectUtilityObject] Add() {
            return $this.Add($this.RandomId(), $this._blueprints.__default__)
        }



        <#
 
            Adding an object via blueprint id.
 
        #>

        [MiniGameObjectUtilityObject] FromBlueprint(
            [System.String] $blueprint,
            [System.String] $id
        ) {
            return $this.Add($id, $this._blueprints[$blueprint])
        }
        [MiniGameObjectUtilityObject] FromBlueprint(
            [System.String] $blueprint
        ) {
            return $this.Add($this.RandomId(), $this._blueprints[$blueprint])
        }



        <#
 
            Adding an object via blueprint object.
 
        #>

        [MiniGameObjectUtilityObject] FromBlueprint(
            [MiniGameObjectUtilityObject] $blueprint,
            [System.String] $id
        ) {
            return $this.Add($id, $blueprint)
        }
        [MiniGameObjectUtilityObject] FromBlueprint(
            [MiniGameObjectUtilityObject] $blueprint
        ) {
            return $this.Add($this.RandomId(), $blueprint)
        }
        


        <#
 
            Removing an object.
 
        #>

        [void] Remove([System.String] $id) {

            # Retrieve the object.
            $object = $this._allObjects[$id]
                        
            # Remove any children from the object.
            foreach ($child in $object.children.Values) {
                $this.Remove($child.id)
            }

            # Remove it from all objects
            $this._allObjects.Remove($id)

            # Remove it from a potential group
            if ($null -NE $object._group) {
                $this._groups[$object._group].Remove($object.id)
            }

            # Remove it from the collision object
            $object.RemoveCollisionGroup()

            # Undraw it from the screen
            $object.Undraw()
        }
        [void] Remove([MiniGameObjectUtilityObject] $object) {
            $this.Remove($object.id)
        } 



        <#
 
            Perform collision calculation for all or a specific group.
 
        #>

        [System.Object] $_collision = (New-MiniGameCollisionUtility -Method $CollisionMethod)
        [void] Collisions([System.String] $group) {
            $this._collision.Collisions($group)
        }
        [void] Collisions() {
            $this._collision.Collisions()
        }



        <#
 
            Calls the objects to Draw, Undraw, Redraw or Move
 
        #>

        [void] _process([System.Management.Automation.PSMethod] $method) {
            [System.String[]]$objectIds = $this._allObjects.Keys
            for ($index = 0; $index -LT $objectIds.Count; $index++) {
                $method.Invoke($objectIds[$index])
            }
        }
        [void] Draw([System.String] $id) {
            $this._allObjects[$id].Draw()
        }
        [void] Undraw([System.String] $id) {
            $this._allObjects[$id].Undraw()
        }   
        [void] Redraw([System.String] $id) {
            $this._allObjects[$id].Redraw()
        }  
        [void] Move([System.String] $id) {
            $this._allObjects[$id].Move()
        }
        [void] Draw() {
            $this._process($this.Draw)
        }
        [void] Undraw() {
            $this._process($this.Undraw)
        }
        [void] Redraw() {
            $this._process($this.Redraw)
        }
        [void] Move() {
            $this._process($this.Move)
        }



        [System.Object] $canvas
        MiniGameObjectUtility([System.Object] $canvas) {
            $this.canvas = $canvas
        }
    }
    
    $drawSettings = @{
        MinimumX   = $MinimumX
        MinimumY   = $MinimumY
        MaximumX   = $MaximumX
        MaximumY   = $MaximumY
        TrimSpaces = $TrimSpaces
        InvertY    = $InvertY.IsPresent
        InvertX    = $InvertX.IsPresent
    }
    $canvas = New-MiniGameDrawUtility @drawSettings
    return [MiniGameObjectUtility]::new($canvas)
}