통 피하기 (Barrel Dodger)
🎯 학습 목표:
- 타일맵(Tilemap)을 사용하여 게임의 바닥과 벽을 디자인합니다.
- 가속도(ay, Gravity)를 적용하여 점프와 착지 물리 효과를 만듭니다.
- 상태 확인(Is Hitting Tile)을 통해 이중 점프를 방지하는 논리를 배웁니다.
- 타일 위치(Tile Location)를 기준으로 적(장애물)을 생성하고 배치합니다.
맵과 바닥 만들기
캐릭터가 밟고 서 있을 바닥을 만들어 봅시다.
||scene:장면||에서||scene:타일맵 설정||블록을 가져옵니다.- 타일맵 편집기를 열고 10x8 크기로 설정한 뒤, 바닥 타일을 그리고 벽(Wall)을 칠해줍니다.
// @highlight
namespace myTiles {
//% blockIdentity=images._tile
export const tile0 = img`
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
`
}
// @highlight
tiles.setTilemap(tiles.createTilemap(
hex`0a0008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000303030303030303030303030303030303030303`,
img`
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2
`,
[myTiles.tile0,sprites.builtin.brick,sprites.builtin.oceanDepths0,sprites.builtin.oceanDepths3],
TileScale.Sixteen
))
영웅 등장 (중력 적용)
주인공을 만들고, 공중에 뜨지 않도록 중력을 적용합니다.
► ||variables(sprites):[mySprite]를 스프라이트 설정|| 블록으로 캐릭터를 생성합니다.
► ||scene:[mySprite]를 타일맵 ... 위에 놓기|| 블록으로 1열 5행즈음에 배치합니다.
► 좌우로만 움직이도록 ||controller:[mySprite]를 버튼으로 이동 (vx: 100, vy: 0)||으로 설정합니다. (vy를 0으로 하면 위아래 키로 움직이지 않습니다.)
► 중력: ||sprites:[mySprite] 가속도 설정 (ax, ay)|| 블록에서 ay(아래쪽 가속도)를 500으로 설정하여 바닥으로 떨어지게 합니다.
namespace myTiles {
//% blockIdentity=images._tile
export const tile0 = img`
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
`
}
// @highlight
tiles.setTilemap(tiles.createTilemap(
hex`0a0008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000303030303030303030303030303030303030303`,
img`
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2
`,
[myTiles.tile0,sprites.builtin.brick,sprites.builtin.oceanDepths0,sprites.builtin.oceanDepths3],
TileScale.Sixteen
))
// @highlight
let mySprite = sprites.create(img`
. . . . . . 5 . 5 . . . . . . .
. . . . . f 5 5 5 f f . . . . .
. . . . f 1 5 2 5 1 6 f . . . .
. . . f 1 6 6 6 6 6 1 6 f . . .
. . . f 6 6 f f f f 6 1 f . . .
. . . f 6 f f d d f f 6 f . . .
. . f 6 f d f d d f d f 6 f . .
. . f 6 f d 3 d d 3 d f 6 f . .
. . f 6 6 f d d d d f 6 6 f . .
. f 6 6 f 3 f f f f 3 f 6 6 f .
. . f f d 3 5 3 3 5 3 d f f . .
. . f d d f 3 5 5 3 f d d f . .
. . . f f 3 3 3 3 3 3 f f . . .
. . . f 3 3 5 3 3 5 3 3 f . . .
. . . f f f f f f f f f f . . .
. . . . . f f . . f f . . . . .
`, SpriteKind.Player)
// @highlight
tiles.placeOnTile(mySprite, tiles.getTileLocation(1, 5))
// @highlight
controller.moveSprite(mySprite, 100, 0)
// @highlight
mySprite.ay = 500
점프하기 (조건문)
바닥에 있을 때만 점프할 수 있도록 만들어 봅시다.
► ||controller:[A] 버튼을 [누를 때]|| 블록을 가져옵니다.
► ||logic:만약(if) [참] 이면|| 블록을 넣고, 조건에 ||scene:[mySprite]가 [아래쪽(bottom)] 벽에 닿았는가?||를 넣습니다.
► 바닥에 닿아있다면, ||sprites:[mySprite] 속도 설정 vy||를 -200으로 설정하여 위로 솟구치게 합니다.
controller.A.onEvent(ControllerButtonEvent.Pressed, function () {
// @highlight
let mySprite: Sprite = null
if (mySprite.isHittingTile(CollisionDirection.Bottom)) {
// @highlight
mySprite.vy = -200
}
})
굴러오는 통 (장애물)
오른쪽에서 통이 굴러오도록 만들어 봅시다.
► ||game:게임 업데이트시 [2000] ms 마다|| 블록을 추가합니다.
► ||sprites:[projectile]을 발사체로...|| 블록을 사용하여 통을 만듭니다. 속도 vx는 -100 ~ -80 사이의 랜덤값으로 설정하세요.
► 위치 잡기: ||scene:[projectile]을 타일맵 [col 9, row 5] 위에 놓기|| 블록을 사용하여 통이 바닥 바로 위에서 시작하도록 합니다.
game.onUpdateInterval(2000, function () {
let projectile = sprites.createProjectileFromSide(img`
. e e e e e e .
e e e e e e e e
1 1 1 1 1 1 1 1
e e e e e e e e
e e e e e e e e
1 1 1 1 1 1 1 1
e e e e e e e e
. e e e e e e .
`, randint(-100, -80), 0)
// @highlight
tiles.placeOnTile(projectile, tiles.getTileLocation(9, 5))
// @highlight
info.changeScoreBy(1)
})
충돌 시 게임 오버
통에 부딪히면 게임이 끝나야 합니다.
► ||sprites:스프라이트||에서 ||sprites:... [sprite]가 ... [otherSprite]와 닿았을 때|| 블록을 가져옵니다.
► 종류를 Player와 Projectile로 설정합니다.
► 닿으면 ||game:게임 종료 <패배>|| 블록을 실행합니다.
sprites.onOverlap(SpriteKind.Player, SpriteKind.Projectile, function (sprite, otherSprite) {
// @highlight
game.gameOver(false)
})
완성!
축하합니다! 통 피하기 게임을 완성했습니다.
타이밍에 맞춰 점프하며 얼마나 오래 버틸 수 있는지 시험해 보세요!
아래는 완성된 전체 블록 코드입니다.
namespace myTiles {
//% blockIdentity=images._tile
export const tile0 = img`
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
`
}
controller.A.onEvent(ControllerButtonEvent.Pressed, function () {
if (mySprite.isHittingTile(CollisionDirection.Bottom)) {
mySprite.vy = -200
}
})
sprites.onOverlap(SpriteKind.Player, SpriteKind.Projectile, function (sprite, otherSprite) {
game.gameOver(false)
})
game.onUpdateInterval(2000, function () {
let projectile = sprites.createProjectileFromSide(img`
. e e e e e e .
e e e e e e e e
1 1 1 1 1 1 1 1
e e e e e e e e
e e e e e e e e
1 1 1 1 1 1 1 1
e e e e e e e e
. e e e e e e .
`, randint(-100, -80), 0)
tiles.placeOnTile(projectile, tiles.getTileLocation(9, 5))
info.changeScoreBy(1)
})
namespace myTiles {
//% blockIdentity=images._tile
export const tile0 = img`
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
`
}
// @highlight
tiles.setTilemap(tiles.createTilemap(
hex`0a0008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000303030303030303030303030303030303030303`,
img`
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2
`,
[myTiles.tile0,sprites.builtin.brick,sprites.builtin.oceanDepths0,sprites.builtin.oceanDepths3],
TileScale.Sixteen
))
let mySprite = sprites.create(img`
. . . . . . 5 . 5 . . . . . . .
. . . . . f 5 5 5 f f . . . . .
. . . . f 1 5 2 5 1 6 f . . . .
. . . f 1 6 6 6 6 6 1 6 f . . .
. . . f 6 6 f f f f 6 1 f . . .
. . . f 6 f f d d f f 6 f . . .
. . f 6 f d f d d f d f 6 f . .
. . f 6 f d 3 d d 3 d f 6 f . .
. . f 6 6 f d d d d f 6 6 f . .
. f 6 6 f 3 f f f f 3 f 6 6 f .
. . f f d 3 5 3 3 5 3 d f f . .
. . f d d f 3 5 5 3 f d d f . .
. . . f f 3 3 3 3 3 3 f f . . .
. . . f 3 3 5 3 3 5 3 3 f . . .
. . . f f f f f f f f f f . . .
. . . . . f f . . f f . . . . .
`, SpriteKind.Player)
tiles.placeOnTile(mySprite, tiles.getTileLocation(1, 5))
controller.moveSprite(mySprite, 100, 0)
mySprite.ay = 500