Sergioz82 Posted December 30, 2021 Share Posted December 30, 2021 (edited) Hi, I'm working a "proof of concept" code for a game I have in mind. I reached an interesting result however I ran into some pesky problems that I hope you can help me to address. First, the idea is to have a randomly generated line of blocks that falls from the top of the screen and that is added to the existing lines which are then moved one line down (windows + scrldn routines). The game over is when the player at the bottom is hit by the blocks. The player can shoot a projectile toward the blocks. Now the annoying part: 1) The block area is 10x20 characters, that is ten characters per line that can be either occupied by blocks or empty. To generate the line string I use a 1 to 10 for loop with a RND call that decides if the character is empy or not. Problem is, the cycle slows down the game loop and gives me problems with hit detection of the projectile sprite and the controls reactivity. So first question is: is there a faster way to generate this random string? 2) The lines are drawn with vwrite. This gives me a problem with the 96 offset: I use character 140 for blocks and without offset their image is replaced with commas. If I add the offset the problem is solved but.. I use vread for collision detection and with the offset the returned string is empty so no collision. How can I align the results? One idea is to use display at in place of vwrite. That way the vread routine should be fine. Is display at slower than vwrite? 3)Now the most painful part, the collision detection.. I can't use a sprite for each block so I simply check with vread what's in position() of the projectile (top-center of the sprite). The idea is that once the sprite enters a block area it returns me the char 140. Problem is: position returns me the pixels, but vwrite needs a character position. I have to convert the data from position into charater with int(row/8) and int(col/8) before calling vread. It seems to do the trick but I don't really like it. Is there a more elegant (and precise) solution? Thanks in advance Edited December 30, 2021 by Sergioz82 Title change Quote Link to comment Share on other sites More sharing options...
Retrospect Posted December 30, 2021 Share Posted December 30, 2021 ! Dimension two arrays for characters to pixels conversions to help with sprite collision 10 DIM SX(24),SY(32) ! but you won't need ALL of this array it's just the full screen 20 N=0 :: FOR L=1 TO 24 :: SX(L)=N :: N=N+8 :: NEXT L :: SX(1)=1 30 N=0 :: FOR L=1 TO 32 :: SY(L)=N :: N=N+8 :: NEXT L :: SY(1)=1 Now , if you have a player missile that goes up the screen, you can use this, let's say PM=0 or PM=1 for player missile on or off, and we'll use a GCHAR to detect a hit; but only do something IF there is a hit; Example line of code could be; 4000 IF PM=1 THEN 4010 ELSE 4099 4010 PMX=PMX-1 ! Player missile's X position decremented by one (8 pixels, one char) 4020 IF PMX<1 THEN 4030 ELSE 4040 4030 PM=0 :: CALL DELSPRITE(#1) :: GOTO 4099 ! Player missile turned OFF if it's got to the top of screen 4040 CALL LOCATE(#1,SX(PMX),SY(PMY)) ! Place the player missile sprite in it's new position 4050 CALL GCHAR(PMX,PMY,CH) :: IF CH>32 THEN 4060 ELSE 4099 ! Checks for anything 4060 GOTO 4100 ! Branch to new code instead of bogging this code down 4099 RETURN This code assumes your block is character 104, but in reality it could be anything you've coded it for; 4100 IF CH=104 THEN 4110 ELSE 4199 4110 CALL LINK("DISPLY",PMX,PMY," ") ! Blank space where the block was 4120 SCORE=SCORE+10 ! Or whatever you want your score to be? 4130 GOSUB 4200 ! Update the score (this is depending on how you do it though) 4140 GOTO 4030 ! Back to the original sub but the part that gets rid of the player missile 4199 goto 4099 ! Because we gosubbed to 4000, otherwise there'll be issues This code just shows the score; 4200 CALL LINK("DISPLY",24,1,STR$(SCORE)) :: RETURN I hope this gives you some help. It's cumbersome with 8 pixel movement of player sprite but in fact appears faster and is deadly accurate when it comes to sprite vs character detection. It was used in Avaris. I'm not sure how you'd be doing the blocks falling into the line thing, but this'll help with the player missile bit? 3 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted December 30, 2021 Author Share Posted December 30, 2021 Hi Retrospect, thanks for the reply. This is a very interesting solution! I'm going to implement it in the game and I'll let you know. About the blocks, they work like a reverse tetris: the projectile stops once it hits a block in the line above. Then the sprite is deleted and replaced with a block character. When the row is full it is cancelled (I still have to implement this part) and all the rows below shift up one line. So I don't really want for the above blocks to fall into the line, that's convenient for me 2 Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted December 30, 2021 Share Posted December 30, 2021 6 hours ago, Sergioz82 said: 1) The block area is 10x20 characters, that is ten characters per line that can be either occupied by blocks or empty. To generate the line string I use a 1 to 10 for loop with a RND call that decides if the character is empy or not. Problem is, the cycle slows down the game loop and gives me problems with hit detection of the projectile sprite and the controls reactivity. So first question is: is there a faster way to generate this random string? You might try IRND to create the random numbers. From the manual: CALL LINK(“IRND”,limit,variable[,.........]) Returns a random number as an integer from 0 to limit-1. The results are the same as variable=INT(limit*RND) but IRND is much faster than the slow XB random number generator. Up to 8 random numbers can be created with one call to IRND. 2) The lines are drawn with vwrite. This gives me a problem with the 96 offset: I use character 140 for blocks and without offset their image is replaced with commas. If I add the offset the problem is solved but.. I use vread for collision detection and with the offset the returned string is empty so no collision. How can I align the results? One idea is to use display at in place of vwrite. That way the vread routine should be fine. Is display at slower than vwrite? i'm not quite sure what you mean here. The following program creates a 10 byte long string made up of ASC 140 characters. It prints it to the screen using VWRITE. It reads it from the screen using VREAD. Then line 20 shows that the length of the string is 10 bytes and the first character is ASC 140 (all the rest will be 140 as 5 A$=RPT$(CHR$(140),10) 10 CALL LINK("VWRITE",1,A$) 15 CALL LINK("VREAD",1,10,A$) 20 PRINT LEN(A$),ASC(A$) You might find DISPLY to be useful. The following program sets a 4 column wide window at the left side of the screen, then uses DISPLY to print A-P within the window. 100 CALL LINK("WINDOW",1,1,24,4) 110 CALL CLEAR 120 FOR I=1 TO 20 130 CALL LINK("DISPLY",I,1," ABCDEFGHIJKLMNOP",0) 140 NEXT I 150 GOTO 110 6 hours ago, Sergioz82 said: 1 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted December 30, 2021 Author Share Posted December 30, 2021 (edited) Hi Senior_falcon, thank you very much for your replies. About point #1 and the suggestion of using disply I'll surely give it a try. About the second point, I see commas in your code as well. Please consider this code: 100 CALL CLEAR 110 CALL CHAR(140,"0000545474545400") 120 ST$="" 130 FOR I=1 TO 10 140 IF RND<=0.5 THEN ST$=ST$&CHR$(140)ELSE ST$=ST$&CHR$(32) 150 NEXT I 160 CALL LINK("VWRITE",11,ST$) 170 CALL LINK("VREAD",11,10,S$) 180 DISPLAY AT(20,2):S$ 190 GOTO 120 At 150 I see on top of the screen a series of "," in place of my defined character. At 170 I correctly see the defined char. If I change 150 to DISPLAY AT(1,12):ST$ I see the right char, but at 170 I see nothing. If I change 170 to use "VWRITE",609,S$ I see commas as displayed on screen with VWRITE, but if I add the offset at 140 CHR$(140+96) then everything works Edited December 30, 2021 by Sergioz82 Quote Link to comment Share on other sites More sharing options...
Retrospect Posted December 30, 2021 Share Posted December 30, 2021 Just to say, if you are intending to compile this program then line 140 would have to be altered as the compiler is integer. 1 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted December 30, 2021 Author Share Posted December 30, 2021 2 minutes ago, Retrospect said: Just to say, if you are intending to compile this program then line 140 would have to be altered as the compiler is integer. Thanks, I'll keep it in mind 1 Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted December 30, 2021 Share Posted December 30, 2021 1 hour ago, Sergioz82 said: 100 CALL CLEAR 110 CALL CHAR(140,"0000545474545400") 120 ST$="" 130 FOR I=1 TO 10 140 IF RND<=0.5 THEN ST$=ST$&CHR$(140)ELSE ST$=ST$&CHR$(32) 150 NEXT I 160 CALL LINK("VWRITE",11,ST$) 170 CALL LINK("VREAD",11,10,S$) 180 DISPLAY AT(20,2):S$ 190 GOTO 120 At 150 I see on top of the screen a series of "," in place of my defined character. At 170 I correctly see the defined char. If I change 150 to DISPLAY AT(1,12):ST$ I see the right char, but at 170 I see nothing. If I change 170 to use "VWRITE",609,S$ I see commas as displayed on screen with VWRITE, but if I add the offset at 140 CHR$(140+96) then everything works Now I see the problem. It's that pesky screen offset. Normally when strings are put on the screen each character has 96 (>60) added to it. PRINT, DISPLAY AT, CALL LINK("DISPLY"...) all do this automatically. However, VREAD and VWRITE do not use the screen offset. The intention was to make it so you could read or write a series of bytes to/from any location in VDP, not necessarily just to/from the screen. If you are reading from the color table or character patterns you would not want to use the offset. From my end, the easiest fix would be to add that information about the screen offset to the manual. Another possibility would be to apply the screen offset only when reading from/to the screen but not from other locations in VDP. At least for now, you have to add the screen offset yourself: 140 IF RND<=0.5 THEN ST$=ST$&CHR$(236)ELSE ST$=ST$&CHR$(128) and to be compiler friendly you could do this: 140 IF RND*2<1 THEN ST$=ST$&CHR$(236)ELSE ST$=ST$&CHR$(128) Now when line 170 reads the string the offset will already be included in the string. Line 180 DISPLAY AT will add the offset again, and so it will not be displayed correctly, but I think you have enough information do deal with this. 2 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted December 31, 2021 Author Share Posted December 31, 2021 19 hours ago, senior_falcon said: Now I see the problem. It's that pesky screen offset. Normally when strings are put on the screen each character has 96 (>60) added to it. PRINT, DISPLAY AT, CALL LINK("DISPLY"...) all do this automatically. However, VREAD and VWRITE do not use the screen offset. The intention was to make it so you could read or write a series of bytes to/from any location in VDP, not necessarily just to/from the screen. If you are reading from the color table or character patterns you would not want to use the offset. From my end, the easiest fix would be to add that information about the screen offset to the manual. Another possibility would be to apply the screen offset only when reading from/to the screen but not from other locations in VDP. At least for now, you have to add the screen offset yourself: 140 IF RND<=0.5 THEN ST$=ST$&CHR$(236)ELSE ST$=ST$&CHR$(128) and to be compiler friendly you could do this: 140 IF RND*2<1 THEN ST$=ST$&CHR$(236)ELSE ST$=ST$&CHR$(128) Now when line 170 reads the string the offset will already be included in the string. Line 180 DISPLAY AT will add the offset again, and so it will not be displayed correctly, but I think you have enough information do deal with this. Understood. Thank you for the explanation. Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted December 31, 2021 Author Share Posted December 31, 2021 (edited) @Retrospect @senior_falcon Restrospect, I had to make a double call to a sub with your collision routine. I had the problem that it worked half the time. It happened because the scroll down block -> check collision -> move missile up sequence in the game loop works only when the space (in characters) between missile and block is even. When it's odd the block goes down and when the check sub is called the gchar read a void space. My solution is check collision -> scroll down block -> check collision -> move missile. Probably there's a better solution, meanwhile I'm glad it works and I can proceed with the development. Senior_falcon, I used the IRND function for the first string and now the block movement is more fluid. Edited December 31, 2021 by Sergioz82 3 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted January 4, 2022 Author Share Posted January 4, 2022 Game is proceeding, I have added graphics and some of the gameplay mechanics I had in mind. It will need 32K expansion + assembly compiler but I knew that and I'm confident once compiled it will be a fast enough game. I have another couple of questions: 1) I realized I'm adding up stuff without exactly considering the memory limitation . I have a general idea of such limit but I'm not doing a precise calculation. How do you guys use to work? Have you got a method to plan the requirements? 2) I already tried a compiled version and it runs too fast and it's unplayable. How do I synchronize all the events in the game loop at the pace I want? Is there some documentation about timing techniques? I tried to search the forum but words like timing and timer are very common so I get too many unrelated result. Thanks in advance 1 Quote Link to comment Share on other sites More sharing options...
tmop69 Posted January 4, 2022 Share Posted January 4, 2022 2 hours ago, Sergioz82 said: Game is proceeding, I have added graphics and some of the gameplay mechanics I had in mind. It will need 32K expansion + assembly compiler but I knew that and I'm confident once compiled it will be a fast enough game. I have another couple of questions: 1) I realized I'm adding up stuff without exactly considering the memory limitation . I have a general idea of such limit but I'm not doing a precise calculation. How do you guys use to work? Have you got a method to plan the requirements? 2) I already tried a compiled version and it runs too fast and it's unplayable. How do I synchronize all the events in the game loop at the pace I want? Is there some documentation about timing techniques? I tried to search the forum but words like timing and timer are very common so I get too many unrelated result. Thanks in advance For point 2, you need to add some delays in your code. In particular on input and, if needed, when you move your hero, enemies on screen. At the beginning of your program add a: CALL LOAD(-1,3) the second parameters sets 3 frames (1/16 sec on NTSC machines) of delay when you use the CALL LINK("SYNC") routine from XB. So, for input you can use: CALL KEY(0,K,S)::CALL LINK("SYNC") to add a delay of 48ms to the routine, that should be enough in general. You can also use the CALL LINK("DELAY", VAL) call to add a specific delay VAL. So, the previous example can be written: CALL KEY(0,K,S)::CALL LINK("DELAY",48) For graphics, just add the same delay after the needed CALL VCHAR / CALL HCHAR. Sprites, in general do not need any delay instruction, in general. This in general. Of course, it's depend on your code where to add additional delays, but this infos could be a good starting point. 2 1 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted January 4, 2022 Author Share Posted January 4, 2022 @tmop69 Thank you very much for the explanation. I'll try to implement this solution in the final phases of my game. Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted January 4, 2022 Share Posted January 4, 2022 I'm not a big fan of adding delays that do nothing. I think it can lead to sluggish keyboard response. For example, if you scan the keyboard/joystick and then have a delay you can tap the keyboard during the delay and nothing will register. To me a better way is to set up a loop to scan the keyboard some number of times, lets say 20x. If any one of those CALL KEYs detects a keypress then the key is stored and when you exit the key scanning loop that key is provided to the program. This way it is much harder to miss a keypress or joystick input. 10 KK=0 20 FOR I=1 TO 20 30 CALL KEY(0,K,S) 40 IF K<>-1 THEN KK=K 50 NEXT I 60 ! if KK<>0 then a key was pressed How can you determine the delay without a lot of trial and error? Do your development in XB so the compiled program loads from XB. (DSK1.PROGRAM-X) In PROGRAM-X have line 1 CALL HCHAR(1,1,20) Here 20 will be the delay to test out In the compiled program have the first line be 1 CALL GCHAR(1,1,DELAY) You have passed the value for Delay from the XB loader to the compiled program 1 CALL GCHAR(1,1,DELAY) 10 KK=0 20 FOR I=1 TO DELAY 30 CALL KEY(0,K,S) 40 IF K<>-1 THEN KK=K 50 NEXT I 60 ! if KK<>0 then a key was pressed You can try different values in the CALL HCHAR until you find the one that works best for the program. Let's say 46 works best. Once you know that you can go back and make line 1 DELAY=46 3 1 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted January 5, 2022 Author Share Posted January 5, 2022 @senior_falcon This is a very interesting solution. Thanks. Can you please explain me the hchar-gchar method to pass the delay? I'm not sure I understood it. Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted January 5, 2022 Share Posted January 5, 2022 The thing that may not be obvious is that, when running a compiled program from XB, the compiled program is just a very large assembly subroutine running from XB. Try this program out: 10 CALL GCHAR(1,1,G) 20 PRINT "You passed the number";G;"to the compiled program" Compile this program and it will read the screen character at 1,1 and print it out. (Usually that will be the edge character 31) The compiled program will be: 10 CALL INIT :: CALL LOAD(8192,255,158):: CALL LINK("RUN") plus a bunch of embedded code that you cannot see. Add line 1 CALL HCHAR(1,1,53) and run the program. XB puts 53 at 1,1 on the screen, then starts the compiled program, which reads 53 from the screen and can use it in the compiled program. This doesn't apply to what you're doing, but when the compiled program ends it returns to XB to the first line after 10. Usually there is no line, so the program ends. But you can have XB do anything that is legal after returning, such as running another program, printing something, or ???? 1 2 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted January 6, 2022 Author Share Posted January 6, 2022 10 hours ago, senior_falcon said: The thing that may not be obvious is that, when running a compiled program from XB, the compiled program is just a very large assembly subroutine running from XB. Try this program out: 10 CALL GCHAR(1,1,G) 20 PRINT "You passed the number";G;"to the compiled program" Compile this program and it will read the screen character at 1,1 and print it out. (Usually that will be the edge character 31) The compiled program will be: 10 CALL INIT :: CALL LOAD(8192,255,158):: CALL LINK("RUN") plus a bunch of embedded code that you cannot see. Add line 1 CALL HCHAR(1,1,53) and run the program. XB puts 53 at 1,1 on the screen, then starts the compiled program, which reads 53 from the screen and can use it in the compiled program. This doesn't apply to what you're doing, but when the compiled program ends it returns to XB to the first line after 10. Usually there is no line, so the program ends. But you can have XB do anything that is legal after returning, such as running another program, printing something, or ???? Ah, now I understand, I tried the code and it read the passed value correctly. Thanks again! Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted January 16, 2022 Author Share Posted January 16, 2022 Ok. It was the Radius codec. I converted the videos in MP4 format Demo2-converted.mp4 Demo1-converted.mp4 5 1 Quote Link to comment Share on other sites More sharing options...
+OLD CS1 Posted January 17, 2022 Share Posted January 17, 2022 The video recording in Classic99 has not been supported and should be considered unusable. 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted January 17, 2022 Share Posted January 17, 2022 1 hour ago, Sergioz82 said: I made a mess in the previous post and I can't delete it. I believe you have only an hour to edit a post. If you become a subscriber to AtariAge, you can edit your posts after a longer delay, generally, but indefinitely in this “Development” subforum. ...lee 2 1 Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted January 17, 2022 Share Posted January 17, 2022 That is absolutely beautiful!!!!! Such a simple concept, elegantly done! 1 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted January 17, 2022 Author Share Posted January 17, 2022 (edited) 7 hours ago, senior_falcon said: That is absolutely beautiful!!!!! Such a simple concept, elegantly done! Thank you very much! I couldn't have made it without XB256 (the scroll down routine in particular is fundamental for the game, but also disply helped me a lot) The game mechanics is complete but there is still some work to do: it lacks menu,sounds, score, some tweaks (i.e in the second video the blue monster is not supposed to move like that). I hope I can complete it in the next weeks. Edited January 17, 2022 by Sergioz82 2 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted January 22, 2022 Author Share Posted January 22, 2022 Here I am again.. The game is proceeding, menu is complete and now it has joystick support (I realized that keyboard control is too tiring for the hand, it's not good for the gaming experience) I have another couple of questions: 1) How do I implement a 6 digits score? I tried a basic for loop that assigns to a variant a starting value of 1000000 and increments it and it had no problem with printing the result so I thought it was managed by basic itself. I used then a variant in the game for the score and it switches to negative values after 32768. 2) Is there any guide about how to make sounds? I read some pdf scans of old manuals but they're mainly focused on music while I'm looking for sound effects. In those manuals they go like "this is the sound of explosion", "this is the sound of a bell chime" and so on but I'd rather like to read the principles behind it. For example how to choose the right number of frequencies to use in a single call sound, how you actually choose the frequencies you need to replicate a bell and so on. I hope I explained myself ? Thanks in advance Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 22, 2022 Share Posted January 22, 2022 5 minutes ago, Sergioz82 said: 2) Is there any guide about how to make sounds? I read some pdf scans of old manuals but they're mainly focused on music while I'm looking for sound effects. In those manuals they go like "this is the sound of explosion", "this is the sound of a bell chime" and so on but I'd rather like to read the principles behind it. For example how to choose the right number of frequencies to use in a single call sound, how you actually choose the frequencies you need to replicate a bell and so on. I hope I explained myself ? Thanks in advance I studied electronic music way back in the age of analog Moog synthesizers where you have to build sounds with multiple oscillators and "voltage controlled" amplifiers. I think your question is outside the realm of programming and more in the realm of the physics of sound and music. Once you understand what the sound is composed of you can code it or something close to it. The alternative is experimenting (a lot) and listening carefully to the results. Some concepts to put in your tool box. "Envelope" of a sound refers to the shape the amplitude (volume) a sound would make over a period of time. You can think of the envelope in terms of "attack"; how fast the volume comes up, sustain; how long the sound stays at the same volume and decay; how fast the sound falls to silence. To get the frequencies required to make a sound can be hard. Artists use their ears and their wet-ware spectrum analyzer. (Everybody has that but with different specs) Technicians might require an electronic spectrum analyzer but that does not tell the entire story because a real sound unfolds over time with different parts coming into the mix over the envelope time ie: each part could have a separate envelope. In computer games this level of detail is typically not needed but some sounds must have an envelope. Bells for example: fast attack with a long decay. Apology if you know this already. If it is somewhat new then code examples and modification, trial and error, will probably be the way to go. 1 1 Quote Link to comment Share on other sites More sharing options...
HOME AUTOMATION Posted January 22, 2022 Share Posted January 22, 2022 Yes, wondering how that CHIME sound from the E/A, book was mastered, always brought me back to my experiences with ADSR, on CASIO's, VL TONE... 1 1 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.