Here's something to play around with. I originally wrote it to demonstrate transforming a rectangular sprite for use in displaying isometric maps.
I updated it to show common transformations as well. I also added a feature to let you transform a sprite in real-time.
Code: Select all
;Program description: Sprite transformation effects
;Author: Demivec
;Written for: Originally for PB 4.61, with Windows OS, may not function correctly for other OS.
; It does not function correctly with the OpenGL subsystem.
;Date Written: 2 July 2012
;Last Updated: 4 October 2015 for PB 5.40b8 and added some additional features.
;
EnableExplicit
; Structure Point
; x.i
; y.i
; EndStructure
Structure Point_f
x.f
y.f
EndStructure
Enumeration sprites
#spr_pb
#spr_corner
#spr_text_1
#spr_text_2
#spr_text_3
#spr_text_4
#spr_mousePointer
EndEnumeration
Enumeration fonts
#font_large
#font_small
EndEnumeration
Enumeration windows
#win_main
EndEnumeration
Procedure handleError(value, text.s = "Unknown Error.")
If value = 0
MessageRequester("Error", text)
End
EndIf
EndProcedure
;Sprite info: (width, height) = original dimensions, (newWidth, newHeight) = zoom dimensions,
; degree = rotation amount, (cx,cy) = center point for rotation
Procedure MakeSpriteTransformation(spriteID, width, height, cx, cy, degree, newWidth, newHeight)
Protected.Point_f p1, p2, p3, p4
Protected.f wf = width / newWidth, hf = height / newHeight
If degree = 0
;simple transformation: no rotation, only zooming and translation movements possible
p1\x = (( -cx) / wf) - 0.5: p1\y = (( -cy) / hf) - 0.5
p2\x = ((width - cx) / wf) - 0.5: p2\y = (( -cy) / hf) - 0.5
p3\x = ((width - cx) / wf) - 0.5: p3\y = ((height - cy) / hf) - 0.5
p4\x = (( -cx) / wf) - 0.5: p4\y = ((height - cy) / hf) - 0.5
Else
;more complex transformation: rotation (around point cx,cy) and zooming possible
Protected.f angCos = Cos(Radian(degree)), angSin = Sin(Radian(degree))
Protected.f v1, v2, v3, v4, v5, v6, v7, v8
v1 = -cx * angCos: v3 = -cy * angSin
v2 = -cx * angSin: v4 = -cy * angCos
v6 = (width - cx) * angCos: v7 = (height - cy) * angSin
v5 = (width - cx) * angSin: v8 = (height - cy) * angCos
p1\x = ((v1 - v3) / wf) - 0.5: p1\y = ((v4 + v2) / hf) - 0.5
p2\x = ((v6 - v3) / wf) - 0.5: p2\y = ((v4 + v5) / hf) - 0.5
p3\x = ((v6 - v7) / wf) - 0.5: p3\y = ((v8 + v5) / hf) - 0.5
p4\x = ((v1 - v7) / wf) - 0.5: p4\y = ((v8 + v2) / hf) - 0.5
EndIf
TransformSprite(spriteID, p1\x, p1\y, p2\x, p2\y, p3\x, p3\y, p4\x, p4\y)
EndProcedure
;iso (squish) rotated clockwise degrees
Procedure MakeSpriteIsoClockwise(spriteID, width, height, cx, cy, degree, newWidth, newHeight)
MakeSpriteTransformation(spriteID, width, height, cx, cy, degree, newWidth, newHeight)
EndProcedure
;iso (squish) rotated counter-clockwise degrees
Procedure MakeSpriteIsoCounterClockwise(spriteID, width, height, cx, cy, degree, newWidth, newHeight)
MakeSpriteTransformation(spriteID, width, height, cx, cy, 360 - degree, newWidth, newHeight)
EndProcedure
Procedure MakeSpriteRotate(SpriteID, cx, cy, degree)
Protected width = SpriteWidth(SpriteID), height = SpriteHeight(SpriteID)
ZoomSprite(SpriteID, #PB_Default, #PB_Default)
MakeSpriteTransformation(spriteID, width, height, cx, cy, degree, width, height)
EndProcedure
Procedure MakeSpriteZoomHorizontal(SpriteID, cx, cy, newWidth)
Protected width = SpriteWidth(SpriteID), height = SpriteHeight(SpriteID)
ZoomSprite(SpriteID, #PB_Default, #PB_Default)
MakeSpriteTransformation(spriteID, width, height, cx, cy, 0, newWidth, height)
EndProcedure
Procedure MakeSpriteZoomVertical(SpriteID, cx, cy, newHeight)
Protected width = SpriteWidth(SpriteID), height = SpriteHeight(SpriteID)
ZoomSprite(SpriteID, #PB_Default, #PB_Default)
MakeSpriteTransformation(spriteID, width, height, cx, cy, 0, width, newHeight)
EndProcedure
Procedure MakeSpriteNormal(SpriteID, cx = 0, cy = 0)
Protected width = SpriteWidth(SpriteID), height = SpriteHeight(SpriteID)
ZoomSprite(SpriteID, #PB_Default, #PB_Default)
MakeSpriteTransformation(spriteID, width, height, cx, cy, 0, width, height)
EndProcedure
handleError(InitSprite(), "Can't open screen & sprite enviroment.")
handleError(InitMouse(), "Can't initialize mouse handler.")
handleError(InitKeyboard(), "Can't initialize keyboard handler.")
handleError(OpenWindow(#win_main, 0, 0, 800, 600, "Sprite Transformations, drag numbered corners to transform",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered), "Can't open window.")
handleError(OpenWindowedScreen(WindowID(#win_main), 0, 0, 800, 600, 0, 0, 0), "Can't open windowed screen.")
handleError(LoadFont(#font_large, "Arial", 36), "Unable to load font.")
handleError(LoadFont(#font_small, "Arial", 8), "Unable to load font.")
handleError(CreateSprite(#spr_pb, 64, 64), "Sprite creation failed.")
handleError(StartDrawing(SpriteOutput(#spr_pb)), "Can't draw on sprite.")
Box(0,0,64,64,RGB($FF,$FF,$00))
DrawingMode(#PB_2DDrawing_Outlined)
DrawingFont(FontID(#font_large))
DrawText(0,0,"PB",RGB($FF,$00,$00))
Box(0,0,64,64,RGB($00,$00,$FF))
LineXY( 0,0,64,64,RGB($00,$00,$FF))
LineXY(64,0, 0,64,RGB($00,$00,$FF))
StopDrawing()
handleError(CreateSprite(#spr_mousePointer, 8, 8, #PB_Sprite_AlphaBlending), "Sprite creation failed.")
handleError(StartDrawing(SpriteOutput(#spr_mousePointer)), "Can't draw on sprite.")
FrontColor(RGB($00,$FF,$FF))
LineXY(0,0, OutputWidth() - 1,OutputHeight() - 1)
LineXY(0,0, 0,OutputHeight() - 3)
LineXY(0,0, OutputWidth() - 3,0)
StopDrawing()
TransparentSpriteColor(#spr_mousePointer, RGB($00,$00,$00))
handleError(CreateSprite(#spr_corner, 8, 8, #PB_Sprite_AlphaBlending), "Sprite creation failed.")
handleError(StartDrawing(SpriteOutput(#spr_corner)), "Can't draw on sprite.")
Box(0, 0, OutputWidth(), OutputHeight(), RGB($00, $FF, $00))
StopDrawing()
DataSection
cornerCoordinates:
Data.i 0,0, 64,0, 64,64, 0,64
EndDataSection
Dim corner.Point(3)
Define customSpritePosX = 400, customSpritePosY = 400, i
Restore cornerCoordinates
For i = 0 To 3
handleError(CreateSprite(#spr_text_1 + i, 8, 16, #PB_Sprite_AlphaBlending), "Sprite creation failed.")
handleError(StartDrawing(SpriteOutput(#spr_text_1 + i)), "Can't draw on sprite.")
DrawingMode(#PB_2DDrawing_Outlined)
DrawingFont(FontID(#font_small))
DrawText(0, 0, Str(i + 1),RGB($FF,$FF,$FF),RGB($00,$00,$00))
StopDrawing()
TransparentSpriteColor(#spr_text_1 + i, RGB($00,$00,$00))
Read.i corner(i)\x: corner(i)\x + customSpritePosX
Read.i corner(i)\y: corner(i)\y + customSpritePosY
Next
Define direction = 2
Define zoomX = 64
Define zoomY = 64
Define angle, newWidth, newHeight, displayRow, event, i
ReleaseMouse(1)
Define mouseIsReleased = #True, dragCorner = -1
Repeat
Repeat
event = WindowEvent()
Select event
Case #PB_Event_CloseWindow
End
EndSelect
Until event = 0
FlipBuffers()
ClearScreen(RGB($40, $40, $40))
displayRow = 70
MakeSpriteNormal(#spr_pb, 32, 32) ;normal appearance
DisplaySprite(#spr_pb, 100, displayRow) ;location is needed because display location was not part of transformation
MakeSpriteRotate(#spr_pb, 32, 32, angle) ;rotate around center
DisplaySprite(#spr_pb, 200, displayRow)
MakeSpriteRotate(#spr_pb, 16, 16, angle) ;rotate around non-center point
DisplaySprite(#spr_pb, 350, displayRow)
newWidth = zoomX: newHeight = zoomX
MakeSpriteZoomHorizontal(#spr_pb, 32, 32, newWidth) ;zoom horizontally
DisplaySprite(#spr_pb, 500, displayRow)
MakeSpriteZoomVertical(#spr_pb, 32, 32, newHeight) ;zoom vertically
DisplaySprite(#spr_pb, 600, displayRow)
displayRow = 280
MakeSpriteIsoCounterClockwise(#spr_pb, 64, 64, 32, 32, angle, 64, 32) ;squished vertically
DisplaySprite(#spr_pb, 200 - zoomX * 0.5, displayRow - zoomY * 0.5)
MakeSpriteIsoCounterClockwise(#spr_pb, 64, 64, 32, 32, angle, 32, 64) ;squished horizontally
DisplaySprite(#spr_pb, 300 - zoomX * 0.5, displayRow - zoomY * 0.5)
MakeSpriteIsoClockwise(#spr_pb, 64, 64, 32, 32, angle, 2 * zoomX, 1 * zoomX) ;squished vertically and zoomed
DisplaySprite(#spr_pb, 700 - zoomX * 0.5, displayRow - zoomY * 0.5)
MakeSpriteIsoClockwise(#spr_pb, 64, 64, 32, 32, angle, 1 * zoomX, 2 * zoomX) ;squished horizontally and zoomed
DisplaySprite(#spr_pb, 500 - zoomX * 0.5, displayRow - zoomY * 0.5)
displayRow = 400
MakeSpriteIsoCounterClockwise(#spr_pb, 64, 64, 32, 32, angle, 64, 20 + zoomX / 2) ;squishiness varying vertically
DisplaySprite(#spr_pb, 100, displayRow)
MakeSpriteIsoCounterClockwise(#spr_pb, 64, 64, 32, 32, angle, 20 + zoomX / 2, 64) ;squishiness varying horizontally
DisplaySprite(#spr_pb, 200, displayRow)
ExamineMouse()
;handle screen mouse activation/deactivation
If MouseX() < 2 Or MouseX() > ScreenWidth() - 3 Or MouseY() < 2 Or MouseY() > ScreenHeight() -3
ReleaseMouse(1): mouseIsReleased = #True
EndIf
If WindowMouseX(#win_main) >= 2 And
WindowMouseX(#win_main) < WindowWidth(#win_main, #PB_Window_InnerCoordinate) - 2 And
WindowMouseY(#win_main) >= 2 And
WindowMouseY(#win_main) < WindowHeight(#win_main, #PB_Window_InnerCoordinate) - 2
ReleaseMouse(0): mouseIsReleased = #False
MouseLocate(WindowMouseX(#win_main), WindowMouseY(#win_main))
EndIf
;handle corner dragging for deformable sprite
If Not mouseIsReleased
If MouseButton(#PB_MouseButton_Left)
If dragCorner = -1
For i = 0 To 3
If SpriteCollision(#spr_corner, corner(i)\x, corner(i)\y, #spr_mousePointer, MouseX(), MouseY())
dragCorner = i
MouseDeltaX(): MouseDeltaY() ;reset these values for further reads
Break
EndIf
Next
Else
corner(dragCorner)\x + MouseDeltaX(): corner(dragCorner)\y + MouseDeltaY()
EndIf
ElseIf dragCorner <> -1
corner(dragCorner)\x + MouseDeltaX(): corner(dragCorner)\y + MouseDeltaY()
dragCorner = -1 ;signal that no corner is being dragged
EndIf
EndIf
TransformSprite(#spr_pb, corner(0)\x,corner(0)\y, corner(1)\x,corner(1)\y, corner(2)\x,corner(2)\y, corner(3)\x,corner(3)\y)
DisplaySprite(#spr_pb, 0, 0) ;drawn at location 0,0 because sprite location is part of transformation
;display corners for dragging
For i = 0 To 3
DisplayTransparentSprite(#spr_corner, corner(i)\x, corner(i)\y, 128)
DisplayTransparentSprite(#spr_text_1 + i, corner(i)\x + 8, corner(i)\y)
Next
If Not mouseIsReleased: DisplayTransparentSprite(#spr_mousePointer, MouseX(), MouseY()): EndIf
;update animation attributes
angle + 1: If angle > 360: angle = 0: EndIf
zoomX + direction
If zoomX > 100 : direction = -2 : EndIf
If zoomX < 20 : direction = 2 : EndIf
ExamineKeyboard()
If KeyboardPushed(#PB_Key_Escape): Break: EndIf
Delay(1)
ForEver