diff --git a/BUILD.md b/BUILD.md
new file mode 100644
index 0000000..f05bc7d
--- /dev/null
+++ b/BUILD.md
@@ -0,0 +1,143 @@
+# Tetris Game 构建系统
+
+这是一个统一的构建系统,支持 Windows、macOS 和 Linux 平台的应用打包,所有分发文件统一存放在 `dist/` 目录中。
+
+## 🚀 快速开始
+
+### 构建所有平台
+```bash
+make all
+```
+
+### 构建特定平台
+```bash
+make windows # 构建 Windows 版本
+make macos # 构建 macOS 版本
+make linux # 构建 Linux 版本
+```
+
+### 查看帮助
+```bash
+make help
+```
+
+## 📁 目录结构
+
+构建完成后,`dist/` 目录结构如下:
+
+```
+dist/
+├── TetrisGame-Windows-v1.0.zip # Windows 分发包
+├── TetrisGame-macOS-v1.0.dmg # macOS 分发包
+├── TetrisGame-Linux-v1.0.tar.gz # Linux 分发包
+├── windows/ # Windows 构建临时目录
+├── macos/ # macOS 构建临时目录
+└── linux/ # Linux 构建临时目录
+```
+
+## 🛠️ 可用命令
+
+### 基础命令
+- `make help` - 显示帮助信息
+- `make info` - 显示构建信息
+- `make check-env` - 检查构建环境
+- `make clean` - 清理所有构建文件
+
+### 构建命令
+- `make windows` - 构建 Windows 版本
+- `make macos` - 构建 macOS 版本
+- `make linux` - 构建 Linux 版本
+- `make all` - 构建所有平台版本
+
+### 验证命令
+- `make verify` - 验证构建结果
+- `make run` - 运行游戏(开发模式)
+- `make test` - 运行测试
+
+### 依赖管理
+- `make deps` - 下载依赖
+
+### 迁移命令
+- `make migrate` - 迁移旧版本文件到 dist 目录
+
+## 📦 分发包内容
+
+### Windows 版本 (TetrisGame-Windows-v1.0.zip)
+```
+TetrisGame-Windows-v1.0/
+├── TetrisGame.exe # 主程序
+├── 启动游戏.bat # 中文启动脚本
+├── README.txt # 英文说明
+├── 安装说明.txt # 中文说明
+└── assets/ # 资源文件
+ └── fonts/ # 字体文件
+ ├── HYSongYunLangHeiW-1.ttf
+ └── README.md
+```
+
+### macOS 版本 (TetrisGame-macOS-v1.0.dmg)
+- 包含完整的 TetrisGame.app 应用包
+- 支持拖拽到 Applications 文件夹安装
+- 包含应用图标和中文显示名称
+
+### Linux 版本 (TetrisGame-Linux-v1.0.tar.gz)
+```
+TetrisGame-Linux-v1.0/
+├── tetris # 主程序
+├── start_game.sh # 启动脚本
+├── README.txt # 说明文档
+└── assets/ # 资源文件
+ └── fonts/ # 字体文件
+```
+
+## 🔧 系统要求
+
+### 构建环境
+- Go 1.19 或更高版本
+- macOS (用于构建 macOS 版本的 DMG)
+- zip 命令 (用于创建 Windows 压缩包)
+- tar 命令 (用于创建 Linux 压缩包)
+
+### 运行环境
+- **Windows**: Windows 10/11 (64-bit)
+- **macOS**: macOS 10.15 或更高版本
+- **Linux**: 64-bit Linux 发行版,支持 X11 或 Wayland
+
+## 🎮 游戏控制
+
+- **←→** 方向键:左右移动方块
+- **↑** 方向键:旋转方块
+- **↓** 方向键:快速下降
+- **空格键**:瞬间下降到底部
+- **L 键**:切换中英文界面
+- **R 键**:游戏结束后重新开始
+
+## 🌍 多语言支持
+
+游戏支持中英文双语界面,按 L 键可以实时切换语言。
+
+## 📝 版本信息
+
+- **版本**: v1.0
+- **构建时间**: 自动生成
+- **Git 提交**: 自动检测
+
+## 🔍 故障排除
+
+### 构建失败
+1. 检查 Go 环境:`go version`
+2. 检查依赖:`make deps`
+3. 清理重建:`make clean && make all`
+
+### 字体问题
+- 确保 `assets/fonts/` 目录存在
+- 确保字体文件 `HYSongYunLangHeiW-1.ttf` 存在
+
+### macOS DMG 创建失败
+- 确保在 macOS 系统上运行
+- 确保有足够的磁盘空间
+- 检查 hdiutil 命令是否可用
+
+## 📄 许可证
+
+© 2025 Tetris Game. All rights reserved.
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 938c8d0..dd8b6d5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,223 +1,331 @@
-# Tetris Game Makefile
-# Supports cross-compilation for Windows, macOS, and Linux
-
-# Variables
-BINARY_NAME=tetris
-MAIN_PATH=.
-BUILD_DIR=build
-VERSION?=dev
-LDFLAGS=-ldflags "-s -w"
-
-# Platform specific settings
-WINDOWS_BINARY=$(BINARY_NAME).exe
-MACOS_BINARY=$(BINARY_NAME)_macos
-LINUX_BINARY=$(BINARY_NAME)_linux
-
-# Default target
+# Tetris Game - 统一构建系统
+# 支持 Windows 和 macOS 应用打包,统一使用 dist 目录
+
+# 基础配置
+BINARY_NAME := tetris
+APP_NAME := TetrisGame
+VERSION := 1.0
+BUILD_TIME := $(shell date '+%Y-%m-%d %H:%M:%S')
+GIT_COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
+
+# 目录配置
+DIST_DIR := dist
+ASSETS_DIR := assets
+BUILD_TEMP := .build_temp
+
+# Go 构建标志
+GO_LDFLAGS := -ldflags "-s -w"
+GO_LDFLAGS_GUI := -ldflags "-s -w -H windowsgui"
+
+# 平台特定配置
+WINDOWS_DIR := $(DIST_DIR)/windows
+MACOS_DIR := $(DIST_DIR)/macos
+LINUX_DIR := $(DIST_DIR)/linux
+
+# 版本文件名
+WINDOWS_ZIP := $(APP_NAME)-Windows-v$(VERSION).zip
+MACOS_DMG := $(APP_NAME)-macOS-v$(VERSION).dmg
+LINUX_TAR := $(APP_NAME)-Linux-v$(VERSION).tar.gz
+
+# 颜色输出
+BLUE := \033[34m
+GREEN := \033[32m
+YELLOW := \033[33m
+RED := \033[31m
+RESET := \033[0m
+
+.DEFAULT_GOAL := help
+
+# 帮助信息
.PHONY: help
-help: ## Show this help message
- @echo "Available targets:"
- @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-20s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
-
-# Build targets
-.PHONY: build
-build: ## Build for current platform
- @echo "Building $(BINARY_NAME) for current platform..."
- go build $(LDFLAGS) -o $(BINARY_NAME) $(MAIN_PATH)
- @echo "Build complete: $(BINARY_NAME)"
-
-.PHONY: build-windows
-build-windows: ## Build for Windows (64-bit) - requires Windows or cross-compilation setup
- @echo "Building $(BINARY_NAME) for Windows..."
- @echo "Note: Cross-compilation for Ebitengine requires proper CGO setup"
- @mkdir -p $(BUILD_DIR)
- CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(WINDOWS_BINARY) $(MAIN_PATH)
- @echo "Build complete: $(BUILD_DIR)/$(WINDOWS_BINARY)"
-
-.PHONY: build-windows-native
-build-windows-native: ## Build for Windows using native Go (may have limitations)
- @echo "Building $(BINARY_NAME) for Windows (native Go)..."
- @mkdir -p $(BUILD_DIR)
- CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build $(LDFLAGS) -tags ebitengine -o $(BUILD_DIR)/$(WINDOWS_BINARY) $(MAIN_PATH)
- @echo "Build complete: $(BUILD_DIR)/$(WINDOWS_BINARY)"
-
-.PHONY: build-macos
-build-macos: ## Build for macOS (Intel) - requires macOS or cross-compilation setup
- @echo "Building $(BINARY_NAME) for macOS (Intel)..."
- @echo "Note: Cross-compilation for Ebitengine requires proper CGO setup"
- @mkdir -p $(BUILD_DIR)
- CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(MACOS_BINARY)_amd64 $(MAIN_PATH)
- @echo "Build complete: $(BUILD_DIR)/$(MACOS_BINARY)_amd64"
-
-.PHONY: build-macos-arm
-build-macos-arm: ## Build for macOS (Apple Silicon) - requires macOS or cross-compilation setup
- @echo "Building $(BINARY_NAME) for macOS (Apple Silicon)..."
- @echo "Note: Cross-compilation for Ebitengine requires proper CGO setup"
- @mkdir -p $(BUILD_DIR)
- CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(MACOS_BINARY)_arm64 $(MAIN_PATH)
- @echo "Build complete: $(BUILD_DIR)/$(MACOS_BINARY)_arm64"
-
-.PHONY: build-macos-current
-build-macos-current: ## Build for current macOS architecture
- @echo "Building $(BINARY_NAME) for current macOS architecture..."
- @mkdir -p $(BUILD_DIR)
- go build $(LDFLAGS) -o $(BUILD_DIR)/$(MACOS_BINARY)_$$(go env GOARCH) $(MAIN_PATH)
- @echo "Build complete: $(BUILD_DIR)/$(MACOS_BINARY)_$$(go env GOARCH)"
-
-.PHONY: build-linux
-build-linux: ## Build for Linux (64-bit) - requires Linux or cross-compilation setup
- @echo "Building $(BINARY_NAME) for Linux..."
- @echo "Note: Cross-compilation for Ebitengine requires proper CGO setup"
- @mkdir -p $(BUILD_DIR)
- CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(LINUX_BINARY) $(MAIN_PATH)
- @echo "Build complete: $(BUILD_DIR)/$(LINUX_BINARY)"
-
-.PHONY: build-current-platform
-build-current-platform: ## Build for current platform (recommended)
- @echo "Building $(BINARY_NAME) for current platform: $$(go env GOOS)/$$(go env GOARCH)"
- @mkdir -p $(BUILD_DIR)
- go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)_$$(go env GOOS)_$$(go env GOARCH) $(MAIN_PATH)
- @echo "Build complete: $(BUILD_DIR)/$(BINARY_NAME)_$$(go env GOOS)_$$(go env GOARCH)"
-
-.PHONY: build-all
-build-all: ## Build for all platforms (requires proper cross-compilation setup)
- @echo "Attempting to build for all platforms..."
- @echo "Note: This may fail without proper cross-compilation environment"
- $(MAKE) build-current-platform
- -$(MAKE) build-windows-native
- @echo "Build summary:"
- @ls -la $(BUILD_DIR)/ 2>/dev/null || echo "No builds completed successfully"
-
-# Development targets
-.PHONY: run
-run: ## Run the game
- @echo "Starting Tetris game..."
- go run $(MAIN_PATH)
+help: ## 显示帮助信息
+ @echo "$(BLUE)🎮 Tetris Game 构建系统$(RESET)"
+ @echo ""
+ @echo "$(GREEN)基础命令:$(RESET)"
+ @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " $(YELLOW)%-20s$(RESET) %s\n", $$1, $$2}' $(MAKEFILE_LIST)
+ @echo ""
+ @echo "$(GREEN)示例:$(RESET)"
+ @echo " make all # 构建所有平台"
+ @echo " make windows # 仅构建 Windows 版本"
+ @echo " make macos # 仅构建 macOS 版本"
+ @echo " make clean # 清理所有构建文件"
-.PHONY: dev
-dev: clean build run ## Clean, build and run for development
+# 信息显示
+.PHONY: info
+info: ## 显示构建信息
+ @echo "$(BLUE)📋 构建信息$(RESET)"
+ @echo " 应用名称: $(APP_NAME)"
+ @echo " 版本: v$(VERSION)"
+ @echo " 构建时间: $(BUILD_TIME)"
+ @echo " Git 提交: $(GIT_COMMIT)"
+ @echo " Go 版本: $(shell go version)"
+ @echo " 当前平台: $(shell go env GOOS)/$(shell go env GOARCH)"
+ @echo " 分发目录: $(DIST_DIR)/"
+
+# 环境检查
+.PHONY: check-env
+check-env: ## 检查构建环境
+ @echo "$(BLUE)🔍 环境检查$(RESET)"
+ @go version >/dev/null 2>&1 || (echo "$(RED)❌ Go 未安装$(RESET)" && exit 1)
+ @echo "$(GREEN)✅ Go 环境正常$(RESET)"
+ @[ -d "$(ASSETS_DIR)" ] || (echo "$(RED)❌ assets 目录缺失$(RESET)" && exit 1)
+ @echo "$(GREEN)✅ 资源目录存在$(RESET)"
+ @[ -f "main.go" ] || (echo "$(RED)❌ main.go 文件缺失$(RESET)" && exit 1)
+ @echo "$(GREEN)✅ 源代码文件存在$(RESET)"
+
+# 准备目录
+.PHONY: prepare
+prepare: ## 准备构建目录
+ @echo "$(BLUE)📁 准备构建目录$(RESET)"
+ @mkdir -p $(DIST_DIR)
+ @mkdir -p $(WINDOWS_DIR)
+ @mkdir -p $(MACOS_DIR)
+ @mkdir -p $(LINUX_DIR)
+ @mkdir -p $(BUILD_TEMP)
+
+# 清理
+.PHONY: clean
+clean: ## 清理构建文件
+ @echo "$(BLUE)🧹 清理构建文件$(RESET)"
+ @rm -rf $(DIST_DIR)
+ @rm -rf $(BUILD_TEMP)
+ @rm -f *.zip *.dmg *.tar.gz
+ @rm -f $(BINARY_NAME) $(BINARY_NAME).exe
+ @rm -rf TetrisGame.app
+ @echo "$(GREEN)✅ 清理完成$(RESET)"
+
+# Windows 构建
+.PHONY: build-windows-binary
+build-windows-binary: check-env prepare
+ @echo "$(BLUE)🪟 构建 Windows 二进制文件$(RESET)"
+ GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build $(GO_LDFLAGS_GUI) -o $(BUILD_TEMP)/$(APP_NAME).exe .
+ @echo "$(GREEN)✅ Windows 二进制文件构建完成$(RESET)"
+
+.PHONY: create-windows-startup-script
+create-windows-startup-script:
+ @echo "创建 Windows 启动脚本..."
+ @echo '@echo off' > $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/启动游戏.bat
+ @echo 'chcp 65001 >nul' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/启动游戏.bat
+ @echo 'echo 🎮 正在启动 Tetris Game...' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/启动游戏.bat
+ @echo 'echo.' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/启动游戏.bat
+ @echo 'echo 🎯 游戏控制:' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/启动游戏.bat
+ @echo 'echo ←→ 左右移动' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/启动游戏.bat
+ @echo 'echo ↑ 旋转方块' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/启动游戏.bat
+ @echo 'echo ↓ 快速下降' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/启动游戏.bat
+ @echo 'echo 空格 瞬间下降' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/启动游戏.bat
+ @echo 'echo L 切换中英文' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/启动游戏.bat
+ @echo 'echo.' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/启动游戏.bat
+ @echo '"%~dp0$(APP_NAME).exe"' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/启动游戏.bat
+
+.PHONY: create-windows-docs
+create-windows-docs:
+ @echo "创建 Windows 说明文档..."
+ @echo '🎮 Tetris Game - Windows Version v1.0' > $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/README.txt
+ @echo '' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/README.txt
+ @echo '📥 Installation: This is a portable version, no installation required.' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/README.txt
+ @echo '🚀 How to Start: Double-click TetrisGame.exe or 启动游戏.bat' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/README.txt
+ @echo '🎯 Game Controls: ←→ Move, ↑ Rotate, ↓ Soft drop, Space Hard drop, L Language' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/README.txt
+ @echo '🔧 System Requirements: Windows 10/11 (64-bit)' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/README.txt
+ @echo '' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/README.txt
+ @echo '© 2025 Tetris Game. All rights reserved.' >> $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/README.txt
+
+.PHONY: package-windows
+package-windows: build-windows-binary
+ @echo "$(BLUE)📦 打包 Windows 应用$(RESET)"
+
+ # 创建 Windows 应用目录
+ @rm -rf $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)
+ @mkdir -p $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)
+
+ # 复制主程序文件
+ @cp $(BUILD_TEMP)/$(APP_NAME).exe $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/
+
+ # 复制资源文件
+ @cp -r $(ASSETS_DIR) $(WINDOWS_DIR)/$(APP_NAME)-Windows-v$(VERSION)/
+
+ # 创建启动脚本和文档
+ @$(MAKE) create-windows-startup-script
+ @$(MAKE) create-windows-docs
+
+ # 创建压缩包
+ @cd $(WINDOWS_DIR) && zip -r ../$(WINDOWS_ZIP) $(APP_NAME)-Windows-v$(VERSION)/
+
+ @echo "$(GREEN)✅ Windows 应用打包完成: $(DIST_DIR)/$(WINDOWS_ZIP)$(RESET)"
+
+# macOS 构建
+.PHONY: build-macos-binary
+build-macos-binary: check-env prepare
+ @echo "$(BLUE)🍎 构建 macOS 二进制文件$(RESET)"
+ go build $(GO_LDFLAGS) -o $(BUILD_TEMP)/$(BINARY_NAME) .
+ @echo "$(GREEN)✅ macOS 二进制文件构建完成$(RESET)"
+
+.PHONY: create-macos-plist
+create-macos-plist:
+ @echo "创建 macOS Info.plist..."
+ @echo '' > $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo '' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo '' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo '' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' CFBundleDisplayName' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' 俄罗斯方块' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' CFBundleExecutable' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' $(BINARY_NAME)' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' CFBundleIdentifier' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' com.tetrisgame.app' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' CFBundleName' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' $(APP_NAME)' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' CFBundleShortVersionString' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' $(VERSION)' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' CFBundleIconFile' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' app' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' LSApplicationCategoryType' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo ' public.app-category.games' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo '' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+ @echo '' >> $(MACOS_DIR)/$(APP_NAME).app/Contents/Info.plist
+
+.PHONY: create-dmg
+create-dmg:
+ @echo "创建 DMG 文件..."
+ @DMG_SIZE="50m" && \
+ TEMP_DMG="$(BUILD_TEMP)/temp_$(MACOS_DMG)" && \
+ MOUNT_POINT="$(BUILD_TEMP)/dmg_mount" && \
+ rm -rf $$MOUNT_POINT && \
+ mkdir -p $$MOUNT_POINT && \
+ hdiutil create -size $$DMG_SIZE -fs HFS+ -volname "Tetris Game" $$TEMP_DMG && \
+ hdiutil attach $$TEMP_DMG -mountpoint $$MOUNT_POINT && \
+ cp -R $(MACOS_DIR)/$(APP_NAME).app $$MOUNT_POINT/ && \
+ ln -s /Applications $$MOUNT_POINT/Applications && \
+ hdiutil detach $$MOUNT_POINT && \
+ hdiutil convert $$TEMP_DMG -format UDZO -o $(DIST_DIR)/$(MACOS_DMG) && \
+ rm -f $$TEMP_DMG
+
+.PHONY: package-macos
+package-macos: build-macos-binary
+ @echo "$(BLUE)📦 打包 macOS 应用$(RESET)"
+
+ # 创建 .app 包结构
+ @rm -rf $(MACOS_DIR)/$(APP_NAME).app
+ @mkdir -p $(MACOS_DIR)/$(APP_NAME).app/Contents/MacOS
+ @mkdir -p $(MACOS_DIR)/$(APP_NAME).app/Contents/Resources
+
+ # 复制可执行文件
+ @cp $(BUILD_TEMP)/$(BINARY_NAME) $(MACOS_DIR)/$(APP_NAME).app/Contents/MacOS/
+ @chmod +x $(MACOS_DIR)/$(APP_NAME).app/Contents/MacOS/$(BINARY_NAME)
+
+ # 复制资源文件
+ @cp -r $(ASSETS_DIR) $(MACOS_DIR)/$(APP_NAME).app/Contents/Resources/
+
+ # 创建 Info.plist
+ @$(MAKE) create-macos-plist
+
+ # 复制应用图标
+ @if [ -f "$(ASSETS_DIR)/icons/app.icns" ]; then \
+ cp "$(ASSETS_DIR)/icons/app.icns" $(MACOS_DIR)/$(APP_NAME).app/Contents/Resources/; \
+ echo "$(GREEN)✅ 已复制应用图标 app.icns$(RESET)"; \
+ else \
+ echo "$(YELLOW)⚠️ 图标文件 $(ASSETS_DIR)/icons/app.icns 不存在$(RESET)"; \
+ fi
+
+ # 创建 DMG
+ @$(MAKE) create-dmg
+
+ @echo "$(GREEN)✅ macOS 应用打包完成: $(DIST_DIR)/$(MACOS_DMG)$(RESET)"
+
+# Linux 构建
+.PHONY: build-linux-binary
+build-linux-binary: check-env prepare
+ @echo "$(BLUE)🐧 构建 Linux 二进制文件$(RESET)"
+ @echo "$(YELLOW)⚠️ 注意: Linux 交叉编译需要 CGO 支持,建议在 Linux 系统上构建$(RESET)"
+ @if [ "$$(go env GOOS)" = "linux" ]; then \
+ go build $(GO_LDFLAGS) -o $(BUILD_TEMP)/$(BINARY_NAME)_linux .; \
+ else \
+ echo "$(YELLOW)⚠️ 当前不在 Linux 系统,跳过 Linux 构建$(RESET)"; \
+ mkdir -p $(BUILD_TEMP); \
+ echo "# Linux 构建需要在 Linux 系统上进行" > $(BUILD_TEMP)/$(BINARY_NAME)_linux.txt; \
+ fi
+ @echo "$(GREEN)✅ Linux 二进制文件构建完成$(RESET)"
+
+.PHONY: package-linux
+package-linux: build-linux-binary
+ @echo "$(BLUE)📦 打包 Linux 应用$(RESET)"
+
+ @if [ "$$(go env GOOS)" = "linux" ] && [ -f "$(BUILD_TEMP)/$(BINARY_NAME)_linux" ]; then \
+ echo "正在打包 Linux 应用..."; \
+ rm -rf $(LINUX_DIR)/$(APP_NAME)-Linux-v$(VERSION); \
+ mkdir -p $(LINUX_DIR)/$(APP_NAME)-Linux-v$(VERSION); \
+ cp $(BUILD_TEMP)/$(BINARY_NAME)_linux $(LINUX_DIR)/$(APP_NAME)-Linux-v$(VERSION)/$(BINARY_NAME); \
+ chmod +x $(LINUX_DIR)/$(APP_NAME)-Linux-v$(VERSION)/$(BINARY_NAME); \
+ cp -r $(ASSETS_DIR) $(LINUX_DIR)/$(APP_NAME)-Linux-v$(VERSION)/; \
+ echo '#!/bin/bash' > $(LINUX_DIR)/$(APP_NAME)-Linux-v$(VERSION)/start_game.sh; \
+ echo 'echo "🎮 Starting Tetris Game..."' >> $(LINUX_DIR)/$(APP_NAME)-Linux-v$(VERSION)/start_game.sh; \
+ echo 'cd "$$(dirname "$$0")"' >> $(LINUX_DIR)/$(APP_NAME)-Linux-v$(VERSION)/start_game.sh; \
+ echo './$(BINARY_NAME)' >> $(LINUX_DIR)/$(APP_NAME)-Linux-v$(VERSION)/start_game.sh; \
+ chmod +x $(LINUX_DIR)/$(APP_NAME)-Linux-v$(VERSION)/start_game.sh; \
+ cd $(LINUX_DIR) && tar -czf ../$(LINUX_TAR) $(APP_NAME)-Linux-v$(VERSION)/; \
+ echo "$(GREEN)✅ Linux 应用打包完成: $(DIST_DIR)/$(LINUX_TAR)$(RESET)"; \
+ else \
+ echo "$(YELLOW)⚠️ 跳过 Linux 打包(需要在 Linux 系统上构建)$(RESET)"; \
+ mkdir -p $(LINUX_DIR); \
+ echo "# Linux 版本需要在 Linux 系统上构建" > $(LINUX_DIR)/README.txt; \
+ echo "$(YELLOW)⚠️ Linux 版本跳过$(RESET)"; \
+ fi
-.PHONY: install
-install: ## Install the game to GOPATH/bin
- @echo "Installing $(BINARY_NAME)..."
- go install $(LDFLAGS) $(MAIN_PATH)
- @echo "Installation complete!"
+# 主要构建目标
+.PHONY: windows
+windows: package-windows ## 构建 Windows 版本
-# Quality assurance targets
-.PHONY: test
-test: ## Run tests
- @echo "Running tests..."
- go test -v ./...
+.PHONY: macos
+macos: package-macos ## 构建 macOS 版本
-.PHONY: test-coverage
-test-coverage: ## Run tests with coverage
- @echo "Running tests with coverage..."
- go test -v -coverprofile=coverage.out ./...
- go tool cover -html=coverage.out -o coverage.html
- @echo "Coverage report generated: coverage.html"
+.PHONY: linux
+linux: package-linux ## 构建 Linux 版本
-.PHONY: fmt
-fmt: ## Format code
- @echo "Formatting code..."
- go fmt ./...
+.PHONY: all
+all: clean windows macos ## 构建所有支持的平台版本
+ @if [ "$$(go env GOOS)" = "linux" ]; then \
+ $(MAKE) linux; \
+ else \
+ echo "$(YELLOW)⚠️ 当前平台不支持 Linux 交叉编译,跳过 Linux 构建$(RESET)"; \
+ fi
+ @echo ""
+ @echo "$(GREEN)🎉 所有平台构建完成!$(RESET)"
+ @echo ""
+ @echo "$(BLUE)📋 构建结果:$(RESET)"
+ @ls -lh $(DIST_DIR)/*.zip $(DIST_DIR)/*.dmg $(DIST_DIR)/*.tar.gz 2>/dev/null || true
-.PHONY: vet
-vet: ## Run go vet
- @echo "Running go vet..."
- go vet ./...
+# 验证和测试
+.PHONY: verify
+verify: ## 验证构建结果
+ @echo "$(BLUE)🔍 验证构建结果$(RESET)"
+ @echo ""
+ @echo "$(GREEN)构建文件:$(RESET)"
+ @ls -lh $(DIST_DIR)/ 2>/dev/null || echo "无构建文件"
+ @echo ""
+ @if [ -f "$(DIST_DIR)/$(WINDOWS_ZIP)" ]; then \
+ echo "$(GREEN)✅ Windows 版本: $(WINDOWS_ZIP)$(RESET)"; \
+ unzip -l $(DIST_DIR)/$(WINDOWS_ZIP) | head -10; \
+ fi
+ @if [ -f "$(DIST_DIR)/$(MACOS_DMG)" ]; then \
+ echo "$(GREEN)✅ macOS 版本: $(MACOS_DMG)$(RESET)"; \
+ fi
+ @if [ -f "$(DIST_DIR)/$(LINUX_TAR)" ]; then \
+ echo "$(GREEN)✅ Linux 版本: $(LINUX_TAR)$(RESET)"; \
+ fi
-.PHONY: lint
-lint: ## Run golangci-lint (requires golangci-lint to be installed)
- @echo "Running golangci-lint..."
- golangci-lint run
+# 开发相关
+.PHONY: run
+run: ## 运行游戏(开发模式)
+ @echo "$(BLUE)🎮 启动游戏$(RESET)"
+ go run .
-.PHONY: check
-check: fmt vet test ## Run format, vet, and tests
+.PHONY: test
+test: ## 运行测试
+ @echo "$(BLUE)🧪 运行测试$(RESET)"
+ go test -v ./...
-# Dependency management
.PHONY: deps
-deps: ## Download dependencies
- @echo "Downloading dependencies..."
+deps: ## 下载依赖
+ @echo "$(BLUE)📦 下载依赖$(RESET)"
go mod download
-
-.PHONY: deps-update
-deps-update: ## Update dependencies
- @echo "Updating dependencies..."
- go mod tidy
- go get -u ./...
go mod tidy
-.PHONY: deps-vendor
-deps-vendor: ## Vendor dependencies
- @echo "Vendoring dependencies..."
- go mod vendor
-
-# Cleanup targets
-.PHONY: clean
-clean: ## Clean build artifacts
- @echo "Cleaning build artifacts..."
- @rm -f $(BINARY_NAME)
- @rm -rf $(BUILD_DIR)
- @rm -f coverage.out coverage.html
- @echo "Clean complete!"
-
-.PHONY: clean-all
-clean-all: clean ## Clean everything including vendor
- @echo "Cleaning vendor directory..."
- @rm -rf vendor/
-
-# Release targets
-.PHONY: release
-release: clean build-current-platform ## Create release build for current platform
- @echo "Creating release package for current platform..."
- @mkdir -p $(BUILD_DIR)/releases
- @CURRENT_OS=$$(go env GOOS); \
- CURRENT_ARCH=$$(go env GOARCH); \
- if [ "$$CURRENT_OS" = "windows" ]; then \
- cd $(BUILD_DIR) && zip releases/$(BINARY_NAME)-$$CURRENT_OS-$$CURRENT_ARCH.zip $(BINARY_NAME)_$$CURRENT_OS_$$CURRENT_ARCH; \
- else \
- cd $(BUILD_DIR) && tar -czf releases/$(BINARY_NAME)-$$CURRENT_OS-$$CURRENT_ARCH.tar.gz $(BINARY_NAME)_$$CURRENT_OS_$$CURRENT_ARCH; \
- fi
- @echo "Release package created in $(BUILD_DIR)/releases/"
- @ls -la $(BUILD_DIR)/releases/
-
-# Docker targets (optional)
-.PHONY: docker-build
-docker-build: ## Build Docker image
- @echo "Building Docker image..."
- docker build -t $(BINARY_NAME):$(VERSION) .
-
-.PHONY: docker-run
-docker-run: ## Run in Docker container
- @echo "Running in Docker container..."
- docker run --rm -it $(BINARY_NAME):$(VERSION)
-
-# Info targets
-.PHONY: info
-info: ## Show build information
- @echo "=== Build Information ==="
- @echo "Binary name: $(BINARY_NAME)"
- @echo "Version: $(VERSION)"
- @echo "Go version: $$(go version)"
- @echo "Build directory: $(BUILD_DIR)"
- @echo "Current platform: $$(go env GOOS)/$$(go env GOARCH)"
- @echo "CGO enabled: $$(go env CGO_ENABLED)"
-
-.PHONY: env
-env: ## Show Go environment
- @echo "=== Go Environment ==="
- @go env
-
-.PHONY: cross-compile-info
-cross-compile-info: ## Show cross-compilation information
- @echo "=== Cross-Compilation Information ==="
- @echo "Ebitengine requires CGO for cross-compilation."
- @echo "To build for other platforms, you need:"
- @echo "1. C compiler for target platform"
- @echo "2. Platform-specific libraries"
- @echo "3. Proper CGO_ENABLED=1 setting"
- @echo ""
- @echo "Recommended approach:"
- @echo "- Build on the target platform directly"
- @echo "- Use GitHub Actions or CI/CD for multi-platform builds"
- @echo "- Use Docker with multi-stage builds"
\ No newline at end of file
diff --git a/assets/icons/README.md b/assets/icons/README.md
new file mode 100644
index 0000000..3d0cb97
--- /dev/null
+++ b/assets/icons/README.md
@@ -0,0 +1,89 @@
+# 应用图标目录
+
+本目录包含 Tetris 游戏的应用图标文件。
+
+## 文件说明
+
+### 主要图标文件
+- `app.icns` - macOS 应用图标文件 (96K)
+ - 格式: Mac OS X icon, "ic12" type
+ - 包含所有标准尺寸 (16x16 到 1024x1024)
+ - 支持 Retina 显示屏 (@2x 版本)
+
+### PNG 图标文件
+- `icon_16.png` - 16x16 像素图标
+- `icon_32.png` - 32x32 像素图标
+- `icon_64.png` - 64x64 像素图标
+- `icon_128.png` - 128x128 像素图标
+- `icon_256.png` - 256x256 像素图标
+
+## 图标设计
+
+### 设计元素
+- **主题**: 俄罗斯方块彩色块图案
+- **背景**: 深蓝色 (#2c3e50) 圆角矩形
+- **方块颜色**:
+ - 红色 (#e74c3c)
+ - 蓝色 (#3498db)
+ - 橙色 (#f39c12)
+ - 绿色 (#2ecc71)
+ - 紫色 (#9b59b6)
+ - 深橙色 (#e67e22)
+ - 青绿色 (#1abc9c)
+ - 深灰色 (#34495e)
+
+### 技术特性
+- **多分辨率支持**: 适配不同显示密度
+- **圆角设计**: 符合 macOS 设计规范
+- **高光效果**: 增加视觉层次感
+- **白色边框**: 增强方块定义
+
+## 构建集成
+
+在 macOS 构建过程中,`app.icns` 文件会自动复制到应用包中:
+
+```bash
+# 构建 macOS 版本
+make macos
+
+# 图标将自动复制到:
+# dist/macos/TetrisGame.app/Contents/Resources/app.icns
+```
+
+## 图标使用
+
+### macOS 应用
+- 文件位置: `TetrisGame.app/Contents/Resources/app.icns`
+- Info.plist 配置: `CFBundleIconFile = app`
+- 显示位置: Finder、Dock、应用启动台
+
+### 其他平台
+- Windows: 可使用 PNG 文件转换为 .ico 格式
+- Linux: 直接使用 PNG 文件
+
+## 维护说明
+
+### 更新图标
+如需更新应用图标:
+
+1. 替换 `app.icns` 文件
+2. 可选:更新对应的 PNG 文件
+3. 重新构建应用: `make clean && make macos`
+
+### 创建新图标
+如需从零创建新图标:
+
+1. 创建 1024x1024 的高分辨率设计
+2. 生成多个尺寸的 PNG 文件
+3. 使用 macOS `iconutil` 工具创建 .icns 文件:
+ ```bash
+ iconutil -c icns icon.iconset -o app.icns
+ ```
+
+## 文件完整性
+
+所有图标文件的校验和:
+- `app.icns`: 96K (Mac OS X icon format)
+- 总体设计一致性: ✅
+- 多分辨率覆盖: ✅
+- macOS 兼容性: ✅
\ No newline at end of file
diff --git a/assets/icons/app.icns b/assets/icons/app.icns
new file mode 100644
index 0000000..31ec769
Binary files /dev/null and b/assets/icons/app.icns differ
diff --git a/assets/icons/icon_128.png b/assets/icons/icon_128.png
new file mode 100644
index 0000000..c003c1c
Binary files /dev/null and b/assets/icons/icon_128.png differ
diff --git a/assets/icons/icon_16.png b/assets/icons/icon_16.png
new file mode 100644
index 0000000..238a4b1
Binary files /dev/null and b/assets/icons/icon_16.png differ
diff --git a/assets/icons/icon_256.png b/assets/icons/icon_256.png
new file mode 100644
index 0000000..7c36796
Binary files /dev/null and b/assets/icons/icon_256.png differ
diff --git a/assets/icons/icon_32.png b/assets/icons/icon_32.png
new file mode 100644
index 0000000..f206c4b
Binary files /dev/null and b/assets/icons/icon_32.png differ
diff --git a/assets/icons/icon_64.png b/assets/icons/icon_64.png
new file mode 100644
index 0000000..7086337
Binary files /dev/null and b/assets/icons/icon_64.png differ
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..d2b73fc
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,267 @@
+#!/bin/bash
+
+# Tetris Game 自动化构建脚本
+# 支持 Windows、macOS 和 Linux 平台
+
+set -e # 遇到错误立即退出
+
+# 颜色定义
+BLUE='\033[34m'
+GREEN='\033[32m'
+YELLOW='\033[33m'
+RED='\033[31m'
+RESET='\033[0m'
+
+# 配置
+VERSION="1.0"
+BUILD_TIME=$(date '+%Y-%m-%d %H:%M:%S')
+GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
+
+# 函数:打印带颜色的消息
+print_info() {
+ echo -e "${BLUE}ℹ️ $1${RESET}"
+}
+
+print_success() {
+ echo -e "${GREEN}✅ $1${RESET}"
+}
+
+print_warning() {
+ echo -e "${YELLOW}⚠️ $1${RESET}"
+}
+
+print_error() {
+ echo -e "${RED}❌ $1${RESET}"
+}
+
+# 函数:显示帮助信息
+show_help() {
+ echo -e "${BLUE}🎮 Tetris Game 自动化构建脚本${RESET}"
+ echo ""
+ echo "用法: $0 [选项] [平台]"
+ echo ""
+ echo "平台:"
+ echo " windows 构建 Windows 版本"
+ echo " macos 构建 macOS 版本"
+ echo " linux 构建 Linux 版本"
+ echo " all 构建所有平台 (默认)"
+ echo ""
+ echo "选项:"
+ echo " -h, --help 显示帮助信息"
+ echo " -c, --clean 构建前清理"
+ echo " -v, --verify 构建后验证"
+ echo " -i, --info 显示构建信息"
+ echo " --no-color 禁用颜色输出"
+ echo ""
+ echo "示例:"
+ echo " $0 # 构建所有平台"
+ echo " $0 windows # 仅构建 Windows"
+ echo " $0 -c all # 清理后构建所有平台"
+ echo " $0 -cv macos # 清理、构建 macOS 并验证"
+}
+
+# 函数:显示构建信息
+show_info() {
+ print_info "构建信息"
+ echo " 版本: v${VERSION}"
+ echo " 构建时间: ${BUILD_TIME}"
+ echo " Git 提交: ${GIT_COMMIT}"
+ echo " Go 版本: $(go version 2>/dev/null || echo '未安装')"
+ echo " 当前平台: $(go env GOOS 2>/dev/null || echo 'unknown')/$(go env GOARCH 2>/dev/null || echo 'unknown')"
+ echo " 工作目录: $(pwd)"
+}
+
+# 函数:检查环境
+check_environment() {
+ print_info "检查构建环境..."
+
+ # 检查 Go
+ if ! command -v go &> /dev/null; then
+ print_error "Go 未安装或不在 PATH 中"
+ exit 1
+ fi
+ print_success "Go 环境正常"
+
+ # 检查必要文件
+ if [ ! -f "main.go" ]; then
+ print_error "main.go 文件不存在"
+ exit 1
+ fi
+
+ if [ ! -d "assets" ]; then
+ print_error "assets 目录不存在"
+ exit 1
+ fi
+
+ print_success "源代码文件检查通过"
+}
+
+# 函数:清理构建文件
+clean_build() {
+ print_info "清理构建文件..."
+ make clean
+ print_success "清理完成"
+}
+
+# 函数:构建指定平台
+build_platform() {
+ local platform=$1
+ print_info "构建 ${platform} 版本..."
+
+ case $platform in
+ windows)
+ make windows
+ ;;
+ macos)
+ make macos
+ ;;
+ linux)
+ make linux
+ ;;
+ all)
+ make all
+ ;;
+ *)
+ print_error "不支持的平台: $platform"
+ exit 1
+ ;;
+ esac
+
+ print_success "${platform} 构建完成"
+}
+
+# 函数:验证构建结果
+verify_build() {
+ print_info "验证构建结果..."
+ make verify
+ print_success "验证完成"
+}
+
+# 函数:显示构建结果摘要
+show_summary() {
+ echo ""
+ print_success "🎉 构建完成!"
+ echo ""
+ print_info "构建结果摘要:"
+
+ if [ -d "dist" ]; then
+ echo " 分发目录: dist/"
+
+ # 显示文件大小
+ if [ -f "dist/TetrisGame-Windows-v1.0.zip" ]; then
+ local size=$(ls -lh dist/TetrisGame-Windows-v1.0.zip | awk '{print $5}')
+ echo " Windows 版本: TetrisGame-Windows-v1.0.zip (${size})"
+ fi
+
+ if [ -f "dist/TetrisGame-macOS-v1.0.dmg" ]; then
+ local size=$(ls -lh dist/TetrisGame-macOS-v1.0.dmg | awk '{print $5}')
+ echo " macOS 版本: TetrisGame-macOS-v1.0.dmg (${size})"
+ fi
+
+ if [ -f "dist/TetrisGame-Linux-v1.0.tar.gz" ]; then
+ local size=$(ls -lh dist/TetrisGame-Linux-v1.0.tar.gz | awk '{print $5}')
+ echo " Linux 版本: TetrisGame-Linux-v1.0.tar.gz (${size})"
+ fi
+
+ echo ""
+ print_info "总计文件数: $(find dist/ -type f -name '*.zip' -o -name '*.dmg' -o -name '*.tar.gz' | wc -l | tr -d ' ')"
+
+ # 计算总大小
+ local total_size=$(find dist/ -type f -name '*.zip' -o -name '*.dmg' -o -name '*.tar.gz' -exec ls -l {} \; | awk '{sum += $5} END {print sum}')
+ if [ -n "$total_size" ] && [ "$total_size" -gt 0 ]; then
+ local total_mb=$((total_size / 1024 / 1024))
+ echo " 总大小: ${total_mb}MB"
+ fi
+ else
+ print_warning "dist 目录不存在"
+ fi
+
+ echo ""
+ print_info "使用说明:"
+ echo " - 查看详细文档: cat BUILD.md"
+ echo " - 验证构建: make verify"
+ echo " - 运行游戏: make run"
+}
+
+# 主函数
+main() {
+ local platform="all"
+ local clean_first=false
+ local verify_after=false
+ local show_info_only=false
+
+ # 解析命令行参数
+ while [[ $# -gt 0 ]]; do
+ case $1 in
+ -h|--help)
+ show_help
+ exit 0
+ ;;
+ -c|--clean)
+ clean_first=true
+ shift
+ ;;
+ -v|--verify)
+ verify_after=true
+ shift
+ ;;
+ -i|--info)
+ show_info_only=true
+ shift
+ ;;
+ --no-color)
+ # 禁用颜色
+ BLUE=''
+ GREEN=''
+ YELLOW=''
+ RED=''
+ RESET=''
+ shift
+ ;;
+ windows|macos|linux|all)
+ platform=$1
+ shift
+ ;;
+ *)
+ print_error "未知参数: $1"
+ echo "使用 $0 --help 查看帮助"
+ exit 1
+ ;;
+ esac
+ done
+
+ # 显示标题
+ echo -e "${BLUE}🎮 Tetris Game 自动化构建脚本 v${VERSION}${RESET}"
+ echo ""
+
+ # 如果只是显示信息
+ if [ "$show_info_only" = true ]; then
+ show_info
+ exit 0
+ fi
+
+ # 检查环境
+ check_environment
+
+ # 清理(如果需要)
+ if [ "$clean_first" = true ]; then
+ clean_build
+ fi
+
+ # 构建
+ build_platform "$platform"
+
+ # 验证(如果需要)
+ if [ "$verify_after" = true ]; then
+ verify_build
+ fi
+
+ # 显示摘要
+ show_summary
+}
+
+# 错误处理
+trap 'print_error "构建过程中发生错误,退出码: $?"' ERR
+
+# 运行主函数
+main "$@"
\ No newline at end of file
diff --git a/internal/font/font.go b/internal/font/font.go
index 515c457..a52bfa1 100644
--- a/internal/font/font.go
+++ b/internal/font/font.go
@@ -62,11 +62,29 @@ func NewFontRenderer() *FontRenderer {
// loadFromAssetsDirectory 专门扫描 assets/fonts 目录并加载字体文件
func loadFromAssetsDirectory() ([]byte, string) {
- assetsPath := "assets/fonts"
+ // 尝试多个可能的路径
+ possiblePaths := []string{
+ "assets/fonts", // 开发环境
+ "Contents/Resources/assets/fonts", // macOS 应用包内部
+ "../Resources/assets/fonts", // macOS 应用包相对路径
+ filepath.Join(getExecutableDir(), "assets/fonts"), // 可执行文件同目录
+ }
+
+ var assetsPath string
+ var found bool
+
+ // 查找存在的资源路径
+ for _, path := range possiblePaths {
+ if _, err := os.Stat(path); err == nil {
+ assetsPath = path
+ found = true
+ log.Printf("📁 找到字体目录: %s", path)
+ break
+ }
+ }
- // 检查目录是否存在
- if _, err := os.Stat(assetsPath); os.IsNotExist(err) {
- log.Printf("📁 assets/fonts 目录不存在,请创建并放入字体文件")
+ if !found {
+ log.Printf("📁 未找到 assets/fonts 目录,已尝试路径: %v", possiblePaths)
return nil, ""
}
@@ -87,16 +105,16 @@ func loadFromAssetsDirectory() ([]byte, string) {
})
if err != nil {
- log.Printf("❌ 扫描 assets/fonts 目录失败: %v", err)
+ log.Printf("❌ 扫描字体目录失败: %v", err)
return nil, ""
}
if len(fontFiles) == 0 {
- log.Printf("📂 assets/fonts 目录为空,请添加字体文件 (.ttf, .ttc, .otf)")
+ log.Printf("📂 字体目录为空,请添加字体文件 (.ttf, .ttc, .otf)")
return nil, ""
}
- log.Printf("🔍 在 assets/fonts 目录找到 %d 个字体文件", len(fontFiles))
+ log.Printf("🔍 在 %s 找到 %d 个字体文件", assetsPath, len(fontFiles))
// 字体优先级:微软雅黑 > 其他中文字体 > 任意字体
fontPriorities := [][]string{
@@ -133,10 +151,20 @@ func loadFromAssetsDirectory() ([]byte, string) {
}
}
- log.Printf("❌ assets/fonts 目录中没有可用的字体文件")
+ log.Printf("❌ 字体目录中没有可用的字体文件")
return nil, ""
}
+// getExecutableDir 获取可执行文件所在目录
+func getExecutableDir() string {
+ execPath, err := os.Executable()
+ if err != nil {
+ log.Printf("⚠️ 获取可执行文件路径失败: %v", err)
+ return "."
+ }
+ return filepath.Dir(execPath)
+}
+
// tryLoadFont 尝试加载字体文件并验证格式
func tryLoadFont(fontPath string) ([]byte, error) {
data, err := os.ReadFile(fontPath)
diff --git a/patch/0001-fix-IBlock-disappear-issue.patch b/patch/0001-fix-IBlock-disappear-issue.patch
deleted file mode 100755
index 6f8a33c..0000000
--- a/patch/0001-fix-IBlock-disappear-issue.patch
+++ /dev/null
@@ -1,151 +0,0 @@
-From ffe9242a73abfb3e8ee9b6effd57e64155717bfb Mon Sep 17 00:00:00 2001
-From: yinqiang
-Date: Sat, 7 Jun 2025 22:50:07 +0800
-Subject: [PATCH] fix IBlock disappear issue
-
----
- block.go | 21 +++++++++++----------
- game.go | 26 +++++++++++++++-----------
- 2 files changed, 26 insertions(+), 21 deletions(-)
-
-diff --git a/block.go b/block.go
-index ad276ea..0b3aaee 100644
---- a/block.go
-+++ b/block.go
-@@ -14,7 +14,7 @@ type Block struct {
- type BlockType int
-
- const (
-- IBlock BlockType = iota
-+ IBlock BlockType = iota + 1
- JBlock
- LBlock
- OBlock
-@@ -32,13 +32,14 @@ type Tetromino struct {
-
- // Colors for different block types
- var BlockColors = map[BlockType]color.Color{
-- IBlock: color.RGBA{0, 255, 255, 255}, // Cyan
-- JBlock: color.RGBA{0, 0, 255, 255}, // Blue
-- LBlock: color.RGBA{255, 165, 0, 255}, // Orange
-- OBlock: color.RGBA{255, 255, 0, 255}, // Yellow
-- SBlock: color.RGBA{0, 255, 0, 255}, // Green
-- TBlock: color.RGBA{128, 0, 128, 255}, // Purple
-- ZBlock: color.RGBA{255, 0, 0, 255}, // Red
-+ EmptyBlock: color.RGBA{0, 0, 0, 0}, // Black
-+ IBlock: color.RGBA{0, 255, 255, 255}, // Cyan
-+ JBlock: color.RGBA{0, 0, 255, 255}, // Blue
-+ LBlock: color.RGBA{255, 165, 0, 255}, // Orange
-+ OBlock: color.RGBA{255, 255, 0, 255}, // Yellow
-+ SBlock: color.RGBA{0, 255, 0, 255}, // Green
-+ TBlock: color.RGBA{128, 0, 128, 255}, // Purple
-+ ZBlock: color.RGBA{255, 0, 0, 255}, // Red
- }
-
- // TetrominoShapes defines the shape of each tetromino type
-@@ -102,8 +103,8 @@ func NewTetromino(blockType BlockType, x, y int) *Tetromino {
- shape := TetrominoShapes[blockType]
- blocks := make([]Block, 0)
-
-- for i := 0; i < len(shape); i++ {
-- for j := 0; j < len(shape[i]); j++ {
-+ for i := range shape {
-+ for j := range shape[i] {
- if shape[i][j] {
- blocks = append(blocks, Block{
- X: j,
-diff --git a/game.go b/game.go
-index aa9896f..2aa98e9 100644
---- a/game.go
-+++ b/game.go
-@@ -24,6 +24,7 @@ const (
- // Game constants
- InitialDropInterval = 60
- MinDropInterval = 5
-+ EmptyBlock = 0
- )
-
- // Game represents the main game state
-@@ -90,9 +91,9 @@ func (g *Game) Draw(screen *ebiten.Image) {
- }
-
- // Draw placed blocks
-- for y := 0; y < BoardHeight; y++ {
-- for x := 0; x < BoardWidth; x++ {
-- if g.board[y][x] != 0 {
-+ for y := range BoardHeight {
-+ for x := range BoardWidth {
-+ if g.board[y][x] != EmptyBlock {
- g.drawBlock(screen, x, y, g.board[y][x])
- }
- }
-@@ -311,7 +312,7 @@ func (g *Game) isColliding() bool {
- }
- // Check collision with other pieces
- if y >= 0 && x >= 0 && x < BoardWidth && y < BoardHeight {
-- if g.board[y][x] != 0 {
-+ if g.board[y][x] != EmptyBlock {
- return true
- }
- }
-@@ -324,6 +325,9 @@ func (g *Game) lockPiece() {
- if g.currentPiece == nil {
- return
- }
-+ // if g.currentPiece.BlockType == IBlock {
-+ // g.currentPiece.BlockType = IBlock
-+ // }
-
- positions := g.currentPiece.GetAbsolutePositions()
- for _, pos := range positions {
-@@ -378,8 +382,8 @@ func (g *Game) clearLines() {
-
- // isLineFull checks if a line is completely filled
- func (g *Game) isLineFull(y int) bool {
-- for x := 0; x < BoardWidth; x++ {
-- if g.board[y][x] == 0 {
-+ for x := range BoardWidth {
-+ if g.board[y][x] == EmptyBlock {
- return false
- }
- }
-@@ -391,15 +395,15 @@ func (g *Game) removeLine(y int) {
- for i := y; i > 0; i-- {
- copy(g.board[i], g.board[i-1])
- }
-- for x := 0; x < BoardWidth; x++ {
-- g.board[0][x] = 0
-+ for x := range BoardWidth {
-+ g.board[0][x] = EmptyBlock
- }
- }
-
- // spawnNewPiece creates a new piece at the top of the board
- func (g *Game) spawnNewPiece() {
- if g.nextPiece == nil {
-- g.nextPiece = NewTetromino(BlockType(rand.Intn(7)), 0, 0)
-+ g.nextPiece = NewTetromino(BlockType(rand.Intn(7)+1), 0, 0)
- }
-
- g.currentPiece = g.nextPiece
-@@ -409,7 +413,7 @@ func (g *Game) spawnNewPiece() {
- }
- g.currentPiece.Y = 0
-
-- g.nextPiece = NewTetromino(BlockType(rand.Intn(7)), 0, 0)
-+ g.nextPiece = NewTetromino(BlockType(rand.Intn(7)+1), 0, 0)
-
- if g.isColliding() {
- g.gameOver = true
-@@ -423,7 +427,7 @@ func (g *Game) wouldCollide(piece *Tetromino) bool {
- if x < 0 || x >= BoardWidth || y >= BoardHeight {
- return true
- }
-- if y >= 0 && y < BoardHeight && x >= 0 && x < BoardWidth && g.board[y][x] != 0 {
-+ if y >= 0 && y < BoardHeight && x >= 0 && x < BoardWidth && g.board[y][x] != EmptyBlock {
- return true
- }
- }
---
-2.39.5 (Apple Git-154)
-