New Video Card and Replay System in GM

Posted by Castypher on June 4, 2011, 9:24 p.m.

Ever since my new computer with the accidental Radeon 5450 video card, I've been looking forward to a new one for quite some time. I spent a few hours today doing some research. Before, I was looking at an nVidia card, then a bunch of ATI ones with slightly lower performance but much lower cost. In the end I paid quite a bit regardless for something I hope will last me a while.

Radeon 6870

Thoughts? Should I cancel it before I'm charged or do you think this'll suffice for mid-to-high-level game graphics?

Also, a screw on the back of my computer feels electrified. I may be sending the computer in since I'm pretty sure it shouldn't do that. But every time I touch it, it's like a watered-down effect of putting your tongue on a 9-volt. Really, it's faint, but electricity surging through the case?

Finally, I've talked lately with Purianite about a replay system in GM, along with random seeding. He actually says that doing a shoot-em-up without random seeding is horribly unfair, and it made absolutely no sense to me why until I tested it.

Random seeding is fucking cool.

Anyway, with random seeding, you can create a replay system in a shoot-em-up (since numbers aren't purely random, the playback-controlled player won't just happen to collide with one). So I decided to try my hand at it using a keylogging technique.

What you should know:

- There are seven keys: left, right, up, down, Z, X, and C

- Each key is toggled on or off

- The system works by checking which keys are pressed at a specific frame

- The game runs at 60 FPS

The first attempt was using an array with the current frame as its index. However, arrays are limited to 32000, which at 60 FPS is 533 seconds, which is just under nine minutes. Some of my stages are intended to be fifteen or so, so that plan wouldn't work.

The next attempt was to make the array based on the number of times said key was pressed, so leftOn[500]=4324 means that the 500th time left was pressed was at frame 4324. This works. However, playback is forced to loop through the entire array, since GM can't check array length, and looping 32000 times each step is VERY slow. It would also break if some ass mashed the same key 32000 times.

Regardless of speed, the playback still works.

The third option has to do with using a queue data structure. However, since multiple keys can be pressed at once, this may not work. So I thought of having a queue for each key, which would be seven queues. However, if I estimate the player could press the key ten times a second tops (more than five is possible, so it has to be idiotproof), that's 600 times a minute, which is 12000 times in 20 minutes (max stage duration). Running seven queues with 12000 entries might be just as slow.

Help. =(

Comments

Ferret 13 years, 4 months ago

I have the same graphics card except it's an Asus, it's pretty fantastic. XFX stuff is good too. Do commit to it, it's a great card.

As far as replay goes, why not do the second option but have a sentinel number to let you know when it's the last frame? Just have at the end of the array "-999" and just check for when that number pops up.

Castypher 13 years, 4 months ago

Yeahhh, maybe the second option wouldn't be good with a for loop. Maybe a while.

Regardless, if there are a lot of keypresses, it'll be hella slow. But if someone played realistically, I'd think it should be fine.

Ferret 13 years, 4 months ago

Even if they had a lot of key presses there are sure to be a huge amount of frames in between. Just have parallel arrays, one array the the frame number, and another for the button(s) pressed on that frame. Then every frame just check to see if the room frame is equal the the frame in the array. If it does just have the player execute the action and add +1 to the variable keeping track of the array indexes.

Example

real_frame=0
index=0

if real_frame = array1[index]{
[tab]switch(array2[index]){
[tab][tab]case blabla:
[tab][tab]blabla
[tab][tab]blabla
[tab][tab]break;
[tab][tab].......
[tab]}
[tab]index+=1

//edit
if array1[index]=-999
[tab]end_replay()
}
real_frame+=1

I thought we had a [tab] function…..

Castypher 13 years, 4 months ago

I like how that's looking, but don't switch statements only accept one value then break? If two keys are somehow pressed at the exact same frame (doable if the computer has a sudden FPS drop, which in bullet hell isn't too unimaginable), then one of them won't be registered.

The other thing is that due to it being late and me being somewhat tired, I can't decide what array1 and array2 represent (obviously the value in array 1 is the frame, but what's the index? As far as I can make of it, it'd represent the number of keypresses for all keys instead of just one, which can create problems).

So far what I can see, it'd work great, but the problem of multiple keypresses persists (and that might be common). There are also key releases to take into consideration.

I hate this stuff but it'll be so rewarding if I can get it right.

Ferret 13 years, 4 months ago

Alright sorry if I missed something. Here I'll explain myself about when it records.

Every time the player presses a key, the frame it's pressed on will be recorded in array1 and the key(s) will be recorded in array2. After the values are set then index+=1, that way your array is purely for when a key is pressed and has no spaces. After a game you should have an array of frame numbers and an array of keys that are parallel to each other.

Ok, when it comes to recording information and saving stuff, I like to try and be as efficient as possible. So here is a rundown of whats in my mind…

Keys should be recorded in terms of when they are pressed and released unless they are a specific key that you can't hold (such as a bomb key). Game maker sucks at recording strings, it goes too slow, so we should record this in an integer. (Most) Keyboards can only tell your computer 3 keys at once, and at 60 fps that's all you need. Each key will be a specific digit (ie left=1 right=2 z=6 ect…) and if the key is being pressed it will be on the left side of the decimal point and if the key is being released it will be on the right side of the decimal point. This will be done by having 2 variables that reset every time a key is pressed, one variable will count the number of keys pressed while the other will count the number released. Then add the key's number to array2[index] multiplied/divided by 10 to the power of the corresponding variable counting the number of keys pressed/released.

What a mouthful, here look at this:

if keyboard_check(vk_anykey)=true{
pressed=0
released=0
if keyboard_check_pressed(<some_key>)=true{
array1[index]=current_frame
array2[index]+=<some number the key corresponds to (1-9)> * (10 * pressed)
pressed+=1
}
...
<repeat the pressed keys for all keys used>
...
if keyboard_check_released(<some_key>)=true{
array1[index]=current_frame
array2[index]+=<some number the key corresponds to (1-9)> / (10 * released)
released+=1
}
...
<repeat the released keys for all keys used>
...
index+=1
}

Yeah, I don't like the idea of all the repeating if statements either… I would recommend a switch statement like switch(keyboard_key), but that variable "keyboard_key" can only hold one key…

This is just my (weird) idea, you don't have to do it.

And you're right, a switch statement wouldn't be the best for my previous code.

Edit: Oh yeah and about how you would get the info back, you would just do the same thing backwards, dividing and multiplying by 10 and using floor() rounding to check each digit, if a digit is 0 that means that you've found all the keys on that side of the decimal. :D

Castypher 13 years, 4 months ago

Ferret, you are brilliant.

With a few modifications (preference on if statements over switch, discarding of pressed and released variables since I didn't get them anyway), I got it to work flawlessly with zero framerate drops. I don't mind repeated if statements, since I'm no efficiency whore, and as long as it works, I'll take what I can get.

The only potential flaw with this system, however, is that if the number of keypresses exceeds 32000 (though 20 keys per second–highly unlikely–is 1200 per minute, which is 24,000 in 20 minutes–the maximum), then it will crash. At this point I think I'd just cut it early if it was going to be a problem, and the replay wouldn't be saved. But I honestly and truly do NOT think the player could get that many keypresses without seriously wearing himself out, especially considering they're based on specific keys and facerolling would accomplish nothing.

Thanks for your time Ferret. I can hardly believe the answer was that simple. I think I'll show a video of the finished system (though a demo may be necessary as it's hard to make this sort of thing believable).

The last thing I have to figure out is how to store all the data into a file for future viewings, since I'm not sure how happy Notepad would be about writing (a maximum of) 24000 values to a text file three times (one to record times, one to record keypresses, and one to record key releases).

If so, the problem is solved. If not, I'll be one unhappy camper.

Ferret 13 years, 4 months ago

You're getting rid of the pressed and released variables? But my whole system was made to accommodate pressed and released D: Also if you plan on recording for every frame that a button is pressed you are definitely going to exceed the limit.

Or do you mean with out the variables entirely? Because that means that you can only record one key per frame.

I'm really tired so I might be reading things wrong.

Cesar 13 years, 4 months ago

That is one hell of an awesome graphics card.

I'm running a 5770 right now and I can run just about anything on max settings that doesn't suck up more resources than it should.

PY 13 years, 4 months ago

6870 is a good card, it should compliment the rest of your PC nicely.

As for your issue, can't you store a timestamp on every keypress, and then for the replay check the first element of the list to see if that and the current timestamp match, and if so pop it and do whatever you do to handle the key? No iteration through neccesary, it just requires a 2d array sorted by timestamp, which shouldn't be difficult if I remember my GM right. Do a while (replayArray.first is now) to handle multiple keys with the same timestamp.

Castypher 13 years, 4 months ago

Well I used the basic concept behind Ferret's code, but not every piece.

The recording system works by incrementing the array index whenever a key is pressed or released, and records the corresponding frame. The playback system works by checking whether the current frame has an event on it, and if it does, it executes all of the ones that occurred then (if statements).

But at the moment, I made a modification and accidentally screwed things up so it doesn't seem to record everything.