AnalogKid
-
Content Count
37 -
Joined
-
Last visited
Posts posted by AnalogKid
-
-
43 minutes ago, youki said:+1 !😀
Put all your VDP work in the NMI routine, it solved lot of headache for me!.
I did the same. For the intermittent video updates I used flags to "queue" updates so all VRAM work could be done in the interrupt handler. I've been working in event-queue driven paradigms for so very long it's against my nature to try to control the flow. I let the flow be in charge and work with it. Game coding has always been about "racing the beam" and for me that's where the fun is. The Atari/Activision developers were absolute geniuses as they didn't have the huge 18ms window we have, they had however long it took for the beam to move from the end of one scan line to the start of the next to perform any computation.
-
9 hours ago, Swami said:CoolCV and ColEm work great and it looks great. I'll try the Phoenix and OEM CV later. My comments at this point are:
1. the small green asteroids appear to cause collisions with the ship with a small, but noticeable, space between them compared to the other asteroids
2. the hits on the asteroids for the original arcade version are more of a thunder boom (about twice as long and lower pitch).
3. in the original arcade, the small asteroids travel twice as fast
4. I didn't see a small UFO
Update: Phoenix works good from SD card and AtariMax cart. I swear I saw a couple instances of 1-frame sprite ghosting (flash of asteroid or something) at the very beginning of play with SD card when I would shoot something, but I'd need someone else to confirm it wasn't a fluke, as I never saw it again after the first ten seconds of play. Could be irreproducible voltage glitch in Phoenix. Only OEM CV left.
1. I'll double-check the collision detection routine, I may not be trimming enough off of the hit-box for the small ones. I had downsized the sprites by a pixel or two as there wasn't enough difference between the medium and the small.
2. The arcade audio was fantastic with great bass. Unfortunately with the Colecovision I have to work with a white-noise generator and it has two frequencies high and low. I believe the alternative is to link the noise channel to tone channel 3 to get lower sounds but I need all three tones, particularly when the UFO on screen. Anyway this is why the explosions in most games sound exactly the same. I will increase the decay time however.
3. Yeah the arcade version has some randomness to how fast the small ones fly off and often they do shoot off very fast, but I did have to scale down the playfield to account for much lower resolution. As a result it's pretty cramped so I scaled back the 90-degree splitting and the extreme acceleration to keep the game from being unplayable. If you think I "nerfed" it too much, I may add back some of the chaos.4. The small UFO won't appear until round 3 and then it's a 1 in 4 chance the UFO will be the smaller until after round 4 when it's a 1 in 3 chance. I really have no idea what the ratio is in the arcade version. Maybe in later rounds I'll increase it to a 1 in 2. The easy skill level never has the smaller one.
I too have seen the ghosting you described at least a couple times in ColEm. I'll double check the sprite attribute update code.
Thanks for all your testing and input. -
The following is a mechanism I created to control the pacing of screen elements so that your game's sprites can have 7 distinct speeds of movement. It has minimal impact because it uses zero math and math is to be avoided at all costs because the goal of course is to keep up with the screen refresh. When you're winning the race with the refresh your sprites move smoothly because you're in perfect sync. Simple addition, for a counter for instance, is actually expensive for the Z80 with compiled C code, so a useful technique is to replace incrementing with bit shifting, if your maximum counter range is 7 or lower. This coupled with logical and-ing to compare against the current counter "tick" gets you the 7 different speeds.
static const static byte HEART_BEAT[8] = {1,2,4,8,16,32,64,128};static const byte *heartBeat = HEART_BEAT;
static register byte currentHeartBeat;
nmi(void)
{
....if (*heartBeat & (byte)128)
{
heartBeat = HEART_BEAT;
}
else
{
++heartBeat;
}
}
heartbeat is a pointer to the values within HEART_BEAT and in the nmi routine we nudge it forward a tick until we hit the end and start over. Note that the first tick's value is 1 so that we can perform the logical and-ingSo now in your main play loop,
static void play(void)
{
....
byte motionTimer = (byte)1;while ((byte)1)
{
currentHeartBeat = *heartBeat; // the nmi function updates what heartBeat is pointing to
if (motionTimer & currentHeartBeat)
{
.......motionTimer = getTimer((byte)3); // reenter the motion code on the third tick after the current one
}
and the method to get the value for the desired heartbeat increment, taking values between 2 and 7 inclusive
static inline byte getTimer(const byte ticks)
{
return (byte) ( (currentHeartBeat << ticks) | (currentHeartBeat >> ((byte)8 - ticks) ) );
}
note: of course screen elements that should move on every refresh, like bullets, etc. don't need the timer code at all. Also, you might want to pass a variable to getTimer, allowing you to speed up the characters in later roundsFor the case of the player's character, because you don't want a delay for the motion to start when the player presses a button or pushes the joystick, the timer should start at 0 and be reset to 0 when the user is no longer providing the input
byte moveLeftTimer = (byte)0;
.....
if ((byte)LEFT & j)
{
if (!moveLeftTimer || (moveLeftTimer & currentHeartBeat))
{
...........
moveLeftTimer = getTimer((byte)2);
}
}
else
{
moveLeftTimer = (byte)0;
}
If a character needs to move more slowly than 8.5 times a second, you can add "multipliers", like a flip-flop variable to reduce the rate in half:
byte bossMoveFlag = (byte)0;
....
if (bossMotionTimer & currentHeartBeat)
{
if (bossMoveFlag)
{
.......
}
bossMoveFlag = !bossMoveFlag;
bossMotionTimer = getTimer((byte)7); // values 4 to 7 now get tick counts of 8 to 14
}
Lastly, you need a flag to keep the nmi and your main loop in sync because if you're ahead of the refresh, you'll get jittery characters the same as you would as if you were lagging. I think there might be a register that can be used for this but the comment I saw about it was vague as to when it's set and reset, so for now I'm using my own volatile variable
static volatile byte renderSyncFlag = (byte)0; // coordinates the game loop and the nmi interupt handler
void nmi(void)
{
....
renderSyncFlag = (byte)0;
}
static void play(void)
{
....while ((byte)1)
{
renderSyncFlag = (byte)1;.....
while (renderSyncFlag)
{
//wait for nmi to flip the flag
}
}
To finish, here are some tips to help you keep head of the refresh, because you should code your C like the compiler/optimizer wants you to. Yes, the code will be ugly and it will feel dirty at first
Avoid putting code in subroutines unless it's to avoid blocks of duplicate code. Jumping, pushing, popping, stack usage, etc. all come at a cost, plus you can reuse local variables and the most common ones that are used repeatedly, like "x" and "y", they can be given the register declaration
Put performance critical variables at the end of the declaration list
Make functions static
Include prototypes for ALL fuctions at the top of your file. This is necessary to avoid numeric arguments being up-casted (don't ask me why)
The variables for your main game loop should be static globals or at least declared as static
Unravel your own critical loops: all looping costs, if your critical loop code is small, unravel it
cast all constants to byte
put constants before variables: if ((byte)1 > x) ..... i = (byte)5 + j;
also for math, cast cast cast: i = (byte)((byte)5 + (byte)j); // numbers get upgraded to 16 bit integers so cast them down as much as possible, don't force the compiler make assumptions
make switch statment cases numerically ordered when possible, even if it means a little duplication
replace counters looping from 1 to a value less than 8 with bit shifting
when you do need a for loop, count down rather than up
replace multiplication by 2,4,8, etc. with bit shifting (some optimizers do this anyway)
replace i++; with ++i; etc. for all the operators
I'm told that compilers like if-then logic like i = j ? 1 : 2; because it's as basic as it gets
replace calculations or complex or bulky logic with lookup tables in ROM, i.e. const arrays of values
avoid floats like the plague, again use lookup tables when you can, like for cosine and sine values
an efficient looping technique is to make your character structs linked-list nodes because it's slightly faster to work with pointers than arrays and arrays take up more RAM because they are indexed by integers (I'll cover this in another post)
don't perform collision detection on every single refresh - if characters are 16x16 and moving 1 pixel at a time, it's really not conceivable that you're going to miss detecting a collision if you only check every third refresh
This is the first of a series of tips to help folks starting out in C coding of Colecovision games. I hope they help in bringing your ideas to the screen-
1
-
-
Release candidate 1 is available for download. I doubled the number of angles for the ship so the ship is more maneuverable, flight is smoother, and it feels closer to the vector version. I made the UFO smarter with some quasi-trigonometry but had to work in some inaccuracy because it had deadly 100% accurate aim. I also eliminated the UFO flicker with a smarter sprite prioritizing algorithm.
https://www.dropbox.com/s/jwdj8bcltlsr37c/asteroids.rom?dl=1
-
1
-
1
-
-
I stumbled across an emulator with actual debugging capabilities, RAM, VRAM, register examination, active tileset viewer, etc.:
-
1
-
-
On 11/19/2020 at 3:52 AM, youki said:Of course not for all asteroids but just some of them. Or use char for the ship.
Or work in "bitmap" mode and use a mix of software sprites and hardware one.
It would avoid to some flickering.
I have checked , it is all sprites. And the flickering is really acceptable.
I have just been surprised about the size of the rom , it is very big for this kind of game.
I would expect more something between 8k and 12k . Here it is 23k , i think.
anyway very well done and fun!!
I love it.
A large portion of the ROM size came from the data: sprites for 16 ship angles, 8 shot images, 9 asteroid images, 2 UFOs, the explosion characters, the sound effects, lookup tables, the title bitmap, etc. The code did bloat up when I started adding AI for the UFO. That's actually about a quarter of all the code, the figuring out how to avoid asteroids and how to shoot at the ship intelligently. Had I been able to use floating point calculations it would have been a couple lines to calculate tragectories but floating point is to be avoided at all costs. The collision detection code is also pretty bulky due to the number of objects to compare and because it's C and all I have is structs, not subclasses, so I can't have a screen object base class or couldn't enhance the sprite class...
Also, for the most speed-critical routines, I unraveled loops like an optimizer would and used objects directly rather than references so again, more bloat, but to keep 20 asteroids, a ship, a ufo, and 4 shots animating at the NMI rate, you do what you have to do to make the CPU do as little as possible.
-
On 11/19/2020 at 12:04 AM, Kiwi said:Very nicely done. I've wondered if you have implanted sub pixel positioning for the ship.
One of my very first Colecovision game I programmed was Computer Space. My routine for the 16 pulsation looks like,
void ShipAccelerate(){ if (rotateme>=0 && rotateme<=15){projy+=-4;} //if (shipr==0){shipp=0;} if (rotateme>=16 && rotateme<=31){projy+=-3;projx+=1;} if (rotateme>=32 && rotateme<=47){projy+=-2;projx+=2;} if (rotateme>=48 && rotateme<=63){projy+=-1;projx+=3;} if (rotateme>=64 && rotateme<=79){projx+=4;} if (rotateme>=80 && rotateme<=95){projy+=1;projx+=3;} if (rotateme>=96 && rotateme<=111){projy+=2;projx+=2;} if (rotateme>=112 && rotateme<=127){projy+=3;projx+=1;} if (rotateme>=128 && rotateme<=143){projy+=4;} if (rotateme>=144 && rotateme<=159){projy+=3;projx+=-1;} if (rotateme>=160 && rotateme<=175){projy+=2;projx+=-2;} if (rotateme>=176 && rotateme<=191){projy+=1;projx+=-3;} if (rotateme>=192 && rotateme<=207){projx+=-4;} if (rotateme>=208 && rotateme<=223){projy+=-1;projx+=-3;} if (rotateme>=224 && rotateme<=239){projy+=-2;projx+=-2;} if (rotateme>=240 && rotateme<=255){projy+=-3;projx+=-1;} if (projx>=49){projx=48;} if (projx<=-49){projx=-48;} if (projy>=49){projy=48;} if (projy<=-49){projy=-48;} }And I applied the movement to the ships every 3 frames to get around doing subpixel position. If I have done this today, I would use tables and divide by 16 verse rotation.
I think Asteroid would be very hard to do without sprites. Vector to background with the slow VDP with nmi corruption issue would be issue. Challenger I did was mostly sprites. Only the bonus bug was the only bg object. The game run at 20 fps due to software collision detection 6 shots vs 16 objects(doing 2 shots vs 16 object per delay(1);).
Actually my trick was to avoid using 360 degrees of angles, but rather a 0 to 16 "angle index" that maps into motion lookup tables, rotation lookup tables, sprites, etc. My goal was to remove as much math as is possible and not calculate anything that can pre-determined. Every time I removed math I saw the game get smoother after I cranked the on-screen asteroid count up to 20 and on-screen shot count to 3. There can be a total of 26 characters in motion on the screen so I was definitely "racing the interrupt". For the sub-pixel motion, again I used lookup tables. I flip-flop a variable that tells the code which table to use, so for instance for a 22.5 degree angle, for every cycle I add 1 to the X and every other cycle I subtract 1 from the Y. Yeah it's a rough approximation but floating point must be avoided at all costs
I'm working on Missile Command and I have to find the fastest possible atan approximator because I can see the game stutter slightly from that single calculation (I'm using a lookup for cos and sin)
-
Yeah I added the red flash because I was thinking of ways to really distinguish the ship exploding. The sound effect is louder and longer and the explosion animation is slowed. I like games adding extra animation when a character is hit by an enemy because it makes the loss more impactful. There's one game, I think it's one of the Mario ones, where the character falls over backwards and a little halo appears over their head... fantastic. I also like games that vary the effects, the sounds and the animations, to avoid monotony, and Asteroids is full of randomness so I did the same. I was limited to two explosion sounds but I also varied the explosion animation speed.
Getting back to the title screen is actually the standard "#" keypad button, but maybe I should throw up text along the lines of * to restart and # to select level. btw keypad 1 is hyperspace and * pauses
Keeping a high score isn't possible between power-offs/resets (with classic CV hardware) so the best I could do is track them for a sessionI was planning to add music to the title. I haven't decided between original music or picking some space-oriented music. I had thought of using 2000 Light Years from Home on a loop though it might be too obscure for the kids
Astronomy Domine or Interstellar Overdrive would definitely be too obscure. Maybe a tribute to the Star Man, Space Oddity
-
Actually I did code hyperspace though it's not obvious so I should have mentioned. I put it on the "1" keypad key so you really have to mean to hit it
I also was nice and checked for a clear landing spot, the same way I have it check for a clear spot before restarting after your ship is hit. I also made the UFO not shoot you the instant it appears from the edge, one of those little things that pisses me off, like the spider in Centipede that is aimed exactly at your player's gun... I'm not out to steal your quarters. I was increasing the pace of the "Jaws" music but yeah I too was feeling like it wasn't enough. I'll add a couple more steps.
One change I definitely see me doing is doubling the number of ship angles. When the play area is small, like in Omega Race, you don't really notice how lame 16 angles is. When you're unable to shoot an asteroid halfway across the screen because it's in your blind spot, that's pretty atrocious.
I do plan to share the framework I came up with as I think it may be helpful to people tackling the coordination of a lot of characters with different speeds and directions. Also I found ways to eliminate as much math as possible which is what the Z80 really wants.
However, at the moment I'm halfway through coding Missile Command and also started a racing game because I feel like there aren't nearly enough games using the steering controller and of those that there are, well, some of them treated the steering wheel like it might as well have been a joystick or an Atari paddle controller. As for Missile Command, today coded the mushroom cloud animation and blew up my first city, added collision detection and shot down my first missile, and added the dreaded MIRV split.
-
5
-
-
The beta version of the ROM is available here, for those that want to play: https://www.dropbox.com/s/vifqxojfm9qvv22/asteroids.rom?dl=0
It has been confirmed that it runs on an un-modified console. I haven't heard back about the Phoenix yet.
Enjoy!-
3
-
-
About two weeks ago I decided to try writing my first arcade port for the Colecovision so I looked around for a title I thought should be available for the standard console and landed on Asteroids. Yeah, I dove in the deep end 😃 After getting past the learning curve of using the library SDK and relearning C, which I haven't used in 20 years, I came up with something that I think demonstrates how powerful and underrated the console really is. At its most intense, my port of the game has 21 independent sprites moving in their own directions at their own paces with animated explosions of different rates and all sound channels filling the room without any lag or jitter. The console could easily support a few more asteroids on the screen but I coded to avoid the dreaded 4-sprite-per-line issue. Anyway, here's my initial proof-of-concept demo:
Starting out, round one
Bring it on!Not too shabby for a 3.5Mhz Z80 with 1K RAM running C based code. Well done Coleco
btw if anyone out there is coding in C and could use some tricks for having a large number of characters with independent pacing, I'd be happy to share-
12
-
2
-

Demo of my next game for you Colecovision enthusiasts
in ColecoVision Programming
Posted
I hope you guys enjoy this one, it has always been one of my favorite early 80's games
Colecovision Missile Command demo
Unfortunately the blueMSX video capture with sound is still a little problematic on my PC, I'll figure it out eventually. The game came alive when I added the air raid siren, the explosions, the satellite/bomber sound, and that sound during the post-round counting.
There's still a lot of little things to be done, like updating the missile batteries as missiles are fired, gradually incrementing the missile speed, changing the ground and sky colors during the later rounds, as the drugs kick in, stuff like that. Still though, this is another good demo of how powerful the console is. There's (unfortunately) a not insignificant amount floating-point math going on there to target and move the players missiles. The Atari ports avoid that by just drawing lines but I don't have that luxury. On the plus side, it's fortunate that the clouds are supposed to be blinky 😉