Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
> **Note:** To access all shared projects, get information about environment setup, and view other guides, please visit [Explore-In-HMOS-Wearable Index](https://github.com/Explore-In-HMOS-Wearable/hmos-index).
# Block Game
Block Game is a captivating puzzle game that challenges you to fit different block shapes onto a grid, clearing lines to score points.
# Block Game

Block Game is a captivating puzzle game that challenges you to fit different block shapes onto a grid, clearing lines to score points.

To access all shared projects, get information about environment setup, and view other guides, please visit [Explore-In-HMOS-Wearable Index](https://github.com/Explore-In-HMOS-Wearable/hmos-index).

# Preview

Expand All @@ -20,8 +20,8 @@ Quick entertainment during free time with its simple yet addictive gameplay mech
# Tech Stack

- **Languages**: ArkTS
- **Frameworks**: HarmonyOS SDK 5.1.0(18)
- **Tools**: DevEco Studio Vers 5.1.0.842
- **Frameworks**: HarmonyOS SDK 6.0.0(20)
- **Tools**: DevEco Studio 6.x
- **Libraries**: @kit.ArkUI, @ohos/lottie

# Directory Structure
Expand Down
2 changes: 1 addition & 1 deletion build-profile.json5
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{
"name": "default",
"signingConfig": "default",
"compatibleSdkVersion": "5.1.0(18)",
"compatibleSdkVersion": "6.0.0(20)",
"runtimeOS": "HarmonyOS",
"buildOption": {
"strictMode": {
Expand Down
88 changes: 68 additions & 20 deletions entry/src/main/ets/common/GameConstants.ets
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { ShapeDefinition } from '../model/Game';

export class GameConstants {
static readonly GRID_SIZE = 5;
static readonly BLOCK_PREVIEW_SIZE = 15;
static readonly BLOCK_PREVIEW_SIZE = 17;
static readonly CLEAR_ANIMATION_DELAY_MS = 60;
static readonly GAMEOVER_FILL_DELAY_MS = 5;
static readonly ALL_SHAPES: ShapeDefinition[] = [

// ── O / Square pieces ────────────────────────────────────────────────────
{
name: 'O_Square_3x3',
cells: [
Expand All @@ -15,7 +17,6 @@ export class GameConstants {
],
color: $r('app.color.shape_color_O')
},

{
name: 'rect_horizontal',
cells: [
Expand All @@ -33,7 +34,13 @@ export class GameConstants {
],
color: $r('app.color.shape_color_O')
},
{
name: 'O_Square_2x2',
cells: [{ row: 0, col: 0 }, { row: 0, col: 1 }, { row: 1, col: 0 }, { row: 1, col: 1 }],
color: $r('app.color.shape_color_O')
},

// ── I pieces ─────────────────────────────────────────────────────────────
{
name: 'I_Vertical',
cells: [{ row: 0, col: 0 }, { row: 1, col: 0 }, { row: 2, col: 0 }, { row: 3, col: 0 }],
Expand All @@ -45,12 +52,7 @@ export class GameConstants {
color: $r('app.color.shape_color_I')
},

{
name: 'O_Square_2x2',
cells: [{ row: 0, col: 0 }, { row: 0, col: 1 }, { row: 1, col: 0 }, { row: 1, col: 1 }],
color: $r('app.color.shape_color_O')
},

// ── L pieces ─────────────────────────────────────────────────────────────
{
name: 'L_0',
cells: [{ row: 0, col: 0 }, { row: 1, col: 0 }, { row: 2, col: 0 }, { row: 2, col: 1 }],
Expand All @@ -71,35 +73,81 @@ export class GameConstants {
cells: [{ row: 0, col: 0 }, { row: 0, col: 1 }, { row: 0, col: 2 }, { row: 1, col: 2 }],
color: $r('app.color.shape_color_L')
},
{
name: 'L_2_0',
cells: [
{ row: 0, col: 0 }, { row: 1, col: 0 }, { row: 2, col: 0 }, { row: 2, col: 1 }, { row: 2, col: 2 }
],
color: $r('app.color.shape_color_L')
},

// ── J pieces ─────────────────────────────────────────────────────────────
{
name: 'J_0',
cells: [{ row: 0, col: 1 }, { row: 1, col: 1 }, { row: 2, col: 1 }, { row: 2, col: 0 }],
color: $r('app.color.shape_color_I')
color: $r('app.color.shape_color_J')
},
{
name: 'J_90',
cells: [{ row: 0, col: 0 }, { row: 1, col: 0 }, { row: 1, col: 1 }, { row: 1, col: 2 }],
color: $r('app.color.shape_color_I')
cells: [{ row: 0, col: 0 }, { row: 0, col: 1 }, { row: 0, col: 2 }, { row: 1, col: 2 }],
color: $r('app.color.shape_color_J')
},
{
name: 'J_180',
cells: [{ row: 0, col: 0 }, { row: 0, col: 1 }, { row: 1, col: 0 }, { row: 2, col: 0 }],
color: $r('app.color.shape_color_I')
color: $r('app.color.shape_color_J')
},
{
name: 'J_270',
cells: [{ row: 0, col: 0 }, { row: 0, col: 1 }, { row: 0, col: 2 }, { row: 1, col: 0 }],
color: $r('app.color.shape_color_I')
color: $r('app.color.shape_color_J')
},

// ── T pieces ─────────────────────────────────────────────────────────────
{
name: 'L_2_0',
cells: [
{ row: 0, col: 0 }, { row: 1, col: 0 }, { row: 2, col: 0 }, { row: 2, col: 1 }, { row: 2, col: 2 }
],
color: $r('app.color.shape_color_L')
}
name: 'T_0',
cells: [{ row: 0, col: 0 }, { row: 0, col: 1 }, { row: 0, col: 2 }, { row: 1, col: 1 }],
color: $r('app.color.shape_color_T')
},
{
name: 'T_90',
cells: [{ row: 0, col: 0 }, { row: 1, col: 0 }, { row: 1, col: 1 }, { row: 2, col: 0 }],
color: $r('app.color.shape_color_T')
},
{
name: 'T_180',
cells: [{ row: 0, col: 1 }, { row: 1, col: 0 }, { row: 1, col: 1 }, { row: 1, col: 2 }],
color: $r('app.color.shape_color_T')
},
{
name: 'T_270',
cells: [{ row: 0, col: 1 }, { row: 1, col: 0 }, { row: 1, col: 1 }, { row: 2, col: 1 }],
color: $r('app.color.shape_color_T')
},

// ── S pieces ─────────────────────────────────────────────────────────────
{
name: 'S_0',
cells: [{ row: 0, col: 1 }, { row: 0, col: 2 }, { row: 1, col: 0 }, { row: 1, col: 1 }],
color: $r('app.color.shape_color_S')
},
{
name: 'S_90',
cells: [{ row: 0, col: 0 }, { row: 1, col: 0 }, { row: 1, col: 1 }, { row: 2, col: 1 }],
color: $r('app.color.shape_color_S')
},

// ── Z pieces ─────────────────────────────────────────────────────────────
{
name: 'Z_0',
cells: [{ row: 0, col: 0 }, { row: 0, col: 1 }, { row: 1, col: 1 }, { row: 1, col: 2 }],
color: $r('app.color.shape_color_Z')
},
{
name: 'Z_90',
cells: [{ row: 0, col: 1 }, { row: 1, col: 0 }, { row: 1, col: 1 }, { row: 2, col: 0 }],
color: $r('app.color.shape_color_Z')
}

];
}
}
30 changes: 19 additions & 11 deletions entry/src/main/ets/components/GameBoard.ets
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,21 @@ const BOARD_CELLS: Array<CellRef> = buildBoardCells(GameConstants.GRID_SIZE);
export struct GameBoard {
@Link gridState: (Resource | null)[][];
@Link cellAnimationState: Map<string, 'clearing' | 'cleared'>;
@Link highlightCells: Cell[];
@Watch('onHighlightCellsChange') @Link highlightCells: Cell[];
@Prop currentShape: ShapeDefinition;
@Link dropZoneArea: Area;
@State highlightCellKeys: Set<string> = new Set();

aboutToAppear(): void {
this.onHighlightCellsChange();
}

private onHighlightCellsChange(): void {
this.highlightCellKeys = new Set(this.highlightCells.map((cell: Cell) => `${cell.row}-${cell.col}`));
}

private isHighlighted(row: number, col: number): boolean {
return this.highlightCells.some((c: Cell) => c.row === row && c.col === col);
return this.highlightCellKeys.has(`${row}-${col}`);
}

build() {
Expand All @@ -37,27 +46,26 @@ export struct GameBoard {
GridItem() {
if (this.gridState[cell.row][cell.col] !== null) {
Text()
.width('100%')
.height('100%')
.width('90%')
.height('90%')
.backgroundColor(this.gridState[cell.row][cell.col] as Resource)
.borderRadius(4)
.padding(4)
.scale(this.cellAnimationState.get(cell.id) === 'clearing'
? { x: 1.3, y: 1.3 }
: { x: 1, y: 1 })
.opacity(this.cellAnimationState.get(cell.id) === 'clearing' ? 0 : 1)
.animation({ duration: 300, curve: Curve.EaseIn })
} else if (this.isHighlighted(cell.row, cell.col)) {
Text()
.width('95%')
.height('95%')
.width('90%')
.height('90%')
.backgroundColor(this.currentShape.color)
.opacity(0.5)
.borderRadius(4)
} else {
Column()
.width('95%')
.height('95%')
.width('90%')
.height('90%')
.backgroundColor(Color.Transparent)
.border({ width: 1, color: '#20284E' })
}
Expand All @@ -67,10 +75,10 @@ export struct GameBoard {
.backgroundColor('#242C54')
.columnsTemplate(Array(GameConstants.GRID_SIZE).fill('1fr').join(' '))
.rowsTemplate(Array(GameConstants.GRID_SIZE).fill('1fr').join(' '))
.width('50%')
.width('62%')
.aspectRatio(1)
.onAreaChange((_, area) => this.dropZoneArea = area)
.margin({ bottom: 15 })
.margin({ bottom: 10 })
.borderRadius(4)
.padding(4)
}
Expand Down
Loading