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

文章目录

一、简介

学习思澈科技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_bitmap images lang thumbnails

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文档
动态应用指南

保留 sport_main_gui.c文件,修改名称如:game_main.c ,并删除其他资源文件,修改其中的代码


c 复制代码
#define DYN_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"
#include "app_module.h"

typedef struct
{
    lv_obj_t *bg_cont;
}game_main_t;

static void on_start(void)
{
	/*从框架获取申请的内存,必须要注册的时候填入需要的内存大小*/
   p_lane_play = APP_GET_PAGE_MEM_PTR;
}

static void on_resume(void)
{

}

static void on_pause(void)
{

}

static void on_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;

c 复制代码
static void on_start(void)
{
		
    game = (game_main_t *)APP_GET_PAGE_MEM_PTR; 
    RT_ASSERT(game);

    jingziqi_app_gui_init(); //自定义页面初始化函数
}

4.3 on_resume 激活配置

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

4.4 on_pause 暂停

c 复制代码
static void on_pause(void)
{
	/*
		同on_resume函数。
	*/
}

4.5 on_stop

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

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

接下来就是在初始化时就完成井字棋游戏的代码编写。

【完整C代码】

c 复制代码
/*********************
 *      INCLUDES
 *********************/
#define DYN_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"


typedef enum {
    PLAYER_X_TURN,  // X玩家回合
    PLAYER_O_TURN,  // O玩家回合
    PLAYER_X_WIN,   // X玩家获胜
    PLAYER_O_WIN,   // O玩家获胜
    GAME_DRAW       // 平局
} GameState;


// 棋盘状态
typedef enum {
    EMPTY,
    PLAYER_X,
    PLAYER_O
}  CellState;

typedef struct
{
    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;

static game_main_t *game = NULL;


/**
 * 检查是否有获胜者
 * @return 获胜者(PLAYER_X, PLAYER_O)或EMPTY
 */
static CellState check_winner(void) {
    // 检查行
    for (int i = 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]) {
            return game->board[i][0];
        }
    }
    
    // 检查列
    for (int j = 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]) {
            return game->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]) {
        return game->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]) {
        return game->board[0][2];
    }
    
    return EMPTY;
}

/**
 * 检查是否平局
 * @return true如果平局
 */
static bool check_draw(void) {
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (game->board[i][j] == EMPTY) {
                return false;
            }
        }
    }
    return true;
}

/**
 * 更新按钮显示
 */
static void update_cell_display(int row, int col) {
    lv_obj_t *btn = game->cell_btns[row][col];
    lv_obj_t *img = lv_obj_get_child(btn, 0);
    
    switch (game->board[row][col]) {
        case EMPTY:
            lv_img_set_src(img, APP_GET_IMG(empty));
            break;
        case PLAYER_X:
            lv_img_set_src(img, APP_GET_IMG(cha));
            break;
        case PLAYER_O:
            lv_img_set_src(img, APP_GET_IMG(gou));
            break;
    }
}

// /**
//  * 处理单元格点击
//  */
static void cell_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);
    
    // 找到按钮对应的行列
    int row = -1, col = -1;
    for (int i = 0; i < 3; i++) {
        for (int j = 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 获胜!");
        } else if (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 回合");
        }
    } 
    else if (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 获胜!");
        } else if (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 回合");
        }
    }
}



void game_init(void) {
    // 清空棋盘
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            game->board[i][j] = EMPTY;
        }
    } 
    // 设置初始状态为X玩家回合
    game->current_state = PLAYER_X_TURN; 
    // 更新状态显示
    lv_label_set_text(game->status_label, "玩家 X 回合");
}

/**
 * 重新开始游戏回调
 */
static void restart_click_cb(lv_event_t *e) {
    (void)e; // 未使用参数
    game_init();
    
    // 清空所有按钮显示
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            update_cell_display(i, j);
        }
    }
}

static void jingziqi_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 (int i = 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 (int j = 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);
}


static void on_start(void)
{
    game = (game_main_t *)APP_GET_PAGE_MEM_PTR;
    RT_ASSERT(game);

    jingziqi_app_gui_init();
}

static void on_resume(void)
{

}

static void on_pause(void)
{

}

static void on_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 显示




相关推荐
智信中科张炜15 小时前
全球及中国游戏小工具行业发展研究及十五五分析报告
游戏
南山电子nscn18 小时前
友顺科技UTC UR56XX系列在18V高压输入与1µA级超低功耗之间实现了出色的平衡
科技·单片机·嵌入式硬件·低压差线性稳压器
wjql219 小时前
steam远程畅玩的方法 怎么远程玩steam游戏
游戏
月光船幽幽20 小时前
构建多节点耦合动力学仿真框架
人工智能·科技·拓扑学
wjql221 小时前
三角洲行动爆破模式玩法介绍 三角洲行动爆破模式怎么远程玩
游戏
劉宇雁21 小时前
C++ ASCII 3D无尽跑酷游戏
c++·游戏·3d
智慧景区与市集主理人21 小时前
巨有科技智慧康养|避开康养文旅内卷,做能变现的疗愈数字化
大数据·人工智能·科技
CLK音视频广播设备21 小时前
河南川利卡科技集团落地贵州毕节某校园定压广播项目
科技
yangdaxiageo1 天前
算法时代的“心智置顶“:《功夫女足》如何构建GEO正向飞轮
人工智能·科技·算法·aigc
机器人零零壹1 天前
越擎科技iRobotCAM人形机器人设计篇:如何实现人形机器人的动力学模型的设计和仿真
科技·机器人·具身智能·人形机器人·机器人仿真·irobotcam