Monday, September 10, 2012

Petit Computer Journal #6


Petit Computer Journal #6

I don't know about you, but I'm itching for a game. I know I'm bucking the convention here, but I want a real game, none of this Hi-Lo stuff that some people call game. I actually have 2 choices: A word guessing game (Like Wheel of Fortune) or a Snake game. Let's do Snake game first. It's probably a good idea to set the difficulty in linear manner, but I think we can handle otherwise.

We'll lower the difficulty by making The Apple Picker Game. What kind of game is that? It's an Apple Eating Snake game without the Snake. Not that implementing a snake is difficult, but I want to explain how strings work before I do that. The reasonable approach is to do the other game first. We'll do this game first because, hey, sometimes it's good to live dangerously!

Haha, joking aside, I think we have all that we need in order to build it. Show-and-Tell time! What makes an Apple Picker game?

1. A Character who picks apples
2. Apples
3. Area
4. Obstacles (optional)
5. Time Limit (optional)
6. Score (optional)

The gameplay area is the console 32x24 character map. We'll use character for graphics. The movement will be once per second (VSYNC 60). A character will keep moving forward until it runs into an obstacle (wall). Do we need time limit? How about ending the game after all the apples are gone? Do we need score?

Let's keep things simple for now. We do a character, put some apples, and build wall along the perimeter. We'll split the program into 3 sections: Init, GameLoop, and Ending

Init:
1. Clear the screen CLS
2. Put character on location 10,20
3. Draw Wall along perimeter
4. Put 10 apples randomly

GameLoop:
1. Wait 1 second VSYNC 60
2. Read BUTTON(0) via SETB2
3. Depending on UDLR, we check the next space
3a. Space - move character there
3b. Apple - Increase score by 1, and move character there
3c. Wall  - Set character movement to STOP.
4. If score is 10 then GOTO Ending

Ending:
1. Display "GAME OVER"
2. Wait 2 seconds
3. Wait for button press SETB3
4. End Program.

That's not too bad. It's not even one page. How hard can it be? Notice how we reuse our old BUTTON reading code. That makes thing simple.

@INIT :'Program Initialization
'1. Clear the screen 
CLS
'2. Put character on location 10,20
LOCATE 10,20:?CHR$(244):'MAN
'3. Draw Wall along perimeter
LOCATE 0,1:?CHR$(3)*32
LOCATE 0,21:?CHR$(3)*32
FOR Y=1 TO 21
LOCATE 0,Y:?CHR$(3)
LOCATE 31,Y:?CHR$(3)
NEXT
'4. Put 10 apples randomly
GOSUB @PUTAPPLE


@ENDING :'Program Ending
'1. Display "GAME OVER"
LOCATE 10,22:?"GAME OVER";
'2. Wait 2 seconds
WAIT 120
'3. Wait for button press SETB3
GOSUB @SETB3
'4. End Program.
END

We need to work on @GameLoop. We also need to work on @PUTAPPLE. Otherwise, the program is half done. Here is PUTAPPLE:

@PUTAPPLE :'CHR$(233) IS APPLE
FOR I=1 TO 10
X=RND(31-1)+1:Y=RND(20-2)+2:C=CHKCHR(X,Y)
IF C==0 THEN LOCATE X,Y:?CHR$(233) ELSE I=I-1
NEXT
RETURN

The thing we want to watch out for is that we only put apple on bare ground. We also make sure that there are EXACTLY 10 apples. Ah, that was easy. We run it, and see if it works. Save it as APPLE.

Now for the main game loop. Wait 1 second. Read Input. Move pieces. Repeat until done. What can be the problem? Oh, wait. We forgot to store our man's location. Put this on Init: "MX=10:MY=20". We also forgot the score: "SCORE=0"


@GameLoop
'1. Wait 1 second 
'2. Read BUTTON(0) via SETB2
VSYNC 60:GOSUB @SETB2

'3. Depending on UDLR, we check the next space
GOSUB @NEXTSPACE:'SETS NX,NY
GOSUB @DOMOVE

'4. If score is 10 then GOTO Ending
IF SCORE==10 GOTO @ENDING
GOTO @GAMELOOP


'3a. Space - move character there
'3b. Apple - Increase score by 1, and move character there
'3c. Wall  - Set character movement to STOP.
@DOMOVE
C=CHKCHR(NX,NY)
IF C==0 THEN GOSUB @MANMOVE:RETURN
IF C==233 THEN GOSUB @EATAPPLE:GOSUB @MANMOVE:RETURN
IF C==3 THEN GOSUB @HITWALL:RETURN
RETURN

@MANMOVE
LOCATE MX,MY:?CHR$(0);
LOCATE NX,NY:?CHR$(244);
MX=NX:MY=NY
RETURN

@EATAPPLE
BEEP 7:'COIN
SCORE=SCORE+1
LOCATE 0,22:?"SCORE ";SCORE;
RETURN

@HITWALL
BEEP 11:'DAMAGE
RETURN

@NEXTSPACE
IF INBUTTON$=="U" THEN NX=MX:NY=MY-1:RETURN
IF INBUTTON$=="D" THEN NX=MX:NY=MY+1:RETURN
IF INBUTTON$=="L" THEN NX=MX-1:NY=MY:RETURN
IF INBUTTON$=="R" THEN NX=MX+1:NY=MY:RETURN
IF INBUTTON$=="" THEN NX=MX:NY=MY:RETURN
RETURN

If you look at @NEXTSPACE structure, you may wonder why I put in so many RETURNs in there. Actually, I'm using it as ELSE-IF ladder. Since SmileBASIC doesn't allow multi-line ELSE-IF ladder, I'm using Subroutine as substitute. It works fine. The many RETURNs can be skipped, at the cost of performance. Since this structure can be difficult to discover on your own, I figure I'll highlight it here.

It took me about one hour to do everything. Not bad at all. Save the game and pat yourself on the back. You have successfully completed a whole new game.

How about some improvements? Once per second is too slow. 3 per second seems about right. Change to VSYNC 20.

How about changing the movement so that once you move, you cannot stop?

How about putting in some other obstacle? Snakes? Who says we can't have snakes in there? Have the program ends when you hit a snake. Otherwise, keep going.

@PUTSNAKE :'CHR$(27) IS SNAKE
LEVEL=LEVEL+1
FOR I=1 TO LEVEL
X=RND(31-1)+1:Y=RND(20-2)+2:C=CHKCHR(X,Y)
IF C==0 THEN LOCATE X,Y:?CHR$(27) ELSE I=I-1
NEXT
LOCATE 21,22:?"LEVEL ";LEVEL;
RETURN



What Have We Learned?
1. We know multiplying a character repeats that character.
2. CHKCHR(X,Y) returns the value of the character on that console location.
3. CLS fills the console with CHR$(0).
4. BEEP makes easy sound effects.
5. Computer Programming is easy. Good Design is hard!

No comments: