Small Basic Game Programming - Shooting Game

In Challenge of the Month - August 2013, there were following two challenges.

Game Challenge

Write a DuckShoot game.

Interface Challenge

Write a fancy Game Opening screen for DuckShoot game - see above.

At that time, I tried only opening.  Today, I will introduce new DuckShoot game as Program ID TLR995-2.

This program has 425 lines.  But there are 166 lines that automatically generated by Shapes editor.  So actual coded lines are 259.

Main

I added shooting part.  But only one duck comes.  Because I thought the code becomes complex and worse readable if ducks increases.

1.``' DuckShoot 0.31b

2.``' Copyright (c) 2013-2014 Nonki Takahashi. The MIT License.

3.``'

4.``' History:

5.``' 0.31b 2014-08-01 Sorted subroutines. (TLR995-2)

6.``' 0.3b 2014-07-26 Supported in remote. (TLR995-1)

7.``' 0.2b 2014-07-13 Created a core of shooting. (TLR995-0)

8.``' 0.1a 2013-08-03 Created as DuckShoot opening. (TLR995)

9.``' 0.0 2013-08-03 14:38:49 Shapes generated by Shapes 1.5b.

10.``'

11.``GraphicsWindow``.`` Title `` = ``"DuckShoot 0.31b"

12.``SB_Workaround``(``)

13.``Opening``(``)

14.``GameInit``(``)

15.``GameLoop``(``)

16.``Ending``(``)

Ending

This subroutine calculates score from a number of shoot and a number of hit.

17.`` Sub ``Ending

18.``  ``Shapes``.``Remove``(``stair``)

19.``  `` If ``hit``[``i`` ] ``Then

20.``    `` point `` = ``1

21.``  ``Else

22.``    `` point `` = ``0

23.``  ``EndIf

24.``  `` score `` = `` point `` * `` 110 `` - `` shoot `` * ``10

25.``  ``GraphicsWindow``.`` BrushColor `` = ``"White"

26.``  ``GraphicsWindow``.`` FontSize `` = ``40

27.``  ``GraphicsWindow``.``DrawText``(``170`` , ``180`` , `` "SCORE " `` + ``score``)

28.``  ``Program``.``Delay``(``500``)

29.``  ``GraphicsWindow``.``DrawText``(``170`` , ``230`` , `` "SHOOT " `` + ``shoot``)

30.``  ``Program``.``Delay``(``500``)

31.``  ``GraphicsWindow``.``DrawText``(``170`` , ``280`` , `` "HIT " `` + ``point``)

32.``  ``Program``.``Delay``(``2000``)

33.``  ``GraphicsWindow``.`` FontSize `` = ``50

34.``  ``GraphicsWindow``.``DrawText``(``170`` , ``80`` , ``"GAME OVER"``)

35.``EndSub

Initialization

At this point there is one duck.  But for the future, arrays are used instead of single variables for the duck.  I decided to use mouse for the operation of the gun, so events for mouse click and move are initialized here. 

36.`` Sub ``GameInit

37.``  ``' Game start

38.``  ``Shapes``.``ShowShape``(``duck``[``1``]``)

39.``  `` mouseDown `` = ``"False"

40.``  ``GraphicsWindow``.`` MouseDown `` = ``OnMouseDown

41.``  ``GraphicsWindow``.`` MouseMove `` = ``OnMouseMove

42.``  ``Shapes``.``Animate``(``duck``[``1``]`` , ``gw`` , ``150`` , ``3000``)

43.``  ``Program``.``Delay``(``3000``)

44.``  `` i `` = ``1

45.``  ``hit``[``i`` ] `` = ``"False"

46.``  ``a``[``i`` ] `` = ``90

47.``  ``x``[``i`` ] `` = ``-``dw

48.``  `` yDuck `` = ``150

49.``  ``y``[``i`` ] `` = ``yDuck

50.``  ``Shapes``.``Move``(``duck``[``i``]`` , ``x``[``i``]`` , ``y``[``i``]``)

51.``  ``GraphicsWindow``.`` PenWidth `` = ``0

52.``  ``GraphicsWindow``.`` BrushColor `` = ``bgColor

53.``  `` yRS `` = `` yStair `` - ``(`` yDuck `` + `` dh `` / ``2``)

54.``  `` shoot `` = ``0

55.``EndSub

Main Loop

Last week obstacles are moved in the timer event handler.  This time, a duck is moved 4 dots rightward in main loop.

When a player shoots the gun, another duck is drawn under the moving Shapes duck to check hitting.  If the target is an ellipse or a rectangle, the hit checking can be calculated from the co-ordinate.  But the duck shape is a little difficult for the calculation.  So, I used GraphicsWindow.GetPixel() and check the color is the background color or not.

There is a problem on a browser at GetPixel.  A browser hangs up and returns no response at GraphicsWindow.GetPixel() after GraphicsWindow.DrawImage().  Program imported into IDE doesn't  have this problem.  To avoid this problem, I changed this program not to use DrawImage() but to use FillRectangle(), FillTriangle() and FillEllipse() with shape data created by Shapes editor.  This workaround works well because the duck drawing was made with Shapes editor.

When the gun hits, the duck image falls down by using Shapes.Zoom().  We can give zoom levels from 0.1 to 20 for this operation.  So, be careful not to give smaller value than 0.1.  In following subroutine, the zoom level is reduced little by little until 0.1.

I found there is another problem with Shapes.Zoom() in imported program.  When the zoom level was smaller than 0.7, the program stopped.  At that time, the image of the duck had transparent background.  To avoid this issue, I changed the image to have opaque background.

56.`` Sub ``GameLoop

57.``  `` While ``x``[``i`` ] `` < ``gw

58.``    ``Program``.``Delay``(``50``)

59.``    `` If `` mouseDown ``Then

60.``      ``Sound``.``PlayClick``(``)

61.``      `` shoot `` = `` shoot `` + ``1

62.``      `` If `` silverlight ``Then

63.``        `` shX `` = ``x``[``i``]

64.``        `` shY `` = ``y``[``i``]

65.``        `` iMin `` = ``1

66.``        `` iMax `` = ``10

67.``        ``Shapes_Draw``(``)

68.``      ``Else

69.``        ``GraphicsWindow``.``DrawImage``(``img`` , ``x``[``i``]`` , ``y``[``i``]``)

70.``      ``EndIf

71.``      `` color `` = ``GraphicsWindow``.``GetPixel``(``dx`` , ``dy``)

72.``      ``GraphicsWindow``.`` PenWidth `` = ``0

73.``      ``GraphicsWindow``.`` BrushColor `` = ``bgColor

74.``      ``GraphicsWindow``.``FillRectangle``(``x``[``i``]`` , ``y``[``i``]`` , ``dw`` , ``dh``)

75.``      `` If `` color ``<`` > `` bgColor ``Then

76.``        ``hit``[``i`` ] `` = ``"True"

77.``      ``EndIf

78.``      `` mouseDown `` = ``"False"

79.``    ``EndIf

80.``    `` If ``hit``[``i`` ] ``Then

81.``      `` If `` 0 `` < ``a``[``i`` ] ``Then

82.``        ``a``[``i`` ] `` = ``a``[``i`` ] `` - ``5

83.``        `` cos `` = ``Math``.``Round``(``Math``.``Sin``(``Math``.``GetRadians``(``a``[``i``]``)`` ) `` * ``100`` ) `` / ``100

84.``        ``Shapes``.``Zoom``(``duck``[``i``]`` , ``1`` , ``Math``.``Max``(``cos`` , ``0.1``)``)

85.``        `` deltaY `` = `` yRS `` - `` yRS `` * ``cos

86.``        ``y``[``i`` ] `` = `` yDuck `` + ``deltaY

87.``      ``EndIf

88.``    ``EndIf

89.``    ``x``[``i`` ] `` = ``x``[``i`` ] `` + ``4

90.``    ``Shapes``.``Move``(``duck``[``i``]`` , ``x``[``i``]`` , ``y``[``i``]``)

91.``  ``EndWhile

92.``EndSub

Mouse Event Handler (on Click)

This subroutine sets a flag mouseDown and saves the co-ordinate of the mouse.  This co-ordinate is used to check that the gun hits the duck in GameLoop(). 

93.`` Sub ``OnMouseDown

94.``  `` mouseDown `` = ``"True"

95.``  `` dx `` = ``GraphicsWindow``.``MouseX

96.``  `` dy `` = ``GraphicsWindow``.``MouseY

97.``EndSub

Mouse Event Handler (on Move)

This subroutine moves the sighter depending on the mouse moving.  The mouse pointer (the arrow mark) is hided when the mouse is in the window, and is showed when the mouse is out of the window.

98.`` Sub ``OnMouseMove

99.``  `` mx `` = ``GraphicsWindow``.``MouseX

100.``  `` my `` = ``GraphicsWindow``.``MouseY

101.``  `` If `` 0 ``<`` = `` mx `` And `` mx `` < `` gw `` And `` 0 ``<`` = `` my `` And `` my `` < `` gh ``Then

102.``    ``Mouse``.``HideCursor``(``)

103.``    ``Shapes``.``Move``(``sighter`` , `` mx `` - ``40`` , `` my `` - ``40``)

104.``  ``Else

105.``    ``Mouse``.``ShowCursor``(``)

106.``  ``EndIf

107.``EndSub

Opening

This subroutine shows the game title and images of a duck and a sighter.  Originally these images were combination of shapes created with Shapes editor.  But to make the motion of the images smoother, I converted these shapes data to .png files.  The detail about how to convert shapes drawings created by Shapes editor to .png files is described here.

For following two reasons, I used both the image and shapes of the duck.  One is for it's eye blink.  The other is to avoid the GetPixel issue in browser described above (Main Loop).

108.`` Sub ``Opening

109.``  `` bgColor `` = `` "#8B0000" ``' DarkRed

110.``  `` stColor `` = `` "#990000" ``' for stair

111.``  ``GraphicsWindow``.`` BackgroundColor `` = ``bgColor

112.``  `` gw `` = ``598

113.``  `` gh `` = ``428

114.``  ``GraphicsWindow``.`` Width `` = ``gw

115.``  ``GraphicsWindow``.`` Height `` = ``gh

116.``  ``GraphicsWindow``.`` PenWidth `` = ``0

117.``  ``GraphicsWindow``.`` BrushColor `` = ``bgColor

118.``  ``GraphicsWindow``.``FillRectangle``(``0`` , ``0`` , ``gw`` , ``gh``)

119.``  ``' add duck image

120.``  `` path `` = ``"https://gallery.technet.microsoft.com/site/view/file/119954/1/Duck2.png"

121.``  `` img `` = ``ImageList``.``LoadImage``(``path``)

122.``  `` If `` silverlight ``Then

123.``    `` dw `` = `` 246 `` + ``1

124.``    `` dh `` = `` 192 `` + ``2

125.``  ``Else

126.``    `` dw `` = ``ImageList``.``GetWidthOfImage``(``img``)

127.``    `` dh `` = ``ImageList``.``GetHeightOfImage``(``img``)

128.``  ``EndIf

129.``  ``duck``[``1`` ] `` = ``Shapes``.``AddImage``(``img``)

130.``  ``Shapes``.``Move``(``duck``[``1``]`` , ``194`` , ``150``)

131.``  ``Shapes``.``HideShape``(``duck``[``1``]``)

132.``  ``' add stair

133.``  ``GraphicsWindow``.`` BrushColor `` = ``stColor

134.``  ``GraphicsWindow``.`` PenWidth `` = ``0

135.``  `` stair `` = ``Shapes``.``AddRectangle``(``gw`` , `` gh `` - ``yStair``)

136.``  `` yStair `` = ``Math``.``Round``(`` gh `` * `` 2 `` / ``3``)

137.``  ``Shapes``.``Move``(``stair`` , ``0`` , ``yStair``)

138.``  ``Shapes``.``HideShape``(``stair``)

139.``  ``' initialize shapes

140.``  ``GraphicsWindow``.`` FontName `` = ``"Trebuchet MS"

141.``  ``GraphicsWindow``.`` FontSize `` = ``50

142.``  ``GraphicsWindow``.`` BrushColor `` = ``"White"

143.``  `` title `` = ``Shapes``.``AddText``(``"DuckShoot"``)

144.``  ``Shapes``.``Move``(``title`` , ``170`` , ``60``)

145.``  ``Shapes_Init``(``)

146.``  ``' add shapes

147.``  `` scale `` = ``1

148.``  `` angle `` = ``0

149.``  `` iMin `` = ``1

150.``  `` iMax `` = ``10

151.``  ``Shapes_Add``(``)

152.``  ``' add sighter image

153.``  `` path `` = ``"https://gallery.technet.microsoft.com/site/view/file/119955/1/Sighter.png"

154.``  `` sighter `` = ``Shapes``.``AddImage``(``path``)

155.``  ``Shapes``.``Move``(``sighter`` , ``250`` , ``200``)

156.``  ``' Blink start

157.``  `` wait `` = ``"True"

158.``  `` ems `` = ``Clock``.``ElapsedMilliseconds

159.``  `` While ``wait

160.``    ``Program``.``Delay``(``1000``)

161.``    `` x `` = `` 250 `` + ``(``Math``.``GetRandomNumber``(``50`` ) `` - ``25``)

162.``    `` y `` = `` 200 `` + ``(``Math``.``GetRandomNumber``(``50`` ) `` - ``25``)

163.``    ``Shapes``.``Move``(``sighter`` , ``x`` , ``y``)

164.``    ``Program``.``Delay``(``100``)

165.``    ``Shapes``.``HideShape``(``shape``[``4``]``[``"obj"``]``)

166.``    ``Program``.``Delay``(``100``)

167.``    ``Shapes``.``ShowShape``(``shape``[``4``]``[``"obj"``]``)

168.``    `` If `` 5000 `` < ``Clock``.`` ElapsedMilliseconds `` - `` ems ``Then

169.``      `` wait `` = ``"False"

170.``    ``EndIf

171.``  ``EndWhile

172.``  ``Shapes``.``ShowShape``(``stair``)

173.``  `` iMin `` = ``1

174.``  `` iMax `` = ``10

175.``  ``Shapes_Remove``(``)

176.``  ``Shapes``.``Remove``(``title``)

177.``EndSub

Drawing Shapes

I wrote this subroutine for checking whether the gun shoot hit the duck or not.  This subroutine is used only for this purpose so far.  But for the future, I wrote this to be as general-purpose as possible.  And it has still restrictions as follows.

  • not support to draw border
  • not support rotation for rectangles and ellipses

This routine is called only when the program is run in browser.

178.`` Sub ``Shapes_Draw

179.``  ``' Shapes | draw shapes

180.``  ``' param iMin, iMax - shape indices to add

181.``  ``' param shape - array of shapes

182.``  ``' param scale - 1 if same scale

183.``  ``' TODO to draw border line for rectangle, triangle and ellipse

184.``  ``' TODO to rotate rectangle and ellipse (text?)

185.``  ``Stack``.``PushValue``(``"local"`` , ``x``)

186.``  ``Stack``.``PushValue``(``"local"`` , ``y``)

187.``  ``Stack``.``PushValue``(``"local"`` , ``i``)

188.``  `` s `` = ``scale

189.``  `` For `` i `` = `` iMin `` To ``iMax

190.``    `` If ``shape``[``i``]``[``"pw"`` ] `` > `` 0 ``Then

191.``      ``GraphicsWindow``.`` PenColor `` = ``shape``[``i``]``[``"pc"``]

192.``    ``EndIf

193.``    `` If ``Text``.``IsSubText``(``"rect|ell|tri|text"`` , ``shape``[``i``]``[``"func"``]`` ) ``Then

194.``      ``GraphicsWindow``.`` BrushColor `` = ``shape``[``i``]``[``"bc"``]

195.``    ``EndIf

196.``    `` x `` = `` shX `` + ``shape``[``i``]``[``"x"`` ] `` * ``s

197.``    `` y `` = `` shY `` + ``shape``[``i``]``[``"y"`` ] `` * ``s

198.``    `` If ``shape``[``i``]``[``"func"`` ] `` = `` "rect" ``Then

199.``      ``GraphicsWindow``.``FillRectangle``(``x`` , ``y`` , ``shape``[``i``]``[``"width"``]`` * ``s`` , ``shape``[``i``]``[``"height"`` ] `` * ``s``)

200.``    `` ElseIf ``shape``[``i``]``[``"func"`` ] `` = `` "ell" ``Then

201.``      ``GraphicsWindow``.``FillEllipse``(``x`` , ``y`` , ``shape``[``i``]``[``"width"``]`` * ``s`` , ``shape``[``i``]``[``"height"`` ] `` * ``s``)

202.``    `` ElseIf ``shape``[``i``]``[``"func"`` ] `` = `` "tri" ``Then

203.``      ``x``[``1`` ] `` = `` shX `` + ``shape``[``i``]``[``"x"`` ] `` * `` s `` + ``shape``[``i``]``[``"x1"`` ] `` * ``s

204.``      ``y``[``1`` ] `` = `` shY `` + ``shape``[``i``]``[``"y"`` ] `` * `` s `` + ``shape``[``i``]``[``"y1"`` ] `` * ``s

205.``      ``x``[``2`` ] `` = `` shX `` + ``shape``[``i``]``[``"x"`` ] `` * `` s `` + ``shape``[``i``]``[``"x2"`` ] `` * ``s

206.``      ``y``[``2`` ] `` = `` shY `` + ``shape``[``i``]``[``"y"`` ] `` * `` s `` + ``shape``[``i``]``[``"y2"`` ] `` * ``s

207.``      ``x``[``3`` ] `` = `` shX `` + ``shape``[``i``]``[``"x"`` ] `` * `` s `` + ``shape``[``i``]``[``"x3"`` ] `` * ``s

208.``      ``y``[``3`` ] `` = `` shY `` + ``shape``[``i``]``[``"y"`` ] `` * `` s `` + ``shape``[``i``]``[``"y3"`` ] `` * ``s

209.``      `` angle `` = ``shape``[``i``]``[``"angle"``]

210.``      `` If `` angle ``<`` > `` 0 ``Then

211.``        `` n `` = ``3

212.``        `` ox `` = ``(``x``[``2`` ] `` + ``x``[``3``]`` ) `` / ``2

213.``        `` oy `` = ``(``y``[``1`` ] `` + ``y``[``2``]`` ) `` / ``2

214.``        ``Shapes_RotatePolyline``(``)

215.``      ``EndIf

216.``      ``GraphicsWindow``.``FillTriangle``(``x``[``1``]`` , ``y``[``1``]`` , ``x``[``2``]`` , ``y``[``2``]`` , ``x``[``3``]`` , ``y``[``3``]``)

217.``    `` ElseIf ``shape``[``i``]``[``"func"`` ] `` = `` "line" ``Then

218.``      ``x``[``1`` ] `` = `` shX `` + ``shape``[``i``]``[``"x"`` ] `` * `` s `` + ``shape``[``i``]``[``"x1"`` ] `` * ``s

219.``      ``y``[``1`` ] `` = `` shY `` + ``shape``[``i``]``[``"y"`` ] `` * `` s `` + ``shape``[``i``]``[``"y1"`` ] `` * ``s

220.``      ``x``[``2`` ] `` = `` shX `` + ``shape``[``i``]``[``"x"`` ] `` * `` s `` + ``shape``[``i``]``[``"x2"`` ] `` * ``s

221.``      ``y``[``2`` ] `` = `` shY `` + ``shape``[``i``]``[``"y"`` ] `` * `` s `` + ``shape``[``i``]``[``"y2"`` ] `` * ``s

222.``      `` If `` angle ``<`` > `` 0 ``Then

223.``        `` n `` = ``3

224.``        `` ox `` = ``(``x``[``2`` ] `` + ``x``[``3``]`` ) `` / ``2

225.``        `` oy `` = ``(``y``[``1`` ] `` + ``y``[``2``]`` ) `` / ``2

226.``        ``Shapes_RotatePolyline``(``)

227.``      ``EndIf

228.``      ``GraphicsWindow``.``DrawLine``(``x``[``1``]`` , ``y``[``1``]`` , ``x``[``2``]`` , ``y``[``2``]``)

229.``    `` ElseIf ``shape``[``i``]``[``"func"`` ] `` = `` "text" ``Then

230.``      `` If `` silverlight ``Then

231.``        `` fs `` = ``Math``.``Floor``(``shape``[``i``]``[``"fs"`` ] `` * ``0.9``)

232.``      ``Else

233.``        `` fs `` = ``shape``[``i``]``[``"fs"``]

234.``      ``EndIf

235.``      ``GraphicsWindow``.`` FontSize `` = `` fs `` * ``s

236.``      ``GraphicsWindow``.`` FontName `` = ``shape``[``i``]``[``"fn"``]

237.``      ``GraphicsWindow``.``DrawText``(``x`` , ``y`` , ``shape``[``i``]``[``"text"``]``)

238.``    ``EndIf

239.``  ``EndFor

240.``  `` i `` = ``Stack``.``PopValue``(``"local"``)

241.``  `` y `` = ``Stack``.``PopValue``(``"local"``)

242.``  `` x `` = ``Stack``.``PopValue``(``"local"``)

243.``EndSub

Polyline Rotation

This subroutine is written for rotating vertices of a triangle.  But this subroutine supports more vertices as a polyline or a polygon for general-purpose.

244.`` Sub ``Shapes_RotatePolyline

245.``  ``' Shapes | rotate polyline

246.``  ``' param n - number of points

247.``  ``' param x, y - array of x and y co-ordinates

248.``  ``' param ox, oy, - center of rotation

249.``  ``' param angle - angle of rotation

250.``  ``Stack``.``PushValue``(``"local"`` , ``i``)

251.``  `` _a `` = ``Math``.``GetRadians``(``angle``)

252.``  `` For `` i `` = `` 1 `` To ``n

253.``    `` xi `` = ``(``x``[``i`` ] `` - ``ox`` ) `` * ``Math``.``Cos``(``_a`` ) `` + ``(``y``[``i`` ] `` - ``oy`` ) `` * ``Math``.``Sin``(``_a``)

254.``    `` yi `` = `` - ``(``x``[``i`` ] `` - ``ox`` ) `` * ``Math``.``Sin``(``_a`` ) `` + ``(``y``[``i`` ] `` - ``oy`` ) `` * ``Math``.``Cos``(``_a``)

255.``    ``x``[``i`` ] `` = `` xi `` + ``ox

256.``    ``y``[``i`` ] `` = `` yi `` + ``oy

257.``  ``EndFor

258.``  `` i `` = ``Stack``.``PopValue``(``"local"``)

259.``EndSub

Auto Generated Code

And this program also have following subroutines.  These subroutines are generated by Shapes editor 1.5b.  So I skip to show these codes.  But I listed up brief description for these.  There are other subroutines generated by Shapes.  But I deleted because they are not called in this program.

  • SB_Workaround - determine which workarounds needed or not for running on browser
  • Shapes_Add - adds Shapes objects along with the array shape
  • Shapes_CalcWidthAndHeight - calculates whole width and height for data in the array shape
  • Shapes_Init - sets duck shapes data into an array shape
  • Shapes_Move - moves shapes added in Shapes_Add
  • Shapes_Remove - removes shapes added in Shapes_Add

As duck shooting game, increasing ducks will make this game more fun.  Would you like to challenge?