Juggling the current numbers…
(collision detection for scoopies is finished btw)
One single bullet in average uses about 133 cycles. This includes positioning, drawing and collision detection. Which in my oppinion is already really fast.
Doing 120 of them round about takes 16000 cyles (about 10000 cycles drawing, the rest “housekeeping” stuff). I don’t think it is possible to fit the entire rest of possible game situations into the rest 14000 cycles. Thus (if I keep this “Megablast”) there will be inevitably gameĀ situations, where the game will not update in 50Hz and the screen might be wobbling a bit.
I think I will be able to live with that – and keep the shot as it is. Reasoning…
I just did a test with twenty enemies (which are for now invincible).
– having all enemies on screen
– all 120 shots
– the fighter
– the score display and some background stars
(but no bonus or shield displayed, neither enemy shots)
The cycle count reaches: about 34500 cylces
I will be able to live with it – I think.
Reasonings:
A) Not earth shatting bad!
B) Will not be for a long time!
While this is bad, it is not earth shattering bad. Also once these shots are in the air, it is highly unlikely that there will be 20 enemies alive for long.
C) Unlikely to happen unless provoked
Actually without PROVOKING it – it is highly unlikely to have a 3*4 shot and have 20 enemies on the screen at all. (but still – you will be able to provoke it!)
D) No in game music
The game will defenitly have no ingame music. To play music in constant speed it is ESSENTIAL to keep a constant update rate. That constant update rate in case of the Vectrex should be 50Hz. If you do not keep constant speed the music “wobbles”which I find about as disturbing as wobbling of stable images
E) The game is “moving”
The game is more or less constantly moving. The “wobble” effect of not keeping a constant 50Hz display is most profoundly noticable with stable images. Objects moving across the screen – while probably not immun – have the human mind on their side. The human brain seems to compensate the “wobble” of moving objects. It defenitly is much less noticable.
F) I just like the Megablast!
g) I am not finished
Perhaps I have some more cycle saving ideas! I am not really in the optimizing phase yet!
Ok 20 minutes later.
I just had a “game” on my vectrex with the current implementation. 4 shots and all of that. During the whole play time I did not have once the “FEELING” of a slow down. It may be, that I sometimes went below 50Hz – but if so I honestly didn’t recognize it.
For now I am content with the implementation.
Sooo…
Actually I started the “project” blog (which now moved to Vide) to also write about what I did while programming. So today I will at least pretend to do that.
While I will not present any “code” I will present some ideas I realized.
Colision Detection
(starting with 10 single bullets)
Thoughts: it is impossible to do efficient collision detection of 10 shots (maximum number of player shots at any given time) with every enemy (20 enemies max). That would be (max) 200 colision detections each game round.
First Optimization:
– divide and conquer
each game round is 1/50 of a second I do not need to check every enemy with every bullet. That would be “over accurate”!
I just check every enemy with ONE shot each round – and each round I test another shot -> “round robin”!
(Problems:
a) for fast player shots, I could “miss” enemies
-> solution – the vertical “length” of a shot increases with its velocity – faster shots have a larger height – larger “hit box”
b) I could still miss some enemies
-> solution – for 10 shots at high velocity “one bullet” test was not enough. I do two bullet tests now. While selecting the bullets to test, I make sure they are not the same and are also (list wise) not near each other
)
further – optimization:
– if a bullet hit an enemy and the bullet was not discarded (see below) than the same bullet is tested next round (the probability it hits again is very high)
– I now keep track of the lowest enemy each round is put on screen. I do not test bullets, which are placed lower than the enemy “min”
(starting shots seldom need testing)
further further optimization:
– realizing the player can only HIT enemies from below. Making sure all sprites start at the bottom and all shot “positions” are from top – makes collision detection (or the not coliding) very easy, since only a “line” has to tested, not a “hit box”
Player Shots
There are only 10 player shots. Ever. Not more!
Here a screenshot of the player list definition:
With that definition all shots are realized (even the 120 shot – Megablast). I will not say I cheat I – ehm – cleverly use my ressources :-).
I have (as of now) 4 different shots:
– 1 bullet
– 2 bullets
– 3 bullets
– 4 bullets
Drawing the bullets like:
(but I have no real vectorlists – I draw them “directly”)
Each of the “not 1” shots is reduced by one shot if it hits an enemy, the 1 shot is reduced to 0 (see later scoopies) and if not needed further discarded.
So – if you see a “tripple” shot, it is simply a shot with 3 vectors (and a wider “hit box”, and the ability to do one point of damage and than to be reduced to a double shot).
Sccopies
Each “shot” has the ability to “carry” two additions (see last entry above in the PlayerShotStruct – SCOOP_SHOTS).
The byte SCOOP_SHOTS is divided into two nibbles – the lower nibble representing a possible right scoopie, and the upper nibble representing a left scoopie.
Each scoopie nibble can be:
0000 – not scoopie
0001 – scoopie with one bullet
0010 – scoopie with two bullets
0011 – scoopie with three bullets
0100 – scoopie with four bullets
If a (or two) scoopie (s) is (are) present – the “hitbox” of the shot is further extended.
When a shot is near an enemy several tests are made, but it burns down to:
– test in what “third” of the entire shot hitbox the shot did impact
– if left or right third an additional y test is made (scoopie shots are further down)
– if a scoopy shot hits it is “decreased” like the parent (0000 -> no scoopie shot anymore)
For scoopies to work it is necessary of have player shots with 0 (zero) bullets, otherwise the scoopie shots would disappear when the player shot vanishes.
Scoopie-shots are drawn in one go with their “parent”-player shots.
So a full 4*3 player scoopie-shot (12 bullets) is game wise a slightly more complicated “single” shot.
To come up with a (more or less) clever collision detection took some time (and a couple of scratchbook pages) but it seems to work out ok!
Here another short video which includes scoopie colision detection!
(BTW. Didn’t do the scoopie drawing beside the player yet)
Regards
Malban
Just for Info.
Did update the collision detection further. For a full 120 shot mega blast (10 shots ‘a 4*3 bullets) – the complete collision detection uses about 850 cycles which IMHO is awsome!
Now I wish I could draw the shots as fast…
(btw the scoopies are drawn now too)
Sounds absolutely awesome, congratulations! Care to elaborate on the improvements?
Nothing all too spectacular. I precalculate some values used in the routine.
I “went over the code” again, and found some redundancies etc.
The (slightly shortened) code for one shot looks like:
COL_DETECT_ENEMY_SHOTS macro tShot, testBase, YBASE, XBASE, IS_BUG, DO_MIN_CHECK
; in b is still the xcoordinate of the current enemy that is tested for thisshot
lda YBASE +u_offset1,s
ldx tShot
beq notHit\?
; yTest
suba tShot +T_YPOS
bpl notHit\?
cmpa shotYRadius
blt notHit\?
;;;;; y done
; xTest
addb #$80 ; x coordinate enemy now 0 based in b 0-255
; test center
cmpb tShot+T_XPOS0_MINUS_1_RADIUS
blo notHitLeftOfCenter\? ; enemy is on left side of shot
; in b 0 based enemy pos
cmpb tShot+T_XPOS0_PLUS_1_RADIUS
bhi notHitRightOfCenter\? ; enemy is on right side of shot
scoopCenterHit\?
; if only scoop shots are left - we can not hit with "own" cannon
tst SHOT_RADIUS, x ; a zero shot cannot hit!
beq notHit\?
jmp removeCurrentShotAndObject
;..........
notHitLeftOfCenter\?
; check if there are scoopies
lda SCOOP_SHOTS,x ; two nibbles
anda #$f0
beq notHit\? ; we know there is no right hit possible, because we came from center!
; in b x pos of enemy - 0 based!
; test left
cmpb tShot+T_XPOS0_MINUS_3_RADIUS
blo notHit\? ; enemy is on left side of left shot
; left HIT!
; i Do not even have to test right side - since we know it is left of MINUS_1_RADIUS
jmp shotHandleDone ; go to enemy "hit" handling
;..........
notHitRightOfCenter\?
; check if there are scoopies
lda SCOOP_SHOTS,x ; two nibbles
anda #$0f
beq notHit\? ; we no there is no left hit possible, because we came from center!
; in b x pos of enemy - 0 based!
; test right
cmpb tShot+T_XPOS0_PLUS_3_RADIUS
bhi notHit\? ; enemy is on right side of right shot
; right HIT!
; i Do not even have to test right side - since we know it is left of MINUS_1_RADIUS
jmp shotHandleDone ; go to enemy "hit" handling
notHit\?
endm
Ooops, formatting doesn’t work in comments… sorry