Showing posts with label collision. Show all posts
Showing posts with label collision. Show all posts

Wednesday, 4 March 2020

Player Characteristics

I still need to calculate coordinate locations for crosses on many levels and seem to be putting this off at the moment, mainly down to the fact I find it rather boring...

However, I'm putting this task off by actually tweaking and/or adding code to other sections!  Honest!

The girl finally became 'properly' selectable/playable the other day and until now has shared characteristics with the boy.  But no longer!  I've altered certain variables so that when the girl or boy are selected they handle slightly differently.

The boy is slightly 'stronger' than the girl - he can be in contact with enemies or dangerous parts of the scenery for just a few milliseconds longer before experiencing an energy drain.

The girl is slightly 'lighter' than the boy - although she can jump no higher, she can stay in the air just a few milliseconds longer than the boy, making it a tiny bit easier to successfully make the longer jumps between platforms on some levels.

Those differences above are only subtle, but does give the player some choice as to how to play - do you choose easier jumps or more strength?  No doubt the variables will be altered further once play testers get their hands on the game and maul my efforts to death, but it will be easy for me to change in the code now since it's all handled by a couple of labels.

I've also finally got round to moving my screen splits slightly to accommodate the fact that on two of the levels (Level 3 - The Apartments and Level 7 -  The Park, if you're interested!) I've used a blue background.

I wanted to 'extend' the blue sky background into the upper border, an easy task since the top border is open, but not-to-hot looking because the split was in the wrong place, therefore the blue colour 'bled' too far down the screen.  Also, the blue was appearing on levels where the background and top border should have been black.

It was simple fix in the end - the two levels needing a blue top border set a flag telling my top border split to colour itself blue, otherwise colour itself black.  The result on Level 3 looks like this...


Next diary entry...

Tuesday, 25 February 2020

Dangerous Scenery

Another piece of the gameplay puzzle has finally fallen into place!

It's not really been necessary to do it until now since some gameplay mechanics just get in the way of testing and debugging, but I have now coded the, er, code that detects 'dangerous' scenery around the player and drains energy if touched.  The main 'obstacles' in the various levels are mushrooms and pools of green slime and if they are walked upon, energy is subtracted.

I've tried to be fair however; the detection is quite 'loose' and you can jump up near or through such scenery without being penalised, with the energy drain only occurring if the player directly walks on such scenery.  However, on some levels the mushrooms may be 'reversed' chars and appear only as silhouettes amongst other scenery; they can be quite difficult to spot in the heat of the action and will probably annoy those who go charging around without stopping, looking and thinking!

Here's how it looks in action (the animation below also showcases the first outing of the girl player, who is now selectable from the title screen!):



The scenery collision detection reuses values calculated from the gravity subroutine so checks what is underneath the players feet.  There is a bit of a cheat going on - the subroutine is not checking the mushrooms directly, but 'special' chars underneath the mushrooms that trigger the collision!

Next diary entry coming soon...

Wednesday, 31 July 2019

Status Bar Score

I gave myself the afternoon off work today so had a little extra time for coding; this time was welcome as there are lots of little background routines that need to be written before the code transitions from being little more than a demo into an actual playable game.

Since the sprite to sprite collision code was written recently along with the energy bar, I thought I'd write a couple of routines to handle the scoring and the cross counter to complement the energy bar.

I did actually tweak the collision detection code the other night so that my yellow cross sprite didn't deplete the energy bar as it was doing; it was originally part of the main enemy check that did remove energy.  As a temporary measure for testing purposes, I made collision with the yellow cross change the background colour so I knew the code was executing in the right order.  My Grandson is doing some testing for me and he made a noise equal to "Urgh!" when he first saw the background change, hence my decision to do some code that increases the score and cross counter.

I've had it in my head for a while now that collecting crosses will score the player 100 points and that 20 yellow crosses will need to be collected to complete a level.  The code keeps this in mind, but this could change in the future.

Here is the code in action; at the moment the yellow cross sprite doesn't change position when "collected" so that multiple collisions can be registered to test the status bar.



How does it work?  When a collision with a yellow cross is detected, a flag is set which then makes the main code loop jump to a subroutine that increase the score by 100 and the cross counter by 1.  Well, that's what it looks like in practise, but I learned a trick a few months ago from Jason 'T.M.R' Kelk: no "real" score is actually ever used.  All that's really happening is that score digits are being checked and manipulated in a table.

Eh?  Well, keeping in mind that my score is six digits long, collecting a cross adds a 1 to the fourth column along from the left (hundreds).  This keeps happening until the column reaches a maximum of 9, at which point that column resets to 0 and the third column along from the left (thousands) increases by 1.

The same happens to the cross counter as well.  When a cross is touched, the units column is increased by 1 until it reaches 9, at which point the tens increases by 1.  No "real" scoring in the sense that humans think of it.

This method has the benefit of using RAM to store the score instead of space in the zeropage.  Also, it's now going to be quite easy to check the end of the level.  All I will need to do is load up the tens column of the cross counter and see if it's equal to 2.  If so, "20" crosses have been collected and the level is complete!

Anyway, here is the score code that increases by 100.  It is flexible so that I can very easily add an increase of, for example, 1000 on level completion in the future.

 ; score accrue subroutine ------------------------------------                 
   
 score_accrue_100  
           ldx #$03  
             
 sa_loop  
           lda score,x  
           clc  
           adc #$01  
           cmp #$0a  
           beq sa_cnt  
           sta score,x  
   
           jmp score_compare  
   
 sa_cnt       
           lda #$00  
           sta score,x  
           dex  
           cpx #$ff  
           bne sa_loop  
   
 ; now compare current score to high score  
   
 score_compare  
           ldx #$00  
 score_chk  
           lda score,x  
           cmp high_score,x  
           beq score_chk_cnt  
           bcc score_chk_end  
           bcs hi_score_update  
 score_chk_cnt       
           inx  
           cpx #$06  
           bne score_chk  
 score_chk_end  
   
           rts  
   
 ; current score is a high score???  
   
 hi_score_update  
           ldx #$00  
 hi_up_loop  
           lda score,x  
           sta high_score,x  
           inx  
           cpx #$06  
           bne hi_up_loop  
             
           rts  
   

If you've just read that code block, you may just see that a high score comparison routine is included for good measure.  I thought at first that the high score would only be updated at the end of a game, but instead decided to update it on the fly should, during play, the player accrues a current score equal to or greater than the current high score.

The cross counter routine works on a similar principle, with the current amount being stored in a "cross_score" table after digit manipulation.  It's shorter because there is no high score check obviously!

 ; cross accrue subroutine ------------------------------------                 
             
 cross_accrue  
           ldx #$01  
 ca_loop  
           lda cross_score,x  
           clc  
           adc #$01  
           cmp #$0a  
           beq ca_cnt  
           sta cross_score,x  
             
           rts  
             
 ca_cnt       
           lda #$00  
           sta cross_score,x  
           dex  
           cpx #$ff  
           bne ca_loop  
             
           rts  
   

Once all this digit manipulation is complete, I have a subroutine that prints the contents of the score, high score and cross tables into the correct places on the screen.

And that's scoring done for now!

Next diary entry...



Thursday, 25 July 2019

Software Collision Detection

Following on from yesterday, I've decided to move away from using the C64 built-in collision detection almost straight away since I know other routines (such as the energy bar) are working.

The reason the built-in sprite collision detection register ($D01E) isn't used is because it requires a (metric?) tonne more code to work out which sprites are involved in the collision.  This means that I wouldn't have been able to (without wasting time and memory on that extra code) have any of the meanie sprites overlapping without there being issues in detection - they would have had to stay clear of each other to keep things simple and this would limit the game play and level layout somewhat.

Thanks to Jason 'T.M.R.' Kelk for pointing me in the direction of some online GitHub resources which, almost unbelievably, I read and then proceeded to write a software based collision that worked first time.

All that's happening in the detection code is that the x-y coordinates of the main player sprite are grabbed from the sprite table and invisible x-y points are calculated within the sprite and stored in collision variables.  These variables are then tested against the x-y coordinates of the meanie sprites, grabbed from the same sprite table.  When an overlap occurs, there is a virtual 'collision'.

From there, in my code, energy is then taken a away from the player, although I have tweaked the timer so that it's possible to be 'colliding with a meanie for some time without losing too much energy in one go.

Here is the collision code as I finished coding today, which will obviously change quite a bit as new features are added, but for now works fine...

 ; player to enemy sprites subroutine ----------------------------  
   
 ; software based collision detection... no $d01e here!!!  
   
 player_collision  
         lda #$00                 ; set the death flag to 0  
         sta ply_death_flag  
   
         lda sprite_x+$00         ; setup a 'bounding box'  
         sec                      ; around the player sprite  
         sbc #$04                 ; for software collision  
         sta coll_temp+$00        ; detection  
         clc                      ; grab the some x positions  
         adc #$09                 ; for our player sprite which      
         sta coll_temp+$01        ; is sprite 0!      
   
         lda sprite_y+$00         ; and do the same for some y  
         sec                      ; positions of sprite 0  
         sbc #$0a  
         sta coll_temp+$02        ; the x and y positions are  
         clc                      ; stored in the 'coll_temp'  
         adc #$16                 ; labels  
         sta coll_temp+$03  
   
         ldx #$00  
 enem_colls_loop  
         ldy colls_timer          ; first load up the collision timer  
         iny                      ; and increase it by '1'  
         cpy #$08                 ; is the timer equal to '8' yet?  
         bne coll_tmr_skip        ; no? go down to coll_tmr_skip  
                                  ; and skip collision checking  
                                   
         lda sprite_x+$01,x       ; now grab the x postion of each sprite  
         cmp coll_temp+$00        ; from the sprite table  
         bcc enem_colls_skip      ; and check against our 'bounding box'  
         cmp coll_temp+$01        ; coordinates to see if they      
         bcs enem_colls_skip      ; overlap...  
   
         lda sprite_y+$01,x       ; and do the same for the y coordinates  
         cmp coll_temp+$02  
         bcc enem_colls_skip      ; if no coordinates overlap, skip down  
         cmp coll_temp+$03        ; to 'enem_colls_skip'  
         bcs enem_colls_skip  
                                  ; if some coordinates overlap, collision!  
         dec ply_energy           ; decrease the player energy flag by 1  
           
         jsr energy_bar           ; redraw the energy bar  
           
         lda ply_energy           ; is the player out of energy?  
         cmp #$00                 ; yes?  
         beq energy_out           ; skip down to 'energy_out'      
   
 enem_colls_skip  
         inx  
         cpx #$07  
         bne enem_colls_loop      
   
         ldy #$00                 ; load x register with '0'      
 coll_tmr_skip                    ; to reset collision timer and...                  
         sty colls_timer          ; store x register to colls_timer  
           
         rts                      ; go back to main game loop  
               
 energy_out  
         lda #$01                 ; load the player death flag with      
         sta ply_death_flag       ; 1 because all the energy is gone!  
                                  ; then...  
         rts                      ; go back to main game loop where  
                                  ; player will be killed!!!! MWHAAAA!
  

Once compiled and running in WinVICE, it looks a little like this...



Next diary entry...

Wednesday, 24 July 2019

Energy Bar

I've fancied tackling the in-game player energy bar for a while now and have decided to keep it very simple for me to code which may mean inefficient or inelegant, but at this stage, if it works then that's fine by me!

Before tackling the energy bar code though, I needed to code some collision detection so when the player touches a meanie, some energy can be taken away!  As a stop gap measure, I used the C64 built-in sprite hardware collision detection register $D01E, which will be changed in the future for reasons I won't explain right now!

Now a collision between sprites can be detected, it's time to subtract some energy!  The way I've gone about this is to keep the amount of energy stored in a 'variable' which in my code is currently called 'ply_energy' and which is set to '33' or '$21' at the start of the game.  Why such a strange number?  Well the energy bar length represented in blocks at the bottom of the screen after the word energy, is 33 chars wide!  There you go, count the blocks...


Now, being new to C64 coding, I've absolutely no idea if there is a standard way of doing this but my solution to the energy bar has been to set up the colour RAM at the beginning of the level to the red, yellow and green that you can see and which always remains the same, but then replace each block character in the screen RAM with a space character when energy is taken away.

So quite simply, upon the player touching a meanie, the energy in variable 'ply_energy' is subtracted by one, then the whole energy bar is deleted and redrawn to the new value.  This is my delete and redraw the energy bar code:

 ; energy bar drawing subroutine ----------------------------------  
   
 energy_bar  
           ldy #$00              ; update the energy bar!  
           lda #$20  
 clr_energy_loop                 ; clear the row of chars that display the  
           sta $07c7,y           ; energy bar by printing a line of  
           iny                   ; blank spaces!  
           cpy #$21  
           bne clr_energy_loop  
             
           ldy #$00                      
           lda #$40  
 drw_energy_loop                 ; now redraw the energy bar with char $40  
           sta $07c7,y           ; (the block making up the bar)  
           iny                   ; equal to the amount of energy   
           cpy ply_energy        ; remaining  
           bne drw_energy_loop       
             
           rts                   ; go back to wherever this was called from!       


And for now, that'll do because it appears to work just fine and dandy!

Next diary entry...