OOoo

Jimmy

New member
3rd Party Module Dev
Tutorial Author
Joined
Jan 2, 2020
Messages
569
As an exercise I converted a few Mojo1-examples that uses Object-Oriented programming as its logical structure.
While converting them into Mojo2 I noticed that you now have to pass the canvas around between everything to give access.

It forces you to add additional parameters (canvas:Canvas in this case) everywhere.
To show what I mean, here's an example (this is not the worst example but it is the one I have in front of me).

Is this normal OO behaviour to add "superflous" parameters like this or should I change the style?

code_language.cerberus:
Strict
Import mojo2
Import monkey.math
Import monkey.random

Const GRAVITY:Float = 0.098
Global DW:Int,DH:Int
Global NumBalls:Int = 5

Function Main:Int()
    New MyApp 
    Return 0
End

Class MyApp Extends App

    Field canvas:Canvas
    Field ballArray:Ball[] 
  
    Method OnCreate:Int()     
        canvas = New Canvas
        DW = DeviceWidth() 
        DH = DeviceHeight()             
        ballArray = New Ball[NumBalls] 
        Seed = Millisecs() 
        For Local i:Int = 0 Until ballArray.Length
            ballArray[i] = New Ball(0,Floor(Rnd(0.0,DW)),Floor(Rnd(0.0,DH)),Rnd(-5.0,5.0),Rnd(-5.0,5.0),1.0,0.001,10.0)
        Next         
        ' Lock to vertical blanking
        SetSwapInterval 1
        SetUpdateRate 0
        Return 0 
    End

    Method OnUpdate:Int()
        If KeyHit(KEY_ESCAPE) Then EndApp
        If TouchHit(0)
        For Local temp:Int = 0 Until 20
            NumBalls += 1
            ballArray = ballArray.Resize(NumBalls)
            ballArray[ballArray.Length - 1] = New Ball(0,TouchX(0),TouchY(0),Rnd(-5.0,5.0),Rnd(-5.0,5.0),1.0,0.001,10.0)
        Next
        End
        For Local i:Int = 0 Until ballArray.Length
            ballArray[i].Update
            For Local j:Int = 0 Until ballArray.Length
                If (i <> j) Then BallVsBall(ballArray[i],ballArray[j])
            End
        End     
        Return 0         
    End

    Method OnRender:Int()     
        canvas.Clear 0,0,0             
        canvas.DrawText "Touch or left click for additional balls",5,5 ' +String(NumBalls)
        For Local i:Int = 0 Until ballArray.Length
            ballArray[i].Render(canvas)
        End
        canvas.Flush
        Return 0         
    End
End

Class Ball

    Field kind:Int
    Field x:Float
    Field y:Float
    Field vx:Float
    Field vy:Float
    Field friction:Float
    Field mass:Float
    Field radius:Float
  
    Method New(k:Int,sx:Float,sy:Float,svx:Float,svy:Float,fric:Float,m:Float,r:Float)
        kind = k
        x = sx
        y = sy
        vx = svx
        vy = svy
        friction = fric
        mass = m
        radius = r
    End 
  
    Method Update:Void()
        vx = (vx * friction)                         
        vy = ((vy + GRAVITY) * friction)
        If kind <> 0 Then vx = 0 ; vy = 0
        x = (x + vx)             
        y = (y + vy)
        If ((x + radius) < 0.0) Then vx = -(vx)
        If (x > (DW - radius)) Then vx = -(vx) ; x = DW - radius
        If ((y + radius) < 0) Then vy = -(vy)     
        If (y > (DH - radius)) Then vy = -(vy) ; y = DH - radius
    End 
  
    Method Render:Void(canvas:Canvas)
        canvas.DrawOval(x - (radius / 2.0),y - (radius / 2.0),radius * 2.0,radius * 2.0) 
    End
End

Function BallVsBall:Void(b:Ball,b2:Ball)
    Local collisiondistance:Float = (b.radius + b2.radius)
    Local actualdistance:Float = GetDistance(b.x,b.y,b2.x,b2.y)
    If (actualdistance < collisiondistance)
        Local normal:Float = ATan2((b2.y - b.y),(b2.x - b.x))
        Local movedist1:Float = ((collisiondistance - actualdistance) * (b2.mass / Float((b.mass + b2.mass))))
        Local movedist2:Float = ((collisiondistance - actualdistance) * (b.mass / Float((b.mass + b2.mass))))
        b.x = (b.x + (movedist1 * Cos(normal + 180.0)))         
        b.y = (b.y + (movedist1 * Sin(normal + 180.0)))
        b2.x = (b2.x + (movedist2 * Cos(normal)))
        b2.y = (b2.y + (movedist2 * Sin(normal)))
        Local nx:Float = Cos(normal)                         
        Local ny:Float = Sin(normal)
        Local a1:Float = ((b.vx * nx) + (b.vy * ny))             
        Local a2:Float = ((b2.vx * nx) + (b2.vy * ny))
        Local opt:Float = ((2.0 * (a1 - a2)) / (b.mass + b2.mass))
        b.vx = (b.vx - (opt * b2.mass * nx))
        b.vy = (b.vy - (opt * b2.mass * ny))
        b2.vx = (b2.vx + (opt * b.mass * nx))
        b2.vy = (b2.vy + (opt * b.mass * ny)) 
    Endif
End

Function GetDistance:Float(x1:Float,y1:Float,x2:Float,y2:Float)
    Return Sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))
End

Screenshot 2020-11-27 at 04.49.02.png
 
Last edited:

dawlane

Well-known member
CX Code Contributor
Joined
Jun 21, 2017
Messages
795
Well as you are trying to access an object contained inside another, then you will have to pass that object as a parameter. Unless you define the canvas as global within the main class and access it via the class definition name and scope operator.
 

MikeHart

Administrator
Joined
Jun 19, 2017
Messages
2,815
Or you store it outside thee main app in a global variable....
Or store it inside the object that will access it. Preferably when you create it as a parameter of your NEW call.
Both ways you have to pass/store it just once.
 

Jimmy

New member
3rd Party Module Dev
Tutorial Author
Joined
Jan 2, 2020
Messages
569
Well I first tried to do define the canvas as a global but somehow I failed a few times . I do not know why so I stopped doing that.
I'll try again, I seriously do not know why Global canvas:Canvas did not work across all classes.
 

Jimmy

New member
3rd Party Module Dev
Tutorial Author
Joined
Jan 2, 2020
Messages
569
What about the performance? Are both methods reasonable? I can't easily follow what happens behind the scenes when I juggle like this but my guess is that it is not terrible but global ought to be the better one if there's a difference.

Globals versus additional parameters.. both feel very secondary to me. I do not know what I expect from Object Orientation but code feels bloated and very quickly.

I might be wrong, I just wanted to hear what you have to say from a more experienced perspective.
 

Jimmy

New member
3rd Party Module Dev
Tutorial Author
Joined
Jan 2, 2020
Messages
569
I think I got confused also because the canvas parameter normally gets accessed through a notation like : somecanvas.somecanvasction(someparameters) and suddenly you're forced to put the left side inside the parameters instead.

Also I've been taught to avoid globals my whole life so It's stupendously hard for me to go against it.
Waiting for that aha moment now..
 

Jimmy

New member
3rd Party Module Dev
Tutorial Author
Joined
Jan 2, 2020
Messages
569
Global variable gives me either an error or empty screen.

code_language.cerberus:
Strict
Import mojo2
Import monkey.math
Import monkey.random

Const GRAVITY:Float = 0.098
Global DW:Int,DH:Int
Global NumBalls:Int = 5
Global canvas:Canvas

Function Main:Int()
    New MyApp   
    Return 0
End

Class MyApp Extends App

    
    Field ballArray:Ball[]   
    
    Method OnCreate:Int()       
        ' canvas = New Canvas
        DW = DeviceWidth()   
        DH = DeviceHeight()               
        ballArray = New Ball[NumBalls]   
        Seed = Millisecs()   
        For Local i:Int = 0 Until ballArray.Length
            ballArray[i] = New Ball(0,Floor(Rnd(0.0,DW)),Floor(Rnd(0.0,DH)),Rnd(-5.0,5.0),Rnd(-5.0,5.0),1.0,0.001,10.0)
        Next           
        ' Lock to vertical blanking
        SetSwapInterval 1
        SetUpdateRate 0
        Return 0   
    End

    Method OnUpdate:Int()
        If KeyHit(KEY_ESCAPE) Then EndApp
        If TouchHit(0)
        For Local temp:Int = 0 Until 20 ' 0 until 0 = 0!
            NumBalls += 1
            ballArray = ballArray.Resize(NumBalls)
            ballArray[ballArray.Length - 1] = New Ball(0,TouchX(0),TouchY(0),Rnd(-5.0,5.0),Rnd(-5.0,5.0),1.0,0.001,10.0)
        Next
        End
        For Local i:Int = 0 Until ballArray.Length
            ballArray[i].Update
            For Local j:Int = 0 Until ballArray.Length
                If (i <> j) Then BallVsBall(ballArray[i],ballArray[j])
            End
        End       
        Return 0           
    End

    Method OnRender:Int()       
        canvas.Clear 0,0,0               
        canvas.DrawText "Touch or left click for additional balls",5,5 ' +String(NumBalls)
        For Local i:Int = 0 Until ballArray.Length
            ballArray[i].Render()
        End
        canvas.Flush
        Return 0           
    End
End

Class Ball

    Field kind:Int
    Field x:Float
    Field y:Float
    Field vx:Float
    Field vy:Float
    Field friction:Float
    Field mass:Float
    Field radius:Float
    
    Method New(k:Int,sx:Float,sy:Float,svx:Float,svy:Float,fric:Float,m:Float,r:Float)
        kind = k
        x = sx
        y = sy
        vx = svx
        vy = svy
        friction = fric
        mass = m
        radius = r
    End   
    
    Method Update:Void()
        vx = (vx * friction)                           
        vy = ((vy + GRAVITY) * friction)
        If kind <> 0 Then vx = 0 ; vy = 0
        x = (x + vx)               
        y = (y + vy)
        If ((x + radius) < 0.0) Then vx = -(vx)
        If (x > (DW - radius)) Then vx = -(vx) ; x = DW - radius
        If ((y + radius) < 0) Then vy = -(vy)       
        If (y > (DH - radius)) Then vy = -(vy) ; y = DH - radius
    End   
    
    Method Render:Void()
        canvas.DrawOval(x - (radius / 2.0),y - (radius / 2.0),radius * 2.0,radius * 2.0)   
    End
End

Function BallVsBall:Void(b:Ball,b2:Ball)
    Local collisiondistance:Float = (b.radius + b2.radius)
    Local actualdistance:Float = GetDistance(b.x,b.y,b2.x,b2.y)
    If (actualdistance < collisiondistance)
        Local normal:Float = ATan2((b2.y - b.y),(b2.x - b.x))
        Local movedist1:Float = ((collisiondistance - actualdistance) * (b2.mass / Float((b.mass + b2.mass))))
        Local movedist2:Float = ((collisiondistance - actualdistance) * (b.mass / Float((b.mass + b2.mass))))
        b.x = (b.x + (movedist1 * Cos(normal + 180.0)))           
        b.y = (b.y + (movedist1 * Sin(normal + 180.0)))
        b2.x = (b2.x + (movedist2 * Cos(normal)))
        b2.y = (b2.y + (movedist2 * Sin(normal)))
        Local nx:Float = Cos(normal)                           
        Local ny:Float = Sin(normal)
        Local a1:Float = ((b.vx * nx) + (b.vy * ny))               
        Local a2:Float = ((b2.vx * nx) + (b2.vy * ny))
        Local opt:Float = ((2.0 * (a1 - a2)) / (b.mass + b2.mass))
        b.vx = (b.vx - (opt * b2.mass * nx))
        b.vy = (b.vy - (opt * b2.mass * ny))
        b2.vx = (b2.vx + (opt * b.mass * nx))
        b2.vy = (b2.vy + (opt * b.mass * ny))   
    Endif
End

Function GetDistance:Float(x1:Float,y1:Float,x2:Float,y2:Float)
    Return Sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))
End
 

Jimmy

New member
3rd Party Module Dev
Tutorial Author
Joined
Jan 2, 2020
Messages
569
I see now how it's the exact same problem as when you start with programming and you learn about operators vs functions :
"x + y" vs "Add(x,y)"

It never really matter how you add your numbers and both are equally fast, it's just a matter of elegance.
 

Phil7

Administrator
CX Code Contributor
3rd Party Tool Dev
Joined
Jun 26, 2017
Messages
535
Global variable gives me either an error or empty screen
If I uncomment "canvas = New Canvas" it works here on html5 target.

Also changed the imports to cerberus.math cerberus.random
 

dawlane

Well-known member
CX Code Contributor
Joined
Jun 21, 2017
Messages
795
Take note that graphical elements such as the canvas type can only be initialised once the the main application object has been created. You will not be able to create or use any graphical method or function until this is completed. And as you are accessing a static data member there is no overhead. Also be aware of the difference of Private and Public within module scope when using static class variables.

code_language.cerberus:
Strict
#CPP_MODE=2
Import mojo2
Import monkey.math
Import monkey.random

Const GRAVITY:Float = 0.098
Global DW:Int,DH:Int
Global NumBalls:Int = 5
' Global canvas:Canvas = New Canvas() ' This will not do anything.

Function Main:Int()
    New MyApp   
    Return 0
End

Class MyApp Extends App

    ' Global canvas:Canvas = New Canvas() ' This will not do anything.
    Global canvas:Canvas ' Static data member. Can be accessed by MyApp.canvas
        
    Field ballArray:Ball[]   
    
    Method OnCreate:Int()       
         canvas  = New Canvas()
        
        DW = DeviceWidth()   
        DH = DeviceHeight()               
        ballArray = New Ball[NumBalls]   
        Seed = Millisecs()   
        For Local i:Int = 0 Until ballArray.Length
            ballArray[i] = New Ball(0,Floor(Rnd(0.0,DW)),Floor(Rnd(0.0,DH)),Rnd(-5.0,5.0),Rnd(-5.0,5.0),1.0,0.001,10.0)
        Next           
        ' Lock to vertical blanking
        SetSwapInterval 1
        SetUpdateRate 0
        Return 0   
    End

    Method OnUpdate:Int()
        If KeyHit(KEY_ESCAPE) Then EndApp
        If TouchHit(0)
        For Local temp:Int = 0 Until 20 ' 0 until 0 = 0!
            NumBalls += 1
            ballArray = ballArray.Resize(NumBalls)
            ballArray[ballArray.Length - 1] = New Ball(0,TouchX(0),TouchY(0),Rnd(-5.0,5.0),Rnd(-5.0,5.0),1.0,0.001,10.0)
        Next
        End
        For Local i:Int = 0 Until ballArray.Length
            ballArray[i].Update
            For Local j:Int = 0 Until ballArray.Length
                If (i <> j) Then BallVsBall(ballArray[i],ballArray[j])
            End
        End       
        Return 0           
    End

    Method OnRender:Int()       
        canvas.Clear 0,0,0               
        canvas.DrawText "Touch or left click for additional balls",5,5 ' +String(NumBalls)
        For Local i:Int = 0 Until ballArray.Length
            ballArray[i].Render()
        End
        canvas.Flush
        Return 0           
    End
End

Class Ball

    Field kind:Int
    Field x:Float
    Field y:Float
    Field vx:Float
    Field vy:Float
    Field friction:Float
    Field mass:Float
    Field radius:Float
    
    Method New(k:Int,sx:Float,sy:Float,svx:Float,svy:Float,fric:Float,m:Float,r:Float)
        kind = k
        x = sx
        y = sy
        vx = svx
        vy = svy
        friction = fric
        mass = m
        radius = r
    End   
    
    Method Update:Void()
        vx = (vx * friction)                           
        vy = ((vy + GRAVITY) * friction)
        If kind <> 0 Then vx = 0 ; vy = 0
        x = (x + vx)               
        y = (y + vy)
        If ((x + radius) < 0.0) Then vx = -(vx)
        If (x > (DW - radius)) Then vx = -(vx) ; x = DW - radius
        If ((y + radius) < 0) Then vy = -(vy)       
        If (y > (DH - radius)) Then vy = -(vy) ; y = DH - radius
    End   
    
    Method Render:Void()
        MyApp.canvas.DrawOval(x - (radius / 2.0),y - (radius / 2.0),radius * 2.0,radius * 2.0)  ' Directly accessing the canvas object 
    End
End

Function BallVsBall:Void(b:Ball,b2:Ball)
    Local collisiondistance:Float = (b.radius + b2.radius)
    Local actualdistance:Float = GetDistance(b.x,b.y,b2.x,b2.y)
    If (actualdistance < collisiondistance)
        Local normal:Float = ATan2((b2.y - b.y),(b2.x - b.x))
        Local movedist1:Float = ((collisiondistance - actualdistance) * (b2.mass / Float((b.mass + b2.mass))))
        Local movedist2:Float = ((collisiondistance - actualdistance) * (b.mass / Float((b.mass + b2.mass))))
        b.x = (b.x + (movedist1 * Cos(normal + 180.0)))           
        b.y = (b.y + (movedist1 * Sin(normal + 180.0)))
        b2.x = (b2.x + (movedist2 * Cos(normal)))
        b2.y = (b2.y + (movedist2 * Sin(normal)))
        Local nx:Float = Cos(normal)                           
        Local ny:Float = Sin(normal)
        Local a1:Float = ((b.vx * nx) + (b.vy * ny))               
        Local a2:Float = ((b2.vx * nx) + (b2.vy * ny))
        Local opt:Float = ((2.0 * (a1 - a2)) / (b.mass + b2.mass))
        b.vx = (b.vx - (opt * b2.mass * nx))
        b.vy = (b.vy - (opt * b2.mass * ny))
        b2.vx = (b2.vx + (opt * b.mass * nx))
        b2.vy = (b2.vy + (opt * b.mass * ny))   
    Endif
End

Function GetDistance:Float(x1:Float,y1:Float,x2:Float,y2:Float)
    Return Sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))
End
 

Jimmy

New member
3rd Party Module Dev
Tutorial Author
Joined
Jan 2, 2020
Messages
569
Thanks for clearing that up, very much appreciated! It's coming along fine now.
 
Top Bottom