← All the games

Hopper

All of Hopper is here, in Quantum's game language: hopper.q, with levels/level-2.tmx, compiled for Godot 4.4.1, with no script besides. Point at a tag or an attribute to see what it does; a tag opens its entry in the language's reference.

Play in the browser
The 50 tags it uses, of the language's 145 qg:animation qg:become qg:behavior qg:bounce qg:button qg:camera qg:character qg:checkpoint qg:continue qg:counter qg:destroy qg:emit qg:fullscreen qg:goto-scene qg:gravity qg:hud qg:input qg:instance qg:language qg:leap qg:map-node qg:map-path qg:menu qg:next-language qg:on-collision qg:on-fall qg:on-input qg:on-tile qg:particles qg:patrol qg:phrase qg:play qg:prefab qg:put qg:rebind qg:save qg:scene qg:slider qg:sound qg:spawn qg:spritesheet qg:state qg:swap qg:text qg:tile qg:tilemap qg:tileset qg:timer qg:touch-button qg:touch-stick
hopper.q592 lines Download On GitHub
<q:application id="hopper" type="game">  <!-- Hopper: the platformer that drives the game language (PLAN_GAMES_2.md).       A title (a new game, or the saved one), a world map and three       levels. A character that walks and jumps on a       tilemap, blocks with power-ups in them, coins, walkers to stomp or be       hurt by, pits, checkpoints, spikes, a flag at the end of each level, a       game over. Art and sounds: Kenney's Pixel Platformer (CC0), in       assets/kenney/. -->  <!-- The game's state: kept across scenes. -->  <q:set name="lives" value="3" type="number" />  <q:set name="score" value="0" type="number" />  <q:set name="cleared" value="[]" type="array" />  <q:set name="map_at" value="level-1" />  <!-- the checkpoint reached: its level and where the character starts again there -->  <q:set name="check_at" value="" />  <q:set name="check_x" value="0" type="number" />  <q:set name="check_y" value="0" type="number" />  <!-- Jump on space, Z, X, up or W; the other actions keep their default keys. -->  <qg:input action="jump" keys="Space, Z, X, Up, W" />  <!-- level 4's tiles of a kind (qg:tile kind=): a ladder, water, snow slid on as ice, spikes that hurt -->  <qg:tileset name="kenney" src="assets/kenney/tilemap_packed.png" tile="18">    <qg:tile frame="71" kind="ladder" />    <qg:tile frame="33" kind="water" />    <qg:tile frame="74" kind="water" />    <qg:tile frame="81" kind="ice" />    <qg:tile frame="68" kind="hurt" shape="0,10; 18,10; 18,18; 0,18" />  </qg:tileset>  <qg:spritesheet name="chars" src="assets/kenney/tilemap-characters_packed.png" tile="24" />  <qg:sound bus="effects" name="jump" src="assets/kenney/audio/jump.ogg" />  <qg:sound bus="effects" name="coin" src="assets/kenney/audio/coin.ogg" />  <qg:sound bus="effects" name="stomp" src="assets/kenney/audio/stomp.ogg" />  <qg:sound bus="effects" name="hurt" src="assets/kenney/audio/hurt.ogg" />  <qg:sound bus="effects" name="block" src="assets/kenney/audio/block.ogg" />  <qg:sound bus="effects" name="powerup" src="assets/kenney/audio/powerup.ogg" />  <qg:sound bus="effects" name="win" src="assets/kenney/audio/win.ogg" />  <!-- the bouncer's hop: heard from where it is, to one side, softer the farther off -->  <qg:sound bus="effects" name="hop" src="assets/kenney/audio/jump.ogg" />  <!-- effects: only seen, the game's state never depends on them -->  <qg:particles name="sparkle" preset="stars" color="#ffe060" />  <qg:particles name="pop" preset="puff" color="#f0e0c0" />  <qg:particles name="power" preset="sparks" color="#ff7070" />  <qg:prefab name="Coin" tag="coin" sheet="kenney" frame="151" hitbox="12x12" />  <qg:prefab name="QBlock" tag="qblock" sheet="kenney" frame="10" hitbox="18x18" solid="true" />  <qg:prefab name="UsedBlock" tag="usedblock" sheet="kenney" frame="11" hitbox="18x18" solid="true" />  <qg:prefab name="Shroom" tag="shroom" sheet="kenney" frame="128" hitbox="12x12"             ai="patrol" speed="40" direction="right" turns-at="wall" />  <qg:prefab name="Spikes" tag="spikes" sheet="kenney" frame="68" hitbox="18x8" />  <qg:prefab name="Checkpoint" tag="checkpoint" sheet="kenney" frame="111" hitbox="18x18" />  <qg:prefab name="CheckpointOn" tag="checkpoint-on" sheet="kenney" frame="112" hitbox="18x18" />  <qg:prefab name="Flag" tag="flag" sheet="kenney" frame="153" hitbox="18x18" />  <!-- A ledge to jump through from below and stand on; a lift that rises and comes back. -->  <qg:prefab name="Ledge" tag="ledge" sheet="kenney" frame="23" hitbox="18x6" solid="true" one-way="true" />  <qg:prefab name="Lift" tag="lift" sheet="kenney" frame="43" hitbox="18x8" solid="true"             ai="shuttle" dy="-80" period="240" />  <!-- an enemy made of parts: it hops to and fro on the snow, and over what it bumps;       each hop is heard from where it is, so it is heard before it is seen -->  <qg:prefab name="Bouncer" tag="enemy" sheet="chars" frame="11" hitbox="18x18" ai="behavior" speed="36"             turns-at="edge">    <qg:animation name="walk" frames="11,12" fps="4" />    <qg:behavior>      <qg:gravity />      <qg:leap height="20" every="45" when="{true}" sound="hop" />      <qg:patrol heading="left" distance="18" />    </qg:behavior>  </qg:prefab>  <qg:prefab name="Walker" tag="enemy" sheet="chars" frame="18" hitbox="18x18"             ai="patrol" speed="30" direction="left" turns-at="edge">    <qg:animation name="walk" frames="18,19,20" fps="6" />  </qg:prefab>  <!-- the game in Portuguese and Spanish (a web page's ?lang=pt, the command line's q-lang, or the system's language) -->    <!-- on a phone: what the keys do, under a finger, in every scene -->  <qg:touch-stick x="40" y="184" />  <qg:touch-button action="jump" label="A" x="220" y="184" />  <qg:language lang="pt" name="Português">    <qg:phrase text="SCORE" as="PONTOS" />    <qg:phrase text="LIVES" as="VIDAS" />    <qg:phrase text="COINS" as="MOEDAS" />    <qg:phrase text="TIME" as="TEMPO" />    <qg:phrase text="YOU WIN" as="VOCÊ VENCEU" />    <qg:phrase text="GAME OVER - PRESS JUMP" as="FIM DE JOGO - APERTE PULAR" />    <qg:phrase text="New game" as="Novo jogo" />    <qg:phrase text="Continue" as="Continuar" />    <qg:phrase text="Language" as="Idioma" />      <qg:phrase text="Options" as="Opções" />    <qg:phrase text="OPTIONS" as="OPÇÕES" />    <qg:phrase text="Sounds" as="Sons" />    <qg:phrase text="Full screen" as="Tela cheia" />    <qg:phrase text="on" as="sim" />    <qg:phrase text="off" as="não" />    <qg:phrase text="Back" as="Voltar" />    <qg:phrase text="Jump" as="Pular" /></qg:language>  <qg:language lang="es" name="Español">    <qg:phrase text="SCORE" as="PUNTOS" />    <qg:phrase text="LIVES" as="VIDAS" />    <qg:phrase text="COINS" as="MONEDAS" />    <qg:phrase text="TIME" as="TIEMPO" />    <qg:phrase text="YOU WIN" as="HAS GANADO" />    <qg:phrase text="GAME OVER - PRESS JUMP" as="FIN DEL JUEGO - PULSA SALTAR" />    <qg:phrase text="New game" as="Nueva partida" />    <qg:phrase text="Continue" as="Continuar" />    <qg:phrase text="Language" as="Idioma" />      <qg:phrase text="Options" as="Opciones" />    <qg:phrase text="OPTIONS" as="OPCIONES" />    <qg:phrase text="Sounds" as="Sonidos" />    <qg:phrase text="Full screen" as="Pantalla completa" />    <qg:phrase text="on" as="sí" />    <qg:phrase text="off" as="no" />    <qg:phrase text="Back" as="Volver" />    <qg:phrase text="Jump" as="Saltar" /></qg:language>  <!-- ===== The title: a new game, or the saved one (each level cleared saves it) ===== -->  <qg:scene name="title" width="256" height="224" background="#3b7d4f">    <qg:hud position="top-center" size="32"><qg:text value="{'HOPPER'}" /></qg:hud>    <qg:menu position="center">      <qg:button label="New game"><qg:goto-scene name="map" /></qg:button>      <qg:button label="Continue" if="{has_save()}"><qg:continue /></qg:button>      <qg:button label="Options"><qg:goto-scene name="options" /></qg:button>      <qg:button label="{tr('Language') + ': ' + language()}"><qg:next-language /></qg:button>    </qg:menu>  </qg:scene>  <!-- ===== The world map ===== -->  <qg:scene name="map" width="256" height="224" background="#3b7d4f">    <qg:map-node name="level-1" x="48" y="112" sheet="kenney" frame="153" scene="level-1" />    <qg:map-node name="level-2" x="128" y="80" sheet="kenney" frame="153" scene="level-2" />    <qg:map-node name="level-3" x="208" y="112" sheet="kenney" frame="153" scene="level-3" />    <qg:map-node name="level-4" x="128" y="168" sheet="kenney" frame="153" scene="level-4" />    <qg:map-path from="level-1" to="level-2" requires="level-1" />    <qg:map-path from="level-2" to="level-3" requires="level-2" />    <qg:map-path from="level-3" to="level-4" requires="level-3" />    <qg:character id="player" controller="map" sheet="chars" frame="0" at="{map_at}" speed="60"                  x="0" y="0" hitbox="18x22">      <qg:animation name="idle" frames="0" />      <qg:animation name="walk" frames="1,2" fps="8" />    </qg:character>    <qg:hud position="top-left">      <qg:counter bind="score" label="SCORE" />      <qg:counter bind="lives" label="LIVES" />    </qg:hud>  </qg:scene>  <!-- ===== Level 1 ===== -->  <qg:scene name="level-1" width="256" height="224" background="#5c94fc" seed="7">    <q:if condition="{check_at == 'level-1'}">      <qg:put target="player" x="{check_x}" y="{check_y}" />    </q:if>    <q:set name="coins" value="0" type="number" />    <q:set name="message" value="" />    <q:set name="time" value="99" type="number" />    <!-- The clock: a second off every 60 ticks; at zero the level is lost. -->    <qg:timer every="60">      <q:set name="time" value="{time - 1}" />      <q:if condition="{time <= 0}">        <q:set name="lives" value="{lives - 1}" />        <q:set name="check_at" value="" />        <qg:goto-scene name="map" />      </q:if>    </qg:timer>    <q:function name="die" params="me">      <qg:play sound="hurt" />      <q:set name="lives" value="{lives - 1}" />      <q:if condition="{lives <= 0}">        <qg:goto-scene name="game-over" />      <q:else>        <qg:goto-scene name="level-1" />   <!-- the level again, enemies and all: from the start or its checkpoint -->      </q:else>      </q:if>    </q:function>    <!-- 24 columns x 13 rows of 18 px = 432 x 234. 0 is empty; 23 is the grass         top, 25 the dirt below it. A platform at row 6, a pit at columns 13-14. -->    <qg:tilemap tileset="kenney" collision="true">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,23,23,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,023,23,23,23,23,23,23,23,23,23,23,23,23,0,0,23,23,23,23,23,23,23,23,2325,25,25,25,25,25,25,25,25,25,25,25,25,0,0,25,25,25,25,25,25,25,25,2525,25,25,25,25,25,25,25,25,25,25,25,25,0,0,25,25,25,25,25,25,25,25,25    </qg:tilemap>    <qg:character id="player" controller="platformer" sheet="chars" frame="0"                  x="40" y="160" hitbox="18x22"                  run-speed="90" jump-height="64" variable-jump="true" coyote-frames="6" buffer-frames="6"                  jump-sound="jump">      <qg:state name="small" hitbox="18x22" frame="0" initial="true">        <qg:animation name="idle" frames="0" />        <qg:animation name="walk" frames="1,2" fps="8" />        <qg:animation name="jump" frames="1" />      </qg:state>      <qg:state name="big" hitbox="18x22" frame="3">        <qg:animation name="idle" frames="3" />        <qg:animation name="walk" frames="4,5" fps="8" />        <qg:animation name="jump" frames="4" />      </qg:state>      <qg:on-collision with="coin">        <qg:emit particles="sparkle" at="other" />        <qg:destroy target="other" />        <qg:play sound="coin" />        <q:set name="coins" value="{coins + 1}" />        <q:set name="score" value="{score + 10}" />      </qg:on-collision>      <qg:on-collision with="qblock" side="bottom">        <qg:swap target="other" prefab="UsedBlock" />        <qg:spawn prefab="Shroom" at="other" dy="-18" />        <qg:play sound="block" />      </qg:on-collision>      <qg:on-collision with="shroom">        <qg:destroy target="other" />        <qg:play sound="powerup" />        <qg:emit particles="power" at="me" />        <qg:become target="me" state="big" />        <q:set name="score" value="{score + 50}" />      </qg:on-collision>      <qg:on-collision with="enemy" side="top">        <qg:emit particles="pop" at="other" />        <qg:destroy target="other" />        <qg:bounce target="me" height="32" />        <qg:play sound="stomp" />        <q:set name="score" value="{score + 100}" />      </qg:on-collision>      <qg:on-collision with="enemy" side="any" cooldown="60">        <q:if condition="{me.state == 'big'}">          <qg:play sound="hurt" />          <qg:become target="me" state="small" />        <q:else>          <q:call function="die" args="me" />        </q:else>        </q:if>      </qg:on-collision>      <qg:on-collision with="spikes" cooldown="60">        <q:call function="die" args="me" />      </qg:on-collision>      <qg:on-collision with="checkpoint">        <qg:checkpoint target="me" at="other" />        <qg:swap target="other" prefab="CheckpointOn" />        <q:set name="check_at" value="level-1" />        <q:set name="check_x" value="297" />        <q:set name="check_y" value="169" />      </qg:on-collision>      <qg:on-collision with="flag" cooldown="100000">        <qg:play sound="win" />        <q:set name="score" value="{score + 500}" />        <q:set name="message" value="YOU WIN" />        <q:set name="cleared" value="{cleared + ['level-1']}" />        <q:set name="check_at" value="" />        <qg:save scene="map" />        <qg:goto-scene name="map" />      </qg:on-collision>      <qg:on-fall>        <q:call function="die" args="me" />      </qg:on-fall>    </qg:character>    <qg:instance prefab="QBlock" x="45" y="135" />    <qg:instance prefab="Coin" x="120" y="160" />    <qg:instance prefab="Coin" x="171" y="96" />    <qg:instance prefab="Walker" x="200" y="160" />    <qg:instance prefab="Checkpoint" x="297" y="171" if="{check_at != 'level-1'}" />    <qg:instance prefab="CheckpointOn" x="297" y="171" if="{check_at == 'level-1'}" />    <qg:instance prefab="Spikes" x="351" y="176" />    <qg:instance prefab="Flag" x="405" y="171" />    <qg:camera follow="player" bounds="tilemap" smooth="8" lead="32" dead-zone="16x56" />    <qg:hud position="top-left">      <qg:counter bind="score" label="SCORE" />      <qg:counter bind="coins" label="COINS" />      <qg:counter bind="lives" label="LIVES" />      <qg:counter bind="time" label="TIME" />      <qg:text bind="message" />    </qg:hud>  </qg:scene>  <!-- ===== Level 2: platforms and two walkers ===== -->  <qg:scene name="level-2" width="256" height="224" background="#4a7cc9" seed="11">    <q:if condition="{check_at == 'level-2'}">      <qg:put target="player" x="{check_x}" y="{check_y}" />    </q:if>    <q:set name="coins" value="0" type="number" />    <q:function name="die" params="me">      <qg:play sound="hurt" />      <q:set name="lives" value="{lives - 1}" />      <q:if condition="{lives <= 0}">        <qg:goto-scene name="game-over" />      <q:else>        <qg:goto-scene name="level-2" />   <!-- the level again, enemies and all: from the start or its checkpoint -->      </q:else>      </q:if>    </q:function>    <!-- The level is drawn in Tiled (levels/level-2.tmx): a decor layer, a ground         layer with a collision property, and an object layer whose points place         the coins, the walkers and the flag by their class. -->    <qg:tilemap tileset="kenney" src="levels/level-2.tmx" />    <qg:character id="player" controller="platformer" sheet="chars" frame="0"                  x="40" y="160" hitbox="18x22"                  run-speed="90" jump-height="64" variable-jump="true" coyote-frames="6" buffer-frames="6"                  jump-sound="jump">      <qg:animation name="idle" frames="0" />      <qg:animation name="walk" frames="1,2" fps="8" />      <qg:animation name="jump" frames="1" />      <qg:on-collision with="coin">        <qg:emit particles="sparkle" at="other" />        <qg:destroy target="other" />        <qg:play sound="coin" />        <q:set name="coins" value="{coins + 1}" />        <q:set name="score" value="{score + 10}" />      </qg:on-collision>      <qg:on-collision with="enemy" side="top">        <qg:emit particles="pop" at="other" />        <qg:destroy target="other" />        <qg:bounce target="me" height="32" />        <qg:play sound="stomp" />        <q:set name="score" value="{score + 100}" />      </qg:on-collision>      <qg:on-collision with="enemy" side="any" cooldown="60">        <q:call function="die" args="me" />      </qg:on-collision>      <qg:on-collision with="flag" cooldown="100000">        <qg:play sound="win" />        <q:set name="score" value="{score + 500}" />        <q:set name="cleared" value="{cleared + ['level-2']}" />        <q:set name="check_at" value="" />        <qg:save scene="map" />        <qg:goto-scene name="map" />      </qg:on-collision>      <qg:on-fall>        <q:call function="die" args="me" />      </qg:on-fall>    </qg:character>    <qg:camera follow="player" bounds="tilemap" smooth="8" lead="32" dead-zone="16x56" />    <qg:hud position="top-left">      <qg:counter bind="score" label="SCORE" />      <qg:counter bind="coins" label="COINS" />      <qg:counter bind="lives" label="LIVES" />    </qg:hud>  </qg:scene>  <!-- ===== Level 3: the spikes gauntlet ===== -->  <qg:scene name="level-3" width="256" height="224" background="#7a4fa8" seed="13">    <q:if condition="{check_at == 'level-3'}">      <qg:put target="player" x="{check_x}" y="{check_y}" />    </q:if>    <q:set name="coins" value="0" type="number" />    <q:function name="die" params="me">      <qg:play sound="hurt" />      <q:set name="lives" value="{lives - 1}" />      <q:if condition="{lives <= 0}">        <qg:goto-scene name="game-over" />      <q:else>        <qg:goto-scene name="level-3" />   <!-- the level again, enemies and all: from the start or its checkpoint -->      </q:else>      </q:if>    </q:function>    <qg:tilemap tileset="kenney" collision="true">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,23,23,0,0,0,0,23,23,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,023,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,2325,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,2525,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25    </qg:tilemap>    <qg:character id="player" controller="platformer" sheet="chars" frame="0"                  x="40" y="160" hitbox="18x22"                  run-speed="90" jump-height="64" variable-jump="true" coyote-frames="6" buffer-frames="6"                  jump-sound="jump">      <qg:animation name="idle" frames="0" />      <qg:animation name="walk" frames="1,2" fps="8" />      <qg:animation name="jump" frames="1" />      <qg:on-collision with="coin">        <qg:emit particles="sparkle" at="other" />        <qg:destroy target="other" />        <qg:play sound="coin" />        <q:set name="coins" value="{coins + 1}" />        <q:set name="score" value="{score + 10}" />      </qg:on-collision>      <qg:on-collision with="spikes" cooldown="60">        <q:call function="die" args="me" />      </qg:on-collision>      <qg:on-collision with="checkpoint">        <qg:checkpoint target="me" at="other" />        <qg:swap target="other" prefab="CheckpointOn" />        <q:set name="check_at" value="level-3" />        <q:set name="check_x" value="207" />        <q:set name="check_y" value="169" />      </qg:on-collision>      <qg:on-collision with="flag" cooldown="100000">        <qg:play sound="win" />        <q:set name="score" value="{score + 1000}" />        <q:set name="cleared" value="{cleared + ['level-3']}" />        <q:set name="check_at" value="" />        <qg:save scene="map" />        <qg:goto-scene name="map" />      </qg:on-collision>      <qg:on-fall>        <q:call function="die" args="me" />      </qg:on-fall>    </qg:character>    <qg:instance prefab="Spikes" x="135" y="176" />    <qg:instance prefab="Spikes" x="153" y="176" />    <qg:instance prefab="Coin" x="144" y="96" />    <qg:instance prefab="Checkpoint" x="207" y="171" if="{check_at != 'level-3'}" />    <qg:instance prefab="CheckpointOn" x="207" y="171" if="{check_at == 'level-3'}" />    <qg:instance prefab="Spikes" x="243" y="176" />    <qg:instance prefab="Spikes" x="261" y="176" />    <qg:instance prefab="Spikes" x="279" y="176" />    <qg:instance prefab="Coin" x="252" y="96" />    <qg:instance prefab="Flag" x="405" y="171" />    <qg:camera follow="player" bounds="tilemap" smooth="8" lead="32" dead-zone="16x56" />    <qg:hud position="top-left">      <qg:counter bind="score" label="SCORE" />      <qg:counter bind="coins" label="COINS" />      <qg:counter bind="lives" label="LIVES" />    </qg:hud>  </qg:scene>  <!-- ===== Level 4: the falls. Water to swim, snow that slides, a ladder up a wall, spikes below it. ===== -->  <qg:scene name="level-4" width="256" height="224" background="#4f8fb0" seed="17">    <q:set name="coins" value="0" type="number" />    <q:function name="die" params="me">      <qg:play sound="hurt" />      <q:set name="lives" value="{lives - 1}" />      <q:if condition="{lives <= 0}">        <qg:goto-scene name="game-over" />      <q:else>        <qg:goto-scene name="level-4" />      </q:else>      </q:if>    </q:function>    <!-- 23 grass, 25 dirt; 34 and 75 water (a pool two tiles deep), 82 snow (ice), 72 the ladder up         the wall at columns 15-16, 69 the spikes beyond it. -->    <qg:tilemap tileset="kenney" collision="true">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,23,23,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,25,25,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,25,25,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,25,25,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,25,25,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,72,25,25,0,0,0,0,0,0,023,23,23,23,23,23,34,34,34,34,82,82,82,82,23,25,25,69,69,69,69,23,23,2325,25,25,25,25,25,75,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,2525,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25    </qg:tilemap>    <qg:character id="player" controller="platformer" sheet="chars" frame="0"                  x="40" y="160" hitbox="18x22"                  run-speed="90" jump-height="64" variable-jump="true" coyote-frames="6" buffer-frames="6"                  jump-sound="jump">      <qg:animation name="idle" frames="0" />      <qg:animation name="walk" frames="1,2" fps="8" />      <qg:animation name="jump" frames="1" />      <qg:on-collision with="coin">        <qg:emit particles="sparkle" at="other" />        <qg:destroy target="other" />        <qg:play sound="coin" />        <q:set name="coins" value="{coins + 1}" />        <q:set name="score" value="{score + 10}" />      </qg:on-collision>      <qg:on-collision with="enemy" side="top">        <qg:emit particles="pop" at="other" />        <qg:destroy target="other" />        <qg:bounce target="me" height="32" />        <qg:play sound="stomp" />        <q:set name="score" value="{score + 100}" />      </qg:on-collision>      <qg:on-collision with="enemy" side="any" cooldown="60">        <q:call function="die" args="me" />      </qg:on-collision>      <!-- the spikes are tiles of a kind, not things: touching one is qg:on-tile -->      <qg:on-tile kind="hurt" cooldown="60">        <q:call function="die" args="me" />      </qg:on-tile>      <qg:on-collision with="flag" cooldown="100000">        <qg:play sound="win" />        <q:set name="score" value="{score + 1500}" />        <q:set name="cleared" value="{cleared + ['level-4']}" />        <q:set name="check_at" value="" />        <qg:save scene="map" />        <qg:goto-scene name="map" />      </qg:on-collision>      <qg:on-fall>        <q:call function="die" args="me" />      </qg:on-fall>    </qg:character>    <qg:instance prefab="Coin" x="144" y="200" />    <qg:instance prefab="Bouncer" x="225" y="171" />    <qg:instance prefab="Coin" x="279" y="54" />    <qg:instance prefab="Flag" x="405" y="171" />    <qg:camera follow="player" bounds="tilemap" smooth="8" lead="32" dead-zone="16x56" />    <qg:hud position="top-left">      <qg:counter bind="score" label="SCORE" />      <qg:counter bind="coins" label="COINS" />      <qg:counter bind="lives" label="LIVES" />    </qg:hud>  </qg:scene>  <!-- ===== Game over: jump to start again ===== -->  <qg:scene name="game-over" width="256" height="224" background="#000000">    <q:set name="message" value="GAME OVER - PRESS JUMP" />    <qg:on-input action="jump">      <q:set name="lives" value="3" />      <q:set name="score" value="0" />      <q:set name="cleared" value="[]" />      <q:set name="map_at" value="level-1" />      <q:set name="check_at" value="" />      <qg:goto-scene name="map" />    </qg:on-input>    <qg:hud position="top-center">      <qg:text bind="message" />    </qg:hud>  </qg:scene>  <!-- ===== Options: how loud, the full screen, the keys; kept between runs ===== -->  <qg:scene name="options" width="256" height="224" background="#3b7d4f">    <qg:hud position="top-center" size="24"><qg:text value="{tr('OPTIONS')}" /></qg:hud>    <qg:menu position="center" size="12">      <qg:slider label="Sounds" bus="effects" />      <qg:button label="{tr('Full screen') + ': ' + (tr('on') if fullscreen() else tr('off'))}"><qg:fullscreen /></qg:button>      <qg:rebind label="Jump" action="jump" />      <qg:button label="Back"><qg:goto-scene name="title" /></qg:button>    </qg:menu>  </qg:scene></q:application>
levels/level-2.tmx69 lines Download On GitHub
<?xml version="1.0" encoding="UTF-8"?><map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="24" height="13" tilewidth="18" tileheight="18" infinite="0" nextlayerid="4" nextobjectid="9"> <tileset firstgid="1" name="kenney" tilewidth="18" tileheight="18" tilecount="180" columns="20">  <image source="../../../assets/kenney/tilemap_packed.png" width="360" height="162"/> </tileset> <layer id="1" name="decor" width="24" height="13">  <data encoding="csv">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,11,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</data> </layer> <layer id="2" name="ground" width="24" height="13">  <properties>   <property name="collision" type="bool" value="true"/>  </properties>  <data encoding="csv">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,23,23,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,23,23,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,23,23,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,023,23,23,23,23,23,23,23,23,23,23,23,0,0,0,23,23,23,23,23,23,23,23,2325,25,25,25,25,25,25,25,25,25,25,25,0,0,0,25,25,25,25,25,25,25,25,2525,25,25,25,25,25,25,25,25,25,25,25,0,0,0,25,25,25,25,25,25,25,25,25</data> </layer> <objectgroup id="3" name="things">  <object id="1" name="coin-1" class="Coin" x="171" y="96">   <point/>  </object>  <object id="2" name="coin-2" class="Coin" x="261" y="78">   <point/>  </object>  <object id="3" name="coin-3" class="Coin" x="351" y="42">   <point/>  </object>  <object id="4" name="walker-1" class="Walker" x="150" y="160">   <point/>  </object>  <object id="5" name="walker-2" class="Walker" x="380" y="160">   <point/>  </object>  <object id="6" name="flag" class="Flag" x="405" y="171">   <point/>  </object>  <object id="7" name="ledge" class="Ledge" x="99" y="144">   <point/>  </object>  <object id="8" name="lift" class="Lift" x="129" y="184">   <point/>  </object> </objectgroup></map>