Log In  


I was only able to find a few examples of rotating sprites using tlines on the forums. While the code worked, I couldn't really make sense of all the variables and magic numbers.

After spending quite a lot of time reading, debugging, and sketching hundreds of triangle, I think I have a decent understanding of the math and how it works. SOH-CAH-TOA haunts my dreams.
At some point, I'd like to make a more detailed post, but for now, I've written a tile rotating function with a lot of comments.

Hopefully it explains the math and the concept well enough that you can modify the code to suit your projects.
This code is intentionally not optimized or minified.

Cart #tline_rotation_tutorial-1 | 2021-11-07 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
15

-- rtile

-- draw rotated map tile
-- x,y: output will be centered here
-- tx,ty: map coords to the
--        top,left tile to draw
-- ang: rotation angle
-- scale: scale factor
--        default:1
-- tw,th: tiles to read
--        default:1
-- tcxo,tcyo:
--        tile center offsets
--        in tile units
--        default:0
-- nb: leave space around the
-- tiles to rotate on the map
-- or adjacent tiles may end
-- up in the rotated output.
function rtile(x,y,tx,ty,ang,
 scale,tw,th,tcxo,tcyo)

 local scale,tw,th=
  scale or 1,tw or 1,th or 1
 local tcxo,tcyo=
  tcxo or 0,tcyo or 0

 -- 8 is base tile->pixel scale
 scale*=8

 -- precalculate a few values
 -- we need. using -sin because
 -- pico-8's sin func is inverted.
 local ca,sa=cos(ang),-sin(ang)

 -- there are two coordinate
 -- domains here, the input
 -- domain is in tile units
 -- and each tile has a domain
 -- of [0..1). the output domain
 -- is in pixels. we'll use "t"
 -- and "p" prefixes.

 -- what we're doing here
 -- is reading the source tile
 -- as a bunch of slices that
 -- stack up to form a square.
 -- we rotate that "reading"
 -- square and it changes how
 -- the input is read. the
 -- way we do this is with a
 -- rotation matrix that we
 -- apply to each point along
 -- one side of the "read"
 -- square.

 -- we need to know the center
 -- of the square for computing
 -- rotations. apply the center
 -- offsets here.
 -- tile center coords
 local tcx,tcy=
  tx+tcxo+tw/2,ty+tcyo+th/2

 -- we're going to read the tile
 -- in a rotating square. if we
 -- use radius=tw/2, the corners
 -- will be clipped. we need the
 -- length from center to corner
 -- to use as the radius. we
 -- need to find the larger
 -- of the two radii to ensure
 -- the output fits.
 -- todo: make more efficient.
 -- tile radius
 local trad=max(
  sqrt((tx-tcx)^2+(ty-tcy)^2),
  sqrt((tx+tw-tcx)^2+(ty+th-tcy)^2))

 -- to output the texture we
 -- draw a bunch of horizontal
 -- tlines. think of it as
 -- drawing scan lines on a
 -- display. using the p
 -- prefix to indicate "pixel".

 -- in order for the output to
 -- be the correct scale and
 -- not clip, we need to have
 -- a output space which can
 -- hold the rotated output.
 -- scale trad by 'scale' to
 -- get a pixel radius that
 -- will hold everything we
 -- need to draw.
 -- pixel radius
 local prad=scale*trad

 -- line scale is the ratio
 -- of read len/write len.
 -- this just makes a few
 -- calculations clearer.
 local lns=1/scale

 -- remember there are two domains.
 -- as we draw the output lines
 -- 0 to 2*prad, we increment
 -- from 0 to 2*trad proportionally.
 -- for every 1 pixel y in the
 -- output, we increment toffy
 -- by tstep.
 -- toffy range is -trad,trad
 local tstep,toffy=lns,-trad

 -- we also need to tell tline
 -- the step of the read lines.
 -- this controls how "far"
 -- each line reads in the tile
 -- domain. we want the length
 -- of the "read" line to be
 -- scaled to the length of an
 -- output line.
 local tdx,tdy=ca*lns,sa*lns

 -- now lets actually rotate the
 -- "read" lines. here's the formula.
 -- [cos, -sin][tx]
 -- [sin,  cos][ty]
 -- because tx is not varied in
 -- the draw loop, we can pre-
 -- compute some values here.
 -- -trad is the "left" side
 -- of the read line. it's an
 -- x offset from the tile center.
 local costx=ca*-trad
 local sintx=sa*-trad

 -- visualize the output space.
 rect(x-prad,y-prad,x+prad,y+prad,5)

 -- we're going to draw a rect
 -- line by line, top down.
 -- because we're centering on
 -- x and y, add and subtract
 -- the pixel radius.
 for poffy=-prad,prad do
  -- now compute the ty values
  -- which is just the ty offset
  local costy=ca*(toffy)
  local sinty=sa*(toffy)

  tline(
   x-prad,y+poffy,
   x+prad,y+poffy,
   tcx+costx-sinty,
   tcy+sintx+costy,
   tdx,tdy)

  toffy+=tstep
 end
end

Here's the same code without comments if you just want to copy/paste in your project.

-- draw rotated map tile
-- x,y: output will be centered here
-- tx,ty: map coords to the
--        top,left tile to draw
-- ang: rotation angle
-- scale: scale factor
--        default:1
-- tw,th: tiles to read
--        default:1
-- tcxo,tcyo:
--        tile center offsets
--        in tile units
--        default:0
function rtile(x,y,tx,ty,ang,
 scale,tw,th,tcxo,tcyo)

 local scale,tw,th=
  scale or 1,tw or 1,th or 1
 local tcxo,tcyo=
  tcxo or 0,tcyo or 0
 scale*=8

 local ca,sa=cos(ang),-sin(ang)
 local tcx,tcy=
  tx+tcxo+tw/2,ty+tcyo+th/2

 local trad=max(
  sqrt((tx-tcx)^2+(ty-tcy)^2),
  sqrt((tx+tw-tcx)^2+(ty+th-tcy)^2))
 local prad=scale*trad

 local lns=1/scale
 local tstep,toffy=lns,-trad
 local tdx,tdy=ca*lns,sa*lns

 local costx=ca*-trad
 local sintx=sa*-trad

 for poffy=-prad,prad do
  local costy=ca*(toffy)
  local sinty=sa*(toffy)

  tline(
   x-prad,y+poffy,
   x+prad,y+poffy,
   tcx+costx-sinty,
   tcy+sintx+costy,
   tdx,tdy)

  toffy+=tstep
 end
end
15



[Please log in to post a comment]