当前位置: 首页 > news >正文

思澈科技solution井字棋游戏【外置应用】

文章目录

  • 一、简介
    • 参考文档
  • 二、应用介绍
  • 三、应用开发
    • 1、应用文件夹创建
    • 2、文件目录结构说明
    • 3、配置图片资源
      • 3.1 图标放置
      • 3.2 应用内游戏资源
      • 3.3 图片资源说明
    • 4、编写代码文件
      • 4.1 主体函数说明
        • on_start
        • on_resume
        • on_pause
        • on_stop
      • 4.2 on_start 初始化
      • 4.3 on_resume 激活配置
      • 4.4 on_pause 暂停
      • 4.5 on_stop
      • 4.6 初始化时完成游戏代码配置
    • 5、编译下载
      • 5.1 编译
      • 5.2 下载
      • 5.3 显示

一、简介

学习思澈科技solution开发外置C应用
这里以开发一个井字棋游戏为例。

参考文档

应用APP
图形应用框架
创建一个新APP

二、应用介绍

solution 应用分为两种 1、内置应用 2、外置应用
1、内置应用:\open_source_solution_v2.4.1\solution\examples\xxx\application
2、外置应用:\open_source_solution_v2.4.1\solution\examples\_dynamic_app
其中C应用在:\open_source_solution_v2.4.1\solution\examples\_dynamic_app\c\app

三、应用开发

优先参考创建一个新APP文档

1、应用文件夹创建

直接复制粘贴sport文件夹,并重命名为game

此时在butterfli下点击刷新,可以看到game出现在C app中,选中预置即可将其编译到工程中

2、文件目录结构说明

目录下包含resourcesrc两个目录
resource:资源目录,存放APP使用到的各种资源
src:源代码目录,存放应用C源代码
readme.ini 编译依赖配置文件 & SConscript 编译连接脚本

其中resource下包含font_bitmapimageslangthumbnails
font_bitmap:bitmap字体
images:图片资源文件,包括gif等
lang:多语言表
thumbnails:APP图标,图片名称需为tn.png

3、配置图片资源

3.1 图标放置

图标名称需为tn.png 当需要不同风格的图标时依次命名为 tn.png\tn2.png\tn3.png ,图标风格可以是方形,圆形等。在应用切换列表显示或者宫格显示时会切换不同风格的图标显示。


3.2 应用内游戏资源

这里井字棋游戏的显示可以完全使用C代码画图实现,这样可以不在这里加任何其他图片资源。也可以使用LVGL图片控件显示img图片资源。
将需要的图片资源替换掉原有的图片资源,其中empty为透明图片用于清空棋盘显示。





3.3 图片资源说明

可以看到,图片资源目录下 如image下还有很多个目录。这些都有各自的用途。
可以参考图片资源说明文档:包括如何在solution使用获取图片文件

4、编写代码文件

应用APP文档
动态应用指南

创建 game_main.c ,并删除刚刚复制的其他资源文件,然后添加代码代码


#defineDYN_APP/* 声明此为外置APP,资源通过外置方式获取 */#include<rtthread.h>#include<lvgl.h>#include"global.h"#include"lvsf_multobj.h"#include"lvsf_multswipe.h"#include"lvsf_scrollbar.h"#include"lvsf_bg_db.h"/* 定义模块名称(必须与动态应用APP_ID相同,模拟器调试使用)且需要在 #include "app_module.h" 之前 */#define_MODULE_NAME_"game"/* 模块名称为 game */#include"app_module.h"typedefstruct{lv_obj_t*bg_cont;}game_main_t;staticvoidon_start(void){/*从框架获取申请的内存,必须要注册的时候填入需要的内存大小*/p_lane_play=APP_GET_PAGE_MEM_PTR;}staticvoidon_resume(void){}staticvoidon_pause(void){}staticvoidon_stop(void){}/* 注册应用 *//* app_get_strid:为应用名称设置 NULL:第二个参数为 缩略图设置,可以填NULL "game": 应用名称,必须与对应的应用ID 相同 size:应用全局内存大小,该内存由框架申请释放,页面可以直接使用 */APPLICATION_REGISTER(app_get_strid(key_sport,"Game"),NULL,"game",sizeof(game_main_t));

4.1 主体函数说明

设置APP文档

前置代码文件编辑设置完成后,后面就是应用主体的编写
应用主体分为4个函数on_start\on_resume\on_pause\on_stop

on_start

页面初始化(仅调用一次)

on_resume

页面激活(每次显示时调用)

on_pause

页面暂停(被切换至后台时调用)

on_stop

页面销毁(退出时调用)

4.2 on_start 初始化

初始化 获取框架申请的内存,全局指针所对应的结构体大小一定要和申请的大小一致
APP_GET_PAGE_MEM_PTR是框架提供的接口,用于获取预先申请的内存指针
game :static game_main_t *game = NULL;

staticvoidon_start(void){game=(game_main_t*)APP_GET_PAGE_MEM_PTR;RT_ASSERT(game);jingziqi_app_gui_init();//自定义页面初始化函数,在这里编写游戏的初始化相关代码}

4.3 on_resume 激活配置

staticvoidon_resume(void){/* 井字棋游戏中只有简单的触摸事件,激活启动时也不需要额外的配置或刷新定时器,因此这个函数为NULL。 */}

4.4 on_pause 暂停

staticvoidon_pause(void){/* 同on_resume函数。 */}

4.5 on_stop

staticvoidon_stop(void){/* 将全局指针变量置空,防止其他地方使用时非空判断出现异常,该指针指向的内存会在框架执行完stop消息后释放 */game=NULL;}

4.6 初始化时完成游戏代码配置

因此一个非常简单的的外置应用,只需要配置好on_start中的初始化代码即可完成。
接下来就是在初始化时就完成井字棋游戏的代码编写。
【完整C代码】

/********************* * INCLUDES *********************/#defineDYN_APP#include<rtthread.h>#include<lvgl.h>#include"global.h"#include"lvsf_multobj.h"#include"lvsf_multswipe.h"#include"lvsf_scrollbar.h"#include"lvsf_bg_db.h"#define_MODULE_NAME_"game"#include"app_module.h"typedefenum{PLAYER_X_TURN,// X玩家回合PLAYER_O_TURN,// O玩家回合PLAYER_X_WIN,// X玩家获胜PLAYER_O_WIN,// O玩家获胜GAME_DRAW// 平局}GameState;// 棋盘状态typedefenum{EMPTY,PLAYER_X,PLAYER_O}CellState;typedefstruct{CellState board[3][3];// 3x3棋盘GameState current_state;// 当前游戏状态lv_obj_t*cell_btns[3][3];// 按钮对象数组lv_obj_t*status_label;// 状态显示标签lv_obj_t*restart_btn;// 重新开始按钮lv_obj_t*game_board;// 游戏棋盘对象lv_obj_t*bg_cont;}game_main_t;staticgame_main_t*game=NULL;/** * 检查是否有获胜者 * @return 获胜者(PLAYER_X, PLAYER_O)或EMPTY */staticCellStatecheck_winner(void){// 检查行for(inti=0;i<3;i++){if(game->board[i][0]!=EMPTY&&game->board[i][0]==game->board[i][1]&&game->board[i][1]==game->board[i][2]){returngame->board[i][0];}}// 检查列for(intj=0;j<3;j++){if(game->board[0][j]!=EMPTY&&game->board[0][j]==game->board[1][j]&&game->board[1][j]==game->board[2][j]){returngame->board[0][j];}}// 检查对角线if(game->board[0][0]!=EMPTY&&game->board[0][0]==game->board[1][1]&&game->board[1][1]==game->board[2][2]){returngame->board[0][0];}if(game->board[0][2]!=EMPTY&&game->board[0][2]==game->board[1][1]&&game->board[1][1]==game->board[2][0]){returngame->board[0][2];}returnEMPTY;}/** * 检查是否平局 * @return true如果平局 */staticboolcheck_draw(void){for(inti=0;i<3;i++){for(intj=0;j<3;j++){if(game->board[i][j]==EMPTY){returnfalse;}}}returntrue;}/** * 更新按钮显示 */staticvoidupdate_cell_display(introw,intcol){lv_obj_t*btn=game->cell_btns[row][col];lv_obj_t*img=lv_obj_get_child(btn,0);switch(game->board[row][col]){caseEMPTY:lv_img_set_src(img,APP_GET_IMG(empty));break;casePLAYER_X:lv_img_set_src(img,APP_GET_IMG(cha));break;casePLAYER_O:lv_img_set_src(img,APP_GET_IMG(gou));break;}}// /**// * 处理单元格点击// */staticvoidcell_click_cb(lv_event_t*e){//如果游戏已经结束,不处理点击if(game->current_state==PLAYER_X_WIN||game->current_state==PLAYER_O_WIN||game->current_state==GAME_DRAW){return;}// 获取点击的按钮lv_obj_t*btn=lv_event_get_target(e);// 找到按钮对应的行列introw=-1,col=-1;for(inti=0;i<3;i++){for(intj=0;j<3;j++){if(game->cell_btns[i][j]==btn){row=i;col=j;break;}}if(row!=-1)break;}// 如果单元格不为空,不处理if(game->board[row][col]!=EMPTY){return;}// 根据当前玩家设置单元格if(game->current_state==PLAYER_X_TURN){game->board[row][col]=PLAYER_X;update_cell_display(row,col);//检查游戏状态CellState winner=check_winner();if(winner==PLAYER_X){game->current_state=PLAYER_X_WIN;lv_label_set_text(game->status_label,"玩家 X 获胜!");}elseif(check_draw()){game->current_state=GAME_DRAW;lv_label_set_text(game->status_label,"平局!");}else{game->current_state=PLAYER_O_TURN;lv_label_set_text(game->status_label,"玩家 O 回合");}}elseif(game->current_state==PLAYER_O_TURN){game->board[row][col]=PLAYER_O;update_cell_display(row,col);//检查游戏状态CellState winner=check_winner();if(winner==PLAYER_O){game->current_state=PLAYER_O_WIN;lv_label_set_text(game->status_label,"玩家 O 获胜!");}elseif(check_draw()){game->current_state=GAME_DRAW;lv_label_set_text(game->status_label,"平局!");}else{game->current_state=PLAYER_X_TURN;lv_label_set_text(game->status_label,"玩家 X 回合");}}}voidgame_init(void){// 清空棋盘for(inti=0;i<3;i++){for(intj=0;j<3;j++){game->board[i][j]=EMPTY;}}// 设置初始状态为X玩家回合game->current_state=PLAYER_X_TURN;// 更新状态显示lv_label_set_text(game->status_label,"玩家 X 回合");}/** * 重新开始游戏回调 */staticvoidrestart_click_cb(lv_event_t*e){(void)e;// 未使用参数game_init();// 清空所有按钮显示for(inti=0;i<3;i++){for(intj=0;j<3;j++){update_cell_display(i,j);}}}staticvoidjingziqi_app_gui_init(){lv_obj_t*parent=lv_scr_act();lv_obj_t*bg_cont=lv_obj_create(parent);lv_obj_set_size(bg_cont,390,390);lv_obj_set_pos(bg_cont,0,0);lv_obj_set_style_bg_color(bg_cont,LV_COLOR_BLACK,LV_PART_MAIN);/* 棋盘 */lv_obj_t*game_board=lv_img_create(bg_cont);lv_img_set_src(game_board,APP_GET_IMG(qipan));lv_obj_set_size(game_board,390,390);lv_obj_set_pos(game_board,0,0);/* 状态标签 */lv_obj_t*label=lv_label_create(parent);lv_label_set_text(label,"玩家 X 回合");// 初始状态lv_obj_set_style_text_color(label,lv_color_white(),0);lv_obj_set_pos(label,50,400);game->game_board=game_board;game->status_label=label;// 创建3x3棋盘按钮for(inti=0;i<3;i++){// 创建行容器lv_obj_t*row_cont=lv_obj_create(bg_cont);//lv_obj_remove_style_all(row_cont);lv_obj_set_size(row_cont,300,100);lv_obj_set_style_bg_opa(row_cont,0,0);lv_obj_set_pos(row_cont,45,45+i*100);for(intj=0;j<3;j++){// 创建单元格按钮lv_obj_t*btn=lv_btn_create(row_cont);lv_obj_set_size(btn,100,100);lv_obj_set_style_bg_opa(btn,0,0);lv_obj_set_pos(btn,j*100,0);// 创建按钮标签lv_obj_t*img=lv_img_create(btn);lv_img_set_src(img,NULL);lv_obj_center(img);// 存储按钮引用game->cell_btns[i][j]=btn;// 添加点击事件lv_obj_add_event_cb(btn,cell_click_cb,LV_EVENT_CLICKED,NULL);}}/* 重新开始按钮 */lv_obj_t*restart_btn=lv_btn_create(parent);lv_obj_set_size(restart_btn,100,40);lv_obj_set_pos(restart_btn,240,400);lv_obj_t*restart_label=lv_label_create(restart_btn);lv_label_set_text(restart_label,"重新开始");lv_obj_center(restart_label);lv_obj_add_event_cb(restart_btn,restart_click_cb,LV_EVENT_CLICKED,NULL);game->restart_btn=restart_btn;game_init();lv_obj_update_layout(parent);}staticvoidon_start(void){game=(game_main_t*)APP_GET_PAGE_MEM_PTR;RT_ASSERT(game);jingziqi_app_gui_init();}staticvoidon_resume(void){}staticvoidon_pause(void){}staticvoidon_stop(void){}APPLICATION_REGISTER(app_get_strid(key_sport,"Game"),NULL,"game",sizeof(game_main_t));

5、编译下载

5.1 编译

C应用中勾选对应应用为预置

如果你和我一样只修改过C应用部分代码,且之前有关编译,可以选择部分编译,否则需要进行全编译


5.2 下载

选中对应的COM口并下载

5.3 显示




http://www.jsqmd.com/news/649204/

相关文章:

  • NFD云解析部署实战:Docker、宝塔、Windows服务全方案详解
  • 2026年ODI备案公司价格揭秘,靠谱品牌费用分析与推荐 - 工业推荐榜
  • YOLO X Layout快速部署:AMD GPU(ROCm)环境ONNXRuntime适配指南
  • WarcraftHelper:5大核心功能全面解决魔兽争霸3现代系统兼容性问题
  • 如何3秒获取百度网盘提取码:智能工具的终极解决方案
  • Equalizer APO终极指南:免费解锁Windows专业级音频调校
  • ASMR下载器终极指南:5分钟掌握asmr.one资源高效获取技巧
  • AWPortrait-Z人像美化全攻略:从参数设置到效果优化,一篇搞定
  • nlp_structbert_sentence-similarity_chinese-large模型效果可视化工具开发
  • 口碑好的ODI备案公司分享,优质服务的靠谱ODI备案机构推荐 - myqiye
  • 飞书文档批量导出完整指南:三步实现高效知识库迁移
  • VLC播放器界面美化终极指南:5款VeLoCity主题打造专属影音空间
  • 别光背公式了!用Python的NumPy和SciPy手把手带你玩转SVD(附实战代码)
  • 【技术干货】Claude Code 桌面版重大更新:AI 辅助编程进入 IDE 原生时代
  • PyTorch-2.x-Universal-Dev-v1.0常见问题解决:环境配置错误排查
  • 终极显卡驱动清理指南:Display Driver Uninstaller (DDU) 完整使用教程
  • 5分钟终极指南:wechat-need-web插件让微信网页版重新可用
  • systemverilog中的package
  • Kandinsky-5.0-I2V-Lite-5s效果展示:基于Stm32的嵌入式AI视觉作品集
  • NAPALM 性能优化技巧:10个提升网络自动化效率的方法
  • 如何优雅更新 Node.js 后台服务:从代码热更说到 systemd 一键重启
  • 2026年3月 GESP CCF编程能力等级认证图形化编程二级真题
  • 2026年|还在愁论文查重率居高不下?AI降重一键高效解决困扰 - 降AI实验室
  • NS-USBLoader终极指南:跨平台Switch游戏管理神器
  • 百度网盘提取码智能解析:告别手动搜索的自动化解决方案
  • IO复用:poll
  • 嵌入式系统安全
  • Qwen3-Reranker-0.6B实战:构建智能客服问答系统
  • 实战指南:基于3D透视变换的高精度图像拼接技术
  • 如何快速备份QQ空间全部历史说说:GetQzonehistory免费开源工具终极指南