--- Log opened Mon Dec 22 00:00:02 2008 |
00:41 | <@AnnoDomini> | I have a question. I want to do a 'while (specified_time_has_not_elapsed_yet())' thingie - a loop is meant to keep going for a set amount of time. Using SDL, what could I use to do this? |
00:43 | <@ToxicFrog> | http://sdl.beuc.net/sdl.wiki/SDL_API |
00:43 | <@ToxicFrog> | Scroll down to "Time" |
00:43 | <@AnnoDomini> | Hmm. Well, I could use GetTicks... but I suppose that might not be the proper way, yes? |
00:44 | <@ToxicFrog> | You can implement the condition in terms of SDL_Ticks(), or you can do something with a repeatedly called timer callback with SDL_AddTimer |
00:44 | <@AnnoDomini> | Unfortunately, I don't understand how AddTimer works. |
00:44 | <@ToxicFrog> | This isn't just a busywait, right, it's an actual stuff loop? |
00:44 | <@ToxicFrog> | I'll explain it in a bit, for now tree |
00:45 | <@AnnoDomini> | It's for input, accepting for a period of time, then moving on with the last bit of input given. |
00:45 | <@McMartin> | Bear in mind that you may be stuck with your system timer's timeslice resolution |
00:45 | <@McMartin> | That's typically 1ms on Windows, which is typically OK |
00:46 | <@McMartin> | But it's typically 10ms on Linux, which is not. |
00:47 | <@AnnoDomini> | Uh... 10ms isn't really that much, given an average human ability to discern time unaided, right? |
00:47 | <@McMartin> | AnnoDomini: Depends on what you're using it for. |
00:47 | <@McMartin> | If you want to, say, update a screen at NTSC speeds, which is 60 Hz, you will need to compensate because you can't actually hit the required 13ms delay reliably. |
00:48 | <@McMartin> | And they *will* notice a 20% slowdown of a 20ms/frame speed |
00:48 | <@AnnoDomini> | I'm trying to remake a snake game thingie I wrote in VHDL. |
00:48 | | * McMartin nods |
00:49 | <@McMartin> | Basically, you'll want to compute "when to fire the frame" based on total time elapsed, not on a reset between frames, unless it ran at 50Hz or some other conveniently divisible speed |
00:49 | <@McMartin> | As in, "Are we at at least 13ms? OK, go. Now are we at at least 26ms? OK, go..." |
00:49 | <@McMartin> | And so on. Then it will essentially do frameskips as needed. |
00:50 | | * AnnoDomini scratches head, understanding little. |
00:50 | <@McMartin> | I think we'll all be better off if you just run it at 50 FPS instead~ |
00:50 | <@McMartin> | Unless you're playing games with fractional framerates the way 3D games often do. |
00:50 | <@ToxicFrog> | Ok, SDL_AddTimer |
00:51 | <@ToxicFrog> | It's a basic callback registration function. |
00:51 | <@McMartin> | Coffeeshop closing; see you later. |
00:51 | <@AnnoDomini> | No, no, I think I'm using "how ever the fuck long it takes the code to go through the stuff it needs to do, then SDL_flip" speeds. |
00:51 | <@ToxicFrog> | Just make sure your update functions know how long a slice they need to update for~ |
00:51 | <@ToxicFrog> | Anyways. |
00:52 | <@AnnoDomini> | What's a callback registration function? |
00:52 | <@ToxicFrog> | Uint32 emit_ping(Uint32 interval, void * data) { printf("ping!\n!"); } |
00:53 | <@ToxicFrog> | SDL_TimerID pingtimer = SDL_AddTimer(400, emit_ping, NULL); |
00:53 | <@ToxicFrog> | emit_ping will be called every 400ms thereafter. |
00:53 | <@ToxicFrog> | ...although, I just realized I forgot the return statement for emit_ping |
00:53 | <@ToxicFrog> | Pretend there's a "return interval;" at the end. |
00:54 | <@ToxicFrog> | A callback registration function is a function that is passed a function: the callback. |
00:54 | <@AnnoDomini> | I'm good at pretending. |
00:54 | <@ToxicFrog> | The callback is then automatically called at some later date. |
00:54 | <@ToxicFrog> | For example, in the xchat C API, you can register a function to be called whenever a message is receieved. |
00:54 | <@ToxicFrog> | So, the way AddTimer works is, you pass it: |
00:54 | <@ToxicFrog> | - an interval in milliseconds |
00:55 | <@ToxicFrog> | - a function with a given signature |
00:55 | <@ToxicFrog> | - a void*, which can be anything you like |
00:55 | <@ToxicFrog> | After that many ms, it calls the function, passing it interval and the void*. |
00:56 | <@ToxicFrog> | The function then returns a number, and SDL waits that many milliseconds before calling it again. |
00:56 | <@ToxicFrog> | And if it returns 0, cancels it completely. |
00:56 | <@ToxicFrog> | Does that make any sense? |
00:57 | <@AnnoDomini> | Yes. Rub the Sacred Machine Oil here, recite the prayer to the machine spirit, and it will work. :P |
00:57 | <@AnnoDomini> | I think I understand. |
00:57 | <@AnnoDomini> | What's a void*? |
00:58 | <@ToxicFrog> | A typeless pointer. |
00:58 | <@ToxicFrog> | malloc returns these, for example, and you typically cast them to whatever type you actually need. |
00:59 | <@ToxicFrog> | The purpose of the void* is to pass any additional information the function needs to it (as C doesn't support closures) |
00:59 | <@AnnoDomini> | Uhuh. |
00:59 | <@ToxicFrog> | Typically you create some user-defined struct that holds the stuff you need, cast a pointer to that to a void* and pass it to AddTimer. |
00:59 | <@AnnoDomini> | And emit_ping doesn't need any arguments. |
00:59 | <@AnnoDomini> | Er, additional information. |
00:59 | <@ToxicFrog> | And then the callback casts it back to a struct whatever * and uses it. |
01:00 | <@ToxicFrog> | Quite. So Addtimer passes NULL and emit_ping doesn't use the userdata at all. |
01:00 | <@ToxicFrog> | This is a very common idiom in C, I'm surprised you haven't seen it before. |
01:02 | <@AnnoDomini> | Well, my C experience is limited to a) basic algorithmic things in ancient compilers, b) flailing at MFC, because the help files wouldn't install and the teacher wasn't being helpful, hoping something will work. This did not exactly inspire me to do much in C in my own free time. |
01:02 | <@ToxicFrog> | Aah |
01:03 | <@ToxicFrog> | Really, C is not a very fun language~ |
01:03 | <@ToxicFrog> | Although it is useful. |
01:04 | <@AnnoDomini> | I think GetTicks will work better for my purpose, though. |
01:04 | <@ToxicFrog> | (high-level languages generally don't have an equivalent of the void* thing, because then you can just pass in a closure) |
01:05 | <@ToxicFrog> | Nod. |
01:05 | <@ToxicFrog> | GetTicks is also conveniently straightforward. |
01:07 | <@ToxicFrog> | And now, dinner |
01:11 | <@McMartin> | Also, MFC is IIRC C++, not C, and is Made Of Spiders |
01:12 | <@gnolam> | Yes. |
01:13 | <@gnolam> | The "Classes" in Microsoft Foundation Classes sort of gives it away. ;) |
01:13 | <@McMartin> | Anyway, SDL_Flip IIRC generally tries to time itself to the monitor's VBLANK, which means you don't want to use it as a timing mechanism if you can avoid it. |
01:14 | <@McMartin> | GetTicks is generally better for implementing a fixed framerate with a recalcitrant timer, because you don't have to check for how long you need to sleep until the last instant, and if the amount turns out to be "we're running behind because I said to sleep for 15ms and it's been 20", you just skip the sleep entirely |
01:14 | <@McMartin> | In fact, if you're *way* behind, you can also skip the flip, for automatic adaptive frameskip. |
01:15 | <@McMartin> | However, for a snake game, that shouldn't happen unless you're running on like a 286. Running XP. Somehow. |
01:16 | <@ToxicFrog> | I generally just do variable framerate: the update function is told how many ms it's been since last time it was called. |
01:17 | <@McMartin> | Well, what I mean here is "the goal is to approximate a constant framerate" here. |
01:17 | <@ToxicFrog> | Is it? |
01:17 | <@McMartin> | As opposed to, for instance, deciding how far to update the world model based on how long it's been since the last time we updated the world. |
01:17 | <@McMartin> | He's porting Snake from an unrelated class to SDL. It's the goal. |
01:17 | <@ToxicFrog> | Aah. |
01:17 | <@McMartin> | Anything else is Just Asking For It. |
01:17 | <@ToxicFrog> | That's the bit I was missing, yeah. |
01:18 | <@ToxicFrog> | Although, I don't know, variable-framerate snake doesn't seem like it would be problematic |
01:18 | <@McMartin> | It makes interpreting input problematic. |
01:18 | <@McMartin> | Since it's (a) realtime and (b) effectively played on a very low-res grid where every pixel counts. |
01:20 | <@McMartin> | The question is really "what happens if you double the framerate?" and the answer is either "the game runs twice as fast" or "the game animates twice as smoothly", and the former is absolutely what your first SDL project should be, especially if it's a port from a fixed-hardware implementation of something. |
01:22 | <@AnnoDomini> | http://pastie.org/344596 <- So far, I've come up with something like this. |
01:24 | <@McMartin> | Close, but there are a few subtleties. |
01:25 | <@McMartin> | This mostly works, but has a couple of bad effects: |
01:25 | <@McMartin> | 1: You'll eat 100% CPU. |
01:25 | <@AnnoDomini> | Ow. |
01:26 | <@AnnoDomini> | Huh. I never noticed the drain Example.exe was having. |
01:26 | <@McMartin> | 2: the frame rate will be slightly under 2 FPS (your goal), and will vary directly with scene complexity, not just when it's overloaded. |
01:26 | <@McMartin> | I'd suggest something more like this: |
01:26 | <@McMartin> | - Call GetTicks() and store the result in a local variable |
01:27 | <@McMartin> | - Do all our work, starting with pumping the event queue with the while(PollEvent) stuff to get your input and ending with SDL_Flip() |
01:28 | <@McMartin> | - Now call SDL_Ticks() again. Subtract off the value we cached before to find out how much time has passed, and subtract that from 500 to get how long we need to sleep. |
01:28 | <@McMartin> | - If it's greater than zero, call SDL_Delay() for that long. This yields the processor and lets the OS wake you up again later. |
01:28 | <@McMartin> | - Repeat until the game is over. |
01:29 | <@McMartin> | The only remaining issue with that is that other UI actions like closing the window will also only update on ticks instead of being instant, but that's something to deal with later |
01:29 | <@McMartin> | (and you'd do that by sleeping repeatedly for a smaller period of time, like, say, 50 ms, and waking up to process events then going back to sleep again if it's not time. But that's work, and it's not immediately relevant, and it's only important for extremely low framerates. It won't show up in 'real' videogame-style projects.) |
01:32 | <@McMartin> | Later on, for controls to work right, you'll have to make sure you properly handle cases where the user "changes his mind", hitting up then down within a frame. |
01:33 | <@AnnoDomini> | I'm having difficulties understanding. |
01:34 | <@McMartin> | Like, you'll be breezing through the input events, and you'll see these three: |
01:34 | <@McMartin> | KEYDOWN(SDLK_UP) |
01:34 | <@McMartin> | KEYUP(SDLK_UP) |
01:34 | <@McMartin> | KEYDOWN(SDLK_DOWN) |
01:34 | <@McMartin> | KEYUP(SDLK_DOWN) |
01:34 | <@McMartin> | That needs to mean "go down". |
01:34 | <@AnnoDomini> | Yes. |
01:35 | <@McMartin> | ... in fact, given that it's snake, you can kind of ignore KEYUPs entirely and just track the most recent KEYDOWN that means something, which was probably already your plan. |
01:35 | <@AnnoDomini> | Yes. |
01:35 | <@McMartin> | So yeah, you've got that one handled, nm~ |
01:35 | <@AnnoDomini> | I rather meant the third and fourth bullets on your list earlier. |
01:36 | <@McMartin> | (UQM had some issues with our joystick/keyboard input where if you tapped the key fast enough, it would fail to actually register it at all since it was scanning the input state instead of actually studying the input stream) |
01:36 | <@McMartin> | I have to load the laundry machine, but I'll be back in like 10? |
01:36 | <@AnnoDomini> | No problem. |
01:36 | <@AnnoDomini> | It's not like I pay you to help me with my problems. :p |
01:37 | <@McMartin> | Having more people use SDL advances my social engineering goals >_> |
01:42 | <@AnnoDomini> | I think I've understood the algorithm you proposed. |
01:42 | <@gnolam> | McMartin: Your social engineering goals? |
01:43 | <@AnnoDomini> | It seems to me, that if that's used, only the keypress that happens in the one instance per loop iteration when the code actually checks what's pressed. |
01:44 | <@AnnoDomini> | If events are checked at 0 and 500, for instance, then what the user does in 1 to 499 doesn't matter, yes? |
01:45 | <@McMartin> | AnnoDomini: That's not the way Events work. |
01:46 | <@McMartin> | When you do that loop it will give you everything that happened since the last time you checked; they build up in the event list. |
01:46 | <@AnnoDomini> | Oh. |
01:46 | <@McMartin> | Happily, in this case, that is exactly what you want, as mentiond. |
01:47 | <@McMartin> | gnolam: The more people who use SDL to implement their little game projects with instead of, say, Macromedia Fusion, the more stuff I can play on my laptop - or on my desktop without having to reboot. |
01:47 | <@McMartin> | More generally, I want SDL to become The Cross-Platform DirectX That People Doing 2D Target As A Matter Of Course. |
01:47 | <@AnnoDomini> | I suppose there is an easy way to get the last event that's happened? |
01:48 | <@McMartin> | You don't want that; it might have been something like "OMG the mouse just jiggled" |
01:48 | <@AnnoDomini> | Oh. |
01:48 | <@McMartin> | I'd go through the list looking for SDL_KEYDOWN, and if that, then look at the keycode and see if it's an arrowkey or a keypad key, update the "most recent seen" based on that, and ignore everything else. |
01:48 | <@AnnoDomini> | Ah, I see. |
01:48 | <@McMartin> | ... oh, you could probably catch the SDL_QUIT event too for shutdown; that means they closed the window. |
01:49 | <@McMartin> | It's good to respect that one~ |
01:49 | <@McMartin> | Anyway, my last two bullet points. |
01:49 | <@McMartin> | SDL_Delay is a function I guess we haven't gone over yet? |
01:49 | <@AnnoDomini> | Yes. One could wonder what's eating up 99% of the CPU despite having 'closed the application'. |
01:49 | <@McMartin> | Oh, the window doesn't even go away |
01:50 | <@AnnoDomini> | Pff. |
01:50 | <@McMartin> | It'll just happily keep going, with the X indented. |
01:50 | <@McMartin> | (The reason this isn't automatic is because lots of apps want to intercept with a "ZOMG UNSAVED DATA" dialog or whatnot) |
01:51 | <@AnnoDomini> | So if I catch SDL_QUIT, then I have to get the hell outta those loops and into somewhere in the code that ends the program. |
01:51 | <@McMartin> | Anyway. SDL_Delay will "sleep" the thread of execution, which means it tells the OS to go do something else for awhile. That's how you solve the CPU consumption. |
01:51 | <@McMartin> | Indeed. |
01:51 | <@McMartin> | That can be as easy as having all this be in a function and calling "return" inside your event processing loop. |
01:53 | <@AnnoDomini> | Uhuh. |
01:54 | <@McMartin> | There are other ways, and there's no need to do it *instantly* |
01:54 | <@McMartin> | Just, you know, before the user notices. |
01:54 | <@AnnoDomini> | Oh, oh! I think I understand what while (PollEvent) does. <_< |
01:56 | <@McMartin> | Yeah, you're "pumping the event list" there, and it's first-in, first-out. |
01:58 | <@AnnoDomini> | Drawback of using primarily extensionless files for notes - they name-collide with directories. >_< |
01:59 | <@McMartin> | Heh |
02:00 | <@AnnoDomini> | Well, my reminder service is telling me to go to sleep. I think I will. |
02:01 | | AnnoDomini [~farkoff@Nightstar-27816.neoplus.adsl.tpnet.pl] has quit [Quit: "I just want you to know... I faked all the orgasms." "So? Mine were real." "...asshole."] |
02:09 | | gnolam [lenin@Nightstar-1382.A163.priv.bahnhof.se] has quit [Quit: Z?] |
03:04 | | Attilla [~The.Attil@92.20.110.ns-2977] has quit [Quit: ] |
05:00 | | Vornicus-Latens is now known as Vornicus |
08:16 | | Reiver [~reaverta@Nightstar-7117.xdsl.xnet.co.nz] has joined #Code |
08:16 | | mode/#code [+o Reiver] by ChanServ |
08:52 | | Vornicus is now known as Vornicus-Latens |
09:08 | | gnolam [lenin@Nightstar-1382.A163.priv.bahnhof.se] has joined #Code |
09:08 | | mode/#code [+o gnolam] by ChanServ |
10:10 | | You're now known as TheWatcher |
10:16 | | AnnoDomini [~farkoff@Nightstar-29289.neoplus.adsl.tpnet.pl] has joined #Code |
10:16 | | mode/#code [+o AnnoDomini] by ChanServ |
11:26 | | Vornotron [~vorn@Admin.Nightstar.Net] has joined #code |
11:26 | | Vornicus-Latens [~vorn@Admin.Nightstar.Net] has quit [Ping Timeout] |
11:29 | | gnolam [lenin@Nightstar-1382.A163.priv.bahnhof.se] has quit [Quit: 16 reindeer on a dead man's chest, yo ho ho and a bottle of eggnog!] |
11:51 | | grossroot[idle] is now known as grossroot |
12:08 | | * AnnoDomini wonders why his program exits immediately upon starting, without even executing anything in main(). |
12:10 | <@AnnoDomini> | http://pastie.org/344766 <- It compiles okay, and runs - I mean, it launches up an SDL window, then exits. |
12:12 | <@AnnoDomini> | It seems to get as far as setting the video mode, since changing the MAX values changes window size, but it doesn't write anything to stdout.txt. |
12:21 | | * AnnoDomini goes check if his array indexes aren't out of bounds. |
12:22 | <@AnnoDomini> | >_< |
12:23 | <@AnnoDomini> | Yeeeeeees. By a factor of 10 or so. |
12:25 | | * AnnoDomini finds a different, unrelated error, too. |
12:48 | <@AnnoDomini> | Okay, yay. It runs, and there's some sort of activity going on, and I can even steer the snake head. Problems include lack of snake body, and unexpected quits on the second frame after user input. |
13:48 | <@AnnoDomini> | http://pastie.org/344805 <- It sorta works, only crashes mysteriously after user input. |
13:54 | <@AnnoDomini> | :D |
13:54 | <@AnnoDomini> | I forgot a break; in that switch. |
13:55 | <@AnnoDomini> | Now to clear out the bugs in the steering. |
14:01 | <@AnnoDomini> | Mm'kay. The right arrow makes it go left, and up and down work regardless of whether the other was issued previously. |
15:33 | | grossroot [~grossroot@Nightstar-2147.hackthisbox.org] has quit [Quit: Reconnecting] |
15:33 | | grossroot [~grossroot@Nightstar-2147.hackthisbox.org] has joined #code |
15:41 | | grossroot [~grossroot@Nightstar-2147.hackthisbox.org] has left #code [] |
16:28 | <@AnnoDomini> | Now it works completely! :D |
16:29 | <@AnnoDomini> | http://pastie.org/344896 |
16:51 | | * AnnoDomini adds going from one side of the screen to the other. |
17:13 | | You're now known as TheWatcher[afk] |
17:39 | <@ToxicFrog> | 'grats |
18:00 | | Vornotron is now known as Vornicus |
18:06 | <@AnnoDomini> | Aaaah! The apples won't stop generating! It's the Applecalypse! |
18:12 | <@AnnoDomini> | Huh. |
18:13 | <@AnnoDomini> | It was caused by using an assignment instead of a comparison in an if statement. |
18:15 | <@McMartin> | Always loads of fun. |
18:16 | <@McMartin> | Also, grats! |
18:16 | <@AnnoDomini> | Thanks. |
18:16 | | * McMartin swipes the source to play with later. |
18:16 | <@AnnoDomini> | I'll pastie the newest version. |
18:16 | <@McMartin> | This'll make a decent test to see whether or not I've got all my devtools handy. |
18:17 | <@McMartin> | (I recently shifted the OS on my 64-bit machine.) |
18:17 | <@AnnoDomini> | http://pastie.org/344995 <- Apple eating, extending snake. No collision detection with self, though. Borders wrap around. |
19:11 | | You're now known as TheWatcher |
20:10 | | crem [~moo@Nightstar-28703.adsl.mgts.by] has joined #code |
20:10 | | crem_ [~moo@Nightstar-28703.adsl.mgts.by] has quit [Connection reset by peer] |
22:27 | <@ToxicFrog> | I love the way the System 7.5 installer says that it'll install 1MB of data, and then installs 6MB. |
23:13 | <@McMartin> | AnnoDomini: Just confirmed it works on my Linux machine. Nice stuff =) |
23:20 | <@AnnoDomini> | There seem to be some bugs concerning the apples. After some amount of time something has a chance of going wonky, and either they will stop appearing, or they appear in greater numbers. |
23:24 | <@AnnoDomini> | I think it may have something to do with the counters for them, not sure. |
23:38 | <@McMartin> | Could be a wacky integer overflow issue. |
--- Log closed Tue Dec 23 00:00:14 2008 |