Hi,
I just wrote a small library handling rendering of advanced sprites (which could easily be changed to use DBPro sprites, Advanced2D-images (e.g. for particle systems using additive blending) or anything else), including a simple camera system with zoom and rotation. The advantage of AdvancedSprites over native DBPro sprites is that they can be rendered with anti aliasing, which really improves the image quality:
While there are a few artifacts in the AS area as well (I don't really know why, but part of the problem surely is the selection of bad source images, I didn't have anything more fancy at hand), it's probably quite clear that it looks a lot better than the DBP pixel mess on the right side.
Well, and apart from the improved visual quality, the library handles world to screen transformations and stuff like that for you and you don't have to worry about writing your own camera code for 2D games.
In case you don't have Advanced Sprites yet, you can get it
here.
And finally, here's the library:
Rem Project: AdvancedSpritesLib
Rem Created: Wednesday, February 19, 2014
rem Author: Shellfish Games
Rem ***** Main Source File *****
rem Trilinear Filtering
#constant AS__DEFAULT_FILTER = 3
type as_camType
x as float
y as float
zoom as float
angle as float
scrw2 as float
scrh2 as float
rot_sin as float
rot_cos as float
zoom1 as float
endtype
function as_init()
dxs initialize
dxs use filter AS__DEFAULT_FILTER
global as_cam as as_camType
as_cam.x = 0.0
as_cam.y = 0.0
as_cam.zoom = 1.0
as_cam.angle = 0.0
global as_outX as float = 0.0
global as_outY as float = 0.0
as_updateResolution()
endfunction
rem Loads Sprite Centered
function as_loadSprite(f$)
spr = as_loadSpriteOff(f$, 0.5, 0.5)
endfunction spr
rem Loads Sprite with given offset (should be between 0.0 and 1.0)
function as_loadSpriteOff(f$,ox#,oy#)
spr = dxs create sprite(f$)
dxs set sprite center spr, dxs get sprite width(spr)*ox#, dxs get sprite height(spr)*oy#
dxs set sprite scaling center spr, 0,0
dxs set sprite filter spr, AS__DEFAULT_FILTER
dxs update sprite spr
endfunction spr
rem Creates Sprite from Image Centered
function as_createSprite(img)
spr = as_createSpriteOff(img,0.5,0.5)
endfunction spr
rem Creates Sprite from Image with given Offset
function as_createSpriteOff(img,ox#,oy#)
spr = dxs create sprite from image(img)
dxs set sprite center spr, dxs get sprite width(spr)*ox#, dxs get sprite height(spr)*oy#
dxs set sprite scaling center spr, 0,0
dxs set sprite filter spr, AS__DEFAULT_FILTER
dxs update sprite spr
endfunction spr
rem Paste Sprite to Screen with the exact given parameters
function as_pasteSpriteRaw(spr, x#, y#, scale#, rotation#)
rem Scaling
scale# = 100*scale#
dxs set sprite scale spr, scale#,scale#
rem Rotation
dxs set sprite rotation center spr, x#, y#
dxs set sprite angle spr, rotation#
rem Apply Changes
dxs update sprite spr
rem Render
dxs begin sprite render spr
dxs draw sprite spr, x#, y#
dxs end sprite render spr
endfunction
rem Paste Sprite to World (coordinates, zoom and rotation will be chanced depending on camera)
function as_pasteSprite(spr, x#, y#, scale#, rotation#)
as_worldToScreen(x#,y#)
as_pasteSpriteRaw(spr, as_outX, as_outY, scale#*as_cam.zoom, rotation#-as_cam.angle)
endfunction
function as_setCam(x#,y#,zoom#,angle#)
as_cam.x = x#
as_cam.y = y#
as_cam.zoom = zoom#
as_cam.angle = angle#
as_cam.rot_sin = sin(-angle#)
as_cam.rot_cos = cos(-angle#)
as_cam.zoom1 = 1.0/zoom#
endfunction
function as_moveCam(fw#,sw#)
inc as_cam.x, sw#*as_cam.rot_cos + fw#*as_cam.rot_sin
inc as_cam.y, sw#*as_cam.rot_sin - fw#*as_cam.rot_cos
endfunction
rem Call this each time after you change your resolution within the code
function as_updateResolution()
as_cam.scrw2 = screen width()*0.5
as_cam.scrh2 = screen height()*0.5
endfunction
rem This is called when renderen a world sprite in order to get its screen coordinate
function as_worldToScreen(wx#,wy#)
rem Camera Position and Zoom
dx# = as_cam.zoom * (wx# - as_cam.x)
dy# = as_cam.zoom * (wy# - as_cam.y)
rem Rotation
as_OutX = as_cam.scrw2 + as_cam.rot_cos * dx# + as_cam.rot_sin * dy#
as_OutY = as_cam.scrh2 + as_cam.rot_sin * dx# - as_cam.rot_cos * dy#
endfunction
rem You may call this to transform screen cordinates (i.e. the mouse) to world coordinates
function as_screenToWorld(sx#,sy#)
rem De-Center
dec sx#, as_cam.scrw2
dec sy#, as_cam.scrh2
rem Inverse Rotation
x# = as_cam.rot_cos * sx# + as_cam.rot_sin * sy#
y# = as_cam.rot_sin * sx# - as_cam.rot_cos * sy#
rem Zoom and Position
as_OutX = as_cam.x + x# * as_cam.zoom1
as_OutY = as_cam.y + y# * as_cam.zoom1
endfunction
And the complete code that resulted in the screenshot above (without the comparison though, I did that in paint), no media required:
Rem Project: AdvancedSpritesLib
Rem Created: Wednesday, February 19, 2014
rem Author: Shellfish Games
Rem ***** Main Source File *****
#constant scrw = 1280
#constant scrh = 960
start:
set display mode scrw, scrh, 32, 0
set window on
sync on
sync rate 0
sync
as_init()
rem Create a few Images
set image colorkey 254,0,0
cls rgb(254,0,0)
ink 0xFFFF0000,0
box 20,20,80,80
get image 1, 0,0,100,100, 1
set image colorkey 0,254,0
cls rgb(0,254,0)
ink 0xFF00FF00,0
box 40,20,60,80
box 20,40,80,60
get image 2, 0,0,100,100, 1
set image colorkey 0,0,254
cls rgb(0,0,254)
ink 0xFF0000FF,0
set text font "Helvetica"
set text to bold
set text size 80
center text 256, 20, "HELLO WORLD"
get image 3, 0,0,512,128, 1
rem Make Text Readable again for Debug Output
set text size 24
rem Create Sprites
dim spr(3)
for i = 1 to 3
spr(i) = as_createSprite(i)
next
rem Draw
do
cls 0x808080
rem Camera
fac# = (0.3 + 2.7*shiftkey())
rl# = (rightkey() - leftkey())*fac#
ud# = (downkey() - upkey())*fac#
z# = 1.0 + 0.003*fac#*(keystate(17)-keystate(31))
ang# = 0.2*fac#*(keystate(32)-keystate(30))
as_setCam( as_cam.x, as_cam.y, as_cam.zoom*z#, as_cam.angle+ang# )
as_moveCam( ud#, rl# )
time = timer()
time = 121009
rem Draw Bunch of Sprites
for i = 1 to 100
randomize i
rem Initial Position
x# = 500-rnd(1000)
y# = 500-rnd(1000)
ang# = rnd(359)
scale# = 1.0 + 0.01*(rnd(100)-rnd(50))
rem Make Some Sprite Static and some Moving
if rnd(1) = 0
rem Moving Sprite
rad = 5+rnd(300)
spd# = rnd(100)*0.002
inc x#, rad*sin(time*spd#)
inc y#, rad*cos(time*spd#)
angspd# = rnd(100)*0.001
inc ang#, time*angspd#
endif
rem Render
sprID = 1 + rnd(2)
if sprID = 3 then scale#=0.5*scale#
as_pasteSprite(spr(sprID), x#, y#, scale#, ang#)
next
as_pasteSpriteRaw(spr(2), mousex(), mousey(), 2.0, time*0.1)
rem Output
ink 0xA0FFFFFF,0
print "FPS: ", screen fps()
print
print " Arrowkeys to Move"
print " W/S to Zoom"
print " A/D to Rotate"
print " Shift to Speed Up Movement"
sync
loop
rem Trilinear Filtering
#constant AS__DEFAULT_FILTER = 3
type as_camType
x as float
y as float
zoom as float
angle as float
scrw2 as float
scrh2 as float
rot_sin as float
rot_cos as float
zoom1 as float
endtype
function as_init()
dxs initialize
dxs use filter AS__DEFAULT_FILTER
global as_cam as as_camType
as_cam.x = 0.0
as_cam.y = 0.0
as_cam.zoom = 1.0
as_cam.angle = 0.0
global as_outX as float = 0.0
global as_outY as float = 0.0
as_updateResolution()
endfunction
rem Loads Sprite Centered
function as_loadSprite(f$)
spr = as_loadSpriteOff(f$, 0.5, 0.5)
endfunction spr
rem Loads Sprite with given offset (should be between 0.0 and 1.0)
function as_loadSpriteOff(f$,ox#,oy#)
spr = dxs create sprite(f$)
dxs set sprite center spr, dxs get sprite width(spr)*ox#, dxs get sprite height(spr)*oy#
dxs set sprite scaling center spr, 0,0
dxs set sprite filter spr, AS__DEFAULT_FILTER
dxs update sprite spr
endfunction spr
rem Creates Sprite from Image Centered
function as_createSprite(img)
spr = as_createSpriteOff(img,0.5,0.5)
endfunction spr
rem Creates Sprite from Image with given Offset
function as_createSpriteOff(img,ox#,oy#)
spr = dxs create sprite from image(img)
dxs set sprite center spr, dxs get sprite width(spr)*ox#, dxs get sprite height(spr)*oy#
dxs set sprite scaling center spr, 0,0
dxs set sprite filter spr, AS__DEFAULT_FILTER
dxs update sprite spr
endfunction spr
rem Paste Sprite to Screen with the exact given parameters
function as_pasteSpriteRaw(spr, x#, y#, scale#, rotation#)
rem Scaling
scale# = 100*scale#
dxs set sprite scale spr, scale#,scale#
rem Rotation
dxs set sprite rotation center spr, x#, y#
dxs set sprite angle spr, rotation#
rem Apply Changes
dxs update sprite spr
rem Render
dxs begin sprite render spr
dxs draw sprite spr, x#, y#
dxs end sprite render spr
endfunction
rem Paste Sprite to World (coordinates, zoom and rotation will be chanced depending on camera)
function as_pasteSprite(spr, x#, y#, scale#, rotation#)
as_worldToScreen(x#,y#)
as_pasteSpriteRaw(spr, as_outX, as_outY, scale#*as_cam.zoom, rotation#-as_cam.angle)
endfunction
function as_setCam(x#,y#,zoom#,angle#)
as_cam.x = x#
as_cam.y = y#
as_cam.zoom = zoom#
as_cam.angle = angle#
as_cam.rot_sin = sin(-angle#)
as_cam.rot_cos = cos(-angle#)
as_cam.zoom1 = 1.0/zoom#
endfunction
function as_moveCam(fw#,sw#)
inc as_cam.x, sw#*as_cam.rot_cos + fw#*as_cam.rot_sin
inc as_cam.y, sw#*as_cam.rot_sin - fw#*as_cam.rot_cos
endfunction
rem Call this each time after you change your resolution within the code
function as_updateResolution()
as_cam.scrw2 = screen width()*0.5
as_cam.scrh2 = screen height()*0.5
endfunction
rem This is called when renderen a world sprite in order to get its screen coordinate
function as_worldToScreen(wx#,wy#)
rem Camera Position and Zoom
dx# = as_cam.zoom * (wx# - as_cam.x)
dy# = as_cam.zoom * (wy# - as_cam.y)
rem Rotation
as_OutX = as_cam.scrw2 + as_cam.rot_cos * dx# + as_cam.rot_sin * dy#
as_OutY = as_cam.scrh2 + as_cam.rot_sin * dx# - as_cam.rot_cos * dy#
endfunction
rem You may call this to transform screen cordinates (i.e. the mouse) to world coordinates
function as_screenToWorld(sx#,sy#)
rem De-Center
dec sx#, as_cam.scrw2
dec sy#, as_cam.scrh2
rem Inverse Rotation
x# = as_cam.rot_cos * sx# + as_cam.rot_sin * sy#
y# = as_cam.rot_sin * sx# - as_cam.rot_cos * sy#
rem Zoom and Position
as_OutX = as_cam.x + x# * as_cam.zoom1
as_OutY = as_cam.y + y# * as_cam.zoom1
endfunction
Feel free to use this code for your own projects. Mentioning me in the credits would be nice, but well... I guess that's up to you.
Cheers.
Edit: Maybe I should add a little overview of the available functions and how to use them.
as_init() - should be called once after starting your program
as_loadSprite(f$) - loads a sprite file, centers it and returns handler (store it in a variable to access the sprite later)
as_loadSpriteOff(f$,offx#,offy#) - load sprite with given offset (relative to sprite size) - using 0.5,0.5 will center the sprite
as_createSprite(img) - creates an Advanced Sprite from an image, centers it and returns the handler
as_createSpriteOff(img,offx#,offy#) - you get the point
as_pasteSpriteRaw(spr, x#,y#, scale#, angle#) - pastes the advanced sprite (using the handler you were given when creating the sprite) to the given
screen coordinates
as_pasteSprite(spr, x#,y#, scale#, angle#) - pastes the advanced sprite to the given
world coordinates
as_setCam(x#,y#,zoom#,angle#) - sets the camera state, x# and y# being the world coordinates of the cam's center, zoom# is 1.0 by default (2.0 meaning everything is twice as big)
as_moveCam(forward#, sideways#) - can be used to make the camera move relative to its angle. Both values can be positive or negative and higher values will make the cam move faster.
as_updateResolution() - should be called if you change the screen resolution while the program is running, to make sure the camera knows what's going on
as_worldToScreen(wx#,wy#) - Transforms world to screen coordinates and stores them in the global variables as_OutX/as_OutY. Usually you don't need to call this function, as as_pasteSprite() takes care of that
as_screenToWorld(sx#,sy#) - Transforms screen to world coordinates and stores them in as_OutX/as_OutY. You can for instance use this function to find out what the woorld coordinate of the mouse is.
And a minimal usage example:
rem Set up Display
set display mode <width>, <height>, 32
sync on : sync rate 0 : sync
rem Initialize Library
as_init()
rem Load some Sprites
spr1 = as_loadSprite("mySprite.png")
spr2 = as_loadSpriteOff("myCharacter.png", 0.5, 1.0) //set sprite center to center of lower edge
spr3 = as_createSprite(someImageThatWasAlreadyLoaded)
rem Main Loop
do
cls
rem Set Camera
as_setCam(0,0, 4.0, timer()*0.01) //Zoomed in, slightly rotating
rem Draw a few sprites to the World
as_pasteSprite(spr1, 0,0, 0.5, 0) //Scaled down to 50%
as_pasteSprite(spr2, 0,300, 1.0, -timer()*0.05) //Rotating
rem Draw third sprite as cursor
as_pasteSpriteRaw(spr3, mousex(), mousey(), 1.0, 0.0)
rem Get World Coordinate of Mouse
as_screenToWorld(mousex(), mousey())
print "Coord: ", as_OutX, ",", as_OutY
sync
loop
Note: Right now the library does not support animated sprites or tilesets. Adding those things wouldn't be a big deal, so just ask if you need it (or add it yourself
).
NoteĀ²: Also, the rendering could be optimized. Right now
dxs begin sprite render and
dxs end sprite render are called before and after
every single sprite drawing operation. I could add some simple optimization, but haven't done it yet since it would make the code and usage (slightly) more complicated.