|
|
# 功能机游戏开发规范
本文档为功能机平台(冒泡平台)游戏开发人员提供编写规范和最佳实践指南。
---
## 一、基础开发约束
### 1.1 编译器和语言标准
- **编译器**:使用 `mrpbuilder` 编译器(基于 ADS 1.2 的 armcc)
- **语言标准**:严格遵循 **C98 标准**,不支持 C99 特性
- **变量声明**:所有变量必须在函数或代码块开头声明,不能在中间声明
```c
/* 正确示例 */
void foo(void) {
int i;
char* text;
/* 变量全部在开头声明 */
i = 0;
text = "hello";
}
/* 错误示例 */
void bar(void) {
doSomething();
int i = 0; /* 错误:变量声明不在开头 */
}
```
### 1.2 不支持的系统头文件
- 不能使用标准 C 库头文件(如 `string.h`、`stdlib.h`)
- 但相关函数已映射到 mrc 函数,可直接使用:
```c
malloc -> mrc_malloc
free -> mrc_free
memcpy -> mrc_memcpy
memset -> mrc_memset
strcpy -> mrc_strcpy
strcmp -> mrc_strcmp
sprintf -> mrc_sprintf
atoi -> mrc_atoi
```
### 1.3 可用头文件
```c
#include <mrc_base.h> /* 基础API、按键定义、事件定义 */
#include "uc3_font.h" /* 字体绘制 */
#include "mrc_graphics.h" /* 图形绘制API */
#include "mpc.h" /* MPC 图片格式支持 */
```
### 1.4 线程与定时器
- **禁止使用线程**:功能机不支持多线程
- **游戏主循环**:使用定时器 `mrc_timerCreate/Start` 模拟游戏循环
```c
/* 创建定时器实现30fps游戏循环 */
timer_cd = mrc_timerCreate();
mrc_timerStart(timer_cd, 33, 0, timer_run, 1); /* 33ms = 30fps */
```
---
## 二、窗口管理系统
### 2.1 窗口类型定义
使用枚举定义所有窗口类型,便于管理:
```c
/* 窗口类型枚举 */
enum {
WINDOW_MENU = 0, /* 主菜单窗口 */
WINDOW_GAME = 1, /* 游戏窗口 */
WINDOW_HELP = 2, /* 帮助窗口 */
WINDOW_ABOUT = 3 /* 关于窗口 */
};
/* 全局当前窗口变量 */
int currentWindow;
```
### 2.2 窗口切换函数
实现统一的窗口切换函数,集中管理页面跳转逻辑:
```c
void switchWindow(int windex, int level) {
if (windex == 1) {
/* 从菜单进入其他窗口 */
if (level == MENU_GAME) {
currentWindow = WINDOW_GAME;
initGame();
drawGame();
} else if (level == MENU_HELP) {
currentWindow = WINDOW_HELP;
drawHelp();
}
} else if (windex == 0) {
/* 返回菜单 */
currentWindow = WINDOW_MENU;
pmenu_draw(&mainMenu);
}
}
```
### 2.3 事件分发机制
在 `mrc_event` 中根据当前窗口分发事件:
```c
int32 mrc_event(int32 code, int32 param0, int32 param1) {
if (currentWindow == WINDOW_MENU) {
/* 菜单窗口事件 */
pmenu_event(&mainMenu, code, param0, param1);
} else if (currentWindow == WINDOW_GAME) {
/* 游戏窗口事件 */
gameEvent(code, param0, param1);
} else if (currentWindow == WINDOW_HELP) {
/* 帮助窗口事件 */
helpEvent(code, param0, param1);
}
return 0;
}
```
### 2.4 游戏逻辑隔离
定时器回调中只在游戏窗口时执行游戏逻辑:
```c
void timer_run(int32 id) {
/* 只在游戏窗口时更新游戏状态 */
if (currentWindow == WINDOW_GAME) {
updateGame();
drawGame();
}
}
```
---
## 三、软键提示规范
### 3.1 软键位置和作用
功能机通常有两个软键(左软键和右软键),位于屏幕下方实体按键区域:
| 软键 | 按键常量 | 位置 | 常用功能 |
|-----|---------|------|---------|
| 左软键 | `_SLEFT` | 屏幕左下 | **确认** / 选择 / 进入 |
| 右软键 | `_SRIGHT` | 屏幕右下 | **返回** / 取消 / 退出 |
### 3.2 屏幕提示显示规范
**必须在屏幕底部显示软键提示文字**,告知用户按键功能:
```
┌─────────────────────────────┐
│ │
│ 游戏内容区域 │
│ │
│ │
├─────────────────────────────┤
│ 确认 返回 │ ← 软键提示区域
└─────────────────────────────┘
↑ ↑
左软键 右软键
```
### 3.3 软键提示实现示例
#### 方法一:简洁文字(推荐)
```c
void drawSoftKeyHints(char* leftText, char* rightText) {
int leftX, rightX, y;
y = SCRH - 25; /* 距离底部25像素 */
/* 左软键提示(屏幕左下) */
if (leftText != NULL) {
leftX = 10;
uc3_drawText(leftText, leftX, y, 0, 0, 0, 0);
}
/* 右软键提示(屏幕右下) */
if (rightText != NULL) {
rightX = SCRW - uc3_getWidth(rightText, 0) - 10;
uc3_drawText(rightText, rightX, y, 0, 0, 0, 0);
}
}
/* 使用示例 */
drawSoftKeyHints("确认", "返回");
```
#### 方法二:带背景高亮(可选)
```c
void drawSoftKeyHintsWithBg(char* leftText, char* rightText) {
int leftX, rightX, y;
int textW;
y = SCRH - 30;
/* 左软键 */
if (leftText != NULL) {
leftX = 10;
textW = uc3_getWidth(leftText, 0);
gl_drawRect(leftX - 5, y - 2, textW + 10, 20, 0xFFE0E0E0);
uc3_drawText(leftText, leftX, y, 0, 0, 0, 0);
}
/* 右软键 */
if (rightText != NULL) {
textW = uc3_getWidth(rightText, 0);
rightX = SCRW - textW - 10;
gl_drawRect(rightX - 5, y - 2, textW + 10, 20, 0xFFE0E0E0);
uc3_drawText(rightText, rightX, y, 0, 0, 0, 0);
}
}
```
### 3.4 不同场景的软键配置
| 场景 | 左软键文字 | 右软键文字 | 说明 |
|-----|-----------|-----------|------|
| 主菜单 | "选择" / "确认" | "退出" | 左键进入选项,右键退出程序 |
| 游戏中 | NULL | "返回" | 只显示返回,游戏操作靠触屏 |
| 帮助页面 | "确认" | "返回" | 确认关闭帮助,返回菜单 |
| 设置页面 | "保存" | "取消" | 保存设置或取消 |
| 对话框 | "确定" | "取消" | 确认或取消操作 |
### 3.5 事件处理示例
```c
void gameEvent(int32 code, int32 param0, int32 param1) {
if (code == KY_UP) { /* 按键松开事件 */
if (param0 == _SRIGHT) {
/* 右软键:返回菜单 */
switchWindow(0, 0);
} else if (param0 == _SLEFT) {
/* 左软键:暂停游戏 */
gamePause();
}
}
}
void menuEvent(int32 code, int32 param0, int32 param1) {
if (code == KY_UP) {
if (param0 == _SLEFT) {
/* 左软键:确认进入选中项 */
enterMenuItem();
} else if (param0 == _SRIGHT) {
/* 右软键:退出程序 */
mrc_exit();
}
}
}
```
---
## 四、图形渲染规范
### 4.1 屏幕刷新
**每次绘制完成后必须调用 `mrc_refreshScreen`**,否则画面不会更新:
```c
void drawGame(void) {
/* 绘制背景 */
drawBitmap(bgBitmap, 0, 0);
/* 绘制游戏元素 */
drawPlayer();
drawEnemies();
/* 绘制UI */
drawScore();
/* 必须刷新屏幕! */
mrc_refreshScreen(0, 0, SCRW, SCRH);
}
```
### 4.2 屏幕尺寸变量
使用全局变量获取屏幕尺寸,支持不同分辨率设备:
```c
extern int16 SCRW; /* 屏幕宽度 */
extern int16 SCRH; /* 屏幕高度 */
```
### 4.3 颜色格式
`mrc_graphics.h` 中的颜色使用 **ARGB 格式**:
```c
0xFFFF0000 /* 红色 */
0xFF00FF00 /* 绿色 */
0xFF0000FF /* 蓝色 */
0x80000000 /* 半透明黑色 */
```
`mrc_base.h` 中的函数使用 **RGB 分量**:
```c
mrc_drawPointEx(x, y, 255, 0, 0); /* 红色 */
mrc_clearScreen(255, 255, 255); /* 白色 */
```
---
## 五、资源管理
### 5.1 图片资源加载
```c
/* 从资源文件加载 */
BITMAP_565* bmp = readBitmap565FromAssets("fish.bma");
/* 使用完毕后必须释放 */
bitmapFree(bmp);
```
### 5.2 字体初始化和释放
```c
/* 在 mrc_init 中初始化字体 */
uc3_init();
/* 在 mrc_exitApp 中释放字体 */
uc3_free();
```
### 5.3 定时器管理
```c
/* 创建和启动 */
int32 timer = mrc_timerCreate();
mrc_timerStart(timer, 33, 0, timer_callback, 1);
/* 退出时必须停止和删除 */
mrc_timerStop(timer);
mrc_timerDelete(timer);
```
---
## 六、按键和触摸事件
### 6.1 事件类型
```c
enum {
KY_DOWN, /* 按键按下 */
KY_UP, /* 按键松开 */
MS_DOWN, /* 触摸按下 */
MS_UP, /* 触摸松开 */
MS_MOVE=12 /* 触摸移动 */
};
```
### 6.2 按键常量
```c
_UP /* 方向键上 */
_DOWN /* 方向键下 */
_LEFT /* 方向键左 */
_RIGHT /* 方向键右 */
_SELECT /* 确认键(方向键中心) */
_SLEFT /* 左软键 */
_SRIGHT /* 右软键 */
_0 到 _9 /* 数字键 */
_STAR /* * 键 */
_POUND /* # 键 */
```
### 6.3 事件处理模板
```c
int32 mrc_event(int32 code, int32 param0, int32 param1) {
if (code == KY_UP) {
/* 按键松开 */
/* param0 是按键码 */
if (param0 == _SELECT) {
/* 处理确认键 */
}
} else if (code == MS_UP) {
/* 触摸松开 */
/* param0 是 X 坐标,param1 是 Y 坐标 */
int x = param0;
int y = param1;
/* 处理点击 */
}
return 0;
}
```
---
## 七、常见陷阱和注意事项
### 7.2 for 循环变量声明
**错误**:
```c
for (int i = 0; i < 10; i++) { /* C99语法,不支持 */
/* ... */
}
```
**正确**:
```c
int i;
for (i = 0; i < 10; i++) {
/* ... */
}
```
### 7.3 注释风格
建议使用 C89 风格注释(`/* */`),虽然 `//` 通常也能工作:
```c
/* 这是推荐的注释方式 */
// 这种注释可能在某些编译器配置下出错
```
### 7.4 函数声明顺序
**函数必须在使用前声明**,否则会产生 `inventing extern` 警告:
```c
/* 前向声明 */
void drawGame(void);
void updateGame(void);
void timer_run(int32 id) {
updateGame(); /* 正确:函数已声明 */
drawGame();
}
void updateGame(void) {
/* 实现 */
}
void drawGame(void) {
/* 实现 */
}
```
---
## 八、项目结构建议
```
project/
├── main.c # 主程序入口
├── game_menu.c # 菜单系统
├── game_menu.h
├── assets/ # 资源文件
│ ├── bg_240.bma # 背景图
│ ├── fish_01.bma # 游戏元素
│ └── logo_240.bma # Logo
├── src/ # 库文件
│ ├── mrc_base.h
│ ├── mrc_graphics.h
│ ├── uc3_font.h
│ └── mpc.h
├── 程序编写说明.md # 平台API文档
└── 功能机开发规范.md # 本文档
```
---
## 九、调试技巧
### 9.1 日志输出
使用 `mrc_printf` 输出调试信息:
```c
mrc_printf("Score: %d\n", score);
mrc_printf("[DEBUG] x=%d, y=%d\n", x, y);
```
### 9.2 分阶段初始化
在 `mrc_init` 中打印各阶段日志,方便定位初始化问题:
```c
int32 mrc_init(void) {
mrc_printf("[INIT] Starting...\n");
mrc_printf("[INIT] Loading resources...\n");
loadAssets();
mrc_printf("[INIT] Initializing font...\n");
uc3_init();
mrc_printf("[INIT] Done!\n");
return 0;
}
```
---
## 十、完整窗口切换示例
以下是一个完整的多窗口游戏框架示例:
```c
#include <mrc_base.h>
#include "uc3_font.h"
#include "mrc_graphics.h"
#include "game_menu.h"
/* 窗口类型 */
enum {
WINDOW_MENU = 0,
WINDOW_GAME = 1,
WINDOW_HELP = 2
};
/* 全局变量 */
int currentWindow;
PMENU mainMenu;
/* 函数声明 */
void drawGame(void);
void drawHelp(void);
void drawSoftKeyHints(char* left, char* right);
/* 窗口切换 */
void switchWindow(int windex, int level) {
if (windex == 1) {
if (level == 0) {
currentWindow = WINDOW_GAME;
drawGame();
} else if (level == 1) {
currentWindow = WINDOW_HELP;
drawHelp();
}
} else {
currentWindow = WINDOW_MENU;
pmenu_draw(&mainMenu);
}
}
/* 绘制软键提示 */
void drawSoftKeyHints(char* left, char* right) {
int leftX, rightX, y;
y = SCRH - 25;
if (left != NULL) {
leftX = 10;
uc3_drawText(left, leftX, y, 0, 0, 0, 0);
}
if (right != NULL) {
rightX = SCRW - uc3_getWidth(right, 0) - 10;
uc3_drawText(right, rightX, y, 0, 0, 0, 0);
}
}
/* 游戏绘制 */
void drawGame(void) {
gl_clearScreen(200, 200, 200);
uc3_drawText("游戏界面", 10, 10, 0, 0, 0, 0);
drawSoftKeyHints(NULL, "返回");
mrc_refreshScreen(0, 0, SCRW, SCRH);
}
/* 帮助绘制 */
void drawHelp(void) {
gl_clearScreen(240, 240, 240);
uc3_drawText("帮助信息", 10, 10, 0, 0, 0, 0);
uc3_drawText("按右软键返回", 10, 40, 0, 0, 0, 0);
drawSoftKeyHints("确认", "返回");
mrc_refreshScreen(0, 0, SCRW, SCRH);
}
/* 事件分发 */
int32 mrc_event(int32 code, int32 param0, int32 param1) {
if (currentWindow == WINDOW_MENU) {
pmenu_event(&mainMenu, code, param0, param1);
} else if (currentWindow == WINDOW_GAME) {
if (code == KY_UP && param0 == _SRIGHT) {
switchWindow(0, 0);
}
} else if (currentWindow == WINDOW_HELP) {
if (code == KY_UP && param0 == _SRIGHT) {
switchWindow(0, 0);
}
}
return 0;
}
/* 初始化 */
int32 mrc_init(void) {
currentWindow = WINDOW_MENU;
uc3_init();
pmenu_init(&mainMenu);
pmenu_draw(&mainMenu);
return 0;
}
/* 退出 */
int32 mrc_exitApp(void) {
pmenu_free(&mainMenu);
uc3_free();
return 0;
}
```
---
## 十一、参考资料
- **平台API详细说明**:见 `程序编写说明.md`
- **常用函数速查**:
- 图形:`gl_drawRect`, `gl_drawLine`, `gl_drawCir`, `drawBitmap`
- 文字:`uc3_drawText`, `uc3_getWidth`
- 系统:`mrc_exit`, `mrc_printf`, `mrc_refreshScreen`
- 定时器:`mrc_timerCreate/Start/Stop/Delete`
---
|
|