背景
在LVGL交流群,有网友提出想要移植物理引擎到LVGL,遂有了本文。阅读本文需要对IDF和LVGL有所了解
过程
2D物理引擎有很多,经过一番调研选择了Chipmunk2D
下载源码
此处省略一万字,Github访问可能会有些慢
添加文件
将源码中的src
文件夹复制到,include/chipmunk/
文件夹下,然后将chipmunk
文件夹复制idf工程
完善lv_mem
在lv_mem.h,lv_mem.c
文件里添加以下代码:
// lv_mem.h
void *lv_cp_malloc(size_t num,size_t size){
// lv_mem.c
void *lv_cp_malloc(size_t num,size_t size){
size_t total_size = num * size;
void* ptr = lv_malloc(total_size); // 分配内存
if (ptr != NULL) {
lv_memset(ptr, 0, total_size); // 初始化为零
}
return ptr;
}
配置Chipmunk2D
修改chipmunk.h
,修改完成以后,物理引擎将共用LVGL
的内存,总共8KB
// 添加头文件
#include "lvgl.h"
// 修改如下代码
#ifndef CP_BUFFER_BYTES
#define CP_BUFFER_BYTES (8*1024) //默认32k,此处改为8k
#endif
#ifndef cpcalloc
/// Chipmunk calloc() alias.
// #define cpcalloc calloc
#define cpcalloc lv_cp_malloc
#endif
#ifndef cprealloc
/// Chipmunk realloc() alias.
// #define cprealloc realloc
#define cprealloc lv_realloc
#endif
#ifndef cpfree
/// Chipmunk free() alias.
// #define cpfree free
#define cpfree lv_free
#endif
编写Demo
新建 chipmunk_demo文件夹如下:
并在component.mk
文件中添加配置路径:
COMPONENT_SRCDIRS := . \
chipmunk \
chipmunk_demo
编写代码:
1.创建一个世界
2. 给这个世界添加重力
3. 创建四面墙围成框
4. 小框里面创建一个小球
5. 给墙和小球分别设置摩擦系数和弹性系数
6. 给小球随机设置一个初始速度
7. 启动定时器,定时调用 cpSpaceStep(space, 1.0/30.0);
来推动时间前进
8. 定时读取并跟新小球的位置
根据物理学原理:小球按照抛物线运动,碰撞到四周时会反弹
物理引擎模拟重力
各个文件代码如下
// chipmunk_demo.h
#ifndef CHIP_MUNK_ESP_DEMO_H
#define CHIP_MUNK_ESP_DEMO_H
#include "lvgl.h"
#include "esp_log.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "chipmunk/chipmunk.h"
#include "chipmunk/chipmunk_structs.h"
void start_chipmunk_esp_example();
#endif
//chipmunk_demo.c
#include "chipmunk_demo.h"
#include "lvgl.h"
#define WIDTH 480
#define HEIGHT 480
#define BALL_RADIUS 20
cpSpace *space;
cpBody *staticBody;
cpBody * ballBody;
lv_obj_t *ballObj;
const static double width = 480.0;
const static double height = 480.0;
void on_timer(lv_timer_t *t);
void start_chipmunk_esp_example(){
lv_obj_t * bg = lv_obj_create(lv_scr_act());
lv_obj_set_size(bg,width,height);
lv_obj_center(bg);
lv_obj_set_style_pad_all(bg,0,LV_PART_MAIN);
lv_obj_set_style_bg_color(bg,lv_color_hex(0x000000),LV_PART_MAIN);
ballObj = lv_obj_create(bg);
lv_obj_remove_style_all(ballObj);
// 生成一个随机颜色
lv_color_t random_color = lv_color_make(rand() % 256, rand() % 256, rand() % 256);
// 将随机颜色应用到对象上
lv_obj_set_style_bg_color(ballObj, random_color, LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(ballObj,LV_OPA_100,LV_PART_MAIN);
lv_obj_set_style_radius(ballObj,LV_RADIUS_CIRCLE,LV_PART_MAIN);
lv_obj_set_size( ballObj,BALL_RADIUS*2,BALL_RADIUS*2);
lv_obj_set_pos(ballObj,0,0);
// 创建物理空间
space = cpSpaceNew();
cpSpaceSetGravity(space, cpv(0, 100));
// 创建静态物体,即边界
staticBody = cpSpaceGetStaticBody(space);
cpShape *shape = cpSegmentShapeNew(staticBody, cpv(0,0), cpv(0, HEIGHT), 0.0f);
cpShapeSetFriction(shape, 1.0);
cpShapeSetElasticity(shape, 1.0);
cpSpaceAddShape(space, shape);
shape = cpSegmentShapeNew(staticBody, cpv(0, HEIGHT), cpv(WIDTH, HEIGHT), 0.0f);
cpShapeSetFriction(shape, 1.0);
cpShapeSetElasticity(shape, 1.0);
cpSpaceAddShape(space, shape);
shape = cpSegmentShapeNew(staticBody, cpv(WIDTH, HEIGHT), cpv(WIDTH, 0), 0.0f);
// cpShapeSetFriction(shape, 1.0);
cpShapeSetElasticity(shape, 1.0);
cpSpaceAddShape(space, shape);
shape = cpSegmentShapeNew(staticBody, cpv(WIDTH, 0), cpv(0, 0), 0.0f);
cpShapeSetFriction(shape, 1.0);
cpShapeSetElasticity(shape, 1.0);
cpSpaceAddShape(space, shape);
// // 创建1个小球
cpFloat radius = 20.0;
cpFloat x = cpflerp(radius * 2, WIDTH - radius * 2, (cpFloat)rand() / (cpFloat)RAND_MAX);
cpFloat y = cpflerp(radius * 2, HEIGHT - radius * 2, (cpFloat)rand() / (cpFloat)RAND_MAX);
ballBody = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForCircle(1.0f, 0.0f, radius, cpvzero)));
cpBodySetPosition(ballBody, cpv(x, y));
cpShape *ball_shape = cpSpaceAddShape(space, cpCircleShapeNew(ballBody, radius, cpvzero));
cpShapeSetFriction(ball_shape, 0.7);
cpShapeSetElasticity(ball_shape, 0.8);
cpVect velocity = cpv((cpFloat)rand() / (cpFloat)RAND_MAX * 200 - 100, (cpFloat)rand() / (cpFloat)RAND_MAX * 200 - 100);
cpBodySetVelocity(ballBody, velocity);
lv_timer_t *timer = lv_timer_create(on_timer,30,NULL);
lv_timer_set_repeat_count(timer,-1);
}
void on_timer(lv_timer_t *t){
cpSpaceStep(space, 1.0/30.0);
printf("Ball Position: (%.2f, %.2f)\n", cpBodyGetPosition(ballBody).x, cpBodyGetPosition(ballBody).y);
lv_obj_set_pos(ballObj, cpBodyGetPosition(ballBody).x-20, cpBodyGetPosition(ballBody).y-20);
}