+Propane13 Posted December 7, 2009 Share Posted December 7, 2009 Hi! I am interested in putting some Driving Controller support into a game I'm writing. But, I don't have much info on how they work. Here's the only thing I could find: http://videogames.org/html/2600Stuff/DrivingControllers It's an old email, that I guess you have to open in Wordpad, and it seems to be missing some info, What I'm gathering is that the 2 nibbles of SWCHA are impacted. The left 4 bits are impacted if the driving controller is plugged into port 1. And the right 4 bits are impacted if the driving controller is plugged into port 2. It sounds like from the article that the bits are impacted as such: Rotating left 1100 1101<--| 1111 | 1110 | 1100---- ... etc Rotating right 1100 1110<--| 1111 | 1101 | 1100---- ... etc This is good, but it brings up some questions: 1) If I wanted to "auto-detect" a Driving controller, I'm guessing I could just poll SWCHA each frame. I would assume the user was using a joystick until I came across 1100 in SWCHA's left nibble, which is a "Joystick Impossibility", and if this occurred, I could jump to driving controller mode, correct? I am worried of a potential con where there is noise on the controller lines, and by accident, we get 1100, and then are stuck in driving controller mode, when we really have a joystick plugged in. How high is the probability of something like this happening? 2) How often do these values get updated in the hardware? I am guessing it's instantaneous. If that's the case, how do you get around this situation: a) Start of frame 1: driving controller in %1100 position b) Middle of frame 1: user spins driving controller left "really fast". It cycles through 3 states-- from 1101 to 1111 to 1110 c) start of frame 2: driving controller is in 1110 position d) Character on-screen moves right instead of left (oops) My guess is is that there's a "number of times per frame" these need to be checked, and that corresponds to the fastest that someone can spin the knob. Does anyone know what that number is? Thanks! -John Quote Link to comment Share on other sites More sharing options...
e1will Posted December 10, 2009 Share Posted December 10, 2009 (edited) I am worried of a potential con where there is noise on the controller lines, and by accident, we get 1100, and then are stuck in driving controller mode, when we really have a joystick plugged in. How high is the probability of something like this happening? I don't have any info on the driving controllers (I'd be curious to see some of those answers myself), but regarding "sticking" them in driving controller mode, you could always snap back to joystick mode if you saw a 10nn or 01nn pattern, meaning they'd pushed the joystick left or right. Should be a quick test you can run every few frames as a sanity check... chances are if there was a noise issue where the game thought it was a DC but they had a joystick, the player would probably wiggle it in various directions to get a response. --Will Edited December 10, 2009 by e1will Quote Link to comment Share on other sites More sharing options...
potatohead Posted December 10, 2009 Share Posted December 10, 2009 (edited) http://en.wikipedia.org/wiki/Gray_code The idea here is that only one bit changes at a time, making it easy to determine whether or not a state change has occurred. Make a table like the wikipedia shows. It will be 8 entries long, I think. So, take the current value and locate that value in your table. That's where the controller was located at the last valid sample. Your baseline. Now you can figure out where it turned based on the new value in relation to the old one in the table like this: Sample a new value. If it's the same as the old value, do nothing. The controller didn't move, loop back and wait for the next sample. If it's different, look it up in the table, and compute the difference between where the reference old value is, and where the new value is. That will be a number you can use to perform your rotate left and rotate right computations. When done, loop back and store that value as the new reference. This gives you a motion vector with the origin being the last sample, and the direction being the latest one. The key to these is bit changes per second. The code is designed to only change one bit at a time, that's why it's not counting up in straight binary. If you don't sample often enough, the values in the table will "wrap around" giving false data, like an indication that a rotate right occured, when really it was a lot of rotating left. In this scenario, enough time passed to exceed the bit changes per sample, essentially giving random data. This will be seen as the player object moving erratically. If you sample too much, you are wasting time you could be using for other things, or the player will have to really be aggressive on the controller to get anything done. Those controllers are very coarse, only offering 45 degree precision, and that's 8 direction changes per turn. So that's say 000 at top dead center, 001 at 45 degrees clockwise, 011 at 90 degrees clockwise, 010 at 120, etc... To determine sample time, do some math based on anticipated controller input and the resolution of the the thing. Since this controller is coarse, there isn't that much resolution, I'll bet a sample per frame, or maybe two per frame should be plenty, or good for all but the quickest inputs. Joystick input would be different enough to detect, but there would have to be some player motion on the controller for that to happen, like the person above said. Wouldn't the impossibility occur, if the user held the joystick diagonal, with the fire button, or something like that? Maybe tracking a sequence would be better? The trackball is just two of these things, and a 4 bit code instead of a 3 bit one, I think. Been a long time. I once wrote a mouse routine for the 8 bitters using the track ball, and had trouble with fast motion and a one sample per frame... I think the trackball has much greater angular precision. 15 degrees or less, and it had a 4 bit grey code, requiring a faster sample. These are coarse, and have 3 bits, requiring less, I think. Anyway, the key to working with it is to know the precision, which is 45 degrees, and the grey code sequence. From there, you can figure out what the player did, given a sufficient sample rate. Edited December 10, 2009 by potatohead Quote Link to comment Share on other sites More sharing options...
Omegamatrix Posted December 10, 2009 Share Posted December 10, 2009 I would start with the disassembly of Indy 500, to see how it's been done before. http://www.bjars.com/source/Indy500.asm Quote Link to comment Share on other sites More sharing options...
potatohead Posted December 10, 2009 Share Posted December 10, 2009 (edited) Thanks, I didn't know that existed. That disassembly (which is bad ass cool to see) confirms the overall path I detailed above. It's simpler actually. INDY 500 does check once per frame, and that game has SPOT ON controls, so that's good to know. The rest is very similar to the overall process above. Tables are used to assign velocity values, based on rotation, which is based on the position of the controller. jsr ReadDrivingControllers jsr DeterminePlayerMotion ReadDrivingControllers ldx #1 ; start with player 2 lda SWCHB ; read the console switches sta difficultySwitchValue ; save to manipulate difficulty settings lda SWCHA ; read driving controller bit gameVariation ; check for one player (time trial) game bvc .readPlayer1Controller .setDrivingControllerValue and #3 ; keep valid driving controller values sta drivingControllerValues,x lda INPT4,x ; read the joystick button bpl .accelerateCar ; branch if fire button pressed lda accelerationRate,x ; get the player's acceleration rate bne .reduceAccelerationRate ; if not 0 then don't move lda playerVelocity,x ; get the player's velocity beq .setPlayerRotation ; branch if at rest dec playerVelocity,x ; reduce the player's velocity lda initDecelerationRate sta accelerationRate,x ; set to show car slowing down jsr InitPlayerMotion jmp .setPlayerRotation .accelerateCar lda accelerationRate,x ; don't change player's velocity until bne .reduceAccelerationRate ; acceleration rate reaches 0 lda maximumVelocity bit difficultySwitchValue ; check the difficulty setting bmi .skipSpeedReduction ; skip speed reduction if EXPERT setting sec sbc #2 ; reduce init speed for NOVICE mode .skipSpeedReduction cmp playerVelocity,x bcc .setPlayerRotation inc playerVelocity,x ; increase player's velocity lda initAccelerationRate ; reset the acceleration rate sta accelerationRate,x jsr InitPlayerMotion .setPlayerRotation lda drivingControllerValues,x ; get driving controller values asl ; multiply the value by 4 (i.e. shift asl ; values to D3 and D2) ora previousDCValues,x ; add in the previous DC values tay ; set for RotationValueTable offset lda playerDirections,x ; get the player's direction cmp previousPlayerDirections,x bne .readNewDirectionValues lda RotationValueTable,y sta playerRotation,x .setPlayerDirections clc adc playerDirections,x ; add in direction to manipulate REFLECT and #$0F ; only keep lower nybbles sta playerDirections,x tya lsr ; shift driving controller value back lsr ; into D1 and D0 sta previousDCValues,x ; to save for next frame jsr CheckToChangeDirectionPath .readPlayer1Controller asl difficultySwitchValue ; shift player 1 difficulty setting to D7 lda SWCHA ; read driving controller lsr ; shift player 1 values to lower nybbles lsr lsr lsr dex beq .setDrivingControllerValue rts Edited December 10, 2009 by potatohead Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.