Help with my game in Family BASIC

Started by childishbeat, July 26, 2019, 11:22:36 am

Previous topic - Next topic

childishbeat

July 26, 2019, 11:22:36 am Last Edit: July 26, 2019, 01:22:18 pm by childishbeat
I'm making a game in Family BASIC in which the player (represented by a ball graphic) has to collect all of the apples on the screen (they're the only things within the walls other than the player). (In case you're wondering and/or if any reference of what I'm using to make that game would help you help me here, I'm using Nestopia v1.40 running on Windows 10 to make that game.) Anyways, I'm unable to make collision work correctly in that game, including the collection of apples and collision with walls. Also, if anyone who comes across this comment can make the existing code take less characters, thus the existing code would more efficient, please help me make such changes as well. Here's my code, for reference:
10 CLS
20 X=1
30 Y=1
40 PRINT CHR$(222);
50 FOR I=1 TO 26
60 PRINT CHR$(227);
70 NEXT
80 PRINT CHR$(223);
90 FOR I=1 TO 21
100 PRINT CHR$(226);
110 FOR J=1 TO 26
120 PRINT CHR$(215);
130 NEXT
140 PRINT CHR$(226);
150 NEXT
160 PRINT CHR$(224);
170 FOR I=1 TO 26
180 PRINT CHR$(227);
190 NEXT
200 PRINT CHR$(225);
210 PRINT "APPLES";
220 LOCATE X,Y
230 PRINT CHR$(207)
240 I$=INKEY$
250 IF I$=CHR$(29) THEN X=X-1: LOCATE X+1,Y: GOTO 300
260 IF I$=CHR$(28) THEN X=X+1: LOCATE X-1,Y: GOTO 300
270 IF I$=CHR$(31) THEN Y=Y+1: LOCATE X,Y-1: GOTO 300
280 IF I$=CHR$(30) THEN Y=Y-1: LOCATE X,Y+1: GOTO 300
290 GOTO 240
300 PRINT " ": GOTO 220

Post Merge: July 26, 2019, 01:22:18 pm

I'm making a game in Family BASIC in which the player (represented by a ball graphic) has to collect all of the apples on the screen (they're the only things within the walls other than the player). (In case you're wondering and/or if any reference of what I'm using to make that game would help you help me here, I'm using Nestopia v1.40 running on Windows 10 to make that game.) Anyways, I'm unable to make collision work correctly in that game, including the collection of apples and collision with walls. Also, if anyone who comes across this comment can make that game render any of its graphics faster and/or the existing code more efficient (that means making the existing code be in less characters), please help me make that change as well. Here's my code, for reference:
10 CLS
20 X=1
30 Y=1
40 PRINT CHR$(222);
50 FOR I=1 TO 26
60 PRINT CHR$(227);
70 NEXT
80 PRINT CHR$(223);
90 FOR I=1 TO 21
100 PRINT CHR$(226);
110 FOR J=1 TO 26
120 PRINT CHR$(215);
130 NEXT
140 PRINT CHR$(226);
150 NEXT
160 PRINT CHR$(224);
170 FOR I=1 TO 26
180 PRINT CHR$(227);
190 NEXT
200 PRINT CHR$(225);
210 PRINT "APPLES";
220 LOCATE X,Y
230 PRINT CHR$(207)
240 I$=INKEY$
250 IF I$=CHR$(29) THEN X=X-1: LOCATE X+1,Y: GOTO 300
260 IF I$=CHR$(28) THEN X=X+1: LOCATE X-1,Y: GOTO 300
270 IF I$=CHR$(31) THEN Y=Y+1: LOCATE X,Y-1: GOTO 300
280 IF I$=CHR$(30) THEN Y=Y-1: LOCATE X,Y+1: GOTO 300
290 GOTO 240
300 PRINT " ": GOTO 220

P

It looks like you have a double post with two different versions of the post. You can use the "modify" button to clean it up.


For collision detection with the static walls you just have to put a check after moving. For example if you move the character to the right, you add 1 to the X-position value and then you have to check that X is still within the level borders (X < border) and if not you have to decrement X by 1 again before continuing so that the character is never actually moved.
Currently you get IL error (illegal function call) at line 220 because locate is passed an illegal position when the character moves outside of screen.

For collision detection with the apples, I imagine you'd need an array (DIM) that contains the whole level. Each tile of the level is an element in the array with each value representing the state of that tile. Say 0=empty tile, 1=apple present for example. When loading the level you need to fill the array with apples (1's) and when you collect an apple you need to set the element representing the tile you are standing on to empty (0). You need to figure out how to index the array with your character position.

UglyJoe

One technique for collision detection is to use the SCR$ command.  It takes an X,Y coordinate and returns whatever background character is at that location.  So you figure out the coordinate where your player is going to be (based on user input) and then use SCR$ to get the character at that location.  If it return a blank/space character then there is nothing there and you can move your player there.  If it returns something else (a wall, an apple, whatever), then you know there is something there that the player is going to collide with.

Here's a documented example from: https://i.imgur.com/EWOBJ.jpg .  It doesn't draw any walls or anything, but you could do that before the control loop to test it.


' general graphics stuff
100 CLS:CGSET 1,1:VIEW
110 SPRITE ON
' P$ is the player, represented by an A
' X and Y will be the player's position on the screen
120 P$="A":X=13:Y=10
' start of control loop
' AX and AY hold the directions that the user will move
200 AX=0:AY=0
' read from the d-pad on controller one, store result in K
' remain here until user presses a direction
210 K=STICK(0):IF K=0 GOTO 210
' parse the user's input
' right button
220 IF K=1 THEN AX=1
' left button
230 IF K=2 THEN AX=-1
' down button
240 IF K=4 THEN AY=1
' up button
250 IF K=8 THEN AY=-1
' X+AX,Y+AY is where we'd like to move the player, so see what's currently there and store in K$
260 K$=SCR$(X+AX,Y+AY)
' erase A from the user's current location
270 LOCATE X,Y:PRINT " "
' if there was nothing at K$, then move the player to their new location, otherwise leave X and Y untouched
280 IF K$=" " THEN X=X+AX:Y=Y+AY
' draw the player at their new location (which may be the same as their old location)
290 LOCATE X,Y:PRINT P$
' back to the top oft the control loop
300 GOTO 200


BTW, I haven't run this code at all or touched Family BASIC in like two years, but I think I got it right ;)

P

Nice, I forgot you can read the screen. Normally I think it would be good to have the level map in RAM (in an array) but if Family BASIC is already keeping the screen in RAM you might as well read that via SCR$.

I tried the program UglyJoe posted and it works (you move around an "A" character using the d-pad). I added one line:

115 LOCATE 5,10:PRINT "XXX"

This draws a wall from X characters, and it turns out to be impassable as expected.

childishbeat

Sure enough, I was able to make collision work correctly in that game with the help of your comments from all of you except P because I had already solved my problems with collision before I found P's message. However, I've tried to implement collision in that code before, but with bugs, such as:

  • The player being able to eat the graphic tile of wall one row below the player, but for the leftmost tile of that row, all if the player went to the rightmost tile of the text area.

  • The player being able to move to a higher tile than the "legal" limits of the LOCATE command, causing an IL (illegal function call) error to appear.

  • The player being unable to move further down than the uppermost tile it was supposed to be able to be able to be in. Instead, the player would move up and down rapidly between that tile and the tile above it, which was a top wall tile.



Here's a revised version of my code with added collision detection and a scoring system (both work correctly), with the scoring system making the game exit to the NS-HUBASIC command line if they collect all 519 apples (However, the code has a modification I intentionally made to it so it would no longer use the last row (To do that, I intentionally made the game field one row shorter while still keeping the score marker one row below the player area.) because when I tried to do so, the screen would scroll down. Can anyone who comes across this message say how I could fix the game's code so the game can use the last row, thus the game field can be one row longer and the score marker can still be one row below the player area?):
10 CLS
20 X=1
30 Y=1
40 PRINT CHR$(222);
50 FOR I=1 TO 26
60 PRINT CHR$(227);
70 NEXT
80 PRINT CHR$(223);
90 FOR I=1 TO 20
100 PRINT CHR$(226);
110 FOR J=1 TO 26
120 PRINT CHR$(215);
130 NEXT
140 PRINT CHR$(226);
150 NEXT
160 PRINT CHR$(224);
170 FOR I=1 TO 26
180 PRINT CHR$(227);
190 NEXT
200 PRINT CHR$(225);
210 PRINT "APPLES"
220 LOCATE 6,22
230 PRINT APPLES
240 LOCATE X,Y
250 PRINT CHR$(207)
260 IF APPLES=519 THEN CLS: END
270 I$=INKEY$
280 IF I$=CHR$(29) THEN IF X>1 THEN X=X-1: LOCATE X+1,Y: GOTO 330
290 IF I$=CHR$(28) THEN IF X<26 THEN X=X+1: LOCATE X-1,Y: GOTO 330
300 IF I$=CHR$(31) THEN IF Y<20 THEN Y=Y+1: LOCATE X,Y-1: GOTO 330
310 IF I$=CHR$(30) THEN IF Y>1 THEN Y=Y-1: LOCATE X,Y+1: GOTO 330
320 GOTO 240
330 IF SCR$(X,Y)=CHR$(215) THEN APPLES=APPLES+1
340 PRINT " "
350 GOTO 220

UglyJoe

July 28, 2019, 01:37:31 pm #5 Last Edit: July 28, 2019, 01:42:34 pm by UglyJoe
Quote from: childishbeat on July 28, 2019, 12:57:38 pm
Can anyone who comes across this message say how I could fix the game's code so the game can use the last row, thus the game field can be one row longer and the score marker can still be one row below the player area?):


The PRINT command will actually print your text/variable and then a newline.  When you print on the last line, the newline will push everything upward.  You can make it not print the newline by adding a semicolon after the PRINT.


10 PRINT "PRINT WITH NEWLINE"
20 PRINT "PRINT WITHOUT NEWLINE";


For example, this will print ABC in the top left and DEF in the lower right:

10 CLS
20 LOCATE 0,0
30 PRINT "ABC"
40 LOCATE 0,23
50 PRINT "DEF";
100 GOTO 20


If you were to remove the semicolon from the end of line 50, it would scroll up every time it prints "DEF" at the bottom.

When printing variables, just add the semicolon after the variable:


10 D$="DEF"
20 PRINT D$;


Also, just as an FYI, the variables names max out at two characters.  You can use a name like APPLES as a variable, but it's really only using AP as the name.  This can cause unexpected issues since variables names that start with the same two characters will be treated the same:


10 APPLES=7
20 APPLICANCES=9
30 PRINT APPLES


That will actually print 9 instead of 7.  Something to look out for :D

P

Quote from: childishbeat on July 28, 2019, 12:57:38 pm
I was able to make collision work correctly in that game with the help of your comments from all of you except P

I'm so lonely. :'(
Wait a minute, it's only me and UglyJoe here and I posted first! ???

UglyJoe

Quote from: P on July 28, 2019, 03:54:06 pm
Quote from: childishbeat on July 28, 2019, 12:57:38 pm
I was able to make collision work correctly in that game with the help of your comments from all of you except P

I'm so lonely. :'(
Wait a minute, it's only me and UglyJoe here and I posted first! ???


I thought that was pretty odd, as well :-[

childishbeat

Thanks to the UglyJoe's message that told me newlines would push everything upward, I successfully adapted that game to use the last row, just as I intended. As a result of that adaption, there are now 545 apples to collect, as opposed to my previous 519 apples, a difference of 26 tiles, as only one row was added, and a row consists of 26 apples. Anyways, here's my new code:
10 CLS
20 X=1
30 Y=1
40 PRINT CHR$(222);
50 FOR I=1 TO 26
60 PRINT CHR$(227);
70 NEXT
80 PRINT CHR$(223);
90 FOR I=1 TO 21
100 PRINT CHR$(226);
110 FOR J=1 TO 26
120 PRINT CHR$(215);
130 NEXT
140 PRINT CHR$(226);
150 NEXT
160 PRINT CHR$(224);
170 FOR I=1 TO 26
180 PRINT CHR$(227);
190 NEXT
200 PRINT CHR$(225);
210 PRINT "APPLES";
220 LOCATE 6,23
230 PRINT APPLES;
240 LOCATE X,Y
250 PRINT CHR$(207)
260 IF APPLES=545 THEN CLS: END
270 I$=INKEY$
280 IF I$=CHR$(29) THEN IF X>1 THEN X=X-1: LOCATE X+1,Y: GOTO 330
290 IF I$=CHR$(28) THEN IF X<26 THEN X=X+1: LOCATE X-1,Y: GOTO 330
300 IF I$=CHR$(31) THEN IF Y<21 THEN Y=Y+1: LOCATE X,Y-1: GOTO 330
310 IF I$=CHR$(30) THEN IF Y>1 THEN Y=Y-1: LOCATE X,Y+1: GOTO 330
320 GOTO 240
330 IF SCR$(X,Y)=CHR$(215) THEN APPLES=APPLES+1
340 PRINT " "
350 GOTO 220