Page 1 of 1

Sprite Functions

Posted: Tue Aug 21, 2007 4:27 pm
by flaith
Here some procedures allowing to open a file of sprites and to draw them (some functions looks like the Blitz procedure :wink: ):

Code: Select all

;*************************
;***                   ***
;***  GoSprite Engine  ***
;***                   ***
;*************************

;- Constantes
#GOSE_VERSION = "0.4.4"
#QUOTE = Chr(34)

#FULLSCREEN = #False

#Screen_width = 800
#Screen_height = 600
#depth = 32

#MAXSPR   = 100     ; NB MAX SPRITE
#VALWALK  = 5       ; Vitesse
#VALRUN   = 20
#VALLANCE = 30

#Fnt_Width = 16     ; Largeur de la fonte
#Fnt_Height = 16    ; Hauteur de la fonte

;- Structures
Structure SPR
  Spr.l[#MAXSPR]    ; #MAXSPR sprites possibles par image
EndStructure

Structure MYSPRITE
  spr_num.l
  spr_color.l
  spr_max.l
  spr_width.l
  spr_height.l
  sprite.SPR
EndStructure

;=========================================================================
;- Declarations
;=========================================================================
Declare GS_Error(num_error.l, val_error.l = 0)

;=========================================================================
;- Procedures
;=========================================================================
Procedure OpenWin()
  If Not #FULLSCREEN
    If OpenWindow(0, 0, 0, #Screen_width+80, #Screen_height, "GOSE v"+#GOSE_VERSION, #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
      If CreateGadgetList(WindowID(0))
        ButtonGadget(0, #Screen_width+20, #Screen_height-30, 45, 25, "Quit")
      EndIf
      If OpenWindowedScreen(WindowID(0), 0, 0, #Screen_width, #Screen_height, 0, 0, 0)
      Else
        MessageRequester("Erreur", "Impossible d'ouvrir un écran dans la fenêtre!", 0)
        End
      EndIf
    EndIf
  Else
    OpenScreen(#Screen_width, #Screen_height, #depth, "GOSE v"+#GOSE_VERSION)
  EndIf
EndProcedure

;> Procedure GS_Init()
Procedure GS_Init()
  If UsePNGImageDecoder() And InitSprite() And InitKeyboard()
    Global NewList Sprite.SPR()
    Global NewList Spr.MYSPRITE()
    Global Quit.l = 0
    Global knight.l, ring.l, fnt.l, fnt2.l, player1.l, player2.l, top_score_txt.l, top_score.l, score_p1.l, score_p2.l, game_over.l
    Global SpriteFileName.s = ""
  Else
    GS_Error(100)
  EndIf
EndProcedure

;> Procedure GS_Error(num_error.l, val_error.l = 0)
Procedure GS_Error(num_error.l, val_error.l = 0)
Protected Text_Error.s

  Select num_error
    Case 100
      Text_Error = "CAN'T INITIALIZE GRAPHICS !"
    Case 101
      Text_Error = "Unknow frame number"
    Case 102
      Text_Error = "Cannot create Image"
    Case 103
      Text_Error = "Cannot load sprite "+#QUOTE+SpriteFileName+#QUOTE
    Default
      Text_Error = "Unknow !!!"
  EndSelect

  If val_error > 0 : Text_Error + " #"+Str(val_error) : EndIf
  MessageRequester("[ ERROR ]",Text_Error)
  End
EndProcedure

;> Procedure.l GS_LoadAnimSprite(FileName.s,spr_Width.l,spr_Height.l,NB_Frame.l = 1,maskcolor.l = -1)
Procedure.l GS_LoadAnimSprite(FileName.s,spr_Width.l,spr_Height.l,NB_Frame.l = 1,maskcolor.l = -1)
Protected spr_num.l, image_width.l, image_height.l, nb_sprite_width.l
Protected spr_new.l, spr_color.l, colonne.l, x.l, y.l

SpriteFileName = FileName

If ReadFile(0,filename)
  spr_num         = LoadSprite(#PB_Any,filename)      ;attribution automatique d'un n° de sprite
  image_width     = SpriteWidth(spr_num)              ;Largeur de l'image
  nb_sprite_width = image_width / spr_width           ;nbre de sprites max par la largeur

  If maskcolor = -1
    StartDrawing(SpriteOutput(spr_num))
      spr_color = Point(0,0)                          ;retourne automatiquement la couleur du fond pour la transparence
    StopDrawing()
  Else
    spr_color = maskcolor                             ;sinon on met la couleur demandée
  EndIf

  AddElement(Spr())
    Spr()\spr_num     = spr_num
    Spr()\spr_color   = spr_color
    Spr()\spr_max     = NB_Frame
    Spr()\spr_width   = spr_width
    Spr()\spr_height  = spr_height
  
  ;Partie du code permetant la récupération des sprites
  ;exemple :
  ;
  ;fichier image contient 5 sprites (NB_Frame) 
  ;max de large = 3 car image_width / spr_width = 3 (nb_sprite_width)
  ;
  ; <----------- image_width ----------->
  ;
  ;   colonne=1
  ; +-----------+-----------+-----------+
  ; !<--------->! ^         !           !
  ; ! spr_width ! |         !           !
  ; !           ! spr_height!           !
  ; !           ! |         !           !
  ; !           ! |         !           !
  ; !          1! v        2!          3!
  ; +-----------+-----------+-----------+
  ; !           !           !
  ; !           !           !
  ; !           !           !
  ; !           !           !
  ; !           !           !
  ; !          4!          5!
  ; +-----------+-----------+
  colonne = 1                                                 ;à partir de la 1ere colonne
  x = y = 0                                                   ;on commence à la position 0,0
  
  UseBuffer(spr_num)                                          ;utilise le sprite chargé en mem
    For i = 1 To NB_Frame
      spr_new = GrabSprite(#PB_Any,x,y,spr_width,spr_height)  ;selectionne une partie pour créer un nouveau frame

      Spr()\Sprite\Spr[i] = spr_new                           ;ajout du nouveau frame dans liste chainée
      TransparentSpriteColor(spr_new, spr_color)              ;définit son maskcolor (pour la transparence)
      
      If colonne % nb_sprite_width = 0                        ;si le reste de la div = 0
        colonne = 1 : x = 0 : y + spr_height                  ;on va a la ligne suivante
      Else
        colonne + 1 : x + spr_width                           ;sinon on continue sur la même ligne
      EndIf
    Next i
  UseBuffer(-1)                                               ;retour buffer précédent
  
  ProcedureReturn spr_num
Else
  GS_Error(103)
EndIf

EndProcedure

;> Procedure GS_DrawSprite(Sprite.l,X.l,Y.l,Frame.l = 1)
Procedure GS_DrawSprite(Sprite.l,X.l,Y.l,Frame.l = 1)
Protected spr.l

  FirstElement(Spr())
  While Spr()\spr_num <> Sprite
    NextElement(Spr())
  Wend
  
  If frame > Spr()\spr_max : GS_Error(101,Frame) : EndIf
  spr = Spr()\Sprite\Spr[frame]
  DisplayTransparentSprite(spr, X, Y)
EndProcedure

;> Procedure GS_DrawText(Font.l, X.l,Y.l,Text$)
Procedure GS_DrawText(Font.l, X.l,Y.l,Text$)
Protected OldX.l = X
Protected position.l

	For i = 1 To Len(Text$)
		position = (Asc(Mid(Text$, i, 1)))-31        ;31 car on commence à partir du sprite 1 (sinon 32 si commence à 0)
		GS_DrawSprite(Font, OldX, Y, position)
		OldX = OldX + #Fnt_Width
	Next
	
EndProcedure

;> Procedure.l GS_MakeSpriteText(Font.l, Text$, Color.l)
Procedure.l GS_MakeSpriteText(Font.l, Text$, Color.l)
Protected SprID.l, spr_width.l, spr_height.l, ccolor.l

  FirstElement(Spr())
  While Spr()\spr_num <> Font
    NextElement(Spr())
  Wend
  spr_width = Spr()\spr_width * Len(Text$)
  spr_height = Spr()\spr_height
  
  SprID = CreateSprite(#PB_Any, spr_width, spr_height)

  If SprID
	  StartDrawing(SpriteOutput(SprID))
      Box(0, 0, spr_width, spr_height, Color)
    StopDrawing()
    UseBuffer(SprID)
      GS_DrawText(Font, 0, 0, Text$)
    UseBuffer(-1)

;     StartDrawing(SpriteOutput(SprID))
;       ccolor = Point(0,0)
;     StopDrawing()
; 
;     TransparentSpriteColor(SprID, ccolor)             ;couleur de la fonte est égale à RGB(0,0,0)

    TransparentSpriteColor(SprID, 0)                  ;couleur de la fonte est égale à RGB(0,0,0)

    AddElement(Spr())
      Spr()\spr_num       = SprID
      Spr()\spr_color     = color
      Spr()\spr_max       = 1
      Spr()\spr_width     = SpriteWidth(SprID)
      Spr()\spr_height    = SpriteHeight(SprID)
      Spr()\Sprite\Spr[1] = SprID

    ProcedureReturn SprID
  Else
    GS_Error(102)
  EndIf
EndProcedure

;>Procedure.l GS_GetFPS()
; Procedure.l GS_GetFPS()
; Static timerFrames.l, timerFPS.l, oldtime.l
;   
;   timerFrames + 1
;   If ElapsedMilliseconds()-OldTime >= 1000
;     OldTime = ElapsedMilliseconds()
;     timerFPS = timerFrames
;     timerFrames = 0
;   EndIf
;  
;   ProcedureReturn timerFPS
; EndProcedure 

Procedure.l GS_GetFPS()
Static time_init.l, time_fps.l, count_fps.l, number_FPS.l

  time_init = ElapsedMilliseconds()
  If time_init - time_fps <= 1000
    count_fps + 1
  Else 
    number_FPS = count_fps
    count_fps = 0
    time_fps = time_init
  EndIf
  
  ProcedureReturn number_FPS
EndProcedure

;>Procedure GS_Flip(RefreshRate.l)
Procedure GS_Flip(RefreshRate.l)
Static timer.f

  timer = ElapsedMilliseconds() - timer
  If timer <= 1000/RefreshRate
    Delay( Int( (1000/RefreshRate) - timer ) )
  EndIf
  FlipBuffers()
  timer = ElapsedMilliseconds()
EndProcedure

; ::::: Procedures Locales :::::
Structure ANIM
  x.l
  y.l
  frame.l
  speed.l
  count.l
EndStructure

Procedure Init_BackGround(nbcount.l)
  Global NewList spranim.ANIM()
  
  For i = 1 To nbcount
    AddElement(spranim())
      spranim()\x = Random(#Screen_width - 64)
      spranim()\y = Random(#Screen_height - 64)
      spranim()\frame = 1 + Random(3)
      spranim()\speed = Random(10)
      spranim()\count = 0
  Next i
EndProcedure

Procedure Draw_BackGround(sprite,max_frame)

  ForEach spranim()
    GS_DrawSprite(sprite,spranim()\x,spranim()\y,spranim()\frame)
    If spranim()\count < spranim()\speed
      spranim()\count + 1
    Else
      spranim()\frame + 1
      If spranim()\frame > max_frame : spranim()\frame = 1 : EndIf
      spranim()\count = 0
    EndIf
  Next
EndProcedure

;=========================================================================
;- Programme Principal
;=========================================================================
GS_Init()
OpenWin()

knight = GS_LoadAnimSprite("Knight_walk.png",48,72,12)
ring = GS_LoadAnimSprite("ring.png",64,64,4)
fnt = GS_LoadAnimSprite("GNG_Fnt.png",#Fnt_Width,#Fnt_Height,94)
fnt2 = GS_LoadAnimSprite("GNG_Fnt.png",#Fnt_Width,#Fnt_Height,94,$FFFFFF) ;$BBGGRR = RGB(255,255,255))
ecran = GS_LoadAnimSprite("ecran3.png",100,64,1,RGB(255,0,255))

player1 = GS_MakeSpriteText(fnt2, "PLAYER #", RGB(238,204,0))
player2 = GS_MakeSpriteText(fnt2, "PLAYER $", RGB(238,204,0))
player2_x = #Screen_width - SpriteWidth(player2)

score_p1 = GS_MakeSpriteText(fnt2, "10000", RGB(255,255,255))
score_p2 = GS_MakeSpriteText(fnt2, "0", RGB(255,255,255))
score_p2_x.l = #Screen_width - SpriteWidth(score_p2)

top_score_txt = GS_MakeSpriteText(fnt2, "TOP SCORE", RGB(255,0,102))
top_score_txt_x.l = (#Screen_width - SpriteWidth(top_score_txt))/2

game_over = GS_MakeSpriteText(fnt2, "GAME OVER", RGB(255,136,136))
game_over_x.l = (#Screen_width - SpriteWidth(game_over))/2
game_over_y.l = (#Screen_height - SpriteHeight(game_over))/2

top_score = GS_MakeSpriteText(fnt2, "100,000,000", RGB(255,255,255))
top_score_x = top_score_txt_x + SpriteWidth(top_score_txt) - SpriteWidth(top_score)

direction = #VALWALK
direction2 = #VALRUN
direction3 = #VALLANCE
x = 25 : x2 = 200 : x3 = 0
spr_knight_walk = 1             ; numéro du sprite du chevalier qui marche
spr_knight_run = 3              ; numéro du sprite du chevalier qui court
spr_knight_lance = 9            ; numéro du sprite du chevalier qui lance
ecr_x = #Screen_width-100
ecr_y = #Screen_height-64
Pas_x = 20
Pas_y = 10

Init_BackGround(400)            ; creation du fond d'écran avec 200 anneaux

Repeat
  ExamineKeyboard()

  If Not #FULLSCREEN
    Repeat
      Event = WindowEvent()

      Select Event 
        Case #PB_Event_Gadget
          If EventGadget() = 0
            quit = 1
          EndIf
        
        Case #PB_Event_CloseWindow
          quit = 1 
      EndSelect
    Until Event = 0
  EndIf
    
  If KeyboardPushed(#PB_Key_Escape) : quit = 1 : EndIf
  
; ====== AFFICHAGE ======
;  FlipBuffers()
  GS_Flip(35)
  ClearScreen(RGB(150, 100, 50))
  
; *** Fond d'écran

;   For i = 1 To 50
;     ax = Random(#Screen_width - 64)
;     ay = Random(#Screen_height - 64)
;     aspr = 1 + Random(3)
;     GS_DrawSprite(ring,ax,ay,aspr)
;   Next i

  Draw_BackGround(ring,4)
  
; *** Animation des sprites
  GS_DrawSprite(knight,x,0,spr_knight_walk)
  GS_DrawSprite(knight,x2,80,spr_knight_run)
  GS_DrawSprite(knight,x3,160,spr_knight_lance)

; *** FPS
  GS_DrawText(fnt,0,64,"FPS:"+Str(GS_GetFPS()))
  
; *** Affichages fixes
  GS_DrawText(fnt,0,#Screen_height-16,"GOSE "+#GOSE_VERSION+" % Flaith 2007")
  GS_DrawText(fnt,0,48,Str(ecr_x)+"/"+Str(ecr_y))
  
  GS_DrawSprite(player1,0,0)
  GS_DrawSprite(player2,player2_x,0)
  GS_DrawSprite(score_p1,0,20)
  GS_DrawSprite(score_p2,score_p2_X,20)
  
  GS_DrawSprite(top_score_txt,top_score_txt_x,0)
  GS_DrawSprite(top_score,top_score_x,20)
  GS_DrawSprite(game_over,game_over_x,game_over_y)

  GS_DrawSprite(ecran,ecr_x,ecr_y)

; *** Sprites suivants
  spr_knight_walk + 1
  spr_knight_run + 1
  spr_knight_lance + 1

; *** Vérification des sprites à afficher  
  If direction > 0
    If spr_knight_walk > 2 : spr_knight_walk = 1 :EndIf
  Else
    If spr_knight_walk > 6 : spr_knight_walk = 5 :EndIf
  EndIf

  If direction2 > 0
    If spr_knight_run > 4 : spr_knight_run = 3 :EndIf
  Else
    If spr_knight_run > 8 : spr_knight_run = 7 :EndIf
  EndIf

  If direction3 > 0
    If spr_knight_lance > 10 : spr_knight_lance = 9 :EndIf
  Else
    If spr_knight_lance > 12 : spr_knight_lance = 11 :EndIf
  EndIf

  If spr_ring > 4 : spr_ring = 1 : EndIf

; *** Vérification des coordonnées de l'écran à afficher  
  If KeyboardPushed(#PB_Key_Up) : ecr_y - Pas_y : EndIf
  If KeyboardPushed(#PB_Key_Down) : ecr_y + Pas_y : EndIf
  If KeyboardPushed(#PB_Key_Left) : ecr_x - Pas_x : EndIf
  If KeyboardPushed(#PB_Key_Right) : ecr_x + Pas_x : EndIf
  If ecr_x > #Screen_width-100 : ecr_x = #Screen_width-100 : EndIf
  If ecr_x < 0 : ecr_x = 0 : EndIf
  If ecr_y > #Screen_height-64 : ecr_y = #Screen_height-64 : EndIf
  If ecr_y < 0 : ecr_y = 0 : EndIf

; *** Modification de la direction chevalier marchant
  x + direction
  If x > #Screen_width-48 : direction = -#VALWALK : spr_knight_walk = 5 : EndIf
  If x < 0  : direction =  #VALWALK : spr_knight_walk = 1 : EndIf

; *** Modification de la direction chevalier courant
  x2 + direction2
  If x2 > #Screen_width-48 : direction2 = -#VALRUN : spr_knight_run = 7 : EndIf
  If x2 < 0  : direction2 =  #VALRUN : spr_knight_runk = 3 : EndIf
  
; *** Modification de la direction chevalier glissant ???
  x3 + direction3
  If x3 > #Screen_width-48 : direction3 = -#VALLANCE : spr_knight_lance = 11 : EndIf
  If x3 < 0  : direction3 =  #VALLANCE : spr_knight_lance = 9 : EndIf

Until quit = 1

End
sprites used for the example:

Image
Image
Image
Image

---
Nota : run under GNU/Linux & Windows

Posted: Mon Aug 27, 2007 4:02 am
by electrochrisso
Hi Flaith
:D Great work, I tinkered with your demo and could get some nice smooth and fast sprite movements.

Thanks.

Posted: Mon Aug 27, 2007 5:14 am
by Rook Zimbabwe
Flaith... nice work!!!! I had a lot of fun poking around this program... cannot get the little window any larger though... nice job though! Keep up the good work! :D

Posted: Mon Aug 27, 2007 7:56 pm
by flaith
Thanks guys :D

[EDIT] new version with a try of Get & Set FPS functions + Fullscreen

http://flaith.free.fr/gose/GoSprite_Engine_0_4_4.pb

Posted: Tue Aug 28, 2007 3:13 am
by Rook Zimbabwe
Grat going!!! On my wife's crappy Netvista Celeron 1000 w 512Mb and internal on board graphics it runs 30fps.

She has a bad LCD in this older IBM so there are artifact issues. NOT FROM YOUR ENGINE!

On my base model Athlon 64 3200+ with 1GB Ram and a ATi 9600 it still runs at 32 fps FULLSCREEN (1280X1024) with slight artifact smudge on the rings...

I do have a question though... the image of the rings is small and look like little gold rings... in the "ENGINE/GAME program" when it is running... they look like fat golden bagels.

Is there a distortion feature I don't see?

I like this... If you could include collisions of all the four types you would be greatly started on a great engine!

Keep up the good work! :D :D :D

Posted: Tue Aug 28, 2007 7:49 pm
by flaith
Rook Zimbabwe wrote:Grat going!!! On my wife's crappy Netvista Celeron 1000 w 512Mb and internal on board graphics it runs 30fps.

She has a bad LCD in this older IBM so there are artifact issues. NOT FROM YOUR ENGINE!

On my base model Athlon 64 3200+ with 1GB Ram and a ATi 9600 it still runs at 32 fps FULLSCREEN (1280X1024) with slight artifact smudge on the rings...

I do have a question though... the image of the rings is small and look like little gold rings... in the "ENGINE/GAME program" when it is running... they look like fat golden bagels.

Is there a distortion feature I don't see?
bagels ???...yummy :)
maybe the speed of the animation makes rings look like it :wink:
Rook Zimbabwe wrote:I like this... If you could include collisions of all the four types you would be greatly started on a great engine!

Keep up the good work! :D :D :D
what is the four types of collision ?

thanks Rook :D

Posted: Thu Aug 30, 2007 5:10 am
by Rook Zimbabwe
Flaith I slowed down the framerate and you are right about hte rings/bagels :D

Why does this thread seem to want me to access your flaith.free.fr website? I have had 6 popup access /password boxes in the past 3 minutes!

OK COllisions:

FULL STOP (useful for WALLS) the object in motion hits a different object and STOPS INSTANTLY... all tangent motion cancelled no relection.

SLIDING (useful for floors/ladders) Object cannot pass throu but can slide along boundry of object

REFLECTIVE (rubber bounce) Object reflects in a tangent from second object based on initial path/velocity

NONE (pretty much self explanitory!) :D

Posted: Thu Aug 30, 2007 8:56 am
by flaith
Rook Zimbabwe wrote:Flaith I slowed down the framerate and you are right about hte rings/bagels :D

Why does this thread seem to want me to access your flaith.free.fr website? I have had 6 popup access /password boxes in the past 3 minutes!
:oops: forgot to change the img dir because i had an .htaccess
Rook Zimbabwe wrote:OK COllisions:

FULL STOP (useful for WALLS) the object in motion hits a different object and STOPS INSTANTLY... all tangent motion cancelled no relection.

SLIDING (useful for floors/ladders) Object cannot pass throu but can slide along boundry of object

REFLECTIVE (rubber bounce) Object reflects in a tangent from second object based on initial path/velocity

NONE (pretty much self explanitory!) :D
noted, thanks Rook :D

[EDIT] - Added a new version, see the first post (added a new backgound/draw_background functions with an randomized speed)

Posted: Sat Sep 01, 2007 3:57 am
by Rook Zimbabwe
Oh yes... the rings are much cleaner!!! My favorite knight is the third one that acts like he has hadd too much chili and needs a screwdriver and a porta potty bad!

Flaith... you make a copuple more tweaks and you have your side scroller engine... a few tweaks after that and you have your complete ISO/2D engine...

I have been looking at Haaf's Game Engine, and while it is a bear to attempt to translate the commands needed to run the .DLL, some of his ideas are sound...

He uses white PNG on a transparent background to create the sprite flashy graphics and sprite tails etc. Simple pathing as well.

His gravity system is pretty well... just move all mobile sprites down to the bottom of the screen unless they collide with another sprite etc.

The main problem I see with the SpriteCollision() command is that it has to check every sprite on the screen... Unless I miss something.