Code: Select all
;:=============================================================================
;:- ShapedSprite.pbi
;:- Author : Eddy
;:- Date : June 28, 2014
;:- Compiler : PureBasic 5.30
;:- Target OS : Mac, Linux, Windows
;:- Source --------------------------------------------------------------------
;:- http://www.purebasic.fr/english/viewtopic.php?f=12&p=447248
;:=============================================================================
CompilerIf Not Defined(MacroBuilder, #PB_Module)
DeclareModule MacroBuilder
EnableExplicit
Macro _DoubleQuote
"
EndMacro
Macro _Colon
:
EndMacro
Macro DefineMacro(_MacroHead, _MacroBody)
_Colon#Macro _MacroHead#_Colon#_MacroBody#_Colon#EndMacro
EndMacro
Macro Override(_Name, _Prefix=_)
DefineMacro(_Name, _Prefix#_Name)
EndMacro
EndDeclareModule
Module MacroBuilder
EndModule
CompilerEndIf
CompilerIf Not Defined(ShapedSprite, #PB_Module)
DeclareModule ShapedSprite
EnableExplicit
Enumeration
#FX_Shape_Shake
#FX_Shape_Twist
#FX_Shape_Wave
#FX_Shape_Stretch
#FX_Shape_Jiggle
#FX_Shape_Rotate
#FX_Shape_SlideIn
#FX_Shape_SlideOut
;#FX_Shape_Wobble
EndEnumeration
Declare HandleSprite(Sprite,HandleX.f,HandleY.f)
Declare MidHandleSprite(Sprite)
Declare DeformSprite(Sprite, x0.f, y0.f, x1.f, y1.f, x2.f, y2.f, x3.f, y3.f)
Declare _ZoomSprite(Sprite,Width.f,Height.f)
Declare _RotateSprite(Sprite,Angle.f,Mode=#PB_Absolute)
Declare TurnSprite(Sprite,Angle.f)
Declare SkewSprite(Sprite, First.f, Second.f, Vertical=#False)
Declare.f _SpriteWidth(Sprite)
Declare.f _SpriteHeight(Sprite)
Declare.f SpriteHandleX(Sprite)
Declare.f SpriteHandleX(Sprite)
Declare.f SpriteAngle(Sprite)
Declare SaveSpriteShape(Sprite,ShapeName$="")
Declare RestoreSpriteShape(Sprite,ShapeName$="")
Declare _CreateSprite(Sprite,Width,Height, Mode=0)
Declare _CopySprite(Sprite1,Sprite2, Mode=0)
Declare _LoadSprite(Sprite,FileName$, Mode=0)
Declare _DisplayTransparentSprite(Sprite,x,y, Intensity=255, Color=#PB_Default)
Declare.i CreateSpriteAnimation(Sprite, FrameWidth, FrameHeight, Frame=0, FitFrameSize=#True)
Declare.i CreateSpriteAnimationSequence(Sprite, SequenceName$, FrameSequence$="")
Declare AnimateSprite(Sprite, Frame)
Declare AnimateSpriteSequence(Sprite, SequenceName$, Progress.f)
Declare AnimateSpriteShape(Sprite, FX, Progress.f, ProgressLag.f, AmplitudeX.f, AmplitudeY.f)
EndDeclareModule
Module ShapedSprite
Structure SPRITE_QUAD
x.f[4]
y.f[4]
EndStructure
Structure SPRITE_SEQUENCE
Array selectedFrames.i(0)
EndStructure
Structure SPRITE_FRAME
x.i : y.i : w.i : h.i
EndStructure
Structure SHAPED_SPRITE
box.SPRITE_QUAD
rotated.SPRITE_QUAD
deformed.SPRITE_QUAD
*deformation.SPRITE_QUAD
w.f
h.f
hx.f
hy.f
angle.f
selectedFrame.i
Array frames.SPRITE_FRAME(0)
Map sequences.SPRITE_SEQUENCE()
EndStructure
Global NewMap ShapedSprites.SHAPED_SPRITE()
;------ Methods to change Sprite Shape
Procedure ReshapeSprite(*s.SHAPED_SPRITE, IsResizing=#True, IsDeforming=#True, IsRotating=#True)
With *s
If IsResizing
\box\x[0]=-\hx : \box\y[0]=-\hy
\box\x[1]=\w-\hx : \box\y[1]=-\hy
\box\x[2]=\w-\hx : \box\y[2]=\h-\hy
\box\x[3]=-\hx : \box\y[3]=\h-\hy
EndIf
If IsDeforming
\deformed=\box
If \deformation
Protected i
For i=0 To 3
\deformed\x[i]+\deformation\x[i]
\deformed\y[i]+\deformation\y[i]
Next
EndIf
EndIf
If IsRotating
\rotated=\deformed
If \angle
Protected radian.f=Radian(\Angle)
Protected cos.f=Cos(radian), sin.f=Sin(radian)
For i=0 To 3
Protected px.f=\rotated\x[i], py.f=\rotated\y[i]
\rotated\x[i]=cos*px-sin*py
\rotated\y[i]=sin*px+cos*py
Next
EndIf
EndIf
EndWith
EndProcedure
Procedure HandleSprite(Sprite,HandleX.f,HandleY.f)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s
\hx=HandleX
\hy=HandleY
ReshapeSprite(*s)
EndWith
EndProcedure
Procedure MidHandleSprite(Sprite)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s
\hx=\w/2 ; centered horizontaly
\hy=\h/2 ; centered verticaly
ReshapeSprite(*s)
EndWith
EndProcedure
Procedure _ZoomSprite(Sprite,Width.f,Height.f)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s
\w=Width
\h=Height
ReshapeSprite(*s)
EndWith
EndProcedure
Procedure DeformSprite(Sprite, x0.f, y0.f, x1.f, y1.f, x2.f, y2.f, x3.f, y3.f)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s
If Not \deformation : \deformation=AllocateMemory(SizeOf(SPRITE_QUAD)) : EndIf
\deformation\x[0]=x0 : \deformation\y[0]=y0
\deformation\x[1]=x1 : \deformation\y[1]=y1
\deformation\x[2]=x2 : \deformation\y[2]=y2
\deformation\x[3]=x3 : \deformation\y[3]=y3
ReshapeSprite(*s,#False)
EndWith
EndProcedure
Procedure _RotateSprite(Sprite,Angle.f,Mode=#PB_Absolute)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s
If Mode=#PB_Absolute
\angle=Angle
Else
\angle+Angle
EndIf
ReshapeSprite(*s,#False,#False)
EndWith
EndProcedure
Procedure TurnSprite(Sprite,Angle.f)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s
\angle+Angle
ReshapeSprite(*s,#False,#False)
EndWith
EndProcedure
Procedure SkewSprite(Sprite, First.f, Second.f, Vertical=#False)
If Vertical
DeformSprite(Sprite, 0, First, 0, Second, 0, Second, 0, First)
Else
DeformSprite(Sprite, First, 0, First, 0, Second, 0, Second, 0)
EndIf
EndProcedure
;------ Methods to save/restore Sprite Shape
Procedure SaveSpriteShape(Sprite,ShapeName$="")
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s
If ShapeName$="" : ShapeName$+"DEFAULT_"+Sprite : EndIf
Protected *shape.SHAPED_SPRITE=AddMapElement(ShapedSprites(),ShapeName$)
CopyStructure(*s,*shape,SHAPED_SPRITE)
EndWith
EndProcedure
Procedure RestoreSpriteShape(Sprite,ShapeName$="")
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s
If ShapeName$="" : ShapeName$+"DEFAULT_"+Sprite : EndIf
Protected *shape.SHAPED_SPRITE=FindMapElement(ShapedSprites(),ShapeName$)
CopyStructure(*shape,*s,SHAPED_SPRITE)
EndWith
EndProcedure
;------ Methods to create/display Sprite
Procedure _LoadSprite(Sprite,FileName$, Mode=0)
Protected Result=LoadSprite(Sprite,FileName$,Mode)
If Sprite=#PB_Any : Sprite=Result : EndIf
;init shape
Protected *s.SHAPED_SPRITE=AddMapElement(ShapedSprites(),""+Sprite)
With *s
StartDrawing(SpriteOutput(Sprite))
\w=OutputWidth()
\h=OutputHeight()
\frames(0)\w=OutputWidth()
\frames(0)\h=OutputHeight()
StopDrawing()
EndWith
MidHandleSprite(Sprite)
;save default shape
SaveSpriteShape(Sprite)
ProcedureReturn Result
EndProcedure
Procedure _CopySprite(Sprite1,Sprite2, Mode=0)
Protected Result=CopySprite(Sprite1,Sprite2,Mode)
If Sprite2=#PB_Any : Sprite2=Result : EndIf
;copy shape
Protected *s.SHAPED_SPRITE=AddMapElement(ShapedSprites(),""+Sprite2)
RestoreSpriteShape(Sprite2,""+Sprite1)
;save default shape
SaveSpriteShape(Sprite2)
ProcedureReturn Result
EndProcedure
Procedure _CreateSprite(Sprite,Width,Height, Mode=0)
Protected Result=CreateSprite(Sprite,Width,Height,Mode)
If Sprite=#PB_Any : Sprite=Result : EndIf
;init shape
Protected *s.SHAPED_SPRITE=AddMapElement(ShapedSprites(),""+Sprite)
With *s
\w=Width
\h=Height
\frames(0)\w=Width
\frames(0)\h=Height
EndWith
MidHandleSprite(Sprite)
;save default shape
SaveSpriteShape(Sprite)
ProcedureReturn Result
EndProcedure
Procedure _DisplayTransparentSprite(Sprite,x,y, Intensity=255, Color=#PB_Default)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s
TransformSprite(Sprite,
\rotated\x[0],\rotated\y[0],
\rotated\x[1],\rotated\y[1],
\rotated\x[2],\rotated\y[2],
\rotated\x[3],\rotated\y[3])
DisplayTransparentSprite(Sprite,x,y,Intensity,Color)
EndWith
EndProcedure
;------ Methods to get Sprite Properties
Procedure.f _SpriteWidth(Sprite)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
ProcedureReturn *s\w
EndProcedure
Procedure.f _SpriteHeight(Sprite)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
ProcedureReturn *s\h
EndProcedure
Procedure.f SpriteAngle(Sprite)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
ProcedureReturn *s\angle
EndProcedure
Procedure.f SpriteHandleX(Sprite)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
ProcedureReturn *s\hx
EndProcedure
Procedure.f SpriteHandleY(Sprite)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
ProcedureReturn *s\hy
EndProcedure
;------ Methods to Animate Sprite
Procedure AnimateSprite(Sprite, Frame)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s\frames(Frame)
*s\selectedFrame=Frame
ClipSprite(Sprite,\x,\y,\w,\h)
EndWith
EndProcedure
Procedure AnimateSpriteSequence(Sprite, SequenceName$, Progress.f)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s
FindMapElement(\sequences(),SequenceName$)
Protected sequenceLength = ArraySize(\sequences()\selectedFrames())+1
Protected num=Int(sequenceLength *Progress) % sequenceLength
AnimateSprite(Sprite,\sequences()\selectedFrames(num))
EndWith
EndProcedure
Procedure AnimateSpriteShape(Sprite, FX, Progress.f, ProgressLag.f, AmplitudeX.f, AmplitudeY.f)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s
Protected a0.f, a1.f, cos.f, sin.f, px.f, py.f,i
Protected x0.f, y0.f, x1.f, y1.f, x2.f, y2.f, x3.f, y3.f
Select FX
Case #FX_Shape_Twist
a0=progress*2*#PI
a1=ProgressLag*2*#PI
x0=AmplitudeX* Cos(a0):y0=AmplitudeY* Sin(a0) : a0+a1
x1=AmplitudeX* Cos(a0):y1=AmplitudeY* Sin(a0) : a0+a1
x2=AmplitudeX* Cos(a0):y2=AmplitudeY* Sin(a0) : a0+a1
x3=AmplitudeX* Cos(a0):y3=AmplitudeY* Sin(a0)
Case #FX_Shape_Shake
If AmplitudeX
a0=1-Mod(Progress,1)
x0=a0*(AmplitudeX-2*Random(AmplitudeX))
x1=x0 : x2=x0 : x3=x0
EndIf
If AmplitudeY
a1=1-Mod(Progress+ProgressLag,1)
y0=a1*(AmplitudeY-2*Random(AmplitudeY))
y1=y0 : y2=y0 : y3=y0
EndIf
Case #FX_Shape_Stretch
If AmplitudeX
a0=(progress)*2*#PI
x1=AmplitudeX * Sin(a0) : x2=x1
x0=-x1 : x3=-x1
EndIf
If AmplitudeY
a1=(progress+ProgressLag)*2*#PI
y0=AmplitudeY * Sin(a1) : y1=y0
y2=-y0 : y3=-y0
EndIf
Case #FX_Shape_Wave
a0=(progress)*2*#PI
a1=(progress+ProgressLag)*2*#PI
If AmplitudeX
x0=AmplitudeX * Sin(a0) : x1=x0
x2=AmplitudeX * Sin(a1) : x3=x2
EndIf
If AmplitudeY
y0=AmplitudeY * Sin(a0) : y3=y0
y1=AmplitudeY * Sin(a1) : y2=y1
EndIf
Case #FX_Shape_Jiggle
a0=(progress*ProgressLag)*2*#PI
a1=(progress)*2*#PI
px=AmplitudeX * Cos(a0)
py=AmplitudeY * Sin(a0)
cos=Cos(a1) : sin=Sin(a1)
x0=cos*px-sin*py
y0=sin*px+cos*py
x1=x0 : x2=x0 : x3=x0
y1=y0 : y2=y0 : y3=y0
Case #FX_Shape_Rotate
a0=(progress+ProgressLag)*2*#PI
cos.f=Cos(a0) : sin.f=Sin(a0)
For i=0 To 3
px.f=\box\x[i] : py.f=\box\y[i]
\deformed\x[i]=cos*px-sin*py
\deformed\y[i]=sin*px+cos*py
Next
ReshapeSprite(*s,#False,#False)
ProcedureReturn
Case #FX_Shape_SlideIn
a0=(1-Mod(Progress,1))
Protected *frame.SPRITE_FRAME=\frames(\selectedFrame)
If AmplitudeY>0
y2=-\h*a0
y3=y2
ElseIf AmplitudeY<0
y0=\h*a0
y1=y0
EndIf
If AmplitudeX>0
x1=-\w*a0
x2=x1
ElseIf AmplitudeX<0
x0=\w*a0
x3=x0
EndIf
Case #FX_Shape_SlideOut
a0=Mod(Progress,1)
If AmplitudeY>0
y2=-\h*a0
y3=y2
ElseIf AmplitudeY<0
y0=\h*a0
y1=y0
EndIf
If AmplitudeX>0
x1=-\w*a0
x2=x1
ElseIf AmplitudeX<0
x0=\w*a0
x3=x0
EndIf
EndSelect
DeformSprite(Sprite, x0.f, y0.f, x1.f, y1.f, x2.f, y2.f, x3.f, y3.f)
EndWith
EndProcedure
Procedure.i CreateSpriteAnimation(Sprite, FrameWidth, FrameHeight, Frame=0, FitFrameSize=#True)
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s
StartDrawing(SpriteOutput(Sprite))
Protected imageW=OutputWidth(),col,lastCol=imageW/FrameWidth
Protected imageH=OutputHeight(),row,lastRow=imageH/FrameHeight
Protected num=0,frameCount=lastCol * lastRow
StopDrawing()
;init all animation frames
ReDim \frames(frameCount-1)
For row=0 To lastRow-1
For col=0 To lastCol-1
\frames(num)\x=col * FrameWidth
\frames(num)\y=row * FrameHeight
\frames(num)\w=FrameWidth
\frames(num)\h=FrameHeight
num+1
Next
Next
;select default sprite frame
AnimateSprite(Sprite, Frame)
If FitFrameSize : _ZoomSprite(Sprite,FrameWidth,FrameHeight) : EndIf
EndWith
ProcedureReturn frameCount
EndProcedure
Procedure.i CreateSpriteAnimationSequence(Sprite, SequenceName$, FrameSequence$="")
Protected *s.SHAPED_SPRITE=FindMapElement(ShapedSprites(),""+Sprite)
With *s
AddMapElement(\sequences(), SequenceName$)
Protected num, sequenceLength
If FrameSequence$=""
sequenceLength=ArraySize(\frames())+1
ReDim \sequences()\selectedFrames(sequenceLength-1)
For num=0 To sequenceLength-1
\sequences()\selectedFrames(num)=num
Next
Else
sequenceLength=CountString(FrameSequence$,",")+1
ReDim \sequences()\selectedFrames(sequenceLength-1)
For num=0 To sequenceLength-1
\sequences()\selectedFrames(num)=Val(StringField(FrameSequence$,num+1,","))
Next
EndIf
EndWith
ProcedureReturn sequenceLength
EndProcedure
EndModule
UseModule ShapedSprite
UseModule MacroBuilder
Override(ZoomSprite)
Override(RotateSprite)
Override(SpriteWidth)
Override(SpriteHeight)
Override(CreateSprite)
Override(LoadSprite)
Override(CopySprite)
Override(DisplayTransparentSprite)
CompilerEndIf