Skip to content

Commit f028100

Browse files
committed
✨ (2048): add game over condition and restart logic
1 parent 677ee1b commit f028100

File tree

5 files changed

+210
-2
lines changed

5 files changed

+210
-2
lines changed

go.mod

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,19 @@ require (
77
github.com/stretchr/testify v1.10.0
88
)
99

10+
require (
11+
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2 // indirect
12+
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
13+
golang.org/x/mobile v0.0.0-20210208171126-f462b3930c8f // indirect
14+
)
15+
1016
require (
1117
github.com/davecgh/go-spew v1.1.1 // indirect
1218
github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 // indirect
1319
github.com/ebitengine/hideconsole v1.0.0 // indirect
1420
github.com/ebitengine/purego v0.8.0 // indirect
1521
github.com/go-text/typesetting v0.2.0 // indirect
22+
github.com/hajimehoshi/ebiten v1.12.12
1623
github.com/jezek/xgb v1.1.1 // indirect
1724
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
1825
github.com/pmezard/go-difflib v1.0.0 // indirect

go.sum

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
12
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
23
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
34
github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 h1:Gk1XUEttOk0/hb6Tq3WkmutWa0ZLhNn/6fc6XZpM7tM=
@@ -6,35 +7,90 @@ github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj
67
github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A=
78
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
89
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
10+
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2 h1:Ac1OEHHkbAZ6EUnJahF0GKcU0FjPc/V8F1DvjhKngFE=
11+
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
912
github.com/go-text/typesetting v0.2.0 h1:fbzsgbmk04KiWtE+c3ZD4W2nmCRzBqrqQOvYlwAOdho=
1013
github.com/go-text/typesetting v0.2.0/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I=
1114
github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY=
1215
github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
16+
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
17+
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
18+
github.com/hajimehoshi/bitmapfont v1.3.0/go.mod h1:/Qb7yVjHYNUV4JdqNkPs6BSZwLjKqkZOMIp6jZD0KgE=
1319
github.com/hajimehoshi/bitmapfont/v3 v3.2.0 h1:0DISQM/rseKIJhdF29AkhvdzIULqNIIlXAGWit4ez1Q=
1420
github.com/hajimehoshi/bitmapfont/v3 v3.2.0/go.mod h1:8gLqGatKVu0pwcNCJguW3Igg9WQqVXF0zg/RvrGQWyg=
21+
github.com/hajimehoshi/ebiten v1.12.12 h1:JvmF1bXRa+t+/CcLWxrJCRsdjs2GyBYBSiFAfIqDFlI=
22+
github.com/hajimehoshi/ebiten v1.12.12/go.mod h1:1XI25ImVCDPJiXox4h9yK/CvN5sjDYnbF4oZcFzPXHw=
1523
github.com/hajimehoshi/ebiten/v2 v2.8.8 h1:xyMxOAn52T1tQ+j3vdieZ7auDBOXmvjUprSrxaIbsi8=
1624
github.com/hajimehoshi/ebiten/v2 v2.8.8/go.mod h1:durJ05+OYnio9b8q0sEtOgaNeBEQG7Yr7lRviAciYbs=
25+
github.com/hajimehoshi/file2byteslice v0.0.0-20200812174855-0e5e8a80490e/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE=
26+
github.com/hajimehoshi/go-mp3 v0.3.1/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
27+
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
28+
github.com/hajimehoshi/oto v0.6.8/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
29+
github.com/jakecoffman/cp v1.0.0/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg=
1730
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
1831
github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
32+
github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk=
33+
github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0=
1934
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
2035
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
2136
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
2237
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
2338
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
2439
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
2540
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
41+
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
2642
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2743
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2844
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
2945
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
46+
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
47+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
48+
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
49+
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
50+
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
51+
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
52+
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
53+
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
54+
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
55+
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
56+
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
57+
golang.org/x/image v0.0.0-20200801110659-972c09e46d76/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
3058
golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw=
3159
golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM=
60+
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
61+
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
62+
golang.org/x/mobile v0.0.0-20210208171126-f462b3930c8f h1:aEcjdTsycgPqO/caTgnxfR9xwWOltP/21vtJyFztEy0=
63+
golang.org/x/mobile v0.0.0-20210208171126-f462b3930c8f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
64+
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
65+
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
66+
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
67+
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
68+
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
69+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
70+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
71+
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
72+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
73+
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
3274
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
3375
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
76+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
77+
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
78+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
79+
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
80+
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
81+
golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
3482
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
3583
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
84+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
3685
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
3786
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
87+
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
88+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
89+
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
90+
golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
91+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
92+
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
93+
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
3894
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
3995
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
4096
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

internal/game/end_condition.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package game
2+
3+
// hasEmptyTile - 判斷是否還有可移動的位置
4+
func (g *Game) hasEmptyTile() bool {
5+
for row := 0; row < sideSize; row++ {
6+
for col := 0; col < sideSize; col++ {
7+
if g.board[row][col] == 0 {
8+
return true
9+
}
10+
}
11+
}
12+
13+
return false
14+
}
15+
16+
// canMerge 判斷是否有可以合併的 tiles
17+
func (g *Game) canMerge() bool {
18+
for row := 0; row < sideSize; row++ {
19+
for col := 0; col < sideSize; col++ {
20+
if col < sideSize-1 && g.board[row][col] == g.board[row][col+1] {
21+
return true
22+
}
23+
if row < sideSize-1 && g.board[row][col] == g.board[row+1][col] {
24+
return true
25+
}
26+
}
27+
}
28+
29+
return false
30+
}
31+
32+
// IsGameOver - 判斷遊戲是否無法繼續
33+
func (g *Game) IsGameOver() bool {
34+
return !g.hasEmptyTile() && !g.canMerge()
35+
}
36+
37+
// InitGame 初始化遊戲
38+
func (g *Game) InitGame() {
39+
board := make([][]int, sideSize)
40+
for idx := range board {
41+
board[idx] = make([]int, sideSize)
42+
}
43+
g.board = board
44+
g.AddRandomTile(Default)
45+
g.AddRandomTile(Default)
46+
}

internal/layout/action_handler.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,24 @@ import (
88

99
// Update - 用來處理畫面偵測,與使用者互動,並且觸發狀態變更
1010
func (g *GameLayout) Update() error {
11+
// 判斷是否遊戲結束
12+
if g.isGameOver {
13+
// 處理 restart 邏輯
14+
g.handleRestartGame()
15+
return nil
16+
}
17+
// 根據輸入產生對應的更新
18+
g.handleInput()
19+
20+
// 根據目前的盤面跟更新是否能夠繼續執行
21+
if g.gameInstance.IsGameOver() {
22+
g.isGameOver = true
23+
}
24+
return nil
25+
}
26+
27+
// handleInput - 處理輸入產生對應的更新
28+
func (g *GameLayout) handleInput() {
1129
if inpututil.IsKeyJustPressed(ebiten.KeyArrowUp) {
1230
g.gameInstance.MoveUp()
1331
g.gameInstance.AddRandomTile(game.DirectionUp)
@@ -24,6 +42,21 @@ func (g *GameLayout) Update() error {
2442
g.gameInstance.MoveRight()
2543
g.gameInstance.AddRandomTile(game.DirectionRight)
2644
}
45+
}
2746

28-
return nil
47+
// handleRestartGame - 偵測目前 restart button
48+
func (g *GameLayout) handleRestartGame() {
49+
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
50+
x, y := ebiten.CursorPosition()
51+
if restartButtonRect.Min.X <= x && x <= restartButtonRect.Max.X &&
52+
restartButtonRect.Min.Y <= y && y <= restartButtonRect.Max.Y {
53+
g.restartGame()
54+
}
55+
}
56+
}
57+
58+
// restartGame - 重設目前遊戲狀態
59+
func (g *GameLayout) restartGame() {
60+
g.gameInstance.InitGame()
61+
g.isGameOver = false
2962
}

internal/layout/layout.go

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@ package layout
22

33
import (
44
"fmt"
5+
"image"
56
"image/color"
67

78
"github.com/hajimehoshi/ebiten/v2"
89
"github.com/hajimehoshi/ebiten/v2/text/v2"
10+
"github.com/hajimehoshi/ebiten/v2/vector"
911
"github.com/leetcode-golang-classroom/2048-game/internal/game"
1012
)
1113

14+
var restartButtonRect = image.Rect(165, 250, 285, 300) // X1,Y1,X2,Y2
15+
1216
const (
1317
tileSize = 100
1418
gridSize = 4
@@ -19,6 +23,7 @@ const (
1923

2024
type GameLayout struct {
2125
gameInstance *game.Game
26+
isGameOver bool
2227
}
2328

2429
// drawCell - 透過目前值來畫出目前 cell 的格子顏色
@@ -56,7 +61,16 @@ func (g *GameLayout) drawTileText(screen *ebiten.Image, value, row, col int) {
5661
func (g *GameLayout) Draw(screen *ebiten.Image) {
5762
// 背景色
5863
screen.Fill(color.RGBA{250, 248, 239, 255})
59-
// 畫 4x4 格子
64+
// 畫出目前局面
65+
g.drawBoard(screen)
66+
// 當 gameOver 顯示 GameOver
67+
if g.isGameOver {
68+
g.drawGameOver(screen)
69+
}
70+
}
71+
72+
// drawBoard - 畫出目前局面
73+
func (g *GameLayout) drawBoard(screen *ebiten.Image) {
6074
for row := 0; row < gridSize; row++ {
6175
for col := 0; col < gridSize; col++ {
6276
// 取出值
@@ -69,6 +83,58 @@ func (g *GameLayout) Draw(screen *ebiten.Image) {
6983
}
7084
}
7185

86+
// drawCoverOnGameOver - 畫出無法操作的灰色遮罩
87+
func (g *GameLayout) drawCoverOnGameOver(screen *ebiten.Image) {
88+
w, h := screen.Bounds().Dx(), screen.Bounds().Dy()
89+
vector.DrawFilledRect(
90+
screen,
91+
0, 0, // x, y
92+
float32(w), float32(h), // width, height
93+
color.RGBA{0, 0, 0, 128}, // 半透明黑色 (128 = 約 50% 透明)
94+
false,
95+
)
96+
}
97+
98+
// drawGameOver 畫出 GameOver
99+
func (g *GameLayout) drawGameOver(screen *ebiten.Image) {
100+
g.drawCoverOnGameOver(screen)
101+
// 設定顯示 Game Over 文字
102+
textXPos := WinHeight / 2
103+
textYPos := WinWidth / 2
104+
textOpts := &text.DrawOptions{}
105+
textOpts.ColorScale.ScaleWithColor(color.RGBA{128, 200, 200, 255})
106+
textOpts.PrimaryAlign = text.AlignCenter
107+
textOpts.SecondaryAlign = text.AlignCenter
108+
textOpts.GeoM.Translate(float64(textXPos), float64(textYPos))
109+
text.Draw(screen, "Game Over", &text.GoTextFace{
110+
Source: mplusFaceSource,
111+
Size: 48.0,
112+
}, textOpts)
113+
g.drawRestartButton(screen)
114+
}
115+
116+
// drawRestartButton - 畫出 restart button
117+
func (g *GameLayout) drawRestartButton(screen *ebiten.Image) {
118+
// 畫 Restart 按鈕
119+
vector.DrawFilledRect(screen,
120+
float32(restartButtonRect.Min.X),
121+
float32(restartButtonRect.Min.Y),
122+
float32(restartButtonRect.Dx()),
123+
float32(restartButtonRect.Dy()),
124+
color.RGBA{200, 200, 200, 255},
125+
true,
126+
)
127+
textOpts := &text.DrawOptions{}
128+
textOpts.ColorScale.ScaleWithColor(color.Black)
129+
textOpts.PrimaryAlign = text.AlignCenter
130+
textOpts.SecondaryAlign = text.AlignCenter
131+
textOpts.GeoM.Translate(float64(restartButtonRect.Min.X+restartButtonRect.Dx()/2), float64(restartButtonRect.Min.Y+restartButtonRect.Dy()/2))
132+
text.Draw(screen, "Restart", &text.GoTextFace{
133+
Source: mplusFaceSource,
134+
Size: 30,
135+
}, textOpts)
136+
}
137+
72138
func (g *GameLayout) Layout(outsideWidth, outsideHeight int) (int, int) {
73139
return WinWidth, WinHeight
74140
}

0 commit comments

Comments
 (0)