There have been a few threads recently mentioning Sprite type programs so here's my contribution to add to them which may be of use to some.
It's not finished and needs a lot of tidying up but it does work.
As posted, it starts displaying 5 animated sprites of 140 x 140 pixels. Keep pressing a key and it displays 100 animated 140 x 140 Sprites then 200 animated number sprites of 100 x 100 pixels, then 1000 medium 14 x 14 pixel animated sprites and then 10,000 small (3 x 3) sprites and finally a mix of all of the above.
Sprite motion is automatic once set. The animation is automatic and consumes no extra time. It's reasonably efficient for large numbers of sprites but for very small numbers, although it works, it's not so efficient as it redraws the entire screen every frame.
The numbers of sprites set above were chosen quite low so it should run at a fixed 25fps on a low powered PC. See the top left corner of the window for current frames per second. I've had it run at 25fps with 150,000 small sprites or 1,500 large sprites on a decent PC.
A few very brief notes.
MaxSprites sets the maximum number of sprites. Not all sprites need to be displayed, they can be turned on and off individually by setting/clearing the %PlotSprite flag.
The %SpriteActive flag allows the sprite position to be updated correctly even if it's not displayed because the %PlotSprite is clear.
The Motion is currently set at 0 meaning linear motion with speed and position as given in xPos, yPos, xSpeed and ySpeed.
The intention is that in future a non-zero motion will call a SUB which will automatically calculate complex motions but this isn't implemented yet.
Each Sprite can have it's own transparent colour. I chose White for convenience but any colour is usable.
The whole thing is driven by a Timer, set at 40ms in the example to give a 25 frames per second. It can be varied to suit from 2ms up to anything.
The TimerFunction is called every time the timer triggers. It must be kept short so it only updates sprite positions and then sets a flag to indicate to the PlotThread that an update has occurred, it then returns. The PlotThread then does the more time consuming job of plotting the sprites.
The PlotFlag event is used to allow the 2 funtions to interact efficiently.
Certain small parts of the code are in ASM as they are speed critical.
The collision function will tell you if any 2 sprites are currently overlapping to allow for collision detection.
No alphablending yet. It's not too difficult but will slow things down a bit.
The sprite bitmaps are created, copied to strings and then destroyed as only the string is needed by the code, not the original bitmap.
The screen is buffered. The first time TimerFunction is called a copy is taken into 2 strings, Bmp and Bmp2
Subsequently, all writes to the screen are done by copying the initial bitmap back from Bmp2 to bmp then copying the active sprites to bmp, overwriting what was already there. A higher numbered Sprite is written later so it appears in the foreground compared to a lower numbered sprite.
Once all sprites are done the entire bmp is copied back to the screen.
Beacuse of the way the plotting and timing are done, if the plot code takes too long (e.g.if there are too many sprites to display) then the motion will still run correctly but display frames will be dropped so frame rate of the display will fall but the motion will still be correct and will be updated each frame even if the frame isn't displayed.
It's not finished and needs a lot of tidying up but it does work.
As posted, it starts displaying 5 animated sprites of 140 x 140 pixels. Keep pressing a key and it displays 100 animated 140 x 140 Sprites then 200 animated number sprites of 100 x 100 pixels, then 1000 medium 14 x 14 pixel animated sprites and then 10,000 small (3 x 3) sprites and finally a mix of all of the above.
Sprite motion is automatic once set. The animation is automatic and consumes no extra time. It's reasonably efficient for large numbers of sprites but for very small numbers, although it works, it's not so efficient as it redraws the entire screen every frame.
The numbers of sprites set above were chosen quite low so it should run at a fixed 25fps on a low powered PC. See the top left corner of the window for current frames per second. I've had it run at 25fps with 150,000 small sprites or 1,500 large sprites on a decent PC.
A few very brief notes.
MaxSprites sets the maximum number of sprites. Not all sprites need to be displayed, they can be turned on and off individually by setting/clearing the %PlotSprite flag.
The %SpriteActive flag allows the sprite position to be updated correctly even if it's not displayed because the %PlotSprite is clear.
The Motion is currently set at 0 meaning linear motion with speed and position as given in xPos, yPos, xSpeed and ySpeed.
The intention is that in future a non-zero motion will call a SUB which will automatically calculate complex motions but this isn't implemented yet.
Each Sprite can have it's own transparent colour. I chose White for convenience but any colour is usable.
The whole thing is driven by a Timer, set at 40ms in the example to give a 25 frames per second. It can be varied to suit from 2ms up to anything.
The TimerFunction is called every time the timer triggers. It must be kept short so it only updates sprite positions and then sets a flag to indicate to the PlotThread that an update has occurred, it then returns. The PlotThread then does the more time consuming job of plotting the sprites.
The PlotFlag event is used to allow the 2 funtions to interact efficiently.
Certain small parts of the code are in ASM as they are speed critical.
The collision function will tell you if any 2 sprites are currently overlapping to allow for collision detection.
No alphablending yet. It's not too difficult but will slow things down a bit.
The sprite bitmaps are created, copied to strings and then destroyed as only the string is needed by the code, not the original bitmap.
The screen is buffered. The first time TimerFunction is called a copy is taken into 2 strings, Bmp and Bmp2
Subsequently, all writes to the screen are done by copying the initial bitmap back from Bmp2 to bmp then copying the active sprites to bmp, overwriting what was already there. A higher numbered Sprite is written later so it appears in the foreground compared to a lower numbered sprite.
Once all sprites are done the entire bmp is copied back to the screen.
Beacuse of the way the plotting and timing are done, if the plot code takes too long (e.g.if there are too many sprites to display) then the motion will still run correctly but display frames will be dropped so frame rate of the display will fall but the motion will still be correct and will be updated each frame even if the frame isn't displayed.
Code:
'PBCC5.02/PBWin9.02 program #COMPILE EXE #DIM ALL %MaxSprites= 10000 'number of sprites to create %FrameDuration = 40 'time between updates in msec (timer trigger interval) 40 = 25fps %vSmall = 1 %medium = 2 %large = 3 %numbers = 4 'should screen wrap left-right or up-down or both %WrapX = 1 '1=sprite leaving side of screen will appear on other side automatically %WrapY = 1 '1=sprite leaving top/bottom of screen will appear at bottom/top automatically #INCLUDE "win32api.inc" 'All bitmaps to consist of a single bitmap split horizontally into frames for animation TYPE Sprite Hnd AS LONG 'handle to the bitmap string to use for this sprite xSize AS LONG 'sprite size x ySize AS LONG 'sprite size y xPos AS LONG 'sprite position x yPos AS LONG 'sprite position y xSpeed AS LONG 'sprite x speed for simple motion ySpeed AS LONG 'sprite y speed for simple motion Motion AS LONG 'index of which SUB should be called to calculate complex motion. 0 = simple motion MaxFrame AS LONG 'number of frames for animation in this bitmap CurrentFrame AS LONG 'start at frame 0 AnimationTime AS LONG 'The limit of AnimationCount before returning to zero and showing next frame AnimationCount AS LONG 'counter of time between updates of animation frames. Transparent AS LONG 'which colour is transparent in this sprite Flags AS LONG 'bit0 = %PlotSprite, bit1 = %SpriteActive END TYPE 'Flags in Sprites %PlotSprite = 1 'the sprite is to be draw on the screen %SpriteActive = 2 'the sprite is to be updated, even if not drawn GLOBAL TimerHandle AS LONG GLOBAL hGraph,hWindow AS LONG GLOBAL Sprites() AS Sprite GLOBAL ScreenWidth, ScreenHeight AS LONG GLOBAL SpriteStrings() AS STRING GLOBAL PlotFlag, QuitFlag AS LONG GLOBAL bmp,bmp2 AS STRING FUNCTION PBMAIN() AS LONG LOCAL nWidth, nHeight, nFile, r, num AS LONG DIM Sprites(%MaxSprites) DIM SpriteStrings(%MaxSprites) DESKTOP GET CLIENT TO ScreenWidth, ScreenHeight 'set size to other than full screen here: ScreenWidth = 800 ScreenHeight = 600 GRAPHIC BITMAP NEW ScreenWidth,ScreenHeight TO hGraph GRAPHIC WINDOW "Sprite tests...press a key",0,0,ScreenWidth,ScreenHeight TO hWindow 'draw the background graphic GRAPHIC ATTACH hGraph,0 'draw/load any image you like here as the background GRAPHIC CLEAR %WHITE 'copy the graphic to the visible window GRAPHIC ATTACH hWindow,0 GRAPHIC COPY hGraph,0 LOCAL x AS LONG LOCAL y,hFont, hBmp, col AS LONG 'a very small sprite GRAPHIC BITMAP NEW 3,3 TO hBmp GRAPHIC ATTACH hBmp,0 GRAPHIC CLEAR %WHITE GRAPHIC ELLIPSE (0,0)-(3,3),%BLACK,%BLACK Sprites(%vSmall).hnd = %vSmall Sprites(%vSmall).xSize = 3 Sprites(%vSmall).ySize = 3 Sprites(%vSmall).xPos = 400 Sprites(%vSmall).yPos =100 Sprites(%vSmall).xSpeed =4 Sprites(%vSmall).ySpeed =1 Sprites(%vSmall).Motion =0 Sprites(%vSmall).MaxFrame = 1 Sprites(%vSmall).CurrentFrame = 0 Sprites(%vSmall).AnimationTime =0 Sprites(%vSmall).AnimationCount =0 Sprites(%vSmall).Flags =%PlotSprite OR %SpriteActive Sprites(%vSmall).Transparent = &hffffff GRAPHIC ATTACH hBmp,0 GRAPHIC GET BITS TO SpriteStrings(%vSmall) GRAPHIC BITMAP END GRAPHIC DETACH 'a medium sized animated sprite 14 x 14 GRAPHIC BITMAP NEW 686,14 TO hBmp GRAPHIC ATTACH hBmp,0 GRAPHIC CLEAR %WHITE FOR x = 0 TO 49 GRAPHIC ELLIPSE (x*14,0)-(x*14+14,14),%RED,%YELLOW '* (x-4.5)/9 GRAPHIC PIE (x*14,0)-(x*14+14,14),x/49*2*3.142,(x+20)/49*2*3.142, %RED,%GREEN GRAPHIC SET POS (x*140+70,70) NEXT Sprites(%medium).hnd = %medium Sprites(%medium).xSize = 14 Sprites(%medium).ySize = 14 Sprites(%medium).xPos = 400 Sprites(%medium).yPos =100 Sprites(%medium).xSpeed =4 Sprites(%medium).ySpeed =1 Sprites(%medium).Motion =0 Sprites(%medium).MaxFrame = 49 Sprites(%medium).CurrentFrame = 0 Sprites(%medium).AnimationTime =10 Sprites(%medium).AnimationCount =0 Sprites(%medium).Flags =%PlotSprite OR %SpriteActive Sprites(%medium).Transparent = &hffffff GRAPHIC ATTACH hBmp,0 GRAPHIC GET BITS TO SpriteStrings(%medium) GRAPHIC BITMAP END GRAPHIC DETACH 'a large animated sprite 140 x 140 GRAPHIC BITMAP NEW 6860,140 TO hBmp GRAPHIC ATTACH hBmp,0 GRAPHIC CLEAR %WHITE FOR x = 0 TO 49 GRAPHIC ELLIPSE (x*140,0)-(x*140+140,140),%RED,%YELLOW '* (x-4.5)/9 GRAPHIC PIE (x*140,0)-(x*140+140,140),x/49*2*3.142,(x+4)/49*2*3.142, %YELLOW,%GREEN GRAPHIC SET POS (x*140+70,70) GRAPHIC PRINT x NEXT Sprites(%large).hnd = %large Sprites(%large).xSize = 140 Sprites(%large).ySize = 140 Sprites(%large).xPos = 400 Sprites(%large).yPos =100 Sprites(%large).xSpeed =4 Sprites(%large).ySpeed =1 Sprites(%large).Motion =0 Sprites(%large).MaxFrame = 49 Sprites(%large).CurrentFrame = 0 Sprites(%large).AnimationTime =10 Sprites(%large).AnimationCount =0 Sprites(%large).Flags =%PlotSprite OR %SpriteActive Sprites(%large).Transparent = &hffffff GRAPHIC ATTACH hBmp,0 GRAPHIC GET BITS TO SpriteStrings(%large) GRAPHIC BITMAP END GRAPHIC DETACH 'animated numbers 100 x 100 GRAPHIC BITMAP NEW 1000,100 TO hBmp GRAPHIC ATTACH hBmp,0 GRAPHIC CLEAR %WHITE FONT NEW "Courier New",100,1 TO hFont GRAPHIC SET FONT hFont FOR x = 0 TO 9 GRAPHIC COLOR RGB(x*RND(1,30),x*RND(1,30),x*RND(30)),%WHITE GRAPHIC SET POS (x*100,-20) GRAPHIC PRINT FORMAT$(x); NEXT Sprites(%numbers).hnd = %numbers Sprites(%numbers).xSize = 100 Sprites(%numbers).ySize = 100 Sprites(%numbers).xPos = 400 Sprites(%numbers).yPos =100 Sprites(%numbers).xSpeed =4 Sprites(%numbers).ySpeed =1 Sprites(%numbers).Motion =0 Sprites(%numbers).MaxFrame = 10 Sprites(%numbers).CurrentFrame = 0 Sprites(%numbers).AnimationTime =10 Sprites(%numbers).AnimationCount =0 Sprites(%numbers).Flags =%PlotSprite OR %SpriteActive Sprites(%numbers).Transparent = &hffffff GRAPHIC ATTACH hBmp,0 GRAPHIC GET BITS TO SpriteStrings(%numbers) GRAPHIC DETACH FOR r = 0 TO 10 Sprites(r).flags = 0 'don't plot these NEXT FOR r = 10 TO 14 num = %large Sprites(r)=Sprites(num) Sprites(r).xpos=RND(0,ScreenWidth) Sprites(r).ypos=RND(0,ScreenHeight) Sprites(r).xSpeed=RND(-5,5) Sprites(r).ySpeed=RND(-5,5) Sprites(r).CurrentFrame = RND(0,Sprites(r).MaxFrame -1) Sprites(r).AnimationTime = 1 Sprites(r).flags = %PlotSprite OR %SpriteActive NEXT PlotFlag = CreateEvent(BYVAL 0,BYVAL 1, BYVAL 0, BYVAL 0) 'default security,Manual Reset, Initially not-signalled, no name ' GLOBAL hPlotThread AS LONG LOCAL junk AS LONG THREAD CREATE PlotThread(junk) TO hPlotThread 'start the main timer '40=milliseconds between triggers, 0=maximum timer resolution, test=the routine to call TimerHandle = timeSetEvent ( BYVAL %FrameDuration, BYVAL 0, CODEPTR(TimerFunction), BYVAL 0&, BYVAL %TIME_PERIODIC) 'wait for key press GRAPHIC ATTACH hWindow,0 GRAPHIC WAITKEY$ FOR r = 10 TO 110 num = %large Sprites(r)=Sprites(num) Sprites(r).xpos=RND(0,ScreenWidth) Sprites(r).ypos=RND(0,ScreenHeight) Sprites(r).xSpeed=RND(-5,5) Sprites(r).ySpeed=RND(-5,5) Sprites(r).CurrentFrame = RND(0,Sprites(r).MaxFrame -1) Sprites(r).AnimationTime = 1 Sprites(r).flags = %PlotSprite OR %SpriteActive NEXT GRAPHIC WAITKEY$ FOR r = 10 TO 210 num = %numbers Sprites(r)=Sprites(num) Sprites(r).xpos=RND(0,ScreenWidth) Sprites(r).ypos=RND(0,ScreenHeight) Sprites(r).xSpeed=RND(-5,5) Sprites(r).ySpeed=RND(-5,5) Sprites(r).CurrentFrame = RND(0,Sprites(r).MaxFrame -1) Sprites(r).AnimationTime = RND(1,10) Sprites(r).flags = %PlotSprite OR %SpriteActive NEXT GRAPHIC WAITKEY$ FOR r = 10 TO 1010 num = %medium Sprites(r)=Sprites(num) Sprites(r).xpos=RND(0,ScreenWidth) Sprites(r).ypos=RND(0,ScreenHeight) Sprites(r).xSpeed=RND(-5,5) Sprites(r).ySpeed=RND(-5,5) Sprites(r).CurrentFrame = RND(0,Sprites(r).MaxFrame -1) Sprites(r).AnimationTime = 1 Sprites(r).flags = %PlotSprite OR %SpriteActive NEXT GRAPHIC WAITKEY$ FOR r = 10 TO %maxsprites num = %vsmall Sprites(r)=Sprites(num) Sprites(r).xpos=RND(0,ScreenWidth) Sprites(r).ypos=RND(0,ScreenHeight) Sprites(r).xSpeed=RND(-5,5) Sprites(r).ySpeed=RND(-5,5) Sprites(r).CurrentFrame = RND(0,Sprites(r).MaxFrame -1) Sprites(r).AnimationTime = 1 Sprites(r).flags = %PlotSprite OR %SpriteActive NEXT GRAPHIC WAITKEY$ FOR r = 10 TO 200 num = RND(%vsmall,%numbers) Sprites(r)=Sprites(num) Sprites(r).xpos=RND(0,ScreenWidth) Sprites(r).ypos=RND(0,ScreenHeight) Sprites(r).xSpeed=RND(-5,5) Sprites(r).ySpeed=RND(-5,5) Sprites(r).CurrentFrame = RND(0,Sprites(r).MaxFrame -1) Sprites(r).AnimationTime = 1 Sprites(r).flags = %PlotSprite OR %SpriteActive NEXT FOR r = 201 TO %MaxSprites Sprites(r).flags = 0 NEXT GRAPHIC WAITKEY$ QuitFlag = 1 'force all threads to terminate timeKillEvent TimerHandle CloseHandle PlotFlag GRAPHIC ATTACH hGraph,0 GRAPHIC BITMAP END GRAPHIC ATTACH hWindow,0 GRAPHIC BITMAP END 'Give timer time to stop in case it triggers again after program ends' should wait and check it SLEEP 100 END FUNCTION FUNCTION TimerFunction ( BYVAL uID AS LONG, BYVAL uMsg AS LONG, _ BYVAL dwUser AS LONG, BYVAL dw1 AS LONG, BYVAL dw2 AS LONG) AS LONG 'this is the routine that is run everytime the timer triggers #REGISTER NONE LOCAL WhichSprite AS LONG STATIC CalledBefore AS LONG GRAPHIC ATTACH hWindow,0 IF NOT CalledBefore THEN CalledBefore = -1 GRAPHIC GET BITS TO bmp Bmp2=Bmp END IF 'do animation FOR WhichSprite = 1 TO %MaxSprites IF (Sprites(WhichSprite).flags AND %SpriteActive) THEN 'update Sprite position Sprites(WhichSprite).yPos = Sprites(WhichSprite).yPos + Sprites(WhichSprite).ySpeed Sprites(WhichSprite).xPos = Sprites(WhichSprite).xPos + Sprites(WhichSprite).xSpeed IF %WrapX THEN IF Sprites(WhichSprite).xPos > ScreenWidth THEN Sprites(WhichSprite).xPos = Sprites(WhichSprite).xPos - ScreenWidth - Sprites(WhichSprite).xSize IF Sprites(WhichSprite).xPos < - Sprites(WhichSprite).xSize THEN Sprites(WhichSprite).xPos = Sprites(WhichSprite).xPos + ScreenWidth + Sprites(WhichSprite).xSize END IF IF %WrapY THEN IF Sprites(WhichSprite).yPos > ScreenHeight THEN Sprites(WhichSprite).yPos = Sprites(WhichSprite).yPos - ScreenHeight - Sprites(WhichSprite).ySize IF Sprites(WhichSprite).yPos < -Sprites(WhichSprite).ySize THEN Sprites(WhichSprite).yPos = Sprites(WhichSprite).yPos + ScreenHeight + Sprites(WhichSprite).ySize END IF INCR Sprites(WhichSprite).AnimationCount IF Sprites(WhichSprite).AnimationCount = Sprites(WhichSprite).AnimationTime THEN Sprites(WhichSprite).AnimationCount = 0 Sprites(WhichSprite).CurrentFrame = (Sprites(WhichSprite).CurrentFrame + 1) MOD Sprites(WhichSprite).MaxFrame END IF END IF NEXT 'Indicate to plot thread that there's been an update so it can start to plot SetEvent PlotFlag #IF 0 'don't bother with collision detection, just rem it out for now FOR x = 1 TO MIN&(220, %MaxSprites) FOR y = x TO MIN&(220,%MaxSprites) IF x <> y THEN IF Collision(x,y) THEN Sprites(x).xSpeed=RND(-20,20) Sprites(x).ySpeed=RND(-20,20) Sprites(y).xSpeed=RND(-20,20) Sprites(y).ySpeed=RND(-20,20) END IF END IF NEXT NEXT #ENDIF GRAPHIC DETACH END FUNCTION FUNCTION Collision ( Sprite1 AS LONG, Sprite2 AS LONG) AS LONG 'Check to see if 2 sprites have collided #REGISTER NONE LOCAL x ,y, xLimit, yLimit, Transparent1, Transparent2 AS LONG LOCAL SpriteStart1, SpriteStart1X, SpriteStart1Y, Sprite1Base, Sprite1Offset, xSprite1TotWidth AS LONG LOCAL SpriteStart2, SpriteStart2X, SpriteStart2Y, Sprite2Base, Sprite2Offset, xSprite2TotWidth AS LONG SpriteStart1 = STRPTR(SpriteStrings(Sprites(Sprite1).hnd))+8 SpriteStart2 = STRPTR(SpriteStrings(Sprites(Sprite2).hnd))+8 xSprite1TotWidth = CVL(SpriteStrings(Sprites(Sprite1).hnd),1) xSprite2TotWidth = CVL(SpriteStrings(Sprites(Sprite2).hnd),1) SpriteStart2X = MAX&(0,Sprites(Sprite1).xPos-Sprites(Sprite2).xPos) SpriteStart2Y = MAX&(0,Sprites(Sprite1).yPos-Sprites(Sprite2).yPos) SpriteStart1X = MAX&(0,Sprites(Sprite2).xPos-Sprites(Sprite1).xPos) SpriteStart1Y = MAX&(0,Sprites(Sprite2).yPos-Sprites(Sprite1).yPos) xLimit = 4*(MIN&(Sprites(Sprite2).xPos + Sprites(Sprite2).xSize, Sprites(Sprite1).xPos + Sprites(Sprite1).xSize ) - Sprites(Sprite2).xPos - SpriteStart2X -1) yLimit = 4*(MIN&(Sprites(Sprite2).yPos + Sprites(Sprite2).ySize, Sprites(Sprite1).yPos + Sprites(Sprite1).ySize ) - Sprites(Sprite2).yPos - SpriteStart2Y -1) IF xLimit>0 AND yLimit>0 THEN 'sprite blocks overlap so look at it in more detail to determine collision Transparent1 = Sprites(Sprite1).Transparent Transparent2 = Sprites(Sprite2).Transparent Sprite1Base = SpriteStart1 + 4 * (SpriteStart1X + Sprites(Sprite1).xSize * Sprites(Sprite1).CurrentFrame + SpriteStart1Y * xSprite1TotWidth) Sprite2Base = SpriteStart2 + 4 * (SpriteStart2X + Sprites(Sprite2).xSize * Sprites(Sprite2).CurrentFrame + SpriteStart2Y * xSprite2TotWidth) FOR y = 0 TO yLimit STEP 4 Sprite1Offset = Sprite1Base + y * xSprite1TotWidth Sprite2Offset = Sprite2Base + y * xSprite2TotWidth ' for x = 0 to xLimit 'The x loop is time sensitive so do it in ASM !mov edx,Transparent1 'the colour to be transparent for sprite1 !mov eax,Transparent2 'the colour to be transparent for sprite2 !mov ecx,xLimit 'x pixel count, Count down to 0 !mov esi,Sprite2Offset 'point to right place in sprite2 !mov edi,Sprite1Offset 'point to right place in sprite1 #ALIGN 16 lp2: !prefetchnta [esi+ecx-64] !prefetchnta [edi+ecx-64] !cmp eax,[esi+ecx] !je short skip1 !cmp edx,[edi+ecx] !je short skip1 !mov function,-1 EXIT FUNCTION skip1: !sub ecx,4 'next x. 4 bytes per pixel !js short xit 'if not -ve then loop back !cmp eax,[esi+ecx] !je short skip2 !cmp edx,[edi+ecx] !je short skip2 !mov function,-1 EXIT FUNCTION skip2: !sub ecx,4 'next x. 4 bytes per pixel !js short xit 'if not -ve then loop back !cmp eax,[esi+ecx] !je short skip3 !cmp edx,[edi+ecx] !je short skip3 !mov function,-1 EXIT FUNCTION skip3: !sub ecx,4 'next x. 4 bytes per pixel !js short xit 'if not -ve then loop back !cmp eax,[esi+ecx] !je short skip !cmp edx,[edi+ecx] !je short skip !mov function,-1 EXIT FUNCTION skip: !sub ecx,4 'next x. 4 bytes per pixel !jns lp2 'if not -ve then loop back xit: NEXT 'y END IF 'xLimit<0 and yLimit<0 END FUNCTION FUNCTION PlotThread(BYVAL junk AS LONG) AS LONG #REGISTER NONE LOCAL WhichSprite, x ,y, xLimit, yLimit, Transparent AS LONG LOCAL BmpStart, BMPStart1, BmpStart2, BmpStartX, BmpStartY, BMPbase, BMPoffset, LenBmp AS LONG LOCAL SpriteStart, SpriteStartX, SpriteStartY, SpriteOffset, xSpriteTotWidth, SpriteBase AS LONG 'Variables that can be deleted after testing: STATIC Cnt AS LONG STATIC Tot,k,k2 AS QUAD GRAPHIC ATTACH hWindow,0 STATIC frames AS LONG STATIC tm1 AS EXT tm1 = TIMER DO 'wait until timer has triggered and updated sprite positions WaitForSingleObject PlotFlag, %INFINITE BmpStart = STRPTR(Bmp2) + 8 BMPStart1 = STRPTR(Bmp) + 8 BMPStart2 = STRPTR(Bmp2) + 8 LenBmp =LEN(bmp) - 8 ReStart: 'Bmp2 = bmp 'This is a time consuming copy in a time sensitive place which can be done a little faster in ASM 'copy entire background image to overwrite bitmap for display !mov ecx,LenBmp !mov esi,BMPstart1 !add esi,ecx !mov edi,BMPstart2 !add edi,ecx !shr ecx,2 !neg ecx #ALIGN 16 lp: !prefetchnta [esi+ecx*4+512] !movq mm0,[esi+ecx*4] !movq mm1,[esi+ecx*4+8] !movq mm2,[esi+ecx*4+16] !movq mm3,[esi+ecx*4+24] !movq mm4,[esi+ecx*4+32] !movq mm5,[esi+ecx*4+40] !movq mm6,[esi+ecx*4+48] !movq mm7,[esi+ecx*4+56] !movntq [edi+ecx*4],mm0 !movntq [edi+ecx*4+8],mm1 !movntq [edi+ecx*4+16],mm2 !movntq [edi+ecx*4+24],mm3 !movntq [edi+ecx*4+32],mm4 !movntq [edi+ecx*4+40],mm5 !movntq [edi+ecx*4+48],mm6 !movntq [edi+ecx*4+56],mm7 !add ecx,16 !jnz lp !sfence !emms FOR WhichSprite = 1 TO %MaxSprites IF (Sprites(WhichSprite).flags AND %PlotSprite) THEN 'copy the sprite into the correct location within Bmp2 which is then copied to the screen SpriteStart = STRPTR(SpriteStrings(Sprites(WhichSprite).hnd))+8 xSpriteTotWidth = CVL(SpriteStrings(Sprites(WhichSprite).hnd),1) BmpStartX = MAX&(0,Sprites(WhichSprite).xPos) BmpStartY = MAX&(0,Sprites(WhichSprite).yPos) SpriteStartX = MAX&(0, - Sprites(WhichSprite).xPos) + Sprites(WhichSprite).xSize * Sprites(WhichSprite).CurrentFrame SpriteStartY = MAX&(0,-Sprites(WhichSprite).yPos) xLimit = (MIN&(Sprites(WhichSprite).xSize + Sprites(WhichSprite).xPos, Sprites(WhichSprite).xSize, ScreenWidth - Sprites(WhichSprite).xPos) -1) *4 yLimit = (MIN&(Sprites(WhichSprite).ySize + Sprites(WhichSprite).yPos, Sprites(WhichSprite).ySize, ScreenHeight - Sprites(WhichSprite).yPos) -1) *4 IF xLimit>0 AND yLimit>0 THEN 'sprite is at least partly on screen so copy it Transparent = Sprites(WhichSprite).Transparent BMPbase = BMPstart + 4 * BmpStartX + 4 * ScreenWidth * BmpStartY SpriteBase = SpriteStart + 4 * SpriteStartX + 4 *SpriteStartY * xSpriteTotWidth FOR y = 0 TO yLimit STEP 4 BMPoffset = BMPbase + y * ScreenWidth SpriteOffset = SpriteBase + y * xSpriteTotWidth ' for x = 0 to xLimit 'The x loop is time sensitive so do it in ASM !mov edx,Transparent 'the colour to be transparent for this sprite !mov edi,BMPoffset 'point to right place in bitmap !mov ecx,xLimit 'x pixel count, Count down to 0 !mov esi,SpriteOffset 'point to right place in sprite #ALIGN 16 lp2: !prefetchnta [esi+ecx-128] !prefetcht0 [edi+ecx-64] !mov eax,[esi+ecx] 'eax = pixel from sprite !cmp eax,edx 'is it to be transparent? !je short skip 'yes, don't write it to screen !mov [edi+ecx],eax 'write pixel to screen skip: !sub ecx,4 'next x. 4 bytes per pixel !js short xit 'if not -ve then loop back !mov eax,[esi+ecx] 'eax = pixel from sprite !cmp eax,edx 'is it to be transparent? !je short skip1 'yes, don't write it to screen !mov [edi+ecx],eax 'write pixel to screen skip1: !sub ecx,4 'next x. 4 bytes per pixel !js short xit 'if not -ve then loop back !mov eax,[esi+ecx] 'eax = pixel from sprite !cmp eax,edx 'is it to be transparent? !je short skip2 'yes, don't write it to screen !mov [edi+ecx],eax 'write pixel to screen skip2: !sub ecx,4 'next x. 4 bytes per pixel !js short xit 'if not -ve then loop back !mov eax,[esi+ecx] 'eax = pixel from sprite !cmp eax,edx 'is it to be transparent? !je short skip3 'yes, don't write it to screen !mov [edi+ecx],eax 'write pixel to screen skip3: !sub ecx,4 'next x. 4 bytes per pixel !jns short lp2 'if not -ve then loop back xit: ' next 'x NEXT 'y END IF 'xLimit<0 and yLimit<0 END IF 'plotsprite NEXT 'WhichSprite GRAPHIC SET BITS bmp2 'Write the entire new bitmap to the screen ResetEvent PlotFlag 'This prevents the plot being called when there's nothing to plot LOCAL fps AS STRING 'print frames per second INCR frames IF frames = 50 THEN frames = 0 fps = "fps="+STR$(INT(100*50/(TIMER-tm1))/100) tm1=TIMER END IF GRAPHIC SET POS (1,1) GRAPHIC PRINT fps LOOP UNTIL QuitFlag END FUNCTION
Comment