aramy 发表于 2024-10-17 09:42

【2024 DigiKey 创意大赛】用esp32-s3-lcd-ev-board制作华容道拼图游戏

本帖最后由 aramy 于 2024-10-17 16:27 编辑

<p>这次参与2024 DigiKey&ldquo;感知万物,乐享生活&rdquo;大赛,我选择的板子是&ldquo;esp32-s3-lcd-ev-board&rdquo;。这块板子非常豪华地配备了一块480*480的触摸屏,这么大的屏幕,能够非常好滴让单片机与人交互。</p>

<hr />
<ol>
        <li><strong>作品简介</strong><br />
        ESP32-S3-LCD-EV-Board 是一款基于 ESP32-S3 芯片的屏幕交互开发板,通过搭配不同类型的 LCD 子板,可以驱动 IIC、SPI、8080 以及 RGB 接口的 LCD 显示屏。同时它还搭载双麦克风阵列,支持语音识别和近/远场语音唤醒,具有触摸屏交互和语音交互功能,满足用户对多种不同分辨率以及接口的触摸屏应用产品的开发需求。本项目是使用ESP32-S3-LCD-EV-Board加上480x480 LCD触摸屏,完成了经典游戏&ldquo;华容道&rdquo;拼图游戏。</li>
        <li><br />
        </li>
        <li><strong>系统框图</strong><br />
        </li>
</ol>

<div>ESP32-S3-LCD-EV-Board开发板带着一块480X480的触摸屏,很适合与用户以触摸方式进行交互。项目使用了LVGL来进行图形展示和与用户交互。将屏幕分为游戏区和控制区两个部分。</div>

<div>控制区负责控制游戏难度等级,提供了三个按钮。一个按钮为退出按钮,可以退出游戏。另外两个按钮,为调整游戏难度按钮,可以调整游戏难度,一共有16级难度(0~15),调整难度后,都会按当前难度重新初始化游戏界面。</div>

<div>游戏区显示当前各个图块的位置,一共有四种类型的图块,每个图块均可上下左右四个方向移动,用户可以在游戏区通过触摸移动相应的图块进行移动,系统判断当前图块是否符合移动条件,条件符合时就重绘游戏区域,达到移动图块的效果。</div>

<div>当图块符合胜利条件,就胜利,并升级到下一难度等级。</div>

<div>系统开发使用esp-idf,版本选择esp-idf 5.2.1,使用vscode作为开发工具,选择LVGL8.4.0作为UI开发的库。</div>

<div><strong>三、各部分功能说明</strong></div>

<ol>
        <li>基础框架:使用官方的例程库作为基础框架。https://github.com/espressif/esp-dev-kits<br />
        官方例程库下载下来后,找到esp32-s3-lcd-ev-board下examples里的lvgl_demos项目作为基础项目,在这个基础上叠加自己的功能。</li>
        <li>现在esp-idf使用了组件方式进行编程,组件无法进行修改。这里将lvgl组件移到本地。建立&ldquo;components&rdquo;文件夹,将&ldquo;managed_components&rdquo;文件夹下的lvgl__lvgl文件夹移动到&ldquo;components&rdquo;文件夹下,并改名为lvgl。<br />
        修改main文件夹下的CMakeLists.txt文件。
        <pre>
<code>set(LV_DEMO_DIR ../components/lvgl/demos)
file(GLOB_RECURSE LV_DEMOS_SOURCES ${LV_DEMO_DIR}/*.c)</code></pre>

        <p>&nbsp;</p>
        </li>
        <li>在main文件夹下,删除原有的&ldquo;ui_printer&rdquo;和&ldquo;ui_tuner&rdquo;文件夹,这两个文件夹,项目中用不到。再创建game文件夹,在game下创建文件夹&ldquo;huarongdao&rdquo;,用来存放自己的游戏代码文件。最后还需要修改一下CMakeLists.txt文件。<br />
        </li>
        <li>修改代码。在main.c主函数中,先引入自己的头函数#include &quot;huarongdao/huaorngdao.h&quot; 。在主函数中保留官方例程的lvgl初始化部分,其余部分删除,添加游戏的调用函数。
        <pre>
<code>void app_main(void)
{
    bsp_i2c_init();
    lv_disp_t *disp = bsp_display_start();
    ESP_LOGI(TAG, "Display LVGL demo");
    /**
   * To avoid errors caused by multiple tasks simultaneously accessing LVGL,
   * should acquire a lock before operating on LVGL.
   */
    bsp_display_lock(0);
    huarongdao();
    /* Release the lock */
    bsp_display_unlock();
}</code></pre>

        <p>&nbsp;</p>
        </li>
        <li></li>
        <li>游戏入口函数huarongdao(),在这里开始初始化游戏界面。包括绘制游戏背景图片,绘制按钮,给按钮添加回调事件,游戏负责与用户交互的回调事件为&ldquo;move_obj_cb&rdquo;,由这个方法来驱动整个游戏的运作。</li>
</ol>

<div style="text-align: left;">
<pre>
<code class="language-cpp">static void move_obj_cb(lv_event_t *e)
{
        static lv_point_t click_point1, click_point2;
        int movex, movey, direction;

        game_obj_type *stage_data = (game_obj_type *)e-&gt;user_data;

        if (e-&gt;code == LV_EVENT_PRESSED)
        {
                lv_indev_get_point(lv_indev_get_act(), &amp;click_point1);
                return;
        }

        if (e-&gt;code == LV_EVENT_RELEASED)
        {
                lv_indev_get_point(lv_indev_get_act(), &amp;click_point2);
                movex = click_point2.x - click_point1.x;
                movey = click_point2.y - click_point1.y;

                if ((movex == 0 &amp;&amp; movey == 0) || (movex == movey) || (movex == -movey))
                        return;

                if ((movex &lt; 0 &amp;&amp; movey &lt; 0 &amp;&amp; movex &gt; movey) || (movex &gt; 0 &amp;&amp; movey &lt; 0 &amp;&amp; movex &lt; -movey))
                        direction = up;
                if ((movex &gt; 0 &amp;&amp; movey &lt; 0 &amp;&amp; movex &gt; -movey) || (movex &gt; 0 &amp;&amp; movey &gt; 0 &amp;&amp; movex &gt; movey))
                        direction = right;
                if ((movex &lt; 0 &amp;&amp; movey &lt; 0 &amp;&amp; movex &lt; movey) || (movex &lt; 0 &amp;&amp; movey &gt; 0 &amp;&amp; movex &lt; -movey))
                        direction = left;
                if ((movex &lt; 0 &amp;&amp; movey &gt; 0 &amp;&amp; movex &gt; -movey) || (movex &gt; 0 &amp;&amp; movey &gt; 0 &amp;&amp; movex &lt; movey))
                        direction = down;

                if (direction == up)
                {

                        if (stage_data-&gt;obj_type == little)
                        {
                                if (stage_data-&gt;y == 0)
                                        return;

                                if (game_map == 0)
                                {
                                        game_map = 1;
                                        game_map = 0;
                                        stage_data-&gt;y--;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }

                        if (stage_data-&gt;obj_type == big)
                        {
                                if (stage_data-&gt;y == 0)
                                        return;

                                if (game_map == 0 &amp;&amp; game_map[(stage_data-&gt;x) + 1] == 0)
                                {
                                        game_map = 1;
                                        game_map[(stage_data-&gt;x) + 1] = 1;
                                        game_map = 0;
                                        game_map[(stage_data-&gt;x) + 1] = 0;
                                        stage_data-&gt;y--;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }

                        if (stage_data-&gt;obj_type == hor)
                        {
                                if (stage_data-&gt;y == 0)
                                        return;

                                if (game_map == 0 &amp;&amp; game_map[(stage_data-&gt;x) + 1] == 0)
                                {
                                        game_map = 1;
                                        game_map[(stage_data-&gt;x) + 1] = 1;
                                        game_map = 0;
                                        game_map[(stage_data-&gt;x) + 1] = 0;
                                        stage_data-&gt;y--;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }

                        if (stage_data-&gt;obj_type == ver)
                        {
                                if (stage_data-&gt;y == 0)
                                        return;

                                if (game_map == 0)
                                {
                                        game_map = 1;
                                        game_map = 0;
                                        stage_data-&gt;y--;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }
                }

                if (direction == down)
                {

                        if (stage_data-&gt;obj_type == little)
                        {
                                if (stage_data-&gt;y == 4)
                                        return;

                                if (game_map == 0)
                                {
                                        game_map = 1;
                                        game_map = 0;
                                        stage_data-&gt;y++;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }

                        if (stage_data-&gt;obj_type == big)
                        {
                                if (stage_data-&gt;y == 3)
                                        return;

                                if (game_map == 0 &amp;&amp; game_map[(stage_data-&gt;x) + 1] == 0)
                                {
                                        game_map = 1;
                                        game_map[(stage_data-&gt;x) + 1] = 1;
                                        game_map = 0;
                                        game_map[(stage_data-&gt;x) + 1] = 0;
                                        stage_data-&gt;y++;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }

                        if (stage_data-&gt;obj_type == hor)
                        {
                                if (stage_data-&gt;y == 4)
                                        return;

                                if (game_map == 0 &amp;&amp; game_map[(stage_data-&gt;x) + 1] == 0)
                                {
                                        game_map = 1;
                                        game_map[(stage_data-&gt;x) + 1] = 1;
                                        game_map = 0;
                                        game_map[(stage_data-&gt;x) + 1] = 0;
                                        stage_data-&gt;y++;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }

                        if (stage_data-&gt;obj_type == ver)
                        {
                                if (stage_data-&gt;y == 3)
                                        return;

                                if (game_map == 0)
                                {
                                        game_map = 1;
                                        game_map = 0;
                                        stage_data-&gt;y++;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }
                }

                if (direction == left)
                {

                        if (stage_data-&gt;obj_type == little)
                        {
                                if (stage_data-&gt;x == 0)
                                        return;

                                if (game_map == 0)
                                {
                                        game_map = 1;
                                        game_map = 0;
                                        stage_data-&gt;x--;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }

                        if (stage_data-&gt;obj_type == big)
                        {
                                if (stage_data-&gt;x == 0)
                                        return;

                                if (game_map == 0 &amp;&amp; game_map[(stage_data-&gt;x) - 1] == 0)
                                {
                                        game_map = 1;
                                        game_map[(stage_data-&gt;x) - 1] = 1;
                                        game_map = 0;
                                        game_map[(stage_data-&gt;x) + 1] = 0;
                                        stage_data-&gt;x--;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }

                        if (stage_data-&gt;obj_type == hor)
                        {
                                if (stage_data-&gt;x == 0)
                                        return;

                                if (game_map == 0)
                                {
                                        game_map = 1;
                                        game_map = 0;
                                        stage_data-&gt;x--;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }

                        if (stage_data-&gt;obj_type == ver)
                        {
                                if (stage_data-&gt;x == 0)
                                        return;

                                if (game_map == 0 &amp;&amp; game_map[(stage_data-&gt;x) - 1] == 0)
                                {
                                        game_map = 1;
                                        game_map = 1;
                                        game_map = 0;
                                        game_map = 0;
                                        stage_data-&gt;x--;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }
                }

                if (direction == right)
                {

                        if (stage_data-&gt;obj_type == little)
                        {
                                if (stage_data-&gt;x == 3)
                                        return;

                                if (game_map == 0)
                                {
                                        game_map = 1;
                                        game_map = 0;
                                        stage_data-&gt;x++;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }

                        if (stage_data-&gt;obj_type == big)
                        {
                                if (stage_data-&gt;x == 2)
                                        return;

                                if (game_map == 0 &amp;&amp; game_map[(stage_data-&gt;x) + 2] == 0)
                                {
                                        game_map = 1;
                                        game_map[(stage_data-&gt;x) + 2] = 1;
                                        game_map = 0;
                                        game_map[(stage_data-&gt;x)] = 0;
                                        stage_data-&gt;x++;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }

                        if (stage_data-&gt;obj_type == hor)
                        {
                                if (stage_data-&gt;x == 2)
                                        return;

                                if (game_map == 0)
                                {
                                        game_map = 1;
                                        game_map = 0;
                                        stage_data-&gt;x++;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }

                        if (stage_data-&gt;obj_type == ver)
                        {
                                if (stage_data-&gt;x == 3)
                                        return;

                                if (game_map == 0 &amp;&amp; game_map[(stage_data-&gt;x) + 1] == 0)
                                {
                                        game_map = 1;
                                        game_map = 1;
                                        game_map = 0;
                                        game_map = 0;
                                        stage_data-&gt;x++;
                                        step_count++;
                                        lv_label_set_text_fmt(step_lable, "STEP:%d", step_count);
                                        lv_obj_set_pos(stage_data-&gt;obj, lv_pct((stage_data-&gt;x) * 25), lv_pct((stage_data-&gt;y) * 20));
                                }
                        }
                }

                if (stage_data-&gt;obj_type == big &amp;&amp; stage_data-&gt;x == 1 &amp;&amp; stage_data-&gt;y == 3)
                {
                        lv_obj_t *clear_lable = lv_label_create(game_window);
                        lv_label_set_text(clear_lable, "STAGE CLEAR");
                        lv_obj_set_style_text_color(clear_lable, lv_color_hex(0xffffff), 0);
                        lv_obj_center(clear_lable);
                        if (current_stage &lt; max_stage - 1)
                        {
                                current_stage++;
                        }
                        stage_clear();
                }
        }
}</code></pre>

<p>&nbsp;</p>
</div>

<div style="text-align: center;">&nbsp;</div>

<div><strong>四、作品源码</strong></div>

<div><a href="https://download.eeworld.com.cn/detail/aramy/634585" target="_blank">https://download.eeworld.com.cn/detail/aramy/634585</a></div>

<div></div>

<div><strong>五、</strong><strong>作品功能演示视频</strong></div>

<div><strong><iframe allowfullscreen="true" border="0" frameborder="no" framespacing="0" height="450px" scrolling="no" src="//player.bilibili.com/player.html?isOutside=true&amp;aid=113316974298010&amp;bvid=BV1FjmKYVEh2&amp;cid=26317816344&amp;p=1" width="700px"></iframe></strong><br />
&nbsp;</div>

<div><strong>六、项目总结</strong></div>

<div>很伤心,没能完成最初设定的目标!本来想在这个板子上实现机器学习的内容的,实在是能力有限,无法完成。LVGL作为UI实现的工具,功能非常强大,可是总觉着版本有些混乱,不同版本方法差异好大,学习成本有点太高了,但是作为一个连接单片机和人的桥梁还是非常好用的,通过单片机、传感器来感知万物,再用合适的UI展示出来,与人互动。非常喜欢ESP32-S3-LCD-EV-Board开发板,希望能借助这个项目留下这块板子!</div>

<p><!--importdoc--></p>

CoderX9527 发表于 2024-10-17 11:05

<p>感谢大佬分享</p>

wangerxian 发表于 2024-10-17 16:29

<p>这游戏有点意思,从零开发的呀?</p>

aramy 发表于 2024-10-18 12:26

wangerxian 发表于 2024-10-17 16:29
这游戏有点意思,从零开发的呀?

<p>不是,移植开源的项目!</p>

wangerxian 发表于 2024-10-18 13:10

aramy 发表于 2024-10-18 12:26
不是,移植开源的项目!

<p>厉害,厉害,这样看来其他GUI框架也能移植这个游戏了。</p>

tobot 发表于 2024-10-18 23:25

<p>华容道规则不麻烦,但算法麻烦,小时候我背过81步解法,现在说是最短48步?</p>

hellokitty_bean 发表于 2024-10-19 10:11

<p><img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/loveliness.gif" width="48" />,如果可以,那很多小游戏都可以移植进来玩。。。。。。。。。。。。。。。</p>

xutong 发表于 2024-10-19 13:53

<p>牛的</p>

Figox 发表于 2024-10-21 16:59

<p>厉害,厉害,这样看来其他GUI框架也能移植这个游戏了。</p>
页: [1]
查看完整版本: 【2024 DigiKey 创意大赛】用esp32-s3-lcd-ev-board制作华容道拼图游戏