March 28, 2024, 05:57:26 AM

News:

Own IWBasic 2.x ? -----> Get your free upgrade to 3.x now.........


How much can I do in DirectX in a few ms?

Started by barry, February 03, 2010, 02:48:50 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

barry

I have a few questions about how to proceed with a little game.  I'm going to use the start I made based on one of the samples in my previous question about the FLIP statement with a timer.  I know practically nothing about the capabilities of DirectX and not much about Windows programming.

The game will have from 2 to 10 sprites rotating in place inside a circle and with each mouse button press they'll move forward while the mouse button is held down.  The sprites will be 32x32 and there won't ever be more than 10 of them.  The movement won't be particularly fast.

What I'm wondering is how to go about organizing this.  The first thing that occurs to me based on my not-so-good understanding of DX so far is to have the update routine redraw the background first and then the sprites in their new positions and orientations.  It seems that if I don't redraw the background each time the mouse's old position isn't erased when the new position is drawn.

Another thing I considered is to place a circle around each sprite that will be the same color as the background and let it rotate with the sprite.  That should work and will probably be faster but it keeps me from having a textured background and there are problems when it gets to the edge of the circle.  The textured background is a minor issue but it would be nice to have.  The problems at the edge of the circle seem like a deal breaker to me.  The game is lost when a sprite touches the edge of the circle.

My slowest computer is the EEE PC with it's 1.6 ghz Atom CPU I bought from Paul.  Everyone I know has a faster computer than that, as do I, and this is the slowest computer it's likely to run on, so I really don't care if older, slower computers can do it.

The background will probably be 640x480 and the circle will probably be something like 440x440.

Do those of you who have done things with DX before think this is something I need to worry about?  Will I be able to redraw the background and up to 10 rotating sprites fast enough?  I don't know yet what I'll be setting the timer at but probably something like 5ms so that multiples of that can be used for each rotation since they'll be rotating at different speeds.  My guess at this point is that that will give me enough granularity.  If not might have to be less than 5ms.

What I'm hoping for is any thoughts you guys have on this.  I can just have it draw the background with the single sprite I'm using to test this but when I figure out how to add more sprites if I find out I have to do it all over a different way because it can't keep up I might cry. :)

By the way, this is just a learning project.  I have a little game like this already, written in a competing Basic by someone else.  I just thought it might be a good introduction to DX for me and might be fun to do.  I don't really have a great need for the program itself.

Thanks,
Barry

ZeroDog

You will have to draw the background and all the sprites for each frame rendered.  You can also use a tile map for the background.   You should also be timing the game to real time, that way if a slower or faster computer is running the game, it will adjust to match the real time speed of the game.  Try looking in the Asteroids sample game that comes with EBasic.  Paul put some good comments in there about how to keep your game running at a constant speed.

barry

I am using a Windows timer to control the speed.    Can I draw the background every 5 to 10 ms and expect a typical system to keep up?  I've got it drawing the background now.  Well, just a simple fill color followed by a filled circle.  Actually I might be ableo to leave out the fill color step since all the movement will be within the circle.

Anyway it's flashing kind of irregularly and I'm not sure why yet but I just got it doing that and I haven't really explored it yet but it has me worried.

I suspect that since the background will be circular a tile map won't help.  In any case wouldn't that be slower?  Or do you mean to just replace the tiles under the sprite?  Would that be faster than redrawing the background?  I guess that's worth thinking about but, again, there's a circle so I'd have problems around the edges.

Thanks,
Barry

barry

Something else I've found is that the sprite passes under the drawn circle instead of over it.  Is there a way to control the priorities or levels?  I am drawing the circle first and then using drawsprite.

Thanks,
Barry

pistol350

Quote from: barry on February 03, 2010, 05:50:31 PM
Something else I've found is that the sprite passes under the drawn circle instead of over it.  Is there a way to control the priorities or levels?  I am drawing the circle first and then using drawsprite.

Thanks,
Barry


Hi Barry!
I'll be following your learning process as i'm interested in anything related to 2D programming.
I'm pretty sure that i'll learn a lot too.
Anyway, about the priority level problem you're facing, remember that when it comes to drawing objects, it's FILO (First In, Last Out).
Which means that objects drawn first are rendered in the background so they'll be hidden by those drawn after.I hope that my explanation makes sense.
Regards,

Peter B.

barry

Quote from: pistol350 on February 04, 2010, 08:37:16 AM
Anyway, about the priority level problem you're facing, remember that when it comes to drawing objects, it's FILO (First In, Last Out).
Which means that objects drawn first are rendered in the background so they'll be hidden by those drawn after.I hope that my explanation makes sense.

It's loud and clear.  Only problem is it doesn't seem to be working that way.  Probably for some reason I haven't found yet.  I'll keep looking.

Barry

pistol350

I remember someone talking about showing pieces of code not long ago... ;)
Regards,

Peter B.

pistol350

OK,
I modified your previous code to add sprites Zorder rendering.
Hope you find it helpful.

/* dx1.eba - sprite sample */
' sprites Zorder example

WINDOW win
speed = 0
width = 640
height = 480
run = 1

TYPE object
DEF xpos:float
DEF ypos:float
DEF width:int
DEF height:int
DEF xvel:int
DEF yvel:int
DEF speed:float
DEF spriteID:int
DEF spriteColor:uint
DEF Zorder:int
ENDTYPE

INT totalSprites,spriteIndex
POINTER newSprite[8]
Def ball[8] as object
totalSprites = 8

INT maxColors = 8
DEF sprcol[8]:uint
DEF red,green,blue,brown,black,yellow,pink,gray as uint

' Set main colors
red = rgb(255,0,0)
green = rgb(0,255,0)
blue = rgb(0,0,255)
brown = rgb(125,85,30)
black = rgb(10,10,10)
yellow = rgb(240,240,30)
pink = rgb(255,135,245)
gray = rgb(150,150,150)
sprcol = red,green,blue,brown,black,yellow,pink,gray

'Calculate the correct window size based on the desired client size
WINRECT rc
rc.top = 0:rc.left = 0:rc.right = width:rc.bottom = height
DECLARE IMPORT, AdjustWindowRectEx(pRect as WINRECT, Style as UINT, bMenu as INT, ExStyle as UINT)
AdjustWindowRectEx(rc, @CAPTION|@BORDER,TRUE,@EXWINDOWEDGE)

OPENWINDOW win,rc.left,rc.top,rc.right-rc.left,rc.bottom-rc.top, _
@NOAUTODRAW,0,"Caption",&handler

IF(ATTACHSCREEN(win,width,height,FALSE) < 0)
MESSAGEBOX win,"Couldn't create DirectX window","Error"
CLOSEWINDOW win
END
ENDIF

DRAWMODE backbuffer,@transparent
BACKPEN BackBuffer,RGB(0,128,128)
FRONTPEN BackBuffer,RGB(255,255,255)

FILLSCREEN RGB(0,128,128)
'WriteText 180,0,"PRESS ESC TO CLOSE, FPS: " + str$(speed)

mySprites()

STARTTIMER(win, 50)

DO
   wait
   'speed = FLIP
UNTIL run = 0 OR KEYDOWN(1)
EndProgram()

SUB handler
SELECT @message
CASE @IDCLOSEWINDOW
run = 0
STOPTIMER(win)

CASE @IDCREATE
CENTERWINDOW win
CASE @IDTIMER
GOSUB update

ENDSELECT
RETURN
ENDSUB

SUB update
FILLSCREEN RGB(0,128,128)
'adjust bug direction
MoveSprites()
RenderScene()
FLIP
RETURN
ENDSUB

/******************************************************************************/

SUB RenderScene()
RenderSprites()
RETURN
ENDSUB

/******************************************************************************/

SUB RenderSprites()
INT planes
' For this example, we need only two levels (0 and 1)
FOR planes = 0 to 1
FOR spriteIndex = 0 to totalSprites-1
IF ball[spriteIndex].Zorder = planes
DRAWSPRITEXY newSprite[spriteIndex],ball[spriteIndex].xpos,ball[spriteIndex].ypos
WRITETEXT ball[spriteIndex].xpos+15,ball[spriteIndex].ypos, str$(ball[spriteIndex].spriteID)
WRITETEXT ball[spriteIndex].xpos+15,ball[spriteIndex].ypos+20, str$(ball[spriteIndex].Zorder)
MoveSprites()
ENDIF
NEXT spriteIndex
NEXT planes
RETURN
ENDSUB

/******************************************************************************/

SUB MoveSprites()
ball[spriteIndex].xpos = ball[spriteIndex].xpos + (ball[spriteIndex].xvel*ball[spriteIndex].speed)
IF ball[spriteIndex].xpos <= 0
ball[spriteIndex].xvel = 1
ball[spriteIndex].ypos = 200
ball[spriteIndex].Zorder=0
ENDIF
IF ball[spriteIndex].xpos >= Width-ball[spriteIndex].width
ball[spriteIndex].xvel = -1
ball[spriteIndex].ypos = 225
ball[spriteIndex].Zorder=1
ENDIF

RETURN
ENDSUB

/******************************************************************************/

SUB mySprites()
'INT iNum
FOR spriteIndex = 0 to totalSprites-1
newSprite[spriteIndex] = CREATESPRITE(50,50,1)
IF newSprite[spriteIndex]<>null
'iNum = int(rnd(maxColors))
ball[spriteIndex].width=50
ball[spriteIndex].height=50
ball[spriteIndex].xpos = rnd(Width-50) : ' 50 => spriteWidth
ball[spriteIndex].ypos = 200
ball[spriteIndex].Zorder = 0
ball[spriteIndex].spriteID=spriteIndex
ball[spriteIndex].spriteColor=sprcol[spriteIndex]
ball[spriteIndex].xvel = rnd(1)-1 : IF ball[spriteIndex].xvel = 0 THEN ball[spriteIndex].xvel = 1
'ball[spriteIndex].yvel = rnd(1)-1 : IF ball[spriteIndex].yvel = 0 THEN ball[spriteIndex].yvel = 1
ball[spriteIndex].speed = rnd(3)+1f
/**********************************************/
SPRITETOBUFFER(newSprite[spriteIndex])
FILLSCREEN RGB(128,128,128), SpriteBuffer
' Draw the shape of our main sprite
DRAWFILLEDRECT 0, 0, 50,50, ball[spriteIndex].spriteColor, SpriteBuffer
'Set the sprites drawing mode to transparent and specify a mask color
SPRITEDRAWMODE newSprite[spriteIndex],@TRANSSCALED
SPRITEMASKCOLOR newSprite[spriteIndex],RGB(128,128,128)
ELSE             
MESSAGEBOX 0,"Could not create sprites!","Error"
run=0
ENDIF
NEXT spriteIndex
RETURN
ENDSUB

/******************************************************************************/

SUB EndProgram()
FOR spriteIndex = 0 to totalSprites-1
IF newSprite[spriteIndex] THEN FREESPRITE newSprite[spriteIndex]
NEXT spriteIndex
CLOSESCREEN
CLOSEWINDOW win
END
RETURN
ENDSUB
Regards,

Peter B.

ZeroDog

QuoteI am using a Windows timer to control the speed.

Dont use a windows timer to time your drawing.  Instead, render the screen as fast as it will let you, and adjust the amount of change in the screen based on the amount of time that has passed since you last flipped the screen.  You will gain quite a bit of speed by avoiding the windows timer.

barry

Quote from: pistol350 on February 04, 2010, 12:44:17 PM
I remember someone talking about showing pieces of code not long ago... ;)

Yeah I remember that, too.  Who was that guy?  :)

I haven't posted any code because I've been asking about theory more than code.  But I'll post it below.  In any case I wasn't suggesting always posting code but that when we do post code we try to make it ready to compile and run.  This is.

I was drawing a circle using the regular Ebasic circle function and having all kinds of problems with that.  In this code I've just changed it to use a rectangle drawn with DX and that seems to be working okay.  The buttons change the timer because I found some of the flashing changed at different speeds.  The circle commands are still there but commented.

Barry


/* dx1.eba - sprite sample */

WINDOW mainwin, win
speed = 0
width = 640
height = 480
centerx = width/2
centery = height/2
run = 1
angle = 0.0
circolor = RGB(0,255,255)

' ----- Main Window -----

OPENWINDOW(mainwin,10,10, 750,550, _
@MINBOX|@MAXBOX|@SIZE,0,"Caption",&handler)
CONTROL(mainwin, @BUTTON, "10", 650,10, 40,15, @CTLBTNDEFAULT,1001)
CONTROL(mainwin, @BUTTON, "20", 650,30, 40,15, @CTLBTNDEFAULT,1002)
CONTROL(mainwin, @BUTTON, "40", 650,50, 40,15, @CTLBTNDEFAULT,1003)

' ----- DX Window -----

OPENWINDOW win, 0,0, 640,480, _
@NOAUTODRAW|@NOCAPTION, mainwin,"",&handler

' ----- Attach DX window

IF(ATTACHSCREEN(win,width,height,FALSE) < 0)
MESSAGEBOX win,"Couldn't create DirectX window","Error"
CLOSEWINDOW win
END
ENDIF

BACKPEN BackBuffer,RGB(0,128,128)
FRONTPEN BackBuffer,RGB(255,255,255)

bug = LoadSprite(GETSTARTPATH+"bug.bmp",0,0,0,TRUE)
IF bug = NULL
MESSAGEBOX win,"Couldn't load mouth.bmp\nMake sure it's in executables path","Error"
CLOSESCREEN
CLOSEWINDOW win
END
END IF

arena = LOADIMAGE("arena.bmp",@IMGBITMAP)

SpriteDrawMode bug,@TRANS
SpriteMaskColor bug,RGB(255,255,255)
SpriteAlpha bug,180
SpriteShadowOffset bug,-5,-5
x = 0
y = 400
xdir = 1
ydir = -1


SpriteDrawMode bug,@TRANSROTOZOOM
STARTTIMER(win, 40)

DO
WAIT
UNTIL run = 0 OR KEYDOWN(1)

FREESPRITE bug
CLOSESCREEN
CLOSEWINDOW win
END

SUB handler
SELECT @message
CASE @IDCREATE
CENTERWINDOW mainwin
CASE @IDCLOSEWINDOW
run = 0
STOPTIMER(win)

CASE @IDTIMER
GOSUB update

CASE @IDCONTROL
SELECT @CONTROLID
CASE 1001
STOPTIMER(win)
STARTTIMER(win, 10)
CASE 1002
STOPTIMER(win)
STARTTIMER(win, 20)
CASE 1003
STOPTIMER(win)
STARTTIMER(win, 40)
ENDSELECT


ENDSELECT
RETURN
ENDSUB

SUB update
FILLSCREEN RGB(0,128,128)
'CIRCLE(win, centerx,centery, 200, circolor, circolor)
DRAWFILLEDRECT 40,40, 400,400, circolor

'adjust bug direction
x += xdir
if(x + GetSpriteWidth(bug)) > width | x < 0 THEN xdir = -xdir
y += ydir
if(y + GetSpriteHeight(bug)) > height | y < 0 THEN ydir = -ydir
angle -= 0.1
SPRITEANGLE bug, angle

DrawSpriteXY bug,x,y
FLIP

RETURN
ENDSUB

barry

Quote from: ZeroDog on February 04, 2010, 03:34:37 PM
Dont use a windows timer to time your drawing.  Instead, render the screen as fast as it will let you, and adjust the amount of change in the screen based on the amount of time that has passed since you last flipped the screen.  You will gain quite a bit of speed by avoiding the windows timer.

I may be wrong but I was kind of hoping I didn't need a lot of speed for this.  That's the reson for my first question in this thread.  I can do it all in a loop but I have to have some control over the speed so it won't go too fast and that seemed easier with a timer.  This isn't the sort of thing where I need to get all the speed I can.  At least I hope it isn't.  If it is then I picked the wrong project to learn DX.

Barry

pistol350

Quote from: ZeroDog on February 04, 2010, 03:34:37 PM
QuoteI am using a Windows timer to control the speed.

Dont use a windows timer to time your drawing.  Instead, render the screen as fast as it will let you, and adjust the amount of change in the screen based on the amount of time that has passed since you last flipped the screen.  You will gain quite a bit of speed by avoiding the windows timer.

I agree with that.
I usually use a Main loop routine that looks like that :


...
lastscreenupdatetime=GetTickCount()
tTime=GetTickCount()
'MAIN LOOP
/******************************************************************************/
DO
timesincelastscreenupdate=GetTickCount()-lastscreenupdatetime
IF timesincelastscreenupdate>1
'At least 2mS since last screen update
'timesincelastscreenupdate will be used as a multiplier to sprite motion
'to make things run at the same speed on different PCs
lastscreenupdatetime=GetTickCount()
timesincelaunch=((lastscreenupdatetime-tTime)/1000)
RenderScene()
CheckMousePos()
PrintText()
fps = FLIP 1
   ENDIF
UNTIL KEYDOWN(1) or run = 0
EndProgram()

/******************************************************************************/
...
Regards,

Peter B.

barry

Why do you do it that way instead of using a timer?

Barry

pistol350

For the same reason zerodog explained above.
Moreover, see the commented lines of this sample code.
Regards,

Peter B.

ZeroDog

i have never found the windows timers to a very accurate way of timing directX programs.  By keeping your timing in its own loop, and adjusting the amount of change based on the time elapsed, it will provide a much more accurate timing across systems of varying speed.       

The asteroids sample has some good comments in it about how to calculate the ajustment amount based on the time elapsed since the previous flip.