The Final Frontier

@Bri_G I modified this on my iPhone to remove the simulated screen sizes. I’ll see what it looks like on my other devices.

It looked Ok on my iPad Air 3.
It looked Ok on my iPad Pro.
It looked Ok on my iPhone 8

So this code looked Ok on all 3 of my devices.

It’s a matter of scaling the sprite images, the text, the screen positions, ellipse sizes, touch positions, etc.

viewer.mode=FULLSCREEN

function setup()
  img=readImage(asset.documents.Dropbox.Planetarium3)
  iw=img.width
  ih=img.height
  stroke(255)
  strokeWidth(2)
  tab={vec2(200,100),vec2(900,100),vec2(200,600),vec2(900,600)}
  sxRatio=WIDTH/1112
  syRatio=HEIGHT/834
  pl=nil
end

function draw()
  background(0)
  if WIDTH<HEIGHT then
      text("Rotate device to lanscape, then restart",WIDTH/2,HEIGHT/2)
      return
  end
  noFill()
  rect(0,0,WIDTH,HEIGHT)
  drawSprite()
  drawText()
  for a,b in pairs(tab) do
      ellipse(b.x*sxRatio,b.y*syRatio,5)
  end
  if pl~=nil then
      noFill()
      ellipse(pl.x,pl.y,40*sxRatio,40*syRatio)
  end
end

function drawSprite()   -- draw sprite 
  if not hit then
      sprite(img,WIDTH/2,HEIGHT/2,sxRatio*iw,syRatio*ih)  
  end
end

function drawText()
  fill(255)
  fontSize(20*sxRatio)
  text("qwertyuiop",WIDTH/2,HEIGHT/2+300*syRatio)
  text("asdfghjkl",WIDTH/2,HEIGHT/2+240*syRatio)
  text("zxcvbnm",WIDTH/2,HEIGHT/2)
end

function touched(t)
  if t.state==BEGAN then
      hit=false
      pl=nil
      distX=58*sxRatio
      distY=58*syRatio
      if t.x>WIDTH/2-distX and t.x<WIDTH/2+distX and t.y>HEIGHT/2-distY-280*syRatio and t.y<HEIGHT/2+distY-280*syRatio then
          hit=true
      end
      for a,b in pairs(tab) do
          if t.x>b.x*sxRatio-30 and t.x<b.x*sxRatio+30 and t.y>b.y*syRatio-30 and t.y<b.y*syRatio+30 then
              pl=vec2(b.x*sxRatio,b.y*syRatio)
          end
      end
  end
  if t.state==ENDED then
      hit=false
  end
end

I recently spent a good deal of time working on scaling things to different screen dimensions, maybe some of my wrangling can help.

So the big challenge is that, I realized, there are two different categories of measurements I need to calculate:

  • Some measurements–in general, the ones concerning the size of things being drawn–need to prioritize their ratio to each other over their ratio to the size of the device
  • Some measurements–in general, the ones concerning positioning of things being drawn–need to prioritize their ratio to the screen dimensions more than their ratio to each other

…So in the example of the control-interface-device thingy in your project, you need it to fit on whatever screen it’s drawn on, and you need it to be in roughly the same place on whatever screen it’s drawn on, but you also need its internal elements to keep the same ratios to each other no matter what the dimensions of the current screen are, so that it doesn’t look all weird and distorted on some screens.

To keep from getting confused between these categories I ended up making two different units of measurement:


  rltvUnit = vec2(WIDTH, HEIGHT) --for units that prioritize relationship to the screen
  abslUnit = math.min(WIDTH, HEIGHT) --for units that prioritize relationship to each other
  --then I did a calibration to convert the absolute unit to a tenth of the screen height:
  abslUnit = abslUnit/10

So for example, in your case, if you need to set the size of the background sprite, you would use:


sprite([background asset], rltvUnit.x, rltvUnit.y)

But to make the boundaries of the control device, you would use something like:


  --guaranteed to make control's height fit the screen size in 
  --landscape mode, which works because abslUnit is a 10th of the height:
  controlHeight = abslUnit * 9
  --guaranteed to make the control's own bounds keep the same ratio of 
  --self-width to self-height no matter what device is being used:
  controlWidth = abslUnit * 4
  --then draw it:
  rect(300, 300, controlHeight, controlWidth)

…and then to place that control, you’d do something like:


  --(assuming rectMode is CENTER)
  --make the control's top always align with the top of the screen, using 4.5 to
  --get half of its height because we used 9 for its overall height:
  controlY = rltvUnit.y - (abslUnit * 4.5)
  --make the control's middle always align with the right third of the screen,
  --using 2 to be half its width because we used 4 for its overall width:
  controlX = (rltvUnit.x * 0.6666) - (abslUnit * 2)
  --draw it however you do:
  control.draw(controlY, controlX)

…now, for some people’s brains this might seem irrelevant and even redundant.

Could this all be done without separating them into different units? Couldn’t you just base all these calculations off the screen sizes directly, since really abslUnit and rltvUnit ultimately are both just different multiples of the same root numbers?

…and yes, you could, and yes, they are, but for folks with brains somewhere around my own size, that’s going to get pretty confusing pretty quickly. And once I found this approach, it became much faster and much easier to control my project’s reactions to different screen sizes.

Anyway, YMMV, but that’s something that helped me a ton when I found it.

@UberGoober - thanks for the input. Any approach, which differs for your own, broadens your knowledge and is there when you need it.

I tend to use aspect ratio a lot for sprites etc.