A couple of people had asked for a way to rotate the AppGameKit camera. (We've all seen it coating the bug reports) As there are no built in solutions, I put together some code to do so. While I was at it, I included my motion based zoom trick as well, just in case anyone was curious(although it is easy to achieve). I chose to not implement full control over the zoom because the effect is already so easily achieved with the setViewZoom() command. Whether or not you're looking for full code, or just a way to get started writing your own code, this is a good place to look. Any questions or potential improvements are welcome.
Command Set
Full 2D Camera - swissolo
Command Set
///////////|\\\\\\\\\\\\\
initCamera(x as float, y as float, angle as float, ZSensitivity as float, ZReturn as float, minZoom as float)
x as float - the starting x position of your camera.
y as float - the starting y position of your camera.
angle as float - the starting angle of your camera.
ZSensitivity as float - determines how sensitive the camera is to movement resulting in zooming out. [smaller values are less sensitive]
ZReturn as float - factor in the rate at which the camera zoom will return to normal.
minZoom as float - the cap on the camera's ability to zoom out.
////
Sets up the camera. Call this command first!
clearCamera()
////
Empties the camera array and restores the objects to their former positions.
dropCamera()
////
Empties the camera array without restoring the objects to their former positions.
updateCamera()
////
Controls camera angle, position, and zoom. Should be called each loop.
addCamObject(spriteID as integer)
spriteID as integer - the ID of the sprite being added to the array.
////
Adds the given sprite into the camera's control. It should be noted, from this point on, that altering the sprite's position or angle should now be done with the provided commands(setCamSpritePosition()/setCamSpriteAngle())
removeCamObject(spriteID as integer) / removeCamObjectArr(ID as integer)
spriteID as integer - the ID of the sprite.
|--ID as integer - accepts the camera array slot of the sprite instead of searching for it.
////
Removes the given sprite from the camera's control. It will not be returned to it's former location. This should be done with the command restoreCamSprite().
restoreCamSprite(spriteID as integer) / restoreCamSpriteArr(ID as integer)
spriteID as integer - the ID of the sprite.
|--ID as integer - accepts the camera array slot of the sprite instead of searching for it.
////
Returns the sprite to its original position.
integer getCamSlot(spriteID as integer)
spriteID as integer - the ID of the sprite.
////
Locates and returns the camera array slot in which the sprite is being stored.
setCamSpritePosition(spriteID as integer, x as float, y as float) / setCamSpritePositionArr(ID as integer, x as float, y as float)
spriteID as integer - the ID of the sprite.
|--ID as integer - accepts the camera array slot of the sprite instead of searching for it.
x as float - the new desired x location of the sprite.
y as float - the new desired y location of the sprite.
////
Positions the sprite properly within the camera's internals. (This is for sprites already in the camera array. It can be checked with getCamSpriteExists())
setCamSpriteAngle(spriteID as integer, angle as float) / setCamSpriteAngleArr(ID as integer, angle as float)
spriteID as integer - the ID of the sprite.
|--ID as integer - accepts the camera array slot of the sprite instead of searching for it.
angle as float - the new desired angle of the sprite.
////
Rotates the sprite properly within the camera's internals. (This is for sprites already in the camera array. It can be checked with getCamSpriteExists())
setCameraPosition(x as float, y as float)
x as float - the new desired x location of the camera.
y as float - the new desired y location of the camera.
float getCamSpriteX(spriteID as integer) / getCamSpriteXArr(ID as integer)
spriteID as integer - the ID of the sprite.
|--ID as integer - accepts the camera array slot of the sprite instead of searching for it.
////
Returns the sprite's x position independent of rotation. (to check the sprite's position relative to camera rotation, call the getSpriteX() command as usual)
float getCamSpriteY(spriteID as integer) / getCamSpriteYArr(ID as integer)
spriteID as integer - the ID of the sprite.
|--ID as integer - accepts the camera array slot of the sprite instead of searching for it.
////
Returns the sprite's y position independent of rotation. (to check the sprite's position relative to camera rotation, call the getSpriteY() command as usual)
float getCamSpriteAngle(spriteID as integer) / getCamSpriteAngleArr(ID as integer)
spriteID as integer - the ID of the sprite.
|--ID as integer - accepts the camera array slot of the sprite instead of searching for it.
////
Returns the sprite's angle independent of rotation. (to check the sprite's position relative to camera rotation, call the getSpriteAngle() command as usual)
integer getCamSpriteExists(spriteID as integer)
spriteID as integer - the ID of the sprite.
////
Returns whether or not the sprite is in the camera array.
float getCameraX()
////
Returns the camera's x position independent of rotation. (this is the camera's center point)
float getCameraY()
////
Returns the camera's y position independent of rotation. (this is the camera's center point)
float getCameraZ()
////
Returns the camera's zoom.
float camConvertX(x as float, y as float)
x as float - the x coordinate being converted.
y as float - the y coordinate being converted.
///
Takes a non-rotated point, relative to the camera, and returns the x coordinate of a rotated one. This can be useful when repositioning objects external to the camera array or when writing your own piece of rotating camera code.
camConvertXFree(x as float, y as float, camAngle as float, camX as float, camY as float)
x as float - the x coordinate being converted.
y as float - the y coordinate being converted.
camAngle as float - the camera's angle.
camX as float - the x position of the camera.
camY as float - the y position of the camera.
///
Takes a non-rotated point, relative to the camera, and returns the x coordinate of a rotated one. This command is identical to CamConvertX(), but it allows for separate camera coordinates to be passed in.
float camConvertY(x as float, y as float)
x as float - the x coordinate being converted.
y as float - the y coordinate being converted.
///
Takes a non-rotated point, relative to the camera, and returns the y coordinate of a rotated one. This can be useful when repositioning objects external to the camera array or when writing your own piece of rotating camera code.
camConvertYFree(x as float, y as float, camAngle as float, camX as float, camY as float)
x as float - the x coordinate being converted.
y as float - the y coordinate being converted.
camAngle as float - the camera's angle.
camX as float - the x position of the camera.
camY as float - the y position of the camera.
///
Takes a non-rotated point, relative to the camera, and returns the y coordinate of a rotated one. This command is identical to CamConvertY(), but it allows for separate camera coordinates to be passed in.
camConvertSprite(spriteID as integer) / camConvertSpriteArr(ID as integer)
spriteID as integer - the ID of the sprite.
|--ID as integer - accepts the camera array slot of the sprite instead of searching for it.
////
takes a sprite that is already inserted into the camera array and repositions it relative to camera rotation. This command is almost identical to the code within the camera update for individual sprite updates, but has been provided in order to make use of such a command, outside of the provided code, easier.
camConvertSpriteFree(spriteID as integer, orginX as float, orginY as float, orginAngle as float)
spriteID as integer - the ID of the sprite.
|--ID as integer - accepts the camera array slot of the sprite instead of searching for it.
orginX as float - the un-camera-rotated x of this sprite.
orginY as float - the un-camera-rotated y of this sprite.
orginAngle as float - the un-camera-rotated angle of this sprite.
////
takes any sprite not in the camera array and repositions it relative to camera rotation.
Code - no media required
remstart
Full 2D Camera
swissolo
remend
setSyncRate(66,0)
setVirtualResolution(100, 60) //I use a resolution of 1280 x 800 in my setup.agc
setOrientationAllowed(0, 0, 0, 1)
////
initCamera(getVirtualWidth()/2.0, getVirtualHeight()/2.0, 0.0, 20.0, 4.0, 0.3)
////
gosub init
do
setPrintSize(1.5)
print("FPS: "+str(screenFPS()))
print("Angle: "+str(cameraAngle))
print("X(10): "+str(camConvertX(10,10))+" | Y(10): "+str(camConvertY(10,10))) //provide an example, for the conversion, at various given angles
cameraX = getPointerX()
cameraY = getPointerY()
if getRawMouseLeftState() then cameraAngle = cameraAngle - 0.5
if getRawMouseRightState() then cameraAngle = cameraAngle + 0.5
//cameraAngle = getDirectionAngle() //if have a mobile device, this adds a cool affect. Be warned that AGK doesn't provide control over the accelerometer frequency which results in a choppy appearence. This is not a code based problem.
updateCamera()
sync()
loop
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
init:
//variables
global worldScale as float = 4.0
global worldX as integer : worldX = round(getVirtualWidth()/worldScale)
global worldY as integer : worldY = round(getVirtualHeight()/worldScale)
//construct world
addWorld()
return
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function initCamera(x as float, y as float, angle as float, ZSensitivity as float, ZReturn as float, minZoom as float)
global camObjects as integer = 0
dim camera[camObjects] as camera_UDT
//
global cameraX as float : cameraX = x
global cameraY as float : cameraY = y
global cameraZ as float = 1.0
global cameraAngle as float : cameraAngle = angle
global cameraOldAngle as float : cameraOldAngle = -angle //force an update of the first frame
global cameraZSensitivity as float : cameraZSensitivity = ZSensitivity //smaller = more sensitive
global cameraZReturn as float : cameraZReturn = ZReturn //greater = faster return
global cameraZMinZoom as float : cameraZMinZoom = minZoom //the cap on zooming out
global cameraOldX as float = cameraX
global cameraOldY as float = cameraY
endfunction
function clearCamera() //resets sprites
for n = 0 to camObjects-1
setSpritePosition(camera[n].ID, camera[n].x, camera[n].y)
setSpriteAngle(camera[n].ID, camera[n].angle)
next n
camObjects = 0
dim camera[camObjects] as camera_UDT
endfunction
function dropCamera() //leaves sprites rotated
camObjects = 0
dim camera[camObjects] as camera_UDT
endfunction
function updateCamera()
if cameraAngle <> cameraOldAngle or cameraX <> cameraOldX or cameraY <> cameraOldY //only if an update is needed
//camera angle
if cameraAngle >= 360 then cameraAngle = 0
if cameraAngle < 0 then cameraAngle = 360
cosVal as float : cosVal = cos(cameraAngle)
sinVal as float : sinVal = sin(cameraAngle)
for n = 0 to camObjects-1 //loop all objects
setSpriteAngle(camera[n].ID, cameraAngle+camera[n].angle)
x as float : y as float
x = cosVal*(camera[n].x-cameraX)-sinVal*(camera[n].y-cameraY)
y = sinVal*(camera[n].x-cameraX)+cosval*(camera[n].y-cameraY)
setSpritePosition(camera[n].ID, cameraX+x, cameraY+y)
next n
cameraOldAngle = cameraAngle
//cameraOldX = cameraX //these values are also needed in zoom. They are updated every frame below
//cameraOldY = cameraY
endif
//zoom
velX as float : velX = cameraX-cameraOldX
velY as float : velY = cameraY-cameraOldY
velocity as float : velocity = sqrt((velX*velX) + (velY*velY))/cameraZSensitivity // /cameraZSensitivity is the cushion
if velocity > cameraZMinZoom then velocity = cameraZMinZoom
cameraZ = (cameraZ+(1-velocity)*getFrameTime()*cameraZReturn)/(1+getFrameTime()*cameraZReturn)
cameraOldX = cameraX
cameraOldY = cameraY
//assign
setViewOffset(cameraX-(getVirtualWidth()/2.0)/cameraZ, cameraY-(getVirtualHeight()/2.0)/cameraZ)
setViewZoom(cameraZ)
endfunction
function addCamObject(spriteID as integer)
camObjects = camObjects + 1 //increase array count
dim camera[camObjects] as camera_UDT //resize to the new - larger - value
camera[camObjects-1].ID = spriteID //fill the new slot
camera[camObjects-1].x = getSpriteX(spriteID)
camera[camObjects-1].y = getSpriteY(spriteID)
camera[camObjects-1].angle = getSpriteAngle(spriteID)
setSpriteOffset(spriteID, getSpriteWidth(camera[n].ID)/2.0, getSpriteHeight(camera[n].ID)/2.0) //must rotate from center
camConvertSpriteArr(camObjects-1) //reposition the sprite immediately to ensure it is correctly placed if the camera has not rotated
endfunction
function removeCamObject(spriteID as integer)
if camObjects > 0 //if there is anything to remove
ID = getCamSlot(spriteID)
if ID <> -1 //make sure it exists in our array
if ID <> camObjects-1
for n = ID to camObjects-1
camera[n] = camera[n+1] //copy over the old values overtop of our uneeded one
next n
endif
camObjects = camObjects - 1 //decrease the array counter
endif
dim camera[camObjects] as camera_UDT //resize to the new - smaller - value
endif
endfunction
function removeCamObjectArr(ID as integer)
if camObjects > 0 //if there is anything to remove
if ID <> -1 //make sure it exists in our array
camObjects = camObjects - 1 //decrease the array counter
if ID <> camObjects-1
for n = ID to camObjects-1
camera[n] = camera[n+1] //copy over the old values overtop of our uneeded one
next n
endif
endif
dim camera[camObjects] as camera_UDT //resize to the new - smaller - value
endif
endfunction
function restoreCamSprite(spriteID as integer)
ID = getCamSlot(spriteID)
setSpritePosition(camera[ID].ID, camera[ID].x, camera[ID].y)
setSpriteAngle(camera[ID].ID, camera[ID].angle)
endfunction
function restoreCamSpriteArr(ID as integer)
setSpritePosition(camera[ID].ID, camera[ID].x, camera[ID].y)
setSpriteAngle(camera[ID].ID, camera[ID].angle)
endfunction
function getCamSlot(spriteID as integer) //find the sprite in the camera array - returns -1 not in array
output as integer = -1
for n = 0 to camObjects-1
if camera[n].ID = spriteID
output = n
exit // break from the loop - we've found our sprite
endif
next n
endfunction output
function setCamSpritePosition(spriteID as integer, x as float, y as float)
ID = getCamSlot(spriteID)
if ID <> -1
camera[ID].x = x
camera[ID].y = y
endif
endfunction
function setCamSpritePositionArr(ID as integer, x as float, y as float)
if ID <> -1
camera[ID].x = x
camera[ID].y = y
endif
endfunction
function setCamSpriteAngle(spriteID as integer, angle as float)
ID = getCamSlot(spriteID)
if ID <> -1
camera[ID].angle = angle
endif
endfunction
function setCamSpriteAngleArr(ID as integer, angle as float)
if ID <> -1
camera[ID].angle = angle
endif
endfunction
function setCameraPosition(x as float, y as float)
cameraX = x
cameraY = y
endfunction
function getCamSpriteX(spriteID as integer)
ID = getCamSlot(spriteID)
if ID <> -1
output as float : output = camera[ID].x
endif
endfunction output
function getCamSpriteXArr(ID as integer)
if ID <> -1
output as float : output = camera[ID].x
endif
endfunction output
function getCamSpriteY(spriteID as integer)
ID = getCamSlot(spriteID)
if ID <> -1
output as float : output = camera[ID].y
endif
endfunction output
function getCamSpriteYArr(ID as integer)
if ID <> -1
output as float : output = camera[ID].y
endif
endfunction output
function getCamSpriteAngle(spriteID as integer)
ID = getCamSlot(spriteID)
if ID <> -1
output as float : output = camera[ID].angle
endif
endfunction output
function getCamSpriteAngleArr(ID as integer)
if ID <> -1
output as float : output = camera[ID].angle
endif
endfunction output
function getCamSpriteExists(spriteID as integer)
if getCamSlot(spriteID) = -1 then output = 0 else output = 1
endfunction output
function getCameraX()
output = cameraX
endfunction output
function getCameraY()
output = cameraY
endfunction output
function getCameraZ()
output = cameraZ
endfunction output
function camConvertX(x as float, y as float)
x = cameraX+cos(cameraAngle)*(x-cameraX)-sin(cameraAngle)*(y-cameraY)
endfunction x
function camConvertXFree(x as float, y as float, camAngle as float, camX as float, camY as float)
x = cameraX+cos(camAngle)*(x-camX)-sin(camAngle)*(y-camY)
endfunction x
function camConvertY(x as float, y as float)
y = cameraY+sin(cameraAngle)*(x-cameraX)+cos(cameraAngle)*(y-cameraY)
endfunction y
function camConvertYFree(x as float, y as float, camAngle as float, camX as float, camY as float)
y = cameraY+sin(camAngle)*(x-camX)+cos(camAngle)*(y-camY)
endfunction y
function camConvertSprite(spriteID as integer) //repositions the sprite passed in (a separate piece of updateCamera)
cosVal as float : cosVal = cos(cameraAngle)
sinVal as float : sinVal = sin(cameraAngle)
ID = getCamSlot(spriteID)
setSpriteAngle(camera[ID].ID, cameraAngle+camera[ID].angle)
x as float : y as float
x = cosVal*(camera[ID].x-cameraX)-sinVal*(camera[ID].y-cameraY)
y = sinVal*(camera[ID].x-cameraX)+cosval*(camera[ID].y-cameraY)
setSpritePosition(camera[ID].ID, cameraX+x, cameraY+y)
endfunction
function camConvertSpriteArr(ID as integer) //repositions sprite in the array slot passed in (a separate piece of the main update) - [faster than above command if used in loop]
cosVal as float : cosVal = cos(cameraAngle)
sinVal as float : sinVal = sin(cameraAngle)
setSpriteAngle(camera[ID].ID, cameraAngle+camera[ID].angle)
x as float : y as float
x = cosVal*(camera[ID].x-cameraX)-sinVal*(camera[ID].y-cameraY)
y = sinVal*(camera[ID].x-cameraX)+cosval*(camera[ID].y-cameraY)
setSpritePosition(camera[ID].ID, cameraX+x, cameraY+y)
endfunction
function camConvertSpriteFree(spriteID as integer, orginX as float, orginY as float, orginAngle as float) //repositions the sprite passed in (this sprite does not need to be in the camera array)
cosVal as float : cosVal = cos(cameraAngle)
sinVal as float : sinVal = sin(cameraAngle)
setSpriteAngle(spriteID, cameraAngle+orginAngle)
x as float : y as float
x = cosVal*(orginX-cameraX)-sinVal*(orginY-cameraY)
y = sinVal*(orginX-cameraX)+cosval*(orginY-cameraY)
setSpritePosition(camera[ID].ID, cameraX+x, cameraY+y)
endfunction
type camera_UDT
ID as integer
x as float
y as float
angle as float
endtype
////////////////////////////////////////////////////////////////
function addWorld()
for x = 0 to worldX - 1
for y = 0 to worldY - 1
if random(0, 1) = 1
temp = createSprite(0)
scale as float : scale = worldScale/getSpriteWidth(temp) //scale to 1 grid unit
setSpriteScale(temp, scale, scale)
setSpritePosition(temp, x*worldScale, y*worldScale)
setSpriteAngle(temp, random(0,360)) //comment this line to see a pretty grid - this line proves that all angles are still relative
addCamObject(temp) //let it loose
endif
next y
next x
endfunction
At the moment sprites in the array will be forced to rotate from their center. Additionally, if a sprite has physics enabled, all of its velocities must be stored and reapplied after the sprites have been repositioned. I'll likely add support for both of these soon, but at the moment they are absent.
Lastly, here's a quick picture of the example (not that it's pretty). If you would prefer to add your own image to replace the white boxes, just import it and replace "temp = createSprite(0)" in addWorld() with "temp = createSprite(
your image ID)" It will be automatically scaled.

Maybe one last bit.

I couldn't get the setViewZoomMode() command to work properly. Within the code, I adjusted accordingly. I'm fully aware of the command.
swis
Joined: Tue Dec 16th 2008
