コルネの進捗や備忘録が記されたなにか

進捗や成果物や備忘録てきななにかを雑に更新していきます。

【GPPB Virtual Bootcamp 2021 Sapporo】30分で作成!?解説しながら作る横スクアクションゲーム


スポンサードリンク

はじめに

この記事はGPPB Virtual Bootcamp 2021 Sapporo での私のセッション【30分で作成!?解説しながら作る横スクアクションゲーム】の登壇資料です。

本イベントの動画は下記にて公開されておりますので併せてご確認ください!

当日配信URL
こちら よりご確認ください

チャンネルURL

www.youtube.com

横スクアクションゲームの作成

概要

今回作成するゲームは、強制横スクロールを行い、画面をタップするとプレイヤーがジャンプして障害物を避けるというゲームです。

アニメーション部分にはSVG を利用します。
これはPower Apps のコントロールを駆使してアニメーションの描画を行うことも可能ではありますが、どうしてもアイテム数が増えてくると動作がもっさりしてしまい、ゲームをプレイしたときの爽快感が薄れてしまうからです。

ステージを作成する

ステージは画面がスクロールすることによって変化するのでSVG を利用して描画します。

まずは今回利用するSVG の基本形となる画面に四角形の図形を表示するためのSVG をコピペして作成します。
画像コントロールは画面領域いっぱいに表示されるようにします。

StageImage.Image

"data:image/svg+xml,"& 
EncodeUrl(
    "<svg viewBox='0 0 "& Self.Width & " " & Self.Height & "' xmlns='http://www.w3.org/2000/svg'>
        <rect 
            x='"& (Self.Width - 200) / 2 & "' 
            y='" & (Self.Height - 200) / 2 & "' 
            width='200' 
            height='200' 
        />
    </svg>"
)

これで画面中央に黒色の四角形が表示されたかと思います。
これが今回作成するステージの基本形となります。

続いて、ステージが"どこに"、"どれだけの大きさで"存在するかを表した情報を作成します。
この情報は stage というコレクションに設定したいと思います。

開発を簡単にするために、 initButton というボタンを作成して、そのボタンが押されたときに各種変数類が初期化されるようにしておきます。

initButton.OnsSelect

ClearCollect(
    stage,
    {x:0, y:Parent.Height - 150, width:Parent.Width, height:150}
)

initButton.OnsSelect = Screen1.OnVisible となります。
開発が完了したらこのボタンは削除し、設定した式は OnVisibel に記載することにします。

上記の式でステージの初期情報を作成することができました。
なのでステージの見た目もこのコレクションの内容と同じものを表示させるようにしたいと思います。

これを実現するために、コレクションのレコード情報をForAll 関数で取得し、その結果をConcat 関数で文字列の結合を行うようにします。

StageImage.Image

"data:image/svg+xml,"& 
EncodeUrl(
    "<svg viewBox='0 0 "& Self.Width & " " & Self.Height & "' xmlns='http://www.w3.org/2000/svg'>" &
        Concat(
            ForAll(
                stage,
                "<rect 
                    x='" & x &"' 
                    y='" & y &"' 
                    width='" & width &"' 
                    height='" & height &"' 
                />"
            ),
            Value
        ) & "
    </svg>"
)

これで、画面下部にスクリーンの幅いっぱいで高さ150の四角形を作成することができました。

これでステージの作成は完了です。

プレイヤーを表示する

プレイヤーもSVG で表示させます。

今回プレイヤーは複雑なアニメーションをさせないので、普通に画像を読み込んで表示させてもよかったのですが、画像の引用元を明らかにしておきたかった( <metadata> にて引用元を記載しています。)のと、機能の追加でプレイヤーにもアニメーションを適用(ジャンプなどで画像が傾くなど)することを想定してSVG で表示させています。

大きさ100の画像コントロールを追加して、以下を設定します。

PlayerImage.Image

"data:image/svg+xml,"& 
EncodeUrl(
    "<svg version='1.0' xmlns='http://www.w3.org/2000/svg'
 width='1198.000000pt' height='1280.000000pt' viewBox='0 0 1198.000000 1280.000000'
 preserveAspectRatio='xMidYMid meet'>
<metadata>
Created by potrace 1.15, written by Peter Selinger 2001-2017
'https://svgsilh.com/ja/image/2029531.html'
</metadata>
    <g
     transform='matrix(-0.1,0,0,-0.1,1198.3267,1280) rotate(0 0 0)'
     fill='#000000'
     stroke='none'
     >
    <path
       d='m 8235,12784 c -140,-31 -244,-82 -481,-236 -180,-117 -202,-120 -244,-34 -25,54 -36,64 -75,68 -26,3 -31,-1 -46,-42 -24,-60 -23,-103 2,-151 33,-65 27,-102 -31,-211 -28,-54 -61,-105 -73,-113 -12,-8 -39,-15 -60,-15 -42,0 -77,28 -109,88 -34,63 -108,29 -138,-64 -16,-46 -9,-85 29,-172 17,-39 31,-77 31,-84 0,-8 -13,-27 -30,-43 -34,-32 -38,-60 -14,-94 14,-20 23,-22 56,-18 64,9 103,-9 181,-82 40,-37 101,-86 134,-109 111,-76 138,-136 128,-294 l -6,-106 30,-34 c 16,-19 69,-58 118,-88 111,-68 192,-144 232,-220 57,-107 101,-127 206,-94 48,15 55,15 110,0 62,-19 115,-61 115,-94 0,-33 -36,-67 -111,-107 -81,-42 -109,-67 -109,-94 0,-29 47,-79 107,-113 29,-16 53,-36 53,-44 0,-8 -21,-24 -47,-35 -26,-11 -79,-44 -118,-73 -38,-29 -91,-63 -117,-75 -47,-22 -70,-51 -123,-156 -31,-60 -74,-97 -171,-147 -113,-58 -191,-88 -340,-133 -179,-55 -361,-129 -544,-223 -373,-192 -431,-217 -596,-251 -197,-41 -395,-50 -689,-30 -179,12 -210,20 -230,60 -24,46 -18,73 37,155 75,112 127,164 166,164 35,0 62,22 62,51 0,24 -43,69 -65,69 -47,0 -149,-81 -330,-259 -178,-177 -193,-189 -242,-201 -90,-21 -126,-8 -209,74 -41,40 -76,84 -80,100 -9,36 3,84 55,218 58,151 75,241 68,358 -7,118 -33,298 -53,367 -19,70 -137,227 -269,359 -81,81 -116,108 -182,141 -136,68 -216,68 -372,-1 -42,-18 -110,-42 -151,-52 -41,-11 -120,-43 -175,-72 -55,-29 -150,-77 -210,-107 -134,-66 -186,-108 -220,-180 -14,-30 -30,-55 -35,-55 -21,0 -40,-34 -40,-73 0,-40 -41,-175 -90,-299 -22,-57 -23,-58 -6,-163 9,-58 19,-147 23,-198 7,-101 -3,-85 106,-179 35,-30 39,-39 47,-102 10,-80 33,-118 78,-127 71,-14 100,-51 121,-154 25,-119 66,-162 231,-245 95,-47 190,-112 235,-160 42,-44 96,-70 149,-70 37,0 53,7 103,46 136,106 180,124 254,104 22,-6 60,-28 82,-49 23,-21 62,-51 87,-68 111,-75 171,-173 197,-321 14,-75 14,-111 4,-257 -7,-93 -16,-240 -21,-325 -11,-184 -32,-274 -95,-408 -61,-128 -158,-284 -200,-321 -19,-16 -51,-33 -72,-37 -56,-10 -562,4 -608,17 -37,11 -51,23 -143,126 -43,47 -127,69 -201,53 -53,-12 -62,-19 -191,-150 -111,-113 -144,-140 -185,-155 -108,-38 -173,-78 -273,-166 -57,-50 -112,-105 -123,-123 -23,-36 -94,-261 -103,-324 -20,-136 61,-298 198,-399 77,-57 123,-80 263,-133 160,-59 203,-70 336,-80 65,-5 125,-14 133,-20 34,-29 20,-90 -103,-465 -118,-361 -196,-542 -251,-585 -34,-27 -60,-25 -187,15 -556,178 -1137,86 -1668,-263 C 1007,4101 893,4007 757,3872 372,3491 121,3016 29,2493 -4,2312 -4,1981 28,1806 132,1242 424,797 956,397 1163,242 1248,197 1481,122 1706,49 1914,13 2165,3 c 471,-18 950,108 1347,354 500,309 858,785 997,1326 54,208 66,317 62,567 -2,124 -4,270 -5,325 l -1,101 38,51 c 42,58 60,105 77,208 24,138 88,211 208,236 144,29 338,-74 409,-219 23,-47 28,-70 28,-132 0,-87 -22,-138 -79,-187 -39,-35 -74,-48 -164,-62 -163,-26 -196,-107 -138,-341 44,-177 35,-221 -65,-328 -64,-68 -98,-129 -99,-173 0,-73 52,-128 108,-114 39,10 275,129 340,172 31,21 88,68 127,104 73,69 102,89 131,89 9,0 55,-17 103,-39 217,-97 426,-119 926,-97 278,12 281,10 305,-149 34,-227 195,-552 400,-810 203,-254 491,-492 770,-634 465,-237 1026,-301 1556,-176 706,166 1315,672 1597,1325 120,278 166,489 174,796 6,220 -4,339 -42,522 -126,596 -520,1146 -1070,1493 -119,74 -326,177 -436,215 -44,15 -85,34 -90,41 -8,9 -8,45 1,130 37,342 -51,701 -238,967 -33,48 -55,90 -55,105 4,159 17,254 89,636 15,80 45,145 80,176 11,9 62,40 114,69 52,28 169,101 260,162 269,181 287,190 710,350 856,323 974,379 1030,489 17,33 20,59 20,186 -1,617 -262,1099 -830,1532 -217,165 -443,308 -653,412 -127,64 -151,85 -127,114 25,30 109,18 333,-50 360,-107 503,-135 701,-135 201,0 268,29 302,133 24,72 40,94 81,108 93,30 232,-9 352,-100 30,-22 62,-41 72,-41 10,0 27,12 39,28 19,23 22,35 17,92 -11,125 -4,123 -329,118 -296,-4 -303,-6 -389,-98 -55,-59 -57,-60 -109,-60 -112,0 -617,99 -791,156 -62,20 -104,64 -95,99 4,15 18,38 31,51 23,23 29,24 178,24 153,0 155,0 278,41 68,22 159,56 201,75 101,44 307,175 363,231 56,55 110,162 100,195 -5,12 -38,55 -76,93 -45,47 -81,97 -108,150 -24,48 -65,106 -102,145 -33,36 -68,79 -77,95 -22,43 -31,102 -21,140 13,47 132,154 211,191 329,153 445,236 554,399 39,58 52,125 35,178 -19,57 -75,62 -101,9 -62,-123 -199,-273 -326,-355 -39,-25 -112,-62 -163,-83 -167,-67 -288,-169 -352,-299 -55,-112 -50,-191 15,-277 47,-61 73,-115 97,-205 27,-101 31,-107 100,-180 67,-70 73,-96 32,-136 -40,-38 -114,-68 -328,-132 -105,-31 -252,-81 -327,-111 -76,-30 -142,-54 -147,-54 -29,0 -157,-52 -193,-79 -52,-39 -89,-40 -117,-5 -31,40 -27,91 14,170 38,73 44,117 19,152 -23,33 -58,27 -140,-24 -91,-56 -106,-74 -172,-201 -93,-175 -113,-180 -223,-45 -40,48 -128,144 -196,213 -145,148 -147,155 -74,217 27,23 52,51 56,62 9,29 -12,60 -45,67 -46,9 -64,37 -70,107 -5,56 -7,61 -30,64 -20,2 -45,-15 -116,-81 -50,-46 -103,-87 -117,-91 -95,-24 -183,117 -194,311 -10,160 36,224 223,321 56,28 107,59 115,68 26,31 91,54 157,54 78,0 118,18 172,76 33,36 41,51 38,77 -3,32 -3,32 -66,35 -81,4 -87,18 -33,77 56,61 55,85 -9,131 -27,20 -50,42 -50,49 0,8 29,28 64,45 70,33 99,68 111,131 14,77 -42,179 -98,179 -13,0 -52,-19 -86,-42 -79,-53 -134,-58 -179,-15 -16,15 -34,38 -40,50 -6,12 -35,74 -65,138 -46,97 -65,124 -115,170 -67,62 -183,126 -287,159 -93,29 -254,36 -350,14 z m -96,-3551 c 95,-47 123,-115 102,-238 -27,-158 -35,-397 -23,-667 42,-888 211,-1647 460,-2061 89,-147 90,-148 81,-502 -4,-160 5,-221 93,-580 47,-189 52,-222 52,-326 1,-112 0,-117 -29,-165 -43,-69 -112,-114 -176,-114 -28,0 -64,6 -82,14 -40,16 -95,77 -103,114 -21,99 -36,105 -263,112 -239,7 -235,5 -309,155 -29,60 -79,168 -109,239 -30,72 -92,217 -138,321 -46,105 -90,212 -99,238 -36,106 -12,154 89,182 33,9 101,35 150,57 141,63 180,56 231,-44 26,-52 41,-58 72,-30 43,38 56,153 23,208 -24,40 -86,70 -183,88 -135,26 -151,51 -117,178 38,143 48,210 49,315 0,177 -44,321 -146,476 -30,44 -54,86 -54,93 0,6 8,22 19,35 15,19 27,23 72,22 79,-2 91,3 87,39 -3,28 -8,32 -73,52 -169,54 -186,88 -117,232 33,71 37,87 37,159 -1,93 -15,142 -51,176 -23,22 -36,24 -152,29 -126,6 -128,6 -212,50 -125,65 -349,219 -562,384 -103,80 -229,174 -280,209 -99,67 -115,92 -82,128 30,33 140,59 359,84 421,49 616,105 923,266 83,44 178,86 210,94 83,21 182,13 251,-22 z M 5565,7315 c 50,-30 121,-68 158,-84 167,-75 293,-170 328,-246 13,-28 19,-64 19,-118 0,-88 17,-139 52,-158 12,-7 52,-15 88,-18 59,-5 65,-8 68,-30 2,-12 1,-34 -2,-47 -6,-24 -110,-109 -217,-177 -31,-20 -94,-68 -140,-107 -46,-39 -122,-94 -169,-122 -47,-28 -107,-72 -133,-97 l -48,-46 -277,-4 c -242,-3 -664,11 -729,25 -40,8 -28,48 50,167 87,133 126,201 282,492 65,121 142,254 173,295 61,83 289,315 324,330 37,15 76,3 173,-55 z M 4218,6678 c 19,-19 14,-108 -9,-178 -69,-210 -241,-446 -372,-509 -38,-18 -62,-21 -183,-21 -190,0 -227,12 -311,96 -35,35 -70,64 -77,64 -7,0 -28,-10 -47,-23 -19,-13 -52,-36 -73,-50 -113,-78 -276,18 -276,161 0,64 23,106 97,184 156,161 356,250 623,277 138,14 614,13 628,-1 z M 2740,6375 c 8,-10 10,-31 5,-64 -14,-100 37,-225 127,-313 60,-59 126,-90 257,-124 97,-25 108,-26 176,-14 63,11 76,10 103,-4 65,-34 71,-99 14,-156 -51,-51 -101,-64 -227,-57 -241,14 -465,94 -565,204 -99,108 -118,234 -61,406 28,83 38,102 65,118 37,23 89,25 106,4 z m 1700,-510 c 36,-19 44,-48 36,-119 -7,-51 -5,-57 23,-88 28,-32 33,-33 113,-36 46,-2 132,3 191,12 59,9 132,16 163,16 65,0 118,20 134,51 8,15 11,45 7,86 -8,81 3,88 117,79 158,-13 162,-39 32,-234 -102,-154 -123,-208 -111,-290 14,-100 82,-207 178,-280 47,-36 145,-93 312,-180 17,-9 52,-17 80,-18 65,-4 75,-19 75,-110 0,-107 -17,-169 -70,-264 -76,-133 -100,-249 -120,-565 -6,-99 -18,-211 -26,-250 -8,-38 -28,-140 -44,-225 -51,-266 -68,-317 -109,-340 -32,-16 -39,-13 -94,47 -76,85 -192,205 -812,848 -674,698 -684,709 -816,858 -57,66 -124,141 -148,168 -24,28 -43,59 -43,72 3,101 21,257 31,277 37,69 77,90 196,100 63,5 106,15 136,30 85,43 288,231 324,299 12,24 49,46 105,65 33,10 113,5 140,-9 z m 2955,-57 c 33,-15 54,-51 103,-173 63,-156 162,-375 251,-552 44,-90 81,-170 81,-178 0,-9 7,-29 14,-44 13,-25 13,-31 0,-50 -12,-17 -25,-21 -68,-21 -76,0 -198,34 -291,80 -98,49 -371,243 -380,270 -4,13 5,44 24,84 17,34 52,118 77,187 25,68 56,145 70,172 19,38 24,64 24,121 0,59 4,76 20,94 24,25 38,27 75,10 z m -275,-88 c 7,-44 -24,-190 -60,-292 -45,-125 -124,-228 -173,-228 -11,0 -46,18 -78,39 -67,45 -146,77 -236,96 -34,7 -66,16 -72,20 -14,8 -14,60 -1,85 12,22 68,40 126,40 64,0 146,48 289,170 116,98 134,109 165,107 32,-2 35,-5 40,-37 z M 3495,4934 c 9,-4 30,-34 47,-68 57,-114 110,-178 352,-429 350,-363 465,-484 614,-649 73,-81 162,-173 198,-205 63,-55 134,-154 134,-186 0,-20 -104,-56 -157,-54 -50,2 -87,-27 -97,-75 -8,-40 -85,-118 -117,-118 -40,0 -144,120 -233,269 -174,290 -395,502 -798,764 -80,52 -154,107 -164,122 -17,26 -16,31 50,248 37,122 73,238 78,257 13,44 36,90 56,113 17,20 15,19 37,11 z m 3148,-476 c 87,-18 86,-36 -27,-301 -91,-216 -197,-511 -242,-675 -19,-69 -48,-158 -63,-197 -15,-38 -31,-88 -35,-110 -11,-57 -50,-120 -93,-146 -28,-17 -56,-24 -118,-27 -106,-6 -130,5 -121,56 10,67 103,233 191,346 47,60 102,143 122,185 20,42 56,103 80,136 57,78 99,165 119,251 21,86 23,278 5,392 -18,108 -11,117 74,107 33,-3 81,-11 108,-17 z m 464,-33 c 158,-20 288,-48 425,-91 224,-71 224,-105 3,-269 -158,-117 -254,-205 -362,-330 -218,-253 -360,-529 -414,-806 -10,-55 -19,-115 -19,-134 0,-67 -48,-82 -137,-41 -38,18 -59,36 -79,68 -14,24 -44,60 -66,80 l -40,38 4,67 c 3,43 16,100 36,153 17,47 54,157 83,245 28,88 78,219 110,292 32,73 78,204 104,295 66,233 125,410 143,431 19,21 55,22 209,2 z m 1981,-147 c 6,-7 26,-87 46,-178 30,-143 60,-296 158,-810 25,-135 27,-281 4,-335 -31,-72 -68,-115 -155,-178 -74,-54 -92,-62 -128,-61 -53,1 -70,15 -83,68 -30,118 -69,227 -135,376 -40,91 -105,240 -144,333 l -71,167 v 299 c 0,259 2,302 16,315 13,14 50,16 248,16 175,0 236,-3 244,-12 z m -6448,-91 c 166,-36 224,-57 228,-84 2,-11 -13,-54 -33,-93 -20,-40 -52,-118 -72,-174 -19,-55 -96,-234 -170,-396 -75,-162 -155,-339 -178,-393 -24,-53 -70,-143 -104,-200 -33,-56 -78,-136 -99,-177 l -39,-75 -81,-5 c -61,-4 -86,-10 -103,-25 -32,-29 -49,-77 -49,-140 0,-48 4,-59 34,-93 19,-20 39,-56 45,-80 22,-81 -29,-76 801,-85 464,-5 847,-4 1040,2 311,11 379,7 408,-23 37,-37 0,-310 -69,-517 C 4017,1076 3551,616 2970,415 2735,334 2555,305 2290,306 2012,307 1795,349 1555,447 685,802 184,1742 371,2664 c 56,277 196,573 377,801 297,372 736,640 1187,724 157,29 159,29 385,26 173,-3 223,-7 320,-28 z m 7237,-58 c 471,-244 828,-639 1043,-1154 47,-115 140,-399 140,-431 0,-12 -8,-32 -19,-45 -26,-32 -132,-45 -506,-60 -274,-11 -309,-10 -410,6 -60,10 -144,20 -185,22 -46,3 -81,11 -92,20 -40,35 -118,304 -145,497 -23,164 -27,1153 -4,1179 24,30 75,20 178,-34 z m -1837,-79 c 27,-15 328,-420 399,-538 138,-230 315,-580 367,-728 16,-45 16,-55 4,-85 -19,-44 -84,-109 -134,-133 -35,-16 -49,-17 -145,-7 -58,6 -187,15 -286,21 -210,12 -269,19 -545,65 -113,19 -259,41 -325,50 -271,35 -337,85 -301,224 18,69 137,315 216,445 66,110 178,261 259,348 107,116 291,261 416,327 45,24 50,25 75,11 z M 3307,3944 c 168,-101 275,-184 423,-333 186,-187 304,-359 406,-591 134,-304 200,-614 144,-670 -19,-19 -33,-20 -522,-19 -848,2 -1289,12 -1315,29 -43,28 -47,60 -20,141 68,202 322,738 612,1290 103,195 118,219 145,219 9,0 67,-30 127,-66 z M 6088,2870 c 69,-42 75,-110 27,-306 -25,-107 -41,-129 -101,-140 -76,-14 -180,60 -204,145 -18,64 -24,187 -10,220 11,28 56,53 170,94 33,12 87,6 118,-13 z m 345,-121 c 50,-26 67,-51 67,-99 0,-137 -35,-200 -111,-200 -78,0 -129,37 -129,92 0,40 47,168 71,194 25,26 66,31 102,13 z m 864,-129 c 333,-65 546,-97 838,-125 84,-8 159,-17 168,-21 10,-3 17,-16 17,-29 0,-50 8,-50 -594,-50 -503,0 -569,2 -636,18 -108,25 -105,22 -105,123 1,78 3,88 24,105 32,26 52,25 288,-21 z m 2333,-316 c 86,-12 221,-15 754,-13 619,2 649,2 658,-16 23,-41 -5,-255 -62,-475 -219,-847 -887,-1437 -1732,-1529 -458,-50 -935,70 -1345,338 -249,163 -433,344 -586,576 -98,149 -267,515 -267,579 0,35 31,64 77,71 21,3 92,23 158,45 163,54 1182,304 1563,384 67,14 155,26 195,27 39,0 117,9 172,19 113,22 236,20 415,-6 z m -3095,-34 c 20,-19 25,-33 25,-74 0,-49 -2,-52 -62,-109 -92,-87 -188,-156 -226,-163 -20,-4 -49,0 -75,10 -29,11 -71,15 -134,14 -81,-1 -98,2 -147,26 -80,39 -118,95 -83,124 22,19 381,154 472,178 114,31 193,28 230,-6 z m 1589,14 c 24,-9 19,-51 -8,-69 -13,-9 -75,-27 -137,-40 -63,-13 -222,-50 -354,-84 -256,-64 -452,-105 -551,-116 -46,-5 -63,-3 -72,8 -7,8 -17,59 -22,113 -13,130 -13,143 5,171 l 15,23 h 554 c 305,0 561,-3 570,-6 z M 6741,2184 c 8,-9 15,-51 17,-103 4,-82 3,-88 -22,-118 -55,-66 -129,-63 -169,5 -18,31 -18,35 -3,75 19,51 68,113 110,138 38,23 50,24 67,3 z'/>
</g>
</svg>"
)

以上でプレイヤーの表示は完了です。

重力を作成する

重力を作成して、プレイヤーが時間経過によって、下に落ちるようにします。

今回時間経過処理にはスライダーを用います。
ゲーム実行中はスライダーコントロールOnChange が呼ばれるようにしてループ処理を実現します。

なので、スライダーコントロールを追加してください。

まずは変数を初期化します。
今回追加する初期化変数は、"プレイヤーの座標" PlayerX, PlayerY 、"スライダーの値" sliderVal 、"ゲーム開始を判定する変数" playGame です。

InitButton.OnSelect

UpdateContext({PlayerX:50, PlayerY:470});
UpdateContext({sliderVal:0});
UpdateContext({playGame:false});

PlayerX, PlayerYPlayerImageXY にそれぞれ設定しておき、 sliderVal はスライダーコントロールDefault に設定しておきましょう。

ゲームの開始はStart ボタンが押されたら開始することにします。
ボタンを追加して以下のように設定します。

StartButton.OnSelect

If(
    playGame,
    UpdateContext({playGame:false});
    Reset(Slider1),
    UpdateContext({sliderVal:sliderVal + 1});
    UpdateContext({playGame:true})
)

ゲームがまだ開始していない場合は、スライダーの OnChange を呼び出すようにして、ゲームが開始している場合にボタンが押されたときはゲームを中断するようにしています。

わかりやすいよう、ボタンに表示される文字も変更しておきましょう。

StartButton.Text

If(!playGame,"Start","Stop")

最後に重力を作成します。
重力 = プレイヤーが下方向に移動する。
なので、 OnChange 処理で PlayerY の値を増やしてあげればよいです。

Slider1.OnChange

If(
    playGame,
    Reset(Slider1);
    UpdateContext({PlayerY:PlayerY + 10});
)

これで重力を実装することができました。
ただこのままでは、プレイヤーがステージを貫通して落ちてしまうので、プレイヤーとステージとの当たり判定を作成します。

プレイヤーとステージとの間に当たり判定を実装する

プレイヤーとステージとの当たり判定にはコントロール名、切り替えのトグルを利用します。

このトグルの値が True の場合がプレイヤーとステージは接触していると判定することにしたいと思います。

当たり判定は以下のようにPlayer とStage を定義したとき

f:id:koruneko:20210214163618p:plain

以下のような式であらわすことができます。

((PlayerX > StageX && PlayerX < StageX + StageWidth) ||
(StageX > PlayerX && StageX < PlayerX + PlayerWidth))  &&
((PlayerY > StageY && PlayerY < StageY + StageHeight) ||
(StageY > PlayerY && StageY < PlayerY + PlayerHeight))

まず2つの図形が"重なっている"というのは、下図のようにX成分でみたときもY成分でみたときも重なっている。
というのが条件になります。

f:id:koruneko:20210214164617p:plain

上の式でいいますと、
この式がX成分の重なり判定で

((PlayerX > StageX && PlayerX < StageX + StageWidth) ||
(StageX > PlayerX && StageX < PlayerX + PlayerWidth))

この式がY成分の重なり判定です。

((PlayerY > StageY && PlayerY < StageY + StageHeight) ||
(StageY > PlayerY && StageY < PlayerY + PlayerHeight))

これら2つの条件を満たしているとき、重なっていると判断できるので && でそれぞれの式を繋げているわけです。

ただし、今回この当たり判定を実装するうえで1点注意しなくてはいけないことがあります。

今回当たり判定を実装したいのは、プレイヤーとステージが接しているか?です。
これをもっと細かく定義すると、
"プレイヤーの下部"と"ステージの上部"が接しているか?
です。

なぜ、ここまで細かく定義するか?というと、上式のまま判定を作成してしまうと、プレイヤーがステージの横方向から突っ込んだ場合でもプレイヤーとステージが接している。と判定してしまうことになるからです。
これはこのゲームを作成するうえで意図した実装ではないので、"プレイヤーの下部"と"ステージの上部"が接しているか?で判定式を作成したいと思います。

これらを踏まえ判定式を作成すると以下のようになります。

isGrand.Default

CountIf(
    ForAll(
        stage,
        ((PlayerImage.X > x && PlayerImage.X < x + width) ||
        (x > PlayerImage.X && x < PlayerImage.X + PlayerImage.Width))  &&
        ((PlayerImage.Y + PlayerImage.Height - 5 > y && PlayerImage.Y + PlayerImage.Height - 5 < y + 5) ||
        (y > PlayerImage.Y + PlayerImage.Height - 5 && y < PlayerImage.Y + PlayerImage.Height))
    ),
    Value = true
) > 0

プレイヤー、ステージそれぞれ高さで定義しています。
プレイヤーの場合、Y座標も調整が必要ですので、そのことも式に盛り込んでいます。

また、判定を行うのはコレクションの存在するステージすべてですので ForAll 関数を用いており、うち1つでも True になれば、ステージのどれかにプレイヤーが接しているということなので、 CountIf 関数で0より大きい場合という条件を指定しています。

プレイヤーがステージと接している場合、プレイヤーのY 座標を加算してはいけないので、重力を設定していた式を以下のように編集します。

Slider1.OnChange

If(
    playGame,
    Reset(Slider1);
    If(!isGrand.Value, UpdateContext({PlayerY:PlayerY + 10}));
)

ジャンプ機能を追加する

画面をクリック(タップ)したときにプレイヤーがジャンプするようにしたいと思います。

ジャンプ機能を追加するにあたり、今回は以下の仕組みを使用しようと思います。

  • ジャンプができる条件:プレイヤーが地面にいる場合。
  • 画面が押されたら:ジャンプができる状態だった場合、変数に一定数値(今回は15)を加算する。
  • ループ処理:変数の値が0より大きければ上昇させる。変数の値を減算する。

これらの条件を満たすことにより、

  • 2重ジャンプの禁止
  • プレイヤーが上昇し続けるバグを抑制

することが可能となります。

では早速実装を行っていきます。

まずは変数の宣言です。

ジャンプをしていない状態 = 0 とするので以下のように宣言します。

InitButton.OnSelect

UpdateContext({PlayerJump:0});

画面が押されたときの処理は、ステージの画像を最終的に最前面に持ってくる StageImage.OnSelect に記載します。

StageImage.OnSelect

If(isGrand.Value, UpdateContext({PlayerJump:15}))

ループ処理はスライダーコントロールOnChange に該当するので、以下のように設定します。

Slider1.OnChange

If(
    playGame,
    Reset(Slider1);
    If(!isGrand.Value && PlayerJump < 0, UpdateContext({PlayerY:PlayerY + 10}));
    If(PlayerJump >= 0, UpdateContext({PlayerY:PlayerY - 10, PlayerJump:PlayerJump - 1}));
)

これで先に述べた機能を満たすことができましたので、ジャンプ機能の作成完了です!

横スクロール機能を作成する

横スクロールはプレイヤーの座標を変更するのではなく、ステージをプレイヤーの進行方向とは反対方向に動かすことで横スクロールしているように見せたいと思います。
* 初回移動時だけプレイヤーを動かします。

プレイヤーは画面左から右へ進んでいるようにみせたいので、反対にステージは右から左へ移動させる。つまりx 座標を減らしてあげればよいです。

これは下記のようにすることで実現可能です。

If(PlayerX > Parent.Width / 3 || !isGrand, UpdateIf(stage, true, {x:x - 10}), UpdateContext({PlayerX:PlayerX + 5}));

PlayerX > Parent.Width / 3 || !isGrand これはゲーム開始時はプレイヤーを画面の1 / 3進めるための処理です。
初回地面と接触するまではプレイヤーのx 成分を動かさないようにしています。
これはこのほうが動きが自然にみえるな。と考えたためです。

ただこのまま減らしただけでは初期で宣言したステージの幅より先はなにもないので、ゲームが終了してしまいます。

なのでステージの自動生成も追加したいと思います。

ステージを自動生成するタイミング = ステージとステージ間の溝

になります。

これは最大でもプレイヤーがギリギリジャンプ可能な位置からジャンプして次のステージまで到達可能な幅にしなくてはいけません。

この計算には、

  • プレイヤーのジャンプ力
  • プレイヤーのx 方向への進む速度
  • ステージ間の高さの差

が分かれば、厳密に計算を行おうと思えば行うことも可能ですが、今回は感覚で設定しています。

また、生成されるステージはランダム性を持たせたいので、 Rand 関数を用います。

ステージのy 座標の値は高さによって決定するので、高さの値はWith 関数で先に決定しておきます。

また、ステージがだんだんずれていくので、コレクション内には不要になったレコードがでてきます。
性能面も考えてこの不要なレコードは削除するようにしましょう。
不要なレコードというのは、画面外にでたステージですね。

上記を踏まえて式を追加すると、以下のようになります。

Slider1.OnChange

If(
    playGame,
    Reset(Slider1);
    If(!isGrand.Value && PlayerJump < 0, UpdateContext({PlayerY:PlayerY + 10}));
    If(PlayerJump >= 0, UpdateContext({PlayerY:PlayerY - 10, PlayerJump:PlayerJump - 1}));
    If(PlayerX > Parent.Width / 3 || !isGrand, UpdateIf(stage, true, {x:x - 10}), UpdateContext({PlayerX:PlayerX + 5}));
    If(
        Parent.Width - (Last(stage).x + Last(stage).width) > 200 + Rand() * 50,
        With(
            {H:Last(stage).height + Rand() * 50 * If(Last(stage).height > 130, If(Rand() > 0.5, 1, -1), 1)},
            Collect(
                stage,
                {
                    x:Parent.Width,
                    y:Parent.Height - H,
                    width:200 + Rand() * 200,
                    height:H
                }
            )
        )
    );
    RemoveIf(stage, x + width < 0)
)

高さを設定している箇所は以下です。

H:Last(stage).height + Rand() * 50 * If(Last(stage).height > 130, If(Rand() > 0.5, 1, -1), 1)

この式を日本語にすると、「追加前のステージの高さが130よりも大きかった場合、50%の確率で最大で高さを50増減させます。130よりも小さかった場合は最大50増やします。」となります。

いくらランダムとはいえ画面外にステージが形成されてはいけないからですね。

スコア表示を行う

ゲーム性を高めるためにスコア表示を行おうと思います。

この機能の実装は簡単にユーザーがゲームをプレーしている間だんだん加算される仕組みにしたいと思います。

InitButton.OnSelect

UpdateContext({Score:0});

Slider1.OnChange(ゲーム実行中に処理されるif 文内に作成)

UpdateContext({Score:Score + 50});

この変数の値を画面に表示させてあげましょう。

ScoreLabel.Text

"Score:" & Score

ゲームオーバー画面を作成する

このゲームの終了条件はプレイヤーが画面外へ落ちてしまうことだけで、クリア条件はありません。
よって今回作成するのはゲームオーバー画面のみです。

ゲームオーバーの条件は、プレイヤーが完全に画面外にでたときとしたいと思います。

地面に接しているときの判定と同じように判定にはトグルを利用します。

トグルの Default に以下の式を適用します。

isGameOver.Default

PlayerY > Parent.Height

ゲームオーバーとなった場合、ゲームの実行を止めたいので以下のように設定します。

isGameOver.OnCheck

UpdateContext({playGame:false});
Reset(Slider1)

これでシステム的にはゲームオーバー判定となりましたが、ゲームをプレーしているユーザーにもゲームオーバーとなったことを知らせてあげましょう。

GameOverLabel.Text

"Game Over..."

GameOverLabel.Visible

isGameOver.Value

このラベルを選択したら、再度ゲームがプレーできる状態としたいため以下のように初期化処理を行うようにします。

GameOverLabel.OnSelect

UpdateContext({PlayerX:50, PlayerY:470});
UpdateContext({sliderVal:0});
UpdateContext({playGame:false});
UpdateContext({PlayerJump:0});
UpdateContext({Score:0});
ClearCollect(
    stage,
    {x:0, y:Self.Height - 150, width:Self.Width, height:150}
)

表示順やVisible を調整

最後に不要なコントロールを非表示に設定します。
不要 = ユーザーには表示する必要がないコントロール
ですので、以下コントロールを非表示にします。

  • Slider1 (スライダー)
  • isGrand (切り替え)
  • isGameOver (切り替え)

また、開発用に作成していた initButtonOnSelect の値を Screen1.OnVisible に移して削除しておきます。

また、コントロールはそれぞれ以下の順序になるように設定します。(値が小さいものが前面)

  1. GameOverLabel
  2. StartButton
  3. StageImage

他コントロールの順序はこれより背面に設定されているのであれば、特に気にしなくて大丈夫です。

以上で横スクアクションゲームの作成完了です!!

おわりに

もし作成してみてわからなかったことや、こんな機能追加したいがどうすればできるか?
などありましたらご気軽にお尋ねくださいー。


スポンサードリンク