Compare commits
6 Commits
da0f355b98
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 845969297e | |||
| 5464b53f3f | |||
| 43c8ccd6d2 | |||
| f8c6bfab49 | |||
| 5e674fa686 | |||
| fe13a61c09 |
@@ -181,12 +181,31 @@ private fun BoardWithPanels(state: GameState, ghostOn: Boolean, l: Layout, stack
|
|||||||
BoardCanvas(state, ghostOn)
|
BoardCanvas(state, ghostOn)
|
||||||
}
|
}
|
||||||
Column(Modifier.width(panelW)) {
|
Column(Modifier.width(panelW)) {
|
||||||
SidePanel("HOLD", listOfNotNull(state.game.hold), s, 1, l.isTiny)
|
SidePanels(state, s, l.isTiny)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HOLD / NEXT 패널.
|
||||||
|
*
|
||||||
|
* `game.hold` 와 `game.peekNext()` 는 Compose 가 관찰하지 못하는 평범한 엔진
|
||||||
|
* 필드라, 값이 바뀌어도 그것만으로는 재구성이 일어나지 않는다. BoardCanvas 와
|
||||||
|
* 똑같이 여기서 `state.version` 을 읽어 구독해야 한다. 이 구독이 없으면 게임이
|
||||||
|
* 시작될 때의 넥스트 큐가 끝까지 그대로 멈춰 있는다.
|
||||||
|
*
|
||||||
|
* 보드 전체가 아니라 패널만 따로 떼어 둔 이유는, 재구성 범위를 이 서브트리로
|
||||||
|
* 좁혀 매 프레임 BoxWithConstraints 측정까지 다시 돌지 않게 하기 위해서다.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
private fun SidePanels(state: GameState, s: Float, tiny: Boolean) {
|
||||||
|
@Suppress("UNUSED_EXPRESSION")
|
||||||
|
state.version
|
||||||
|
val nextCount = if (tiny) 3 else 5
|
||||||
|
SidePanel("HOLD", listOfNotNull(state.game.hold), s, 1, tiny)
|
||||||
Spacer(Modifier.height((8 * s).dp))
|
Spacer(Modifier.height((8 * s).dp))
|
||||||
SidePanel("NEXT", state.game.peekNext(if (l.isTiny) 3 else 5), s, if (l.isTiny) 3 else 5, l.isTiny)
|
SidePanel("NEXT", state.game.peekNext(nextCount), s, nextCount, tiny)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -299,43 +318,61 @@ private fun MiniPiece(type: Char) {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun ControlPad(state: GameState, lefty: Boolean, scale: Float, compactHeight: Boolean) {
|
private fun ControlPad(state: GameState, lefty: Boolean, scale: Float, compactHeight: Boolean) {
|
||||||
val h = ((if (compactHeight) 130 else 168) * scale).dp
|
val h = ((if (compactHeight) 130 else 168) * scale).dp
|
||||||
Row(
|
val gap = (8 * scale).dp
|
||||||
|
// 아랫줄 3개(하드 드롭·홀드·소프트 드롭)를 정확히 1/3 씩 주려면 그 줄이
|
||||||
|
// 화면 전체 폭을 차지해야 한다. 그래서 좌/우 컬럼으로 쪼개지 않고
|
||||||
|
// 바깥을 Column 으로 두어 윗줄·아랫줄을 각각 통짜 Row 로 깐다.
|
||||||
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(h)
|
.height(h)
|
||||||
.padding(horizontal = (10 * scale).dp, vertical = (6 * scale).dp),
|
.padding(horizontal = (10 * scale).dp, vertical = (6 * scale).dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy((8 * scale).dp),
|
verticalArrangement = Arrangement.spacedBy(gap),
|
||||||
) {
|
) {
|
||||||
val actions: @Composable (Modifier) -> Unit = { m ->
|
// ── 윗줄: 회전 | 좌우 이동 ──
|
||||||
Column(m.fillMaxHeight(), verticalArrangement = Arrangement.spacedBy((8 * scale).dp)) {
|
Row(Modifier.weight(1.4f).fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(gap)) {
|
||||||
Row(Modifier.weight(1f).fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy((8 * scale).dp)) {
|
val rotates: @Composable (Modifier) -> Unit = { m ->
|
||||||
CtrlButton("HOLD", Modifier.weight(2f).fillMaxHeight(), scale) { state.hold() }
|
Row(m.fillMaxHeight(), horizontalArrangement = Arrangement.spacedBy(gap)) {
|
||||||
|
// 180° 는 폭을 유지해야 해서 4등분 중 1칸을 그대로 두고,
|
||||||
|
// ↺·↻ 가 1.5 씩 받아 3/8 폭이 된다.
|
||||||
|
CtrlButton("↺", Modifier.weight(1.5f).fillMaxHeight(), scale) { state.rotate("ccw") }
|
||||||
|
CtrlButton("↻", Modifier.weight(1.5f).fillMaxHeight(), scale) { state.rotate("cw") }
|
||||||
CtrlButton("180°", Modifier.weight(1f).fillMaxHeight(), scale) { state.rotate("180") }
|
CtrlButton("180°", Modifier.weight(1f).fillMaxHeight(), scale) { state.rotate("180") }
|
||||||
}
|
}
|
||||||
Row(Modifier.weight(1f).fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy((8 * scale).dp)) {
|
|
||||||
CtrlButton("↺", Modifier.weight(1f).fillMaxHeight(), scale) { state.rotate("ccw") }
|
|
||||||
CtrlButton("↻", Modifier.weight(1f).fillMaxHeight(), scale) { state.rotate("cw") }
|
|
||||||
CtrlButton("하드\n드롭", Modifier.weight(1f).fillMaxHeight(), scale, accent = true) { state.hardDrop() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
val moves: @Composable (Modifier) -> Unit = { m ->
|
val moves: @Composable (Modifier) -> Unit = { m ->
|
||||||
Column(m.fillMaxHeight(), verticalArrangement = Arrangement.spacedBy((8 * scale).dp)) {
|
Row(m.fillMaxHeight(), horizontalArrangement = Arrangement.spacedBy(gap)) {
|
||||||
Row(Modifier.weight(1.4f).fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy((8 * scale).dp)) {
|
|
||||||
HoldCtrl("◀", Modifier.weight(1f).fillMaxHeight(), scale, { state.press(-1) }, { state.release(-1) })
|
HoldCtrl("◀", Modifier.weight(1f).fillMaxHeight(), scale, { state.press(-1) }, { state.release(-1) })
|
||||||
HoldCtrl("▶", Modifier.weight(1f).fillMaxHeight(), scale, { state.press(1) }, { state.release(1) })
|
HoldCtrl("▶", Modifier.weight(1f).fillMaxHeight(), scale, { state.press(1) }, { state.release(1) })
|
||||||
}
|
}
|
||||||
HoldCtrl("▼ 소프트", Modifier.weight(1f).fillMaxWidth(), scale, { state.setSoft(true) }, { state.setSoft(false) })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (lefty) {
|
if (lefty) {
|
||||||
moves(Modifier.weight(1f))
|
moves(Modifier.weight(1f))
|
||||||
actions(Modifier.weight(1.1f))
|
rotates(Modifier.weight(1.35f))
|
||||||
} else {
|
} else {
|
||||||
actions(Modifier.weight(1.1f))
|
rotates(Modifier.weight(1.35f))
|
||||||
moves(Modifier.weight(1f))
|
moves(Modifier.weight(1f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── 아랫줄: 하드 드롭 · 홀드 · 소프트 드롭 3등분 ──
|
||||||
|
Row(Modifier.weight(1f).fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(gap)) {
|
||||||
|
val third = Modifier.weight(1f).fillMaxHeight()
|
||||||
|
val hardDrop: @Composable (Modifier) -> Unit = { m ->
|
||||||
|
CtrlButton("하드 드롭", m, scale, accent = true) { state.hardDrop() }
|
||||||
|
}
|
||||||
|
val hold: @Composable (Modifier) -> Unit = { m -> CtrlButton("HOLD", m, scale) { state.hold() } }
|
||||||
|
val soft: @Composable (Modifier) -> Unit = { m ->
|
||||||
|
HoldCtrl("▼ 소프트", m, scale, { state.setSoft(true) }, { state.setSoft(false) })
|
||||||
|
}
|
||||||
|
// 왼손 배치에서는 좌우를 뒤집는다 (소프트가 왼쪽 엄지 쪽으로).
|
||||||
|
if (lefty) {
|
||||||
|
soft(third); hold(third); hardDrop(third)
|
||||||
|
} else {
|
||||||
|
hardDrop(third); hold(third); soft(third)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -10,5 +10,5 @@ kotlin.code.style=official
|
|||||||
kotlin.daemon.jvmargs=-Xmx2g
|
kotlin.daemon.jvmargs=-Xmx2g
|
||||||
|
|
||||||
# 버전은 CI/로컬에서 -PappVersionName -PappVersionCode 로 덮어쓸 수 있다.
|
# 버전은 CI/로컬에서 -PappVersionName -PappVersionCode 로 덮어쓸 수 있다.
|
||||||
appVersionName=2.0.1
|
appVersionName=2.0.4
|
||||||
appVersionCode=3
|
appVersionCode=6
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 60 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 60 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 64 KiB |
Reference in New Issue
Block a user