Showing posts with label sprite. Show all posts
Showing posts with label sprite. Show all posts

Friday, December 13, 2013

Petit Computer Journal #29

A Simple Sprite Game


I've always said that writing a computer program is pretty easy if you know what you're doing. The question is, how easy can it be? I've written a version of Snakes and Ladders game in under 10 minutes. How quickly and easily can I write a sprite based game? Certainly, not too difficult.

I'm curious as to just how easy it is to write one. Certainly, there is the control. I settled for Up-Down-Fire, which is a variation of Left-Right-Fire. I was hoping for a single screen challenge program, and I managed to do it. However, it isn't fun at all. Also, it turns out that I don't need the Up-Down movement at all. Just sit there and hit the fire button!


  1. CLS:Y=0:A=256:C=128:S=0
  2. SPSET 1,96,0,0,0,0:T=3600
  3. SPSET 2,40,0,0,0,0
  4. SPOFS 2,-16,0,0:SPANIM 2,2,15
  5. SPSET 10,13,3,0,0,0
  6. @LOOP
  7. T=T-1:IF T<0 THEN END
  8. VSYNC 1:B=BUTTON(0):DX=0:DY=0
  9. LOCATE 0,0:?"S=";S,"T=";T;" "
  10. IF SPHIT(10) THEN S=S+1:BEEP
  11. IF !SPCHK(10) THEN GOSUB @MM
  12. IF (B AND 16) THEN GOSUB @FR
  13. IF !(B AND 3) GOTO @LOOP
  14. DY=(B AND 3)*2-3
  15. Y=Y+DY:IF Y<0 THEN Y=Y+192
  16. Y=Y%192:SPOFS 1,0,Y,0
  17. GOTO @LOOP
  18. @FR
  19. SPOFS 2,0,Y,0:SPOFS 2,A,Y,C/2
  20. RETURN
  21. @MM
  22. SPOFS 10,99+RND(99),-20,0
  23. SPOFS 10,99+RND(99),2*C,C
  24. RETURN


It's all very well and good for a single screen challenge, but I was curious as to how much better it would be, had it been a better fleshed out game. So, forget about the single screen challenge and just do better!


  1. CLS:Y=0:A=256:C=128:S=0
  2. BGFILL 0,0,0,63,63,9,8,0,0
  3. FOR I=1 TO 32
  4. BGPUT 0,RND(64),RND(64),32,8,0,0
  5. NEXT
  6. SPSET 1,96,0,0,0,0
  7. SPSET 2,40,0,0,0,0
  8. SPOFS 2,-16,0,0:SPANIM 2,2,15
  9. FOR I=10 TO 70
  10. SPSET I,13,3,0,0,0
  11. SPOFS I,99,-50,0
  12. NEXT
  13. @INIT
  14. BGMPLAY 23
  15. T=10800:S=0
  16. @LOOP
  17. IF HYC<S THEN HC=S
  18. IF !BGCHK(0) THEN BGOFS 0,RND(512),RND(512),360
  19. T=T-1:M=FLOOR(T/180):IF T<0 THEN GOTO @END
  20. VSYNC 1:B=BUTTON(0):DX=0:DY=0
  21. LOCATE 0,0:?"$=";S,"TIME=";M;"   ","HI $=";HC;"   "
  22. FOR I=10 TO 70-M
  23. IF SPHITSP(I,2) THEN S=S+1:BEEP 3:GOSUB @HIT
  24. IF !SPCHK(I) THEN GOSUB @MM
  25. NEXT
  26. IF (B AND 16) THEN GOSUB @FR
  27. IF !(B AND 3) GOTO @LOOP
  28. DY=(B AND 3)*2-3
  29. Y=Y+DY:IF Y<0 THEN Y=Y+192
  30. Y=Y%192:SPOFS 1,0,Y,0
  31. GOTO @LOOP
  32. @FR
  33. SPOFS 2,0,Y,0:SPOFS 2,A,Y,C/2
  34. RETURN
  35. @MM
  36. SPCHR I,13
  37. SPOFS I,99+RND(99),-20+256*RND(2),0
  38. SPOFS I,99+RND(99),-20+256*RND(2),99+RND(C)
  39. RETURN
  40. @HIT
  41. SPCHR I,248
  42. SPANIM I,8,6,1
  43. RETURN
  44. @END
  45. BGMPLAY 6
  46. WAIT 360
  47. FOR I=0 TO 16:I=BUTTON(0):NEXT
  48. GOTO @INIT


So far, I'm using standard graphics. It's very convenient that the graphics are all built-in. However, there is always a way to have custom made graphic. I won't list the source code, but check out the QR made for this game.

http://petitcomputer.wikia.com/wiki/Santa_Helper










Friday, October 25, 2013

Petit Computer Journal #22


5. Loop with Player input + background process


Last post, we were dealing with idle loop and how we process the button inputs and the corresponding code handlers. This time, I'm going to show you a clearer form to handle player input.

One of the most asked question is how to move the player sprite UDLR, and shoot. I'm going to show you how you should structure your code and divide it into two parts:


  1. Process Input
  2. Act on Input


While we are processing input, we want it very, very quick. This means that we do not act on any of the input at all. We simply store the information and that's it.

Later on, depending on the variables set, we can process the data. I'm using an extra variable to determine whether or not the player is shooting, and whether or not the bullet is being shot.

This is a very simple structure. If you can use array for directions, then I suggest that you use it. Even loading array using DATA statements is preferable to hand-coding it, like I'm doing here. Even though it doesn't take much time at all, copying and pasting, using array is the neater and better solution. Easier to maintain, too.

We'll take a look at multi-threading process next.



  1. ACLS
  2. SPSET 1,68,0,0,0,0
  3. SPOFS 1,128,96
  4. SPSET 10,40,0,0,0,0
  5. SPANIM 10,2,6
  6. SPOFS 10,-50,-50


  7. @INIT
  8. PD=0:'PD=PLAYER DIRECTION. 0=STOP.1234=UDLR
  9. BB=0

  10. @LOOP
  11. ' PROCESS INPUT
  12. VSYNC 1:TX=TCHX:TY=TCHY:TS=TCHST:TT=TCHTIME
  13. B=BUTTON(0)
  14. SPREAD(1),PX,PY

  15. IF (B AND 1) THEN PD=1
  16. IF (B AND 2) THEN PD=2
  17. IF (B AND 4) THEN PD=3
  18. IF (B AND 8) THEN PD=4
  19. IF (B AND 16) THEN PA=1 ELSE PA=0:'PLAYER ACTION: SHOOT

  20. 'ACT ON INPUT
  21. 'THIS SECTION IS THE IDLE SECTION
  22. IF (SPCHK(1) AND 1) GOTO @LOOP ELSE GOSUB @PMOVE
  23. IF (SPCHK(10) AND 1) THEN BB=1 ELSE BB=0

  24. IF !BB THEN SPOFS 10,-50,-50
  25. IF PA AND !BB THEN GOSUB @PSHOOT:PD=0
  26. GOTO @LOOP

  27. @PMOVE
  28. IF PD>4 OR PD<0 THEN PD=0
  29. ON PD GOTO @MSTOP,@MUP,@MDOWN,@MLEFT,@MRIGHT
  30. @MSTOP
  31. SPCHR 1,68
  32. SPANIM 1,1,0
  33. RETURN
  34. @MUP
  35. SPCHR 1,76
  36. SPANIM 1,4,15
  37. SPOFS 1,PX,PY-16,60
  38. RETURN
  39. @MDOWN
  40. SPCHR 1,68
  41. SPANIM 1,4,15
  42. SPOFS 1,PX,PY+16,60
  43. RETURN
  44. @MLEFT
  45. SPCHR 1,72
  46. SPANIM 1,4,15
  47. SPOFS 1,PX-16,PY,60
  48. RETURN
  49. @MRIGHT
  50. SPCHR 1,64
  51. SPANIM 1,4,15
  52. SPOFS 1,PX+16,PY,60
  53. RETURN

  54. @PSHOOT
  55. IF PD>4 OR PD<0 THEN RETURN
  56. ON PD GOTO @PSTOP,@PUP,@PDOWN,@PLEFT,@PRIGHT
  57. @PSTOP
  58. GOTO @PDOWN
  59. RETURN
  60. @PUP
  61. SPCHR 10,46
  62. SPOFS 10,PX,PY,0
  63. SPOFS 10,PX,PY-160,160
  64. RETURN
  65. @PDOWN
  66. SPCHR 10,42
  67. SPOFS 10,PX,PY,0
  68. SPOFS 10,PX,PY+160,160
  69. RETURN
  70. @PLEFT
  71. SPCHR 10,44
  72. SPOFS 10,PX,PY,0
  73. SPOFS 10,PX-160,PY,160
  74. RETURN
  75. @PRIGHT
  76. SPCHR 10,40
  77. SPOFS 10,PX,PY,0
  78. SPOFS 10,PX+160,PY,160
  79. RETURN


Friday, October 18, 2013

Petit Computer Journal #21


A Simple Timer

This here is a simplified version of the timer program. I put it here just to clarify that there are other ways to do this. The previous version was used to show LOOP construct, but this version is an optimized version of it. Notice that duplicate codes are collapsed into one, thus simplifying the whole structure.



  1. 'TIMER PROGRAM BY HARRY HARDJONO
  2. SPSET 1,156,0,0,0,0
  3. SPOFS 1,-50,-50
  4. SPANIM 1,4,15
  5. SPSCALE 1,200

  6. @INIT
  7. CLS
  8. SPOFS 1,0,96,0
  9. SPREAD(1),X,Y
  10. ?"HOW MANY SECONDS?"
  11. INPUT T
  12. IF 0>=T GOTO @END
  13. CLS
  14. ?"A STARTS TIMER"
  15. ?"B SETS NEW TIME"
  16. ?"X EXIT"
  17. ?"Y PAUSE"

  18. @LOOP
  19. VSYNC 1:B=BTRIG()
  20. IF (SPCHK(1) AND 1) THEN SPOFS 1,X,Y,0 
  21. IF (B AND 16) THEN SPOFS 1,224,96,FLOOR((224-X)*T/224)*60:GOTO @TICK
  22. IF (B AND 32) GOTO @INIT
  23. IF (B AND 64) GOTO @END
  24. GOTO @LOOP
  25. @TICK
  26. IF (BUTTON(0) AND 128) GOTO @LOOP
  27. LOCATE 0,8:SPREAD(1),X,Y:?"TIME LEFT: ";FLOOR((224-X)*T/224);"   "
  28. IF (SPCHK(1) AND 1) THEN BEEP 28:WAIT 60:GOTO @TICK
  29. ?"TIME'S UP!":SPOFS 1,0,96,0:X=0:BEEP 50:WAIT 120
  30. GOTO @LOOP

  31. @END
  32. ?"GOODBYE!"
  33. SPCLR 1:WAIT 300


Friday, October 11, 2013

Petit Computer Journal #20


IdleLoop


I'm going to do something different this time around. Usually I would make a new program, illustrating a new concept. However, this time, I won't be doing something new and exciting. I'll be doing something that is old and boring: idle loop.

What is idle loop? Well, you know the main loop on the program that goes like this:


  1. @MAINLOOP
  2. 'get player input
  3. 'process player input
  4. GOTO @MAINLOOP


That's it. Idle loop is so called because if the player has no input, it'll just wait around doing nothing. In other words, being idle.

So, you're probably wondering why I want to show that here. Shouldn't that process be known to everybody? Yes, it should. But not to everybody. Just like loop, there are various situations that you have to watch out for. For example, where would you want to put the @INIT function?

INIT function deals with initial state of the program. This is where default values come into play. Also, I like to put in @QUIT function, where the program does clean up when it exits. I don't put it in all my programs, especially simple ones, but for completeness, I want my programs to clean up after itself. It's just good programming practice.

So, the structure would be


  1. Global settings, DIM, and such
  2. INIT function
  3. MAIN function
  4. END/QUIT/CLEANUP function


There is a difference between Global Settings and INIT. In Global Settings, you allocate memory to the variables, and load any static resource needed. Fortunately, Petit Computer takes care most of it for you. Very little needed to be here. Some programming language requires you to load modules/libraries here as well.

INIT function deals with initializing the variables to start-up state. That means loading any custom screens if any. Backgrounds, Icons, and the like. This function gets called whenever the player quit to the title screen. It basically start fresh. If you have game states then you load that separately from this function. And yes, you want to have that special loading function, so you can override default graphics and provide the player with custom sprites and skin.

QUIT function basically says good-bye. There's not much here for Petit Computer. If you don't need anything, just leave it out, or just put out a good-bye message to the player.

The MAIN function will handle several types of cases:


  1. Infinite Loop
  2. Loop with Timer
  3. Loop with VSYNC
  4. Loop with Player input (wait)
  5. Loop with Player input (no wait)
  6. Loop with Player input + background process
  7. Loop with Player input + multi-thread


1. Infinite Loop


The first case is the simplest. You want the program to run forever, like in showing demo.


  1. @LOOP
  2. ?"Hello World"
  3. GOTO @LOOP


2. Loop with Timer


Sometimes, you want to have the program run for a certain length of time. Say, like a Timer. You can do it like this:


  1. 'TIMER PROGRAM BY HARRY HARDJONO
  2. SPSET 1,156,0,0,0,0
  3. SPOFS 1,-50,-50
  4. SPANIM 1,4,15
  5. SPSCALE 1,200

  6. @INIT
  7. CLS
  8. ?"HOW MANY SECONDS?"
  9. INPUT T
  10. IF 0>=T GOTO @END
  11. SPOFS 1,0,48,0
  12. SPOFS 1,224,48,T*60

  13. @LOOP
  14. IF (SPCHK(1) AND 1)==0 GOTO @END
  15. BEEP 28:WAIT 60
  16. GOTO @LOOP

  17. @END
  18. ?"TIME'S UP!"
  19. SPCLR 1
  20. BEEP 50
  21. WAIT 300


And that's it. The code before the INIT creates and sets the sprite. INIT is used to send the sprite across the screen, timed according to T variable. The loop then simply waits for the sprite to stop moving, and gave a "ding" sound when it's done. Also, sprite clean up is done at the END.


3. Loop with Player input (wait)


Let's modify the program a bit. We want to have a player input, and we want it to wait for each input. Let's say A to start the timer, B to set the timer, and X to end the program.


  1. @INIT
  2. CLS
  3. ?"HOW MANY SECONDS?"
  4. INPUT T
  5. IF 0>=T GOTO @END
  6. ?"A STARTS TIMER"
  7. ?"B SETS NEW TIME"
  8. ?"X EXIT"

  9. @LOOP
  10. VSYNC 1:B=BTRIG()
  11. IF (B AND 16) GOTO @TICK
  12. IF (B AND 32) GOTO @INIT
  13. IF (B AND 64) GOTO @END
  14. GOTO @LOOP
  15. @TICK
  16. SPOFS 1,0,48,0
  17. SPOFS 1,224,48,T*60
  18. @TOCK
  19. LOCATE 0,4:SPREAD(1),X,Y:?"TIME LEFT: ";FLOOR((224-X)*T/224);"   "
  20. IF (SPCHK(1) AND 1) THEN BEEP 28:WAIT 60:GOTO @TOCK
  21. ?"TIME'S UP!":SPOFS 1,-50,-50:BEEP 50
  22. GOTO @LOOP

  23. @END
  24. ?"GOODBYE!"
  25. SPCLR 1:WAIT 300


If you notice, there's TICK and TOCK. Normally, I would put these on separate subroutine, but since I'm showing you how to do LOOP while waiting for user input, I simply group it all at once. Does it look like spaghetti code to you?

4. Loop with Player input (no wait)


Let's do one more improvement, and this time, we're going to do player input without waiting. Let's implement a PAUSE button. The truth is, it's very simple. Petit Computer, by default, does not wait for user input.

Here, we simply check if the sprite is moving. If so, then we stop it. Else, we start it.


  1. 'TIMER PROGRAM BY HARRY HARDJONO
  2. SPSET 1,156,0,0,0,0
  3. SPOFS 1,-50,-50
  4. SPANIM 1,4,15
  5. SPSCALE 1,200

  6. @INIT
  7. CLS
  8. ?"HOW MANY SECONDS?"
  9. INPUT T
  10. IF 0>=T GOTO @END
  11. ?"A STARTS TIMER"
  12. ?"B SETS NEW TIME"
  13. ?"X EXIT"
  14. ?"Y PAUSE"

  15. @LOOP
  16. VSYNC 1:B=BTRIG()
  17. IF (B AND 16) GOTO @TICK
  18. IF (B AND 32) GOTO @INIT
  19. IF (B AND 64) GOTO @END
  20. GOTO @LOOP
  21. @TICK
  22. SPOFS 1,0,48,0
  23. SPOFS 1,224,48,T*60
  24. @TOCK
  25. LOCATE 0,8:SPREAD(1),X,Y:?"TIME LEFT: ";FLOOR((224-X)*T/224);"   "
  26. IF (BUTTON(0) AND 128) GOTO @PAUSE
  27. IF (SPCHK(1) AND 1) THEN BEEP 28:WAIT 60:GOTO @TOCK
  28. ?"TIME'S UP!":SPOFS 1,-50,-50:BEEP 50
  29. GOTO @LOOP

  30. @PAUSE
  31. IF (SPCHK(1) AND 1) THEN SPOFS 1,X,Y,0 
  32. IF (BUTTON(0) AND 16) THEN SPOFS 1,224,48,FLOOR((224-X)*T/224)*60:GOTO @TOCK
  33. IF (BUTTON(0) AND 32) GOTO @INIT
  34. IF (BUTTON(0) AND 64) GOTO @END
  35. GOTO @PAUSE

  36. @END
  37. ?"GOODBYE!"
  38. SPCLR 1:WAIT 300



Simple as that. We simply use the variable values from TIME LEFT to stop the sprite where it stands, and starts it in according to TIME LEFT. There's a trick here, that we use BUTTON(0) instead of BTRIG. We do that because of WAIT 60 that we have will cause BTRIG signal to get lost. BUTTON(0) will read the input continuously. Press A to restart the timer.

Petit Computer Programming can be so easy! Next, we'll examine loops with background processes.

Friday, September 13, 2013

Petit Computer Journal #17

WITCH

I wrote WITCH to showcase the SPRITE capabilities of Petit Computer. There are lots of people who doesn't know how to use sprites. In fact, Petit Computer sprites are so powerful, you can easily replicate old games.

So here is the WITCH program that is purposely SPRITE heavy. There are certain techniques where I could have done it traditionally, but chose to implement it using a sprite instead. Notably, there is a TIMER sprite, where the sole purpose is to spread the bullets so that they don't trip over one another.

I chose the firing of the fireball using stylus because somebody was asking the way to do it. He was wondering about how to fire the bullet in the direction of the stylus, without stopping at the point of the stylus. In my solution, I also took care of the speed of the bullets. Done carelessly, the bullets may have different speed.

'THE WITCH AND THE UNDEAD
'A SPRITE EXERCISE

'1. WITCH
'2. FIREBALL
'3. UNDEAD FALLING
'4. UNDEAD WALKING
'5. UNDEAD HIT
'6. WITCH HIT
'7. EXTRAS

@INIT
H=100:'HEALTH
NUMZ=1:'CURRENT NUM OF ENEMY
MAXZ=20:'MAXNUM OF ENEMY
SCORE=0

ACLS
PNLTYPE "OFF"
SPSET 1,108,1,0,0,0:'WITCH
SPCOL 1,0,0,16,16,FALSE
SPOFS 1,120,170

'BULLET
FOR I=2 TO 9
SPSET I,142,0,0,0,0
SPANIM I,2,10,0
SPOFS I,30,180,I*90
SPCOL I,0,0,16,16,FALSE,2
NEXT

'CURSOR
SPSET 10,253,1,0,0,0
SPCOL 10,0,0,16,16,FALSE,0

'TIMER
SPSET 11,3,1,0,0,0
SPCOL 11,0,0,16,16,FALSE,0
SPOFS 11,200,200

'UNDEAD
FOR I=20 TO 20+MAXZ
SPSET I,130,RND(16),0,0,0
SPANIM I,2,20,0
SPOFS I,200,-300
SPCOL I,0,0,16,16,FALSE,1+2
NEXT



@MAINLOOP
VSYNC 1:X=TCHX:Y=TCHY:S=TCHST:B=BUTTON(0)
LOCATE 0,0:?"HEALTH=";H;"   ":?"SCORE=";SCORE
SPOFS 10,X,Y
GOSUB @FIREBALL
GOSUB @ENEMY

IF SPHIT(1) THEN H=H-1:BEEP 37
IF B AND 4 THEN DX=-8: GOSUB @MOVE
IF B AND 8 THEN DX=8:GOSUB @MOVE
'IF B AND 16 GOTO @END
IF H<=0 GOTO @END
GOTO @MAINLOOP

@MOVE
SPREAD(1),SX,SY
IF SX+DX>0 AND SX+DX<240 THEN SPOFS 1,SX+DX,SY,8
RETURN

@ENEMY
SPREAD(1),SX,SY
FOR I=20 TO 20+NUMZ
SPREAD(I),ZX,ZY
IF (1 AND SPCHK(i)) GOTO @ENEMY2
IF ZY>SY THEN SPOFS I,SX,SY,SPEED:GOTO @ENEMY2
SPEED=(50000-SCORE)/100:IF SPEED<60 THEN SPEED=60
SPOFS I,RND(255),0
SPOFS I,RND(255),SY+1,SPEED
@ENEMY2
NEXT
RETURN

@FIREBALL
FOR I=2 TO 9
IF SPHIT(I) THEN GOSUB @HITZ
IF !(1 AND SPCHK(I)) THEN SPOFS I,I*18,300
IF S==1 AND !(1 AND SPCHK(11) THEN @SHOOT
NEXT
RETURN

@HITZ
BEEP 14
SPOFS I,80,0
SPOFS SPHITNO,30,-150
SCORE=SCORE+FLOOR(SQR((SPHITX-SX)*(SPHITX-SX)+(SPHITY-SY)*(SPHITY-SY))/3)
NUMZ=FLOOR(SCORE/1000)+1
IF NUMZ>MAXZ THEN NUMZ=MAXZ
RETURN

@SHOOT
CB=((CB+1)%8:CBS=CB+2
IF (1 AND SPCHK(CBS)) GOTO @SHOOT2
BEEP 10:SCORE=SCORE-1:IF SCORE<0 THEN SCORE=0
SPREAD(1),SX,SY
A=ATAN((X-SX),(Y-SY))
SPOFS CBS,SX,SY
SPOFS CBS,SX+300*SIN(A),SY+300*COS(A),150
SPOFS 11,250,250,20
@SHOOT2
RETURN

@END
CLS:?"GAMEOVER"
BGMPLAY 6
WAIT 60
END