Jump to content
IGNORED

my first basic question


Recommended Posts

ok, I'm trying to make sure I search before asking my simple questions, but I can't find this.

 

I've taken some code logic from Oscar's book in chapter 6 (Space Raiders).  The code works in my game, the ship moves and shoots just fine, but I just don't fully understand exactly how it works.

 

below is the code with my comments that ask the couple of questions I have. 

 

thanks in advance....you all rock for being so helpful. please feel free to link me to where this might have been already answered...

 

also, is this the best practice? My game is a fixed shooter so L-R movement only is fine

 

' Player Movement along X-axis
'
c = CONT                                                          ' Assigns c to CONTROLLER (1 or 2)
d = c AND $E0                                                   ' ? What does AND $E0 do? Is this just the address that deal with controller actions?
IF (d = $80) + (d = $40) + (d = $20) THEN        ' Keypad pressed, but does the '+' treat this as OR ?  keypad being not used so no additional code....
ELSE
     IF (d = $60) + (d = $a0) + (d = $c0) THEN    ' Side button   ' Ok, this means that 1 of the 4 side buttons of controller were pressed
          IF shot_y = 0 THEN                                  ' Ok, I get this, it fires the weapon (one shot at a time!)
               shot_x = ship_xpos
               shot_y = ship_ypos
               shot_exp = 0
               sound_shot1 = 1
          END IF
     END IF
     d = controller_direction(c and $1F)                 ' Ok, this is to cover disc movement, we mainly limit to Left (4), right (2), up(1) and down (3)
     IF d = 2 THEN                                               ' I don't get how this returns 2 from controller direction table
          IF ship_xpos < 152 THEN ship_xpos = ship_xpos + 2    ' This stops the ship at the right side of screen (I use pixels 152-160)
     END IF
     IF d = 4 THEN                                             ' I dont get how this returns 4 from controller direction table
          IF ship_xpos > 16 THEN ship_xpos = ship_xpos - 2     ' This stops the ships at the left side of screen (I use pixles 8-16)
     END IF                                                       ' Will add code for 1 and 3 for conditional logic later
END IF

 

 

 ' I get that this table is used to basically turn the disc controller into a 4 way d-pad, but with some leeway in case you don't exactly hit 
 ' the L/R/U/D, but I don't get how it works.   How does c and $1F return 2  when pressing right or 4 for left?  
  ' I also don't get how we arrive at these 32 values. I assume all the 0 values are the controller keypads, side buttons and presses on the
  ' disc we don't want to register, but that's it. How does this map to each index?

controller_direction:
    DATA 0,3,2,3,1,0,2,0
    DATA 4,4,0,0,1,0,0,0
    DATA 0,3,2,0,0,0,0,0
    DATA 4,0,0,0,1,0,0,0
 

Edited by Mik's Arcade
fix format
  • Like 1
Link to comment
Share on other sites

To your questions:

  • What does (c AND $E0) do?
    It masks the action-button bits (the upper 3 bits of the 8-bit signal returned from CONT) discarding the bits applicable to the keypad and disc.
     
  • Does the '+' treat this as OR?
    The Intellivision CPU has no native OR operation, so implementing one is expensive.  However, because OR results in a "True" value when any of the arguments is "True"; then adding numeric values would serve as a reasonable substitute.  Think about it: if all values are zero (False), then the result is zero (False); but if any of them is different from zero, then the result is going to be not-zero (True).

    The specific combination of values being "ORed" here are those that overlap with the keypad, so the line is testing to see if the signal represents a keypad press.
     
  • I don't get how this returns 2 from controller direction table?
    The expression (c AND $1F) masks the lower 5 bits of the signal returned by CONT ($1F being the 1's complement of $E0).  This value is then used as an index into the data table "controller_direction."  Essentially, the 5-bit signal codes for each direction are sorted in the table, and for each of the valid values, a corresponding direction is assigned.

    Consider that the signals are a type of "gray code" -- that is, a set of "on bits" that uniquely identify a particular signal, but which are highly unlikely to occur accidentally by mechanical artefacts, noise, and other extraneous factors.  Thus, it is just a bunch of on-and-off bits which perhaps may seem even random.  Because the disc codes fit within only 5-bits, we can build a table of 32 values (2^5), each entry representing one of those codes.

    Note that not all codes will be valid (remember, only 16 signals are used for the disc, yet there are 32 possible ones).  Thus, you will see that those entries will yield a zero.  A zero is also returned for valid disc directions that do not fit within the four cardinal points (i.e., the so-called perfect diagonals).

    For reference, the disc signal codes are listed below, taken from the documentation included with P-Machinery.  In reality, you don't have to worry about those magic values at all, for they are abstracted in the "constants.bas" file included with the IntyBASIC SDK.

    The values below are sorted by the disc input as we recognize it:  a value from 0 to 15 indicating the direction that was pressed; starting from the tippity-top of the disc and going clockwise:
    Disc:
    ------- ------- --------
    Input   Code    Signal
    ------- ------- --------
    0       0x04    00000100
    1       0x14    00010100
    2       0x16    00010110
    3       0x06    00000110
    4       0x02    00000010
    5       0x12    00010010
    6       0x13    00010011
    7       0x03    00000011
    8       0x01    00000001
    9       0x11    00010001
    10      0x19    00011001
    11      0x09    00001001
    12      0x08    00001000
    13      0x18    00011000
    14      0x1C    00011100
    15      0x0C    00001100
    ------- ------- --------

    If we sort by the binary codes, instead of disc direction, we get the following list:
    ; SORTED BIT PATTERN
    ; Signal    Input       Code Decimal
    ;-------    ------      ---- -------
    00000001         8      0x01    1
    00000010         4      0x02    2
    00000011         7      0x03    3
    00000100         0      0x04    4
    00000110         3      0x06    6
    00001000         12     0x08    8
    00001001         11     0x09    9
    00001100         15     0x0C   12
    00010001         9      0x11   17
    00010010         5      0x12   18
    00010011         6      0x13   19
    00010100         1      0x14   20
    00010110         2      0x16   22
    00011000         13     0x18   24
    00011001         10     0x19   25
    00011100         14     0x1C   28


    Look at the "Decimal" column, which indicates the sorted input code in decimal.  Notice that there are gaps in the sequence from 0 to 31.  That's because we are using only 16 signals out of the 32 possible codes that could fit in the 5 bits that the hand-controller uses for disc input.

    We can build the full table plugging up those "missing" entries with zeros:

    ; SORTED BIT PATTERN
    ; Signal    Input       Code Decimal    Table Value
    ;-------    ------      ---- -------    -----------
                            ----     0         DATA 0
    00000001       8        0x01     1         DATA 3
    00000010       4        0x02     2         DATA 2
    00000011       7        0x03     3         DATA 3
    00000100       0        0x04     4         DATA 1
                            ----     5         DATA 0
    00000110       3        0x06     6         DATA 2
                            ----     7         DATA 0
    00001000      12        0x08     8         DATA 4
    00001001      11        0x09     9         DATA 4
                            ----    10         DATA 0
                            ----    11         DATA 0
    00001100      15        0x0C    12         DATA 1
                            ----    13         DATA 0
                            ----    14         DATA 0
                            ----    15         DATA 0
                            ----    16         DATA 0
    00010001       9        0x11    17         DATA 3
    00010010       5        0x12    18         DATA 2
    00010011       6        0x13    19         DATA 0
    00010100       1        0x14    20         DATA 0
                            ----    21         DATA 0
    00010110       2        0x16    22         DATA 0
                            ----    23         DATA 0
    00011000      13        0x18    24         DATA 4
    00011001      10        0x19    25         DATA 0
                            ----    26         DATA 0
                            ----    27         DATA 0
    00011100      14        0x1C    28         DATA 1
                            ----    29         DATA 0
                            ----    30         DATA 0
                            ----    31         DATA 0


    That's how the "controller_direction" table is constructed:  It is just the set of 32 values that we map to each of the possible 32 input codes provided by the hardware, filling in any gaps or invalid values with zeroes.

    So, why did we use those table values specifically?  Because we are mapping the 16 possible directions of the disc into the four cardinal points.  That is, the 16 values, starting from zero at the top, and going clockwise on the disc, are converted into the values 1, 2, 3, and 4 representing North, East, South, and West, respectively (reserving zero for invalid values that we will ignore).

    Below is an illustration.  I've shaded the disc valid areas showing.  As you correctly pointed out in your original message, we are not only considering the specific, perfectly aligned direction for each cardinal point; but we've instead widen the range accepted for each direction to compensate for typical human inaccuracy when pressing the disc.  The perfect-diagonals then act as the boundaries between each direction.

    ;;                        1   1   1                                         ;;
    ;;                   0     N..N..N     0                                    ;;
    ;;                     x  ';:::::;'  x         Legend:                      ;;
    ;;                4        ';:::;'        2    -------------                ;;
    ;;                   W:,     ':'     ,:E       N: North    1                ;;
    ;;                   ::::-_   v   _:::::       E: East     2                ;;
    ;;               4  W::::::"> + <"::::::E  2   S: South    3                ;;
    ;;                   :::::-'  ^  '-:::::       W: West     4                ;;
    ;;                   W:-"    ,:,    "-:E       x: Invalid  0                ;;
    ;;                4        .;:::;.        2                                 ;;
    ;;                     x  ,:::::::,  x                                      ;;
    ;;                   0     S''S''S     0                                    ;;
    ;;                        3   3   3                                         ;;


    The same applies to the next question.

 

I hope this helps. :)

 

     -dZ.

 

Edited by DZ-Jay
  • Like 1
Link to comment
Share on other sites

wow.  thank you for the long, detailed answer.  I don't quite get all of it yet, but it's from my lack of understanding and not your lack of explanation.

 

It will take a bit to get this to sink it, but I do get it at a basic level. And now I have a great reference guide. Some of this stuff really gets into computer science. I used to always joke that programming video games is the most complex code to write......maybe there is some truth to that.  :)

 

I'm going to crack open a cold beer a little later and go through this again and keep working on my game.  It's a lot of work, but also a lot of fun.

 

have a great weekend

  • Like 1
Link to comment
Share on other sites

2 hours ago, Mik's Arcade said:

wow.  thank you for the long, detailed answer.  I don't quite get all of it yet, but it's from my lack of understanding and not your lack of explanation.

 

It will take a bit to get this to sink it, but I do get it at a basic level. And now I have a great reference guide. Some of this stuff really gets into computer science. I used to always joke that programming video games is the most complex code to write......maybe there is some truth to that.  :)

 

I'm going to crack open a cold beer a little later and go through this again and keep working on my game.  It's a lot of work, but also a lot of fun.

 

have a great weekend

 

Not a problem, it is my pleasure.  I may have made some assumptions as to your level of understanding when I wrote my response, and I am sorry for that.  So if anything at all is not clear, please feel free to ask, and I'll do my best to provide a more suitable answer.  :)

 

      -dZ.

Link to comment
Share on other sites

It just occurred to me that my response relies on some core details about the console and the hand-controller which may not be completely clear to you.  So, let us get down to basics.

 

The Intellivision hand-controllers have the following control surfaces:

  • 12 key keypad (0-9, Enter, clear)
  • 16-direction disc
  • 3 action buttons (physically four, but two of them are hard-wired to the same lines, making them act as one)

 

That's a total of 31 individual control surfaces, each one requiring a unique signal in order for the program to distinguish them.  In an ideal situation, each control surface would have a dedicated line to uniquely identify its input unambiguously.  In such an ideal scenario, there is no real need for any fancy decoding function:  if the signal for, say, action button #3 is pressed, you know exactly what it is and where it came from, and can handle it without issues.

 

However, due to technical considerations, costs, and other factors, the Intellivision hand-controllers have only 8 wires on which to send signals to the I/O chip (which, by the way, takes the role as the sound chip as well).

 

So, if we do the math, that's 31 individual signals to be sent across 8 wires, and somehow they need to be uniquely distinguished by the program in order for them to be useful.

 

So the hardware engineers came up with a neat solution:  they assigned each of the 31 control signal a unique combination of those 8 wires.  Because those 8 wires eventually feed into a digital circuit, they are turned into an 8-bit byte.  Thus, you could consider each one of those 31 signals a unique 8-bit binary code.

 

Nonetheless, they are still 31 signals all using the same 8 bits, and so there is obviously some overlap between them.


The good news is that the original hardware engineers thought of all that, and chose wire combinations that not only were unique to each control signal, but that each had a unique set of bits that were used for itself.  That means that you could easily tell the difference between, say, a keypad key and a disc press, because there is at least one bit that only one of them uses.  Therefore, the presence of that bit or bits guarantees (to some degree) that the signal is one or the other.

 

Notwithstanding, the fact that there are 31 distinct codes spread around those 8 bits forces us to be a bit more creative in our input handling, and requires us to decode the input signal into a usable value.

 

The code above exploits the fact that some combination of bits apply only to one input or another, and uses this to detect the disc vs. the action buttons vs. the keypad.  That's what the "AND $E0" and "AND $1F" expressions do:  they mask one set of bits to check whether they are set or not, and from this it can be gleaned which type of signal it is.

 

If you do not know what "bit masking" is or how it works, or need a refresher on your Boolean logic, no worries!  Just check out this post I made some years ago on the subject for some guidance.

 

So that's what those expressions do.

 

Now, because the input signal is 8-bits long, that means that there are in theory 256 unique values that the controller could provide.  In practice, as we already established, there are only 31; but they are not neatly packed -- they are actually spread out throughout all those 8 bits in order to guarantee their uniqueness.

 

Still, 256 values means that we could easily build a lookup table with every single possible value and just use whatever the controller gives us as an index into the table.

 

That would work, of course.  However, I hope you can appreciate how exceedingly inefficient it would be if every time there is a new signal we go and search through 256 possible entries, comparing each one, to see which one we have.  On a resource-constrained machine running a fast-paced gain, this can be horrendously slow.

This is why we do that special masking and try to figure out which are the unique set of bits that apply to one control type versus the other:  because if we narrow it down to, say, a disc input, or a keypad input, our search space becomes a lot smaller.  :)

 

Let me know if any of this is not clear, or if you have any other questions.

 

     -dZ.

Edited by DZ-Jay
Link to comment
Share on other sites

now that REALLY helps even more.  thanks.

 

To be honest, it's a bit above my skill level, but now it makes more sense, especially the bit masking.

 

I literally took BASIC and PASCAL programming in high school, threw a bit of FORTRAN and C in there during college, and over the years picked up tidbits here and there. This gets into engineering and computer science, which is great!  I'd rather learn more about it than just accept that it works and move on because I'll never be able to create really cool games without knowing how this all works.  By watching youtube videos, I was able wire a pair of Sanwa joysticks and 14 buttons into a USB encoder so I can now play Street Fighter II on the bartop arcade I built running off my raspberry pi build just like it was in the arcades. It was very hard but I got it eventually, so I think I'll have an "aha" moment on this as well. My goal is to add some of my own Intellivision games onto my bartop for fun.

 

thanks again for taking the time to write up such a detailed explanation of things to fill in the gaps. I think Oscar's book takes an assumption that you know a bunch of assembly knowledge under your belt before tackling the IntyBasic.

 

this is all great stuff......so much fun to tinker with. Can't wait to delve into music and using IntyColor to make better graphics, but I need to get this code mastered first. I can control the movement sprites all around the screen now, so I am making progress. Half the fun is tinkering with stuff to see what it does and how you can make use of it...haha

Link to comment
Share on other sites

7 hours ago, Mik's Arcade said:

now that REALLY helps even more.  thanks.

 

To be honest, it's a bit above my skill level, but now it makes more sense, especially the bit masking.

 

I literally took BASIC and PASCAL programming in high school, threw a bit of FORTRAN and C in there during college, and over the years picked up tidbits here and there. This gets into engineering and computer science, which is great!  I'd rather learn more about it than just accept that it works and move on because I'll never be able to create really cool games without knowing how this all works.  By watching youtube videos, I was able wire a pair of Sanwa joysticks and 14 buttons into a USB encoder so I can now play Street Fighter II on the bartop arcade I built running off my raspberry pi build just like it was in the arcades. It was very hard but I got it eventually, so I think I'll have an "aha" moment on this as well. My goal is to add some of my own Intellivision games onto my bartop for fun.

 

thanks again for taking the time to write up such a detailed explanation of things to fill in the gaps. I think Oscar's book takes an assumption that you know a bunch of assembly knowledge under your belt before tackling the IntyBasic.

 

this is all great stuff......so much fun to tinker with. Can't wait to delve into music and using IntyColor to make better graphics, but I need to get this code mastered first. I can control the movement sprites all around the screen now, so I am making progress. Half the fun is tinkering with stuff to see what it does and how you can make use of it...haha

 

It's my pleasure.  When I started programming for the Intellivision, I was in a similar situation:  I had a computer science and engineering background, but haven't really not done much in several years.  When I joined the community, there was no IntyBASIC, so programming for the Intellivision was all done in Assembly Language -- I was utterly lost!

 

I basically started from scratch, and it was the fine folks in this forum and in the old IntvProg mailing list that provided a lot of insight and assistance to get me to where I am today.  Like you, I am not satisfied with merely "getting it to work"; I must understand how something works as well in order to assimilate it and build upon it as a foundation.  I am just paying it forward, and glad that I have the opportunity to share my experience and knowledge with others. :)

 

Good luck, and do not hesitate to ask anything else.  If I'm not around, I'm sure others will chime in.

 

      -dZ.

Edited by DZ-Jay
  • Like 1
Link to comment
Share on other sites

It's a great answer @DZ-Jay. Thanks.

 

Just would like to point to @Mik's Arcade that the page 50 of Programming Games for Intellivision contains the table for controller values.

 

My procedure to generate the table was to set a 32 bytes table, and index it by the controller values. Choosing 1 as up, 2 as right, 3 as down, and 4 as left. 0 for no movement.

 

As you can see some values are repeated because no player presses exactly the disc when moving straight.

 

Link to comment
Share on other sites

this has been a very helpful topic, thanks DZ-Jay

 

And yes, I have bookmarked page 50 of the book for future reference.  For now I am making simple game so just treating the disc as a d-pad is perfect.

 

I need to get my hands on an intellivision controller and then an adapter so I can plug it into a USB on my laptop and then I can fully test one.  I've seen a few topic on getting the adapter now I just need to find a controller. I may have to just buy an intellivision and take it apart....haha.

 

I should be good for now....at least until I have issues with collision detection...

Edited by Mik's Arcade
Link to comment
Share on other sites

Ok, don't want to pollute the board with a bunch of topics, so hopefully you will see this.

 

Before I commit to this concept, I better make sure what I am doing is the correct way to go.  I'm trying to work on a sort of a Space Armada knockoff for the 2020 contest, though I totally doubt I will make it in time.  I have a few original ideas to add to mix up the formula.

 

However, I'm not sure how I can create a whole screen of space invaders when I am limited to 8 spites. I'm using spites 1 and 2 for the ship and the laser fire alread.

 

This tells that I am going to have to use static bitmaps and place them on the screen.

 

I wrote some nifty code to to do just this, and attached a screen shot of the results.  I suppose I could have just used a SCREEN statement and place the defined BITMAPS that way, but I need to move and remove them.

 

these is a subset of the BITMAPS I have defined

CONST OO = 0
CONST SPRITE_PLAYER = $0807 + 0 * 8    ' Player ship
CONST SPRITE_LASER  = $1804 + 1 * 8    ' Default laser
CONST DEF02              = $0806 + 2 * 8    ' Lvl 1 Shield generator 1
CONST DEF03              = $0806 + 3 * 8    ' Lvl 1 Shield generator 2
CONST DEF04              = $0806 + 4 * 8    ' Platform
CONST DEF05              = $0801 + 5 * 8    ' Invader row 1-2
CONST DEF06              = $0805 + 6 * 8    ' Invader row 3-4
CONST DEF07              = $0806 + 7 * 8    ' Invader row 5-6
CONST DEF08              = $0803 + 8 * 8    ' Invader row 7-8

 

     DEFINE 0,5,lvl_bitmaps

     DEFINE 5,4,invader_bitmaps
 

I do use a SCREEN card to lay out some static objects in the lvl_bitmaps

 

I use sprites to place the ship in it's starting location, and already have the code working to move the ship from left to right and shoot.

 

This is the code that fills the screen with enemies (I will probably use less..this is too much!)

 

'
' Enemies DEF05-DEF08 BITMAPS - 2 rows of 9 enemies each, 4 different enemy types
'
start_card = 21
FOR a = 5 to 8                                                 '  Defined Enemy Bitmaps 5-8
   FOR b = 1 to 2                                              '  Create 2 Rows of Same Enemy
      FOR c = 1 to 9                                           '  9 copies of the enemies per row 
         #backtab(start_card) = $0801 + a * 8     ' this works, but how can I get the different colors in here (don't want to hard code the $0801)
         start_card = start_card + 2                     ' Add a space in between each enemy
      NEXT c
      start_card = start_card + 2                        ' Pushes to the start of next row
   NEXT b
NEXT a                                                          ' Is this code even needed? Should I just put them directly on a SCREEN layout?

 


 

Obviously, I will need all kinds of code in the background to keep track off, move, and then of course deal with ships actually them colliding with laser fire, and I know that COL only works with spites so I would have to do it the sprite location and check if an enemy occupies the same space...ugh.

 

Forgive my primitive graphics, I'm dealing with music, sound and IntyColor last.  I want to build a game first.

 

Am I totally off base here? Should I just plop them on the screen layout and still use #backtab to control and remove them?  Is there some way to build an array of sprites and do it that way?  I'd still get close to the limit really fast.... 

 

Please don't spent too much time on this, just nudge me in the right direction. I have fun in between all the curse words....haha.

 

Thanks in advance  :)

 

Mik

 

 

inv.jpg

Edited by Mik's Arcade
Link to comment
Share on other sites

$0801 is a magic number here, where the last digit sets the foreground colour 0-7, the 8 enables GRAM and the other bits set the background colour according to a layout you can read about here. One way to do it would be to use DATA statements instead of defining constants:

 

invaders: DATA $0801 + 5*8, $0805 + 6*8, $0806 + 7*8, $0803 + 8*8

 

and in your loop use FOR a = 0 TO 3 and #BACKTAB(start_card) = invaders(a). As a bonus, you save some real time calculations for what looks to be static data. Not tested, but I think it should work out as expected.

 

Edit: Actually, you might find that adding #BACKTAB(start_card+20) = invaders(a) and removing the FOR b loop, adding start_card = start_card + 40 after the FOR c loop, would be just as effective, though a little less dynamic as it would hard code that each type of invader is displayed for exactly two rows.

Edited by carlsson
Link to comment
Share on other sites

17 hours ago, carlsson said:

$0801 is a magic number here, where the last digit sets the foreground colour 0-7, the 8 enables GRAM and the other bits set the background colour according to a layout you can read about here. One way to do it would be to use DATA statements instead of defining constants:

 

invaders: DATA $0801 + 5*8, $0805 + 6*8, $0806 + 7*8, $0803 + 8*8

 

and in your loop use FOR a = 0 TO 3 and #BACKTAB(start_card) = invaders(a). As a bonus, you save some real time calculations for what looks to be static data. Not tested, but I think it should work out as expected.

 

Edit: Actually, you might find that adding #BACKTAB(start_card+20) = invaders(a) and removing the FOR b loop, adding start_card = start_card + 40 after the FOR c loop, would be just as effective, though a little less dynamic as it would hard code that each type of invader is displayed for exactly two rows.

Oh, this is great info, thanks.  I really need to study up on the STIC...understanding the "magic numbers" as you call them is the key to unlocking intyBasic.

 

I'm fine with using the DATA statements instead of defining constants (DEF05-DEF08), but I guess I am missing one last piece of understanding.....

 

Do I still need to do this?

 DEFINE 5,4,invader_bitmaps

 

in order to do:

invaders: DATA $0801 + 5*8, $0805 + 6*8, $0806 + 7*8, $0803 + 8*8

or is it

invader_bitmaps: DATA $0801 + 5*8, $0805 + 6*8, $0806 + 7*8, $0803 + 8*8  ?

 

It seems like DATA is also creating a table of the 8x8 BITMAPS values I have after the invader_bitmaps: LABEL, is that's what is going on?

 

I'll test around and see what happens....

 

I had thought that the DEFINE takes the, in this case, the 4   8x8 BITMAP statement and assigns them to the +5, +6, +7, +8  (we start at 5 because I already defined 4 others)

 

This is great stuff.  At least is seems like my idea to the static images will work.  Now I will just have to code a way to make them move, keep track of their position and of course clear them when shot.  I'm assuming COL will work with spite hitting a non-sprite.

 

I've got plenty to work on....thanks for your help!!

 

Link to comment
Share on other sites

16 hours ago, carlsson said:

Yes, you still need DEFINE to create the custom graphics. The DATA statements would only contain constants, or pointers if you wish to call them, containing the combination of which GRAM, which background and foreground colours each type of invader has.

thanks my friend!   I've been studying up a bit more, so hopefully my next questions will be more intelligent!  :)

 

It seems like using the CONST over the DATA statements is advantageous for a layout of cards for use with SCREEN.

 

This stuff is starting to come to me.

 

Maybe I will have to wait until the 2021 contest to get a complete game done....haha.

 

I wonder if the 2020 contest will have enough entries....

 

Link to comment
Share on other sites

Sure, you can combine the two so CONST INV1 to INV4 would define the combination of GRAM, foreground and background for the invaders, then use those both in a long row of DATA statements intended to use with SCREEN to plot all at the same time, as well as using in a shorter array of DATA statements like I showed to use to index through for each line if you prefer to plot those manually, or just need to replenish a row of invaders after the last one was shot down. Sure, a regular Space Invaders game doesn't work in that way but some other games like Clowns/Circus which is more like a Breakout style game replenish the balloons on a row when all are shot down and in that respect the invaders and balloons have a little in common.

Link to comment
Share on other sites

what I plan to do is just put a couple on the screen for now, and then getting the rest of the game to function, ie figure out how to get them to move, shoot, and then detect when they collide with laser fire.

 

then, I will just keep adding more until I run out of resources.....haha.  If I make good progress then maybe, just maybe I can have a working game for the contest. I know I will probably have the worst game, but I'd love to at least submit the code so the judges can critique the code and them offer additional tips.

Link to comment
Share on other sites

  • 3 weeks later...

Ahh...this stuff is tough. I feel like I get 1 step forward and then go 2 steps back.  

 

But, hopefully this is simple one...I just want to test when a sprite hits a stationary object on the screen (not another sprite)

 

Ok, I'm using 

 

IF COL1 AND $0100 THEN               ' Sprite hits a BITMAP on the screen, ie it detect the background color.
     #backtab(???) = 0     ' Clear out the BITMAP sitting on this card
     shot_x = 0
     shot_y = 0
     #score = #score + 1
END IF
 

I just can't seem to figure out what to pass into #backtab to represent what CARD I am on.  I've tried many options. I know that the 'F' parameter bits 11-3 of a sprite is supposed to be the location but I can't get that to work, or don't quite get it.

 

I get that the cards are located in the STIC from $0200 to $02EF, but how can you fetch the value the SPRITE is on when this collision happens?  I feel like this should be an easy answer......but I can't figure it out!!!   I've thought about using POKE, READ , AT...can't come up with a winning value...haha

 

The rest of this code works.  The ship fires a shot, and when it collides with an BITMAP on the screen, it triggers this code and wipes out the bullet.

 

thanks all...

 

Link to comment
Share on other sites

I'll tell you what I do to get the #backtab position I want, when I want it.  I know this is long, so take only what you need from this and don't get too intimidated by the details.

 

I usually keep variables for sprites' X position, Y position, and "cardtab" position.  In cases where sprites are made to move from one tile to the next before changing position, that's easy: just initialize that value and when moving, update it as follows after doing rudimentary boundary checks:

NW: card_pos = card_pos - 21
 N: card_pos = card_pos - 20
NE: card_pos = card_pos - 19
 W: card_pos = card_pos - 1
 E: card_pos = card_pos + 1
SW: card_pos = card_pos + 19
 S: card_pos = card_pos + 20
SE: card_pos = card_pos + 21

For strategy games, that's usually fine.  But for action-based games, that usually won't do.  So I fetch the value of card_pos based on x_pos and y_pos when needed.  It would look something like this:

card_pos = ((x_pos + 4) / 8) + (((y_pos + 4) / 8) * 20) - 21

To break it down:

  • The instances of "+4" in the code would be to see which tile on each axis does the majority of the sprite overlap.  That might not work for you, so you might need to do different calculations based on which way the player is moving.
  • The instances of "/8" you probably can guess: because background cards are 8x8 pixels.
  • You can probably also guess what "*20" is about: because there are 20 cards in a row, so moving up or down a row changes the index by 20.
  • Finally, to understand "-21", consider the example where the sprite is contained within the card in the top left corner of the screen (assuming you're not using border extensions for scrolling).  Its x_pos and y_pos would both have a value of 8.  That's because the values 1-7 have the sprite partially off the screen, and at 0 are completely off.  But in this case the #backtab value we want is 0.  So we have to subtract out (1 * 1) for the x_pos and (20 * 1) for the y_pos.

Fun fact: in my prototype of FUBAR, which I wrote entirely in Assembly Language, what I did to fetch the cardtab value was:

  1. Get the y_pos from memory into a register
  2. Bit-shift that register one position to the right
  3. Copy to another register
  4. Bit-shift one of the registers two positions to the left
  5. Add them together (at this point one of the registers has (y_pos * 2.5), which is 20 / 8 )
  6. Use the other register to store x_pos from memory
  7. Bit-shift that register three positions to the right (takes two instructions, since you can only shift one or two positions at a time)
  8. Add those registers together, now giving us (y_pos * 2.5) + (x_pos / 8 )
  9. Subtract 21.

There's a little more to it than that, since I use Colored Squares Mode to break each tile up into four quadrants of solid colors, and so I move the sprites in 4-pixel increments.  The above algorithm gives us the SE quadrant that the sprite overlaps.  To find out which tiles to look at for the other quadrants depends on fetching Bit 10 (since the x_pos and y_pos values are 16-bit, using only the 8 high bits for actual positioning, leaving the 8 low bits for "sub-pixel precision").  But you get the idea.

Link to comment
Share on other sites

7 hours ago, Zendocon said:

I'll tell you what I do to get the #backtab position I want, when I want it.  I know this is long, so take only what you need from this and don't get too intimidated by the details.

 

I usually keep variables for sprites' X position, Y position, and "cardtab" position.  In cases where sprites are made to move from one tile to the next before changing position, that's easy: just initialize that value and when moving, update it as follows after doing rudimentary boundary checks:


NW: card_pos = card_pos - 21
 N: card_pos = card_pos - 20
NE: card_pos = card_pos - 19
 W: card_pos = card_pos - 1
 E: card_pos = card_pos + 1
SW: card_pos = card_pos + 19
 S: card_pos = card_pos + 20
SE: card_pos = card_pos + 21

For strategy games, that's usually fine.  But for action-based games, that usually won't do.  So I fetch the value of card_pos based on x_pos and y_pos when needed.  It would look something like this:


card_pos = ((x_pos + 4) / 8) + (((y_pos + 4) / 8) * 20) - 21

To break it down:

  • The instances of "+4" in the code would be to see which tile on each axis does the majority of the sprite overlap.  That might not work for you, so you might need to do different calculations based on which way the player is moving.
  • The instances of "/8" you probably can guess: because background cards are 8x8 pixels.
  • You can probably also guess what "*20" is about: because there are 20 cards in a row, so moving up or down a row changes the index by 20.
  • Finally, to understand "-21", consider the example where the sprite is contained within the card in the top left corner of the screen (assuming you're not using border extensions for scrolling).  Its x_pos and y_pos would both have a value of 8.  That's because the values 1-7 have the sprite partially off the screen, and at 0 are completely off.  But in this case the #backtab value we want is 0.  So we have to subtract out (1 * 1) for the x_pos and (20 * 1) for the y_pos.

Fun fact: in my prototype of FUBAR, which I wrote entirely in Assembly Language, what I did to fetch the cardtab value was:

  1. Get the y_pos from memory into a register
  2. Bit-shift that register one position to the right
  3. Copy to another register
  4. Bit-shift one of the registers two positions to the left
  5. Add them together (at this point one of the registers has (y_pos * 2.5), which is 20 / 8 )
  6. Use the other register to store x_pos from memory
  7. Bit-shift that register three positions to the right (takes two instructions, since you can only shift one or two positions at a time)
  8. Add those registers together, now giving us (y_pos * 2.5) + (x_pos / 8 )
  9. Subtract 21.

There's a little more to it than that, since I use Colored Squares Mode to break each tile up into four quadrants of solid colors, and so I move the sprites in 4-pixel increments.  The above algorithm gives us the SE quadrant that the sprite overlaps.  To find out which tiles to look at for the other quadrants depends on fetching Bit 10 (since the x_pos and y_pos values are 16-bit, using only the 8 high bits for actual positioning, leaving the 8 low bits for "sub-pixel precision").  But you get the idea.

wow.

 

I'm surprised this needs to be coded, so thanks I know I can use this.  Since I'm doing a simple fixed shoot'em up, the calculation will be easy because it simply goes srtaight up, but I can also use the rest for future reference.

 

Since the sprite collide with a background, I'm rather surprised that the card number cannot simply be derived by the X and Y position of the sprite when this event occurs.  But, since I already have the X and Y position of sprite stored, I just need one more for the card number. It is supposed to be part of the 'F' parameter of the sprite, but don't know how to get it..

 

thanks

Edited by Mik's Arcade
Link to comment
Share on other sites

26 minutes ago, Mik's Arcade said:

wow.

 

I'm surprised this needs to be coded, so thanks I know I can use this.  Since I'm doing a simple fixed shoot'em up, the calculation will be easy because it simply goes srtaight up, but I can also use the rest for future reference.

 

Since the sprite collide with a background, I'm rather surprised that the card number cannot simply be derived by the X and Y position of the sprite when this event occurs.  But, since I already have the X and Y position of sprite stored, I just need one more for the card number. It is supposed to be part of the 'F' parameter of the sprite, but don't know how to get it..

 

thanks

 

Actually, it can.  You just take the sprite's X and Y coordinates and divide by 8 (there's eight pixels per background card on each axis), then use that to compute the background card offset from the top of the BACKTAB.  This can serve as an index in the BACKTAB array.

 

In pseudo-code:

   col = sprite_x / 8
   row = sprite_y / 8
   offset = (row * Y) + X

 

You then can use POKE to update the card value in that location.

 

Keep in mind that the sprite coordinates give you the top-left corner of the sprite block, not its center.  This may or may not be important when you translate it into the background card underneath.  This is why Zendocon adds "+4" to each coordinate before dividing by 8.  It would then compute the offset under the center of the sprite.

 

   col = (sprite_x + 4) / 8
   row = (sprite_y + 4) / 8
   offset = (row * Y) + X

 

Edited by DZ-Jay
Link to comment
Share on other sites

4 hours ago, DZ-Jay said:

 

Actually, it can.  You just take the sprite's X and Y coordinates and divide by 8 (there's eight pixels per background card on each axis), then use that to compute the background card offset from the top of the BACKTAB.  This can serve as an index in the BACKTAB array.

 

In pseudo-code:


   col = sprite_x / 8
   row = sprite_y / 8
   offset = (row * Y) + X

 

You then can use POKE to update the card value in that location.

 

Keep in mind that the sprite coordinates give you the top-left corner of the sprite block, not its center.  This may or may not be important when you translate it into the background card underneath.  This is why Zendocon adds "+4" to each coordinate before dividing by 8.  It would then compute the offset under the center of the sprite.

 


   col = (sprite_x + 4) / 8
   row = (sprite_y + 4) / 8
   offset = (row * Y) + X

 

awesome, this is what i needed...I think I might have seen this code before in some other programs, but didn't understand it, but your explanation makes send....thanks

Link to comment
Share on other sites

10 hours ago, Mik's Arcade said:

awesome, this is what i needed...I think I might have seen this code before in some other programs, but didn't understand it, but your explanation makes send....thanks

Well, I just made a mess with that pseudo-code, and it doesn't help that you can't edit a post after a few hours. :(

 

Here's what I meant to say:

   col = (sprite_x + 4) / 8
   row = (sprite_y + 4) / 8
   offset = (20 * row) + col

There are 20 columns per row, so once you compute the row and column coordinates from the sprite's position, you just multiple the row by 20 and add the column.

 

Sorry about that.

 

    -dZ.

Link to comment
Share on other sites

10 hours ago, DZ-Jay said:

Well, I just made a mess with that pseudo-code, and it doesn't help that you can't edit a post after a few hours. :(

 

Here's what I meant to say:


   col = (sprite_x + 4) / 8
   row = (sprite_y + 4) / 8
   offset = (20 * row) + col

There are 20 columns per row, so once you compute the row and column coordinates from the sprite's position, you just multiple the row by 20 and add the column.

 

Sorry about that.

 

    -dZ.

no worries at all, you are super helpful

  • Like 1
Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...