Re-virtual resolution

Jimmy

Active Member
Joined
Jan 2, 2020
I've been looking a long time for a efficient and easy way to create a virtual resolution on all devices including mobiles and desktops.

There's autofit, it's for mojo1 only but I was thinking of converting it only to discover it might not be a good idea, also it's blurry (which could be a postive thing sometimes but it was too blurry for my taste).

Next up was letterbox for mojo2, I don't know why but it just wasn't reliable on my Mac and phone, strangely offset and getting stuck in all kinds of resolutions. It might be me. But I tried for a some weeks.

The last resort would be to go for a framework, there are a few. This is not something I ever do so what I would do would be to dive into the frameworks just to see how they did it. I didn't have the energy to do that.

So I made this. Maybe someone else likes it too so let me share it.

What it does :
You tell what resolution you have and what you would like to have instead and it will
find the biggest integer scale that you can fit and centre it nicely on the screen, and it does this with an optional background!

EDIT if you wonder weather it's pixel-perfect if you happen to use your exact native screen as your virtual screen,
the answer is actually yes. It does not make a single misstake, so it's a nice base to have.

Cerberus X:
Import mojo2

Function Main ()
    New Game
End

Class Game Extends App

    Field screen2:Image
    Field screen2canvas:Canvas
    Field intwidth:Int, intheight:Int
    Field startx:Int, starty:Int
    Field canvas:Canvas
    Field size:Int=8
    Field cx:Int
    Field wx:Int
    Field wy:Int
    Field tilemap:Int[512*512]
    Field s:Int = 2
    Field background:Image

    ' --------------------------------
    ' Virtual resolution
    Field vwidth:Int = 320 ' 200'320
    Field vheight:Int = 200 ' 320'200

    ' Native resolution or windowsize
    Field awidth:Int = 1440 ' 720'1440
    Field aheight:Int = 900 ' 1280'900
    ' ---------------------------------

    Method OnCreate ()
        For Local y:=0 To 511
            For Local x:=0 To 511
                tilemap[x + y * 512] = Int(Rnd(127))
            Next
        Next
        background=Image.Load("80s.png",0,0,0)
        Image.SetFlagsMask(Image.Managed)
        SetDeviceWindow awidth,aheight,1 ' 4
        canvas=New Canvas
        screen2=New Image(vwidth,vheight,0,0,0)
        screen2.SetFlagsMask(screen2.Managed)
        screen2canvas=New Canvas(screen2)
        SetSwapInterval 1
        SetUpdateRate 0
        HideMouse
        Return 0
    End

    Method OnUpdate ()
        If KeyHit(KEY_ESCAPE) Then EndApp
        wx=wx-s*KeyDown(KEY_LEFT)+s*KeyDown(KEY_RIGHT)
        wy=wy-s*KeyDown(KEY_UP)+s*KeyDown(KEY_DOWN)
        wx=Max(0,Min(10000,wx))
        wy=Max(0,Min(10000,wy))
    End

    Method OnRender ()
        canvas.Clear 0,0,0
        ' Calculate how to put the biggest integer version of our wanted virtual resolution on the screen
        intwidth = Floor(Float(awidth)/Float(vwidth))
         intheight = Floor(Float(aheight)/Float(vheight))
         If intwidth > intheight Then intwidth = intheight
         If intheight > intwidth Then intheight = intwidth
         startx = (awidth-(vwidth*intwidth)) / 2
         starty = (aheight-(vheight*intheight)) / 2

         ' Done thinking, let's make it happen! Let's draw on our virtual screen, below follows an example
        screen2canvas.Clear 0,0,0
        screen2canvas.SetColor 1,1,1
        Local w=320
        Local h=200
        wx=wx+1 Mod 4096
        Local scrx:=wx Mod size
        Local scry:=wy Mod size
        Local mapx:=wx / size
        Local mapy:=wy / size
        Local cnty:= -scry
        For Local y:=mapy To mapy+((h/size)+1)
            Local cntx:=-scrx
            For Local x:=mapx To mapx+((w/size)+1)
                screen2canvas.DrawOval cntx,cnty,size,size
                cntx=cntx+size
            Next
            cnty=cnty+size
        Next
        screen2canvas.Flush

        ' Now draw everything onto the native canvas
        canvas.SetColor 1,1,1
        canvas.DrawRect 0,0,awidth,aheight,background,0,0,890,1213 ' hardcoded width and height of example image
        canvas.DrawImage screen2,startx,starty,0,intwidth,intheight
        canvas.Flush
    End
End
 

Attachments

Last edited:

Jimmy

Active Member
Joined
Jan 2, 2020
By the way if you don't care about integer scaling and what you want is to have everything as big
as possible on any screen while keeping the ratio intact, just change the two "FLOOR" into "FLOAT" and you are perfect to go. You can achieve this by just deleting the two FLOOR's actually.
 

Jimmy

Active Member
Joined
Jan 2, 2020
As a bonus version which gives you information how much room you've got in the border if any.

With this you can decide if you want to use the space for e.g. showing information such as score or whatever in the border instead of overlaying the gameplay area. It gives you this information in the form of pixels and percentages of the screen area.

Just make sure that you set your wanted virtual resolution, your Native resolution and finally the mode which is 1 for fullscreen or 4 for windowed.

EDIT Windowed mode is great for testing, and as an exercise you can try changing the virtual resolution and maybe set it to the same as the native resolution and sSee how different combinations react.


Cerberus X:
Import mojo2

Function Main ()
    New Game
End

Class Game Extends App
  
    Field screen2:Image
    Field screen2canvas:Canvas
    Field intwidth:Float, intheight:Float ' Float or Int, both works
    Field startx:Int, starty:Int
    Field canvas:Canvas
    Field size:Int=8
    Field cx:Int
    Field wx:Int
    Field wy:Int
    Field tilemap:Int[512*512]
    Field s:Int = 2
    Field background:Image
  
    ' --------------------------------
    ' Virtual res§olution
    Field vwidth:Int= 320' 200'320
    Field vheight:Int = 200 ' 320'200
  
    ' Native resolution or windowsize
    Field awidth:Int = 1440/4 ' 720'1440 < put anything you want here
    Field aheight:Int = 900/4 ' 1280'900 <
    ' ---------------------------------
  
    Method OnCreate ()
        For Local y:=0 To 511
            For Local x:=0 To 511
                tilemap[x + y * 512] = Int(Rnd(127))
            Next
        Next
        background=Image.Load("80s.png",0,0,0)
        Image.SetFlagsMask(Image.Managed)
        SetDeviceWindow awidth,aheight,4 '  < Put 4 here for Windowed and 1 for Fullscreen
        canvas=New Canvas
        screen2=New Image(vwidth,vheight,0,0,0)
        screen2.SetFlagsMask(screen2.Managed)
        screen2canvas=New Canvas(screen2)
        SetSwapInterval 1
        SetUpdateRate 0
        HideMouse
        Return 0
    End

    Method OnUpdate ()      
        If KeyHit(KEY_ESCAPE) Then EndApp      
        wx=wx-s*KeyDown(KEY_LEFT)+s*KeyDown(KEY_RIGHT)
        wy=wy-s*KeyDown(KEY_UP)+s*KeyDown(KEY_DOWN)
        wx=Max(0,Min(10000,wx))
        wy=Max(0,Min(10000,wy))
    End

    Method OnRender ()
        canvas.Clear 0,0,0
        ' Calculate how to put the biggest integer version of our wanted virtual resolution on the screen
        intwidth = Floor(Float(awidth)/Float(vwidth))
         intheight = Floor(Float(aheight)/Float(vheight))
       
         ' Uncomment if you don't care about integer scaling, you just want to fit everything as big as possible perfectly and keep the ratio
         ' intwidth = (Float(awidth)/Float(vwidth)) ' Lots of floats statements here, they're actually not all needed
         ' intheight = (Float(aheight)/Float(vheight))
       
         If intwidth > intheight Then intwidth = intheight
         If intheight > intwidth Then intheight = intwidth
         startx = (awidth-(vwidth*intwidth)) / 2
         starty = (aheight-(vheight*intheight)) / 2
       
         ' -------------------------------------------------------------------
         ' CALCULATE BORDERSIZE SO WE CAN CHOOSE TO WRITE SCORE AND LIVES NEATLY
         ' IF THERE'S ENOUGH SPACE ON A PARTICULAR SCREEN

        ' GET SIZE IN PIXELS :
         ' STARTX = HORISONTAL BORDERSIZE IN PIXEL, we already calculated above
         ' STARTY = VERTICAL BORDERSIZE IN PIXELS, we already calculated above
       
         ' GET SIZE IN PERCENTAGE :
            Local borderwidthpercetage:Float = Float(startx)/Float(awidth)
            Local borderheightpercentage:Float = Float(starty)/Float(aheight)
         ' -------------------------------------------------------------------
       
         ' Adding calcluating sides and also dynamic superpositioned left rght up down big area
       
         ' Done thinking, let's make it happen! Let's draw on our virtual screen
        screen2canvas.Clear 0,0,0
        screen2canvas.SetColor 1,1,1
        Local w=320
        Local h=200
        wx=wx+1 Mod 4096
        Local scrx:=wx Mod size
        Local scry:=wy Mod size
        Local mapx:=wx / size
        Local mapy:=wy / size  
        Local cnty:= -scry
        For Local y:=mapy To mapy+((h/size)+1)
            Local cntx:=-scrx
            For Local x:=mapx To mapx+((w/size)+1)
                screen2canvas.DrawOval cntx,cnty,size,size
                cntx=cntx+size
            Next
            cnty=cnty+size
        Next
        screen2canvas.Flush

        ' Now draw everything onto then native canvas
        canvas.SetColor 1,1,1
        canvas.DrawRect 0,0,awidth,aheight,background,0,0,890,1213 ' hardcoded width and height of example image
        canvas.DrawImage screen2,startx,starty,0,intwidth,intheight
  
        ' ---------------------------------------------------------------------------------------------------
        '
        ' If we have at least e.g. 1% of the screen height (or 20 pixels) to spare in the
        ' border then write score in the border instead of on top of the game screen
        '
        If borderheightpercentage > 0.01 ' If we want to check percentage of space the border takes of the screen
        ' If starty > 20                  ' If we want to check the number of pixels instead to get the feeling of how much room we've got to spare
            canvas.DrawText("SCORE GOES HERE", awidth / 2.0, (starty-10) / 2, .5)                   ' top border
            canvas.DrawText("SCORE GOES HERE", awidth / 2.0, aheight - ((starty+10) / 2), .5) ' bottom border
        Endif
        '
        ' ---------------------------------------------------------------------------------------------------
                  
        canvas.Flush
    End

    Method OnLoading:Int()
        Return 0
    End
  
    Method OnResume:Int()
        Return 0
    End
  
    Method OnSuspend:Int()
        Return 0
    End

End
 
Last edited:
Top Bottom