How to properly animate my game world
BlitzMax Forums/BlitzMax Programming/How to properly animate my game world
| ||
Hello everyone. I'm currently working on a turn based RPG game that might be done in the year 2030 :) In the meantime, I've come across a design question that I'm going to need some help with. How would I go about animating the game world in a way that doesn't create lag or be cumbersome? Take for example my current game world. Right now, I'm only drawing what's on the screen even though a game level extends well beyond that. Currently I redraw the screen when something happens, such as scrolling, or clicking on an icon, displaying text, etc. Now I'm faced with coding animated sprites. Animating sprites will require constantly updating the screen at some interval. The other problem I see happening is I'm sure sprites will need to be animated at different speeds. Example, my explosion needs a new cell displayed every 500MS but a creature animation requires animation at every 100MS. Can anyone send me in the general direction of how to best process this? Should I just update my entire game screen every 5MS all the time and create points on when to update an individual sprite? If that's the case, what Blitz function should I be looking at? Is there a better way? Thanks in advance for anyone's help :) |
| ||
You should use timestamps with the command MILLISECS(). Give a future event a timestamp of Now+2sec and let it happen, when this time is reached: here is a sample with tow individual events: Graphics 800,600 Repeat Cls If Event_1_Time<MilliSecs() Event_1_Time=MilliSecs()+500 A=A+1 EndIf If Event_2_Time<MilliSecs() Event_2_Time=MilliSecs()+100 B=B+1 EndIf DrawText "A=" + A + " B=" + B, 100, 100 Flip 1 Until KeyHit(Key_Escape) |
| ||
Does the Millisec value return to zero 0 at all, in which case the check will fail sill it will not be less then millsecs? |
| ||
Midimaster I also want to say thank you! I did exactly that and I'm updating my game screen perfectly as well as updating my animations at their own speed. It's a very simple solution but something I overlooked. |
| ||
" Does the Millisec value return to zero" I don' believe that, I though it was the amount of millisecs since 1970 so it won't become 0, (the difference on th other hand..) |
| ||
Millisecs() returns the amount of milliseconds since system startup. It will become a negative value after roughly 24 days of up-time. To expand on what midimaster said, create a 10Hz timer (or even a 1Hz timer), and use that for scheduling future events. It's what I do and it works really well. The good part is, although its value is still a signed int and will therefore still wrap around to a negative number just like Millisecs() does, this won't happen for about nine months with a 10Hz timer, or about seven years with a 1Hz timer. As if that isn't good enough, you can reset the timer to zero (myTimer._ticks = 0) whenever it's convenient so the wrap-around issue need never arise. |
| ||
If done right the wrap-around with the Millisecs() function should never become an issue. Check my comment in the linked page below for more info. http://www.blitzbasic.com/b3ddocs/command.php?name=Millisecs&ref=2d_cat |
| ||
I am not positive, but I think if you search, you can find an old post GfK did with a code example of what he is describing. |
| ||
GfK, here is where I get lost. How can I use a 1mz or 10 mz timer to update an animation that needs to change frames every 200ms or so? A 10mz timer, as I understand it, will tick much slower then that. |
| ||
thats all not important! Folks talking about a seldom strange situation of MILLISECS() which you never will see... Millisecs() will only return to Zero after 24 days of computer is running continuosly. Basic is: MILLISECS() return you the time since the computer started in millisecs. So you always get a value for "Now". If you want to wait for a certain moment 2 seconds later you only have to save "Now" into a variable and add 2*1000 to this and wait until the MILLISECS() reaches this time: Graphics 800,600 Future%=MilliSecs()+2*500 Repeat Cls If Future<MilliSecs() DrawText "Time has reached" , 100, 100 EndIf Flip 1 Until KeyHit(Key_Escape) If you want to do thing periodic you only have to "reset" you variable FUTURE% when reached the target: Graphics 800,600 Future%=MilliSecs()+1000 Repeat Cls If Future<MilliSecs() Future=MilliSecs()+1000 DrawText "Time has reached" , 100, 100 EndIf Flip 1 Until KeyHit(Key_Escape) If you need a timer that switches every 200msec a animated image you do it like the same: Graphics 800,600 Repeat Cls If Future<MilliSecs() Future=MilliSecs()+200 Steps=Steps+1 If Steps=10 Steps=0 EndIf 'DrawImage Animation, 100,100,Steps SetColor Steps*10+100,0,0 DrawRect 100,500,50,-30-Steps*10 Flip 1 Until KeyHit(Key_Escape) ABOUT TIMERS: I would use the CreateTimer() Function to get a constant FLIP frame rate in your games, but not to animate individual characters. There is no need to use CREATETIMER() if you want to work with MILLISECS(): Graphics 800,600 FPS=CreateTimer(60) Repeat Cls DrawText "This is a game running on 60Hz" , 100, 100 Flip 0 WaitTimer FPS Until KeyHit(Key_Escape) |
| ||
Folks talking about a seldom strange situation of MILLISECS() which you never will see... Millisecs() will only return to Zero after 24 days of computer is running continuosly. ronny@ronnyPC ~ $ uptime 14:00:53 up 43 days, 21:21, 2 users, load average: 0.65, 0.93, 0.89 Instead of using "Millisecs" you could do it a bit differently: - each game-update call is "calculating" the time gone since last update - if you want to time something: - - object.timer (holding rest until "action" in milliseconds) - - object.time (holding the interval) - - object.update(timesincelastupdate) - - - object.timer :- timesincelastupdate - - - if object.timer < 0 then object.action - - object.action is a method which resets object.timer to object.time if doing a game pause you wont update the game. If hitting "resume" you just set the "last update call time" to the current one and the game will continue flawless. The game-update call could be linked to a TTimer if needed but that is an individual decision - if not you will end up using Millisecs() ONE time in the code - in the game-update-time-calculation. bye Ron edit: Here is the current "TTimer"-Object I use to animate certain things. 'for things happening every X moments Type TTimer field interval:int = 0 'happens every ... field intervalToUse:int = 0 'happens every ... field actionTime:int = 0 'plus duration field randomness:int = 0 'value the interval can "change" on GetIntervall() to BOTH sides - minus and plus field timer:int = 0 'time when event last happened Function Create:TTimer(interval:int, actionTime:int = 0, randomness:int = 0) local obj:TTimer = new TTimer obj.interval = interval obj.actionTime = actionTime obj.randomness = randomness 'set timer obj.reset() return obj End Function Method GetInterval:int() return self.intervalToUse End Method Method SetInterval(value:int, resetTimer:int=false) self.interval = value if resetTimer then self.Reset() End Method Method SetActionTime(value:int, resetTimer:int=false) self.actionTime = value if resetTimer then self.Reset() End Method 'returns TRUE if interval is gone (ignores action time) 'action time could be eg. "show text for actiontime-seconds EVERY interval-seconds" Method doAction:int() local timeLeft:int = Millisecs() - (self.timer + self.GetInterval() ) return ( timeLeft > 0 AND timeLeft < self.actionTime ) End Method 'returns TRUE if interval and duration is gone (ignores duration) Method isExpired:int() return ( self.timer + self.GetInterval() + self.actionTime <= Millisecs() ) End Method Method reachedHalftime:int() return ( self.timer + 0.5*(self.GetInterval() + self.actionTime) <= Millisecs() ) End Method Method expire() self.timer = -self.GetInterval() End Method Method reset() self.intervalToUse = self.interval + rand(-self.randomness, self.randomness) self.timer = Millisecs() End Method End Type Maybe it helps. Last edited 2012 |
| ||
Here is what I just did and it seems to work fine, haven't really tested it. Now of course this probably isn't a representation of real time passing but for my purposes it doesn't matter, I just need a way to control how fast things get checked and so on. Going to post here for comments or maybe it will help someone else out global MasterTimer:long Repeat UpdateTimers() WaitEvent ' event stuff here forever Method UpdateTimers() MasterTimer:+1 If Timer < MasterTimer Timer = MasterTimer + 7 ' the 7 can be any number, this controls the speed ' do stuff here End If If MasterTimer = 10000000 ' this number can be any number actually Timer:-MasterTimer ' just deduct the master timer amount from all existing timers MasterTimer = 0 End If Last edited 2012 |
| ||
@ blackwater: no further comments from my side... good luck! @folks: I think he is a beginner. Could you please write him even more complicated code to demonstrate him your skills, please? |