PB5.2 : Prefab, Layer, Sprite, Anim, FX
Posted: Sat Jul 06, 2013 2:45 pm
- added: prefab & instance tools
- added: sprite & layer drawing methods
- added: sprite & layer customizable life-cycle methods (init,start,method,destroy,draw)
- added: sprite animation & frameset
- added: sprite offset (hotspot)
- added: delayed actions
- added: sprite & layer drawing methods
- added: sprite & layer customizable life-cycle methods (init,start,method,destroy,draw)
- added: sprite animation & frameset
- added: sprite offset (hotspot)
- added: delayed actions
Code: Select all
;-TOP
; Comment : Prefab functions, Layer & Sprite system, Sprite FX
; Author : eddy
; Web : http://www.purebasic.fr/english/viewtopic.php?f=12&t=417196
; File: : PrefabSpriteFX.pbi
; Version : v0.9
EnableExplicit
CompilerIf #PB_Compiler_Debugger
Import ""
PB_DEBUGGER_SendError(Message.p-ascii)
PB_DEBUGGER_SendWarning(Message.p-ascii)
EndImport
CompilerElse
Macro PB_DEBUGGER_SendError(Message) : EndMacro
Macro PB_DEBUGGER_SendWarning(Message) : EndMacro
CompilerEndIf
Prototype InstanceMethod(*Instance)
Prototype InstanceDataMethod(*Instance, *CustomData)
Enumeration
#Instance_Empty=0
#Instance_Sprite
#Instance_Layer
#Instance_Particles
#Blending_Normal=0
#Blending_Add
#Blending_Mult
#Blending_2XMult
#Blending_Invert
#State_Prefab=%1
#State_Alive=%10
#State_Visible=%100
#State_Enabled=%1000
#State_Starting=%10000
#State_AliveEnabledStarting=#State_Alive | #State_Enabled | #State_Starting
#State_AliveEnabled=#State_Alive | #State_Enabled
#State_AliveVisible=#State_Alive | #State_Visible
EndEnumeration
Structure VECTOR
x.f
y.f
EndStructure
Structure FRAME
x.f
y.f
w.f
h.f
EndStructure
Structure ANIMATION
Array Frames.FRAME(0)
Array FrameIds.i(0)
*CurrentFrame.FRAME
CurrentIndex.i
EndStructure
Structure INSTANCE
*PrefabSource.PREFAB
*InitInstance.InstanceMethod
*DestroyInstance.InstanceMethod
State.i
*CustomData
EndStructure
Structure POINT_INSTANCE Extends INSTANCE
Pos.VECTOR
Offset.VECTOR
Angle.f
EndStructure
Structure SIZE_INSTANCE Extends POINT_INSTANCE
Scale.VECTOR
Width.i
Height.i
EndStructure
Structure VISIBLE_INSTANCE Extends SIZE_INSTANCE
Opacity.f
Color.i
EndStructure
Structure SPRITE Extends VISIBLE_INSTANCE
Sprite.i
*Anim.ANIMATION
*StartInstance.InstanceDataMethod
*UpdateInstance.InstanceDataMethod
*DrawInstance.InstanceMethod
EndStructure
Structure LAYER Extends VISIBLE_INSTANCE
Blending.i
List Sprites.SPRITE()
*StartInstance.InstanceMethod
*UpdateInstance.InstanceMethod
*DrawInstance.InstanceMethod
EndStructure
Structure DISPLAY_SCREEN
List Layers.LAYER()
EndStructure
Structure PREFAB
Prefab.i
InstanceType.i
CreatePrefabResult.i
StructureUnion
*InstanceTemplate.INSTANCE
*SpriteTemplate.SPRITE
*LayerTemplate.LAYER
EndStructureUnion
StructureUnion
*Parent
*ParentLayer.LAYER
*ParentScreen.DISPLAY_SCREEN
EndStructureUnion
EndStructure
Structure DELAYED_ACTION
StartTime.i
EndTime.i
Interval.i
StructureUnion
*Action
EndStructureUnion
StructureUnion
*a
EndStructureUnion
StructureUnion
*b
EndStructureUnion
StructureUnion
*c
EndStructureUnion
StructureUnion
*d
EndStructureUnion
EndStructure
Global NewList Prefabs.PREFAB()
Global NewList Layers.LAYER()
Global NewList DelayedActions.DELAYED_ACTION()
Global DisplayScreen.DISPLAY_SCREEN
; ********************
; DELAYED ACTIONS
; ********************
Procedure DelayActionLoop(Delay, Duration, LoopInterval, *Action, *a=#Null, *b=#Null, *c=#Null, *d=#Null)
Protected *newAction.DELAYED_ACTION=AddElement(DelayedActions())
With *newAction
\StartTime=ElapsedMilliseconds()+Delay
\EndTime=\StartTime+Duration
\Interval=LoopInterval
\Action=*Action
\a=*a
\b=*b
\c=*c
\d=*d
EndWith
EndProcedure
Procedure DelayAction(Delay, *Action, *a=#Null, *b=#Null, *c=#Null, *d=#Null)
DelayActionLoop(Delay, 0, 0, *Action, *a, *b, *c, *d)
EndProcedure
; ********************
; DRAW FUNCTIONS
; ********************
Procedure RotateVector(*vector.VECTOR, Angle.f)
Protected px.f=*vector\x
Protected py.f=*vector\y
Protected radian.f=Radian(Angle)
Protected ca.f=Cos(radian)
Protected sa.f=Sin(radian)
*vector\x=ca*px-sa*py
*vector\y=sa*px+ca*py
EndProcedure
Procedure ScaleVector(*vector.VECTOR, *Scale.VECTOR)
*vector\x**Scale\x
*vector\y**Scale\y
EndProcedure
Procedure ComputeVisibleInstances(*Source.VISIBLE_INSTANCE, *Target.VISIBLE_INSTANCE, *Result.VISIBLE_INSTANCE)
With *Result
\Scale=*Target\Scale
\Angle=*Target\Angle
\Pos\x=*Source\Pos\x+*Target\Pos\x
\Pos\y=*Source\Pos\y+*Target\Pos\y
RotateVector(\Pos, *Target\Angle)
\Pos\x+*Target\Offset\x
\Pos\y+*Target\Offset\y
\Opacity=*Source\Opacity*(*Target\Opacity)
\Color=*Source\Color
If *Target\Color<>#PB_Default
If *Source\Color=#PB_Default
\Color=*Target\Color
Else
\Color=AlphaBlend(*Source\Color, *Target\Color)
EndIf
EndIf
EndWith
EndProcedure
Procedure DefaultDrawSprite(*Sprite.SPRITE)
With *Sprite
If \Anim
Protected *frm.FRAME=\Anim\CurrentFrame
ClipSprite(\Sprite, *frm\x, *frm\y, *frm\w, *frm\h)
EndIf
Protected final.VISIBLE_INSTANCE
ComputeVisibleInstances(*Sprite, \PrefabSource\ParentLayer, final)
Protected x1.f=-\Offset\x*\Scale\x, x2.f=(\Width-\Offset\x)*\Scale\x
Protected y1.f=-\Offset\y*\Scale\y, y2.f=(\Height-\Offset\y)*\Scale\y
Protected Dim p.VECTOR(3), i
p(0)\x=x1 : p(0)\y=y1
p(1)\x=x2 : p(1)\y=y1
p(2)\x=x2 : p(2)\y=y2
p(3)\x=x1 : p(3)\y=y2
For i=0 To 3
RotateVector(p(i), \Angle)
Next
If final\Scale\x<>1 Or final\Scale\y<>1
For i=0 To 3
ScaleVector(p(i), final\Scale)
Next
EndIf
If final\Angle
For i=0 To 3
RotateVector(p(i), final\Angle)
Next
EndIf
TransformSprite(\Sprite, p(0)\x, p(0)\y, p(1)\x, p(1)\y, p(2)\x, p(2)\y, p(3)\x, p(3)\y)
DisplayTransparentSprite(\Sprite, final\Pos\x, final\Pos\y, 255*final\Opacity, final\Color)
EndWith
EndProcedure
Procedure DefaultDrawLayer(*Layer.LAYER)
ForEach *Layer\Sprites()
Protected *sprite.SPRITE=*Layer\Sprites()
With *sprite
If (\State & #State_AliveEnabledStarting)=#State_AliveEnabledStarting And \StartInstance
\State & ~#State_Starting
\StartInstance(*sprite, *sprite\CustomData)
EndIf
If (\State & #State_AliveEnabled)=#State_AliveEnabled And \UpdateInstance
\UpdateInstance(*sprite, *sprite\CustomData)
EndIf
If (\State & #State_AliveVisible)=#State_AliveVisible And \DrawInstance
; Select *Layer\Blending
; Case #Blending_Normal
; SpriteBlendingMode(#PB_Sprite_BlendSourceAlpha, #PB_Sprite_BlendInvertSourceAlpha)
; Case #Blending_Add
; SpriteBlendingMode(#PB_Sprite_BlendOne, #PB_Sprite_BlendOne)
; Case #Blending_Mult
; SpriteBlendingMode(#PB_Sprite_BlendZero, #PB_Sprite_BlendSourceColor)
; Case #Blending_2XMult
; SpriteBlendingMode(#PB_Sprite_BlendDestinationColor, #PB_Sprite_BlendSourceColor)
; Case #Blending_Invert
; SpriteBlendingMode(#PB_Sprite_BlendInvertDestinationColor, #PB_Sprite_BlendOne)
; EndSelect
\DrawInstance(*sprite)
EndIf
EndWith
Next
EndProcedure
Procedure DisplayScreen()
ForEach DelayedActions()
With DelayedActions()
If \StartTime<ElapsedMilliseconds()
CallFunctionFast(\Action, \a, \b, \c, \d)
If \Interval
\StartTime=ElapsedMilliseconds()+\Interval
EndIf
If \EndTime<ElapsedMilliseconds()
DeleteElement(DelayedActions())
EndIf
EndIf
EndWith
Next
ForEach DisplayScreen\Layers()
Protected *layer.LAYER=DisplayScreen\Layers()
With *layer
If (\State & #State_AliveEnabledStarting)=#State_AliveEnabledStarting And \StartInstance
\State & ~#State_Starting
\StartInstance(*layer)
EndIf
If (\State & #State_AliveEnabled)=#State_AliveEnabled And \UpdateInstance
\UpdateInstance(*layer)
EndIf
If (\State & #State_AliveVisible)=#State_AliveVisible And \DrawInstance
\DrawInstance(*layer)
EndIf
EndWith
Next
EndProcedure
; ********************
; PREFAB
; ********************
Procedure.b IsPrefab(Prefab)
ForEach Prefabs()
If Prefabs()\Prefab=Prefab : ProcedureReturn #True : EndIf
Next
EndProcedure
Procedure.i PrefabID(Prefab)
If IsPrefab(Prefab)
ProcedureReturn Prefabs()
EndIf
EndProcedure
Procedure FreePrefab(Prefab)
Protected *prefabID.PREFAB=PrefabID(Prefab)
With *prefabID
FreeMemory(\InstanceTemplate)
DeleteElement(Prefabs())
EndWith
EndProcedure
Procedure.i CreatePrefab(Prefab, InstanceType, *Parent, TemplateSize)
Static PrefabCounter.i=1000000
If Prefab>1000000 : PB_DEBUGGER_SendError("#Prefab object number is very high (over 1000000)!") : EndIf
If Prefab<>#PB_Any And IsPrefab(Prefab)
FreePrefab(Prefab)
EndIf
Protected *prefabID.PREFAB=AddElement(Prefabs())
With *prefabID
\InstanceType=InstanceType
\Parent=*Parent
\InstanceTemplate=AllocateMemory(TemplateSize)
\InstanceTemplate\PrefabSource=*prefabID
\InstanceTemplate\State | #State_Prefab | #State_Enabled | #State_Visible | #State_Starting
If Prefab=#PB_Any
PrefabCounter+17
\Prefab=PrefabCounter
\CreatePrefabResult=\Prefab
Else
\Prefab=Prefab
\CreatePrefabResult=*prefabID
EndIf
ProcedureReturn *prefabID
EndWith
EndProcedure
Procedure.i CreateSpritePrefab(Prefab, Image, Layer, *Init=#Null, *Start=#Null, *Update=#Null, *Destroy=#Null)
Protected *prefabID.PREFAB=CreatePrefab(Prefab, #Instance_Sprite, Layer, SizeOf(SPRITE))
With *prefabID\SpriteTemplate
InitializeStructure(*prefabID\SpriteTemplate, SPRITE)
\Scale\x=1
\Scale\y=1
\Width=ImageWidth(Image)
\Height=ImageHeight(Image)
\Offset\x=\Width/2
\Offset\y=\Width/2
\Opacity=1
\Color=#PB_Default
\InitInstance=*Init
\StartInstance=*Start
\UpdateInstance=*Update
\DestroyInstance=*Destroy
\DrawInstance=@DefaultDrawSprite()
Protected *ImageMemory=EncodeImage(Image, #PB_ImagePlugin_PNG, 10, 32)
\Sprite=CatchSprite(#PB_Any, *ImageMemory, #PB_Sprite_BilinearFiltering | #PB_Sprite_AlphaBlending)
FreeMemory(*ImageMemory)
EndWith
ProcedureReturn *prefabID\CreatePrefabResult
EndProcedure
Procedure.i CreateLayerPrefab(Prefab, BlendingMode=#Blending_Normal, *Init=#Null, *Start=#Null, *Update=#Null, *Destroy=#Null)
Protected *prefabID.PREFAB=CreatePrefab(Prefab, #Instance_Layer, @DisplayScreen, SizeOf(LAYER))
With *prefabID\LayerTemplate
InitializeStructure(*prefabID\LayerTemplate, LAYER)
\Scale\x=1
\Scale\y=1
\Width=0
\Height=0
\Offset\x=0
\Offset\y=0
\Opacity=1
\Color=#PB_Default
\InitInstance=*Init
\StartInstance=*Start
\UpdateInstance=*Update
\DestroyInstance=*Destroy
\DrawInstance=@DefaultDrawLayer()
\Blending=BlendingMode
EndWith
ProcedureReturn *prefabID\CreatePrefabResult
EndProcedure
; ********************
; INSTANCE
; ********************
Procedure.i CreateThis(Prefab, Count=1, Delay=0)
If Delay=0
If Count=1
Protected *prefabID.PREFAB=PrefabID(Prefab)
With *prefabID
Protected *instance.INSTANCE
Select \InstanceType
Case #Instance_Sprite
*instance=AddElement(\ParentLayer\Sprites())
CopyStructure(\InstanceTemplate, *instance, SPRITE)
Case #Instance_Layer
*instance=AddElement(\ParentScreen\Layers())
CopyStructure(\InstanceTemplate, *instance, LAYER)
EndSelect
*instance\State | #State_Alive & ~#State_Prefab
If *instance\InitInstance
*instance\InitInstance(*instance)
EndIf
ProcedureReturn *instance
EndWith
Else
Protected creationLoop
For creationLoop=1 To Count
CreateThis(Prefab)
Next
EndIf
Else
DelayAction(Delay, @CreateThis(), Prefab, Count)
EndIf
EndProcedure
Procedure FreeThis(*This.INSTANCE)
With *This
If \DestroyInstance
\DestroyInstance(*This)
EndIf
If \CustomData
FreeMemory(\CustomData)
EndIf
*This\State & ~#State_Alive
Select \PrefabSource\InstanceType
Case #Instance_Sprite
ChangeCurrentElement(\PrefabSource\ParentLayer\Sprites(), *This)
DeleteElement(\PrefabSource\ParentLayer\Sprites())
Case #Instance_Layer
ChangeCurrentElement(\PrefabSource\ParentScreen\Layers(), *This)
DeleteElement(\PrefabSource\ParentScreen\Layers())
EndSelect
EndWith
EndProcedure
Procedure OffsetThis(*This.POINT_INSTANCE, x.f, y.f)
*This\Offset\x=x
*This\Offset\y=y
EndProcedure
Procedure RotateThis(*This.POINT_INSTANCE, Angle.f, Mode=#PB_Absolute)
If Mode=#PB_Absolute
*This\Angle=Angle
Else
*This\Angle+Angle
EndIf
EndProcedure
Procedure MoveThis(*This.POINT_INSTANCE, x.f, y.f, Mode=#PB_Absolute)
If Mode=#PB_Absolute
*This\Pos\x=x
*This\Pos\y=y
Else
*This\Pos\x+x
*This\Pos\y+y
EndIf
EndProcedure
Procedure ScaleThis(*This.SIZE_INSTANCE, x.f, y.f)
*This\Scale\x=x
*This\Scale\y=y
EndProcedure
Procedure OpacifyThis(*This.VISIBLE_INSTANCE, Opacity.f=1.0)
*This\Opacity=Opacity
EndProcedure
Procedure ColorizeThis(*This.VISIBLE_INSTANCE, Color=#PB_Default)
*This\Color=Color
EndProcedure
Procedure HideThis(*This.POINT_INSTANCE, hide.b)
If hide
*This\State & ~#State_Visible
Else
*This\State | #State_Visible
EndIf
EndProcedure
Procedure AnimateThis(*This.SPRITE, FrameIndex, FrameLoop.s="", FrameSet.s="")
With *This\Anim
If FrameSet<>""
FrameSet=LCase(FrameSet)
Protected rowLength=Val(StringField(FrameSet, 1, "x"))
Protected colLength=Val(StringField(FrameSet, 2, "x"))
If colLength<1 : PB_DEBUGGER_SendError("FrameSet is invalid: column length must be greater than 1!") : EndIf
If rowLength<1 : PB_DEBUGGER_SendError("FrameSet is invalid: row length must be greater than 1!") : EndIf
*This\Anim=AllocateMemory(SizeOf(ANIMATION))
InitializeStructure(*This\Anim, ANIMATION)
ReDim \Frames(colLength*rowLength-1)
Protected i, j, frameId, frmW.f, frmH.f
frmW=SpriteWidth(*This\Sprite)/colLength
frmH=SpriteHeight(*This\Sprite)/rowLength
For j=0 To rowLength-1
For i=0 To colLength-1
frameId=i+j*colLength
\Frames(frameId)\x=i* frmW
\Frames(frameId)\y=j* frmH
\Frames(frameId)\w=frmW
\Frames(frameId)\h=frmH
Next
Next
EndIf
If FrameLoop<>""
Protected loopIndex, loopLength=CountString(FrameLoop, ",")+1
ReDim \FrameIds(loopLength-1)
For loopIndex=0 To loopLength-1
frameId=Val(StringField(FrameLoop, loopIndex+1, ","))
If frameId<0 : PB_DEBUGGER_SendError("FrameLoop is invalid: frame ID must be greater than 1!") : EndIf
\FrameIds(loopIndex)=frameId
Next
EndIf
If *This\Anim
\CurrentIndex=FrameIndex
\CurrentFrame=\Frames(\FrameIds(\CurrentIndex))
*This\Width=\CurrentFrame\w
*This\Height=\CurrentFrame\h
EndIf
EndWith
EndProcedure
Procedure.b ThisIsAnimated(*This.SPRITE)
ProcedureReturn Bool(*This\Anim)
EndProcedure
Procedure.b ThisIsHidden(*This.POINT_INSTANCE)
ProcedureReturn Bool(*This\State & #State_Visible)
EndProcedure
Procedure.b ThisIsAlive(*This.POINT_INSTANCE)
ProcedureReturn Bool(*This\State & #State_Alive)
EndProcedure
Procedure.b ThisIsEnabled(*This.POINT_INSTANCE)
ProcedureReturn Bool(*This\State & #State_Enabled)
EndProcedure
Procedure.f ThisPositionX(*This.POINT_INSTANCE)
ProcedureReturn *This\Pos\x
EndProcedure
Procedure.f ThisPositionY(*This.POINT_INSTANCE)
ProcedureReturn *This\Pos\y
EndProcedure
Procedure.f ThisOffsetX(*This.POINT_INSTANCE)
ProcedureReturn *This\Offset\x
EndProcedure
Procedure.f ThisOffsetY(*This.POINT_INSTANCE)
ProcedureReturn *This\Offset\y
EndProcedure
Procedure.f ThisAngle(*This.POINT_INSTANCE)
ProcedureReturn *This\Angle
EndProcedure
Procedure.f ThisWidth(*This.SIZE_INSTANCE)
ProcedureReturn *This\Width
EndProcedure
Procedure.f ThisHeight(*This.SIZE_INSTANCE)
ProcedureReturn *This\Height
EndProcedure
Procedure.f ThisScaleX(*This.SIZE_INSTANCE)
ProcedureReturn *This\Scale\x
EndProcedure
Procedure.f ThisScaleY(*This.SIZE_INSTANCE)
ProcedureReturn *This\Scale\y
EndProcedure
Procedure.f ThisOpacity(*This.VISIBLE_INSTANCE)
ProcedureReturn *This\Opacity
EndProcedure
Procedure.i ThisColor(*This.VISIBLE_INSTANCE)
ProcedureReturn *This\Color
EndProcedure
Procedure.i ThisFrameIndex(*This.SPRITE)
If *This\Anim
ProcedureReturn *This\Anim\CurrentIndex
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure.i ThisFrameCount(*This.SPRITE)
If *This\Anim
ProcedureReturn ArraySize(*This\Anim\FrameIds())
Else
ProcedureReturn 1
EndIf
EndProcedure