diff --git a/Tiled地图解析思路.md b/Tiled地图解析思路.md index 16239fb..25dfaae 100644 --- a/Tiled地图解析思路.md +++ b/Tiled地图解析思路.md @@ -15,7 +15,15 @@ - 如果 type : objectgroup 载入对象层 - 对象数据在"objects"字段中,每个对象都创建一个游戏对象 - - 关注的要点:gid查找源图片信息,layer数据中确认目标位置(TransformComponent)信息 + - 关注的要点:gid查找**源图片信息**,layer数据中确认目标位置(TransformComponent)信息 + - 源图片信息:如果是"solid"类型,则 为静止障碍物,碰撞盒大小为整个图片大小 + - 源图片信息:如果非"solid"类型,则 + - 碰撞盒大小、偏移: 获取 "objectgroup" --> 获取 "objects" --> "x","y","width","height" + - 获取其他指定自定义属性(模版函数),进行必要设置/组件添加。(例如tag、gravity) - 如果没有gid,则代表自己绘制的形状(可能是碰撞盒、触发器等,未来按需处理) -- 自定义属性的获取方法:图块集中 获取 "tiles" --> 获取 "properties" \ No newline at end of file +- 自定义属性的获取方法:图块集中 获取 "tiles" --> 获取 "properties" + +- **对象层物体特别注意:** 信息分散在两个区块中: + - 地图tmj中: 绘制区域、名字 (及针对当前地图添加的自定义属性) + - gid对应的图块集tsj中: 源图片信息 (画在任何地图上都会有的属性) \ No newline at end of file diff --git a/assets/maps/actor.tsj b/assets/maps/actor.tsj index 15c1091..1b23ab1 100644 --- a/assets/maps/actor.tsj +++ b/assets/maps/actor.tsj @@ -21,7 +21,6 @@ "objectgroup": { "draworder":"index", - "id":2, "name":"", "objects":[ { @@ -41,6 +40,12 @@ "x":0, "y":0 }, + "properties":[ + { + "name":"tag", + "type":"string", + "value":"enemy" + }], "width":40, "x":0, "y":0 @@ -54,7 +59,6 @@ "objectgroup": { "draworder":"index", - "id":3, "name":"", "objects":[ { @@ -74,6 +78,17 @@ "x":0, "y":0 }, + "properties":[ + { + "name":"gravity", + "type":"bool", + "value":true + }, + { + "name":"tag", + "type":"string", + "value":"player" + }], "width":32, "x":0, "y":0 @@ -87,7 +102,6 @@ "objectgroup": { "draworder":"index", - "id":2, "name":"", "objects":[ { @@ -107,6 +121,17 @@ "x":0, "y":0 }, + "properties":[ + { + "name":"gravity", + "type":"bool", + "value":true + }, + { + "name":"tag", + "type":"string", + "value":"enemy" + }], "width":35, "x":0, "y":0 @@ -120,7 +145,6 @@ "objectgroup": { "draworder":"index", - "id":2, "name":"", "objects":[ { @@ -140,6 +164,17 @@ "x":0, "y":0 }, + "properties":[ + { + "name":"gravity", + "type":"bool", + "value":true + }, + { + "name":"tag", + "type":"string", + "value":"enemy" + }], "width":36, "x":0, "y":0 @@ -153,7 +188,6 @@ "objectgroup": { "draworder":"index", - "id":2, "name":"", "objects":[ { @@ -173,6 +207,12 @@ "x":0, "y":0 }, + "properties":[ + { + "name":"tag", + "type":"string", + "value":"item" + }], "width":21, "x":0, "y":0 @@ -186,7 +226,6 @@ "objectgroup": { "draworder":"index", - "id":2, "name":"", "objects":[ { @@ -206,6 +245,12 @@ "x":0, "y":0 }, + "properties":[ + { + "name":"tag", + "type":"string", + "value":"item" + }], "width":15, "x":0, "y":0 diff --git a/assets/maps/level1.tmj b/assets/maps/level1.tmj index e56a5b9..d309774 100644 --- a/assets/maps/level1.tmj +++ b/assets/maps/level1.tmj @@ -81,8 +81,8 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 129, 129, 337, 129, 129, 129, 129, 337, 129, 337, 333, 129, 129, 129, 129, 129, 129, 129, 333, 129, 129, 337, 129, 129, 129, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 362, 0, 0, 0, 0, 362, 0, 362, 358, 0, 0, 0, 0, 0, 0, 0, 358, 0, 0, 362, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 602, 602, 602, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 366, 368, 368, 368, 368, 368, 368, 368, 370, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 261, 0, 0, 0, 0, 0, 0, 261, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 576, 1, 0, 0, 0, 0, 0, 0, 602, 602, 602, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 366, 368, 368, 368, 368, 368, 368, 368, 370, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 261, 0, 0, 0, 0, 0, 0, 261, 0, 0, 0, 0, 0, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 0, 0, 0, 27, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 117, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, 33, 29, 29, 29, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 262, 262, 262, 262, 262, 262, 27, 29, 29, 29, 29, 29, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 77, 79, 181, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 142, 117, 0, 0, 0, 585, 0, 115, 33, 33, 65, 79, 181, 79, 79, 148, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 468, 0, 0, 0, 0, 0, 0, 0, 0, 121, 146, 79, 79, 79, 79, 79, 79, 181, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 79, 79, 79, 181, 79, 79, 79, 79, 79, 79, 79, 79, 83, 84, 0, 0, 77, 79, 79, 79, 79, 79, 79, 79, 79, 181, 79, 79, 79, 79, 79, 79, 142, 33, 33, 258, 33, 33, 140, 79, 79, 79, 79, 79, 79, 79, 79, 148, 29, 29, 29, 29, 29, 29, 48, 49, 0, 0, 0, 0, 0, 0, 468, 0, 0, 121, 29, 29, 29, 146, 79, 79, 183, 79, 79, 183, 79, 79, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 183, 79, 79, 79, 79, @@ -93,10 +93,10 @@ 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 77, 193, 193, 193, 193, 193, 193, 193, 295, 335, 291, 193, 193, 193, 193, 193, 193, 193, 295, 258, 291, 193, 193, 502, 502, 193, 335, 193, 333, 193, 502, 529, 530, 502, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 502, 181, 183, 79, 181, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 183, 183, 79, 79, 79, 79, 79, 79, 79, 183, 79, 79, 79, 79, 81, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 0, 0, 0, 0, 0, 0, 360, 0, 358, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 183, 181, 79, 79, 79, 79, 79, 79, 79, 79, 183, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 79, 183, 79, 79, 79, 79, 183, 79, 79, 79, 183, 79, 81, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 385, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 0, 0, 0, 0, 0, 0, 385, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 183, 79, 79, - 79, 79, 183, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 591, 0, 0, 0, 0, 0, 0, 0, 258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 599, 591, 0, 599, 599, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 79, 79, 181, 79, 79, 79, 79, 79, 79, 183, 79, 181, 181, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 183, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 0, 0, 0, 0, 591, 0, 0, 586, 0, 0, 0, 599, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 79, 183, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 181, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 181, 79, 81, 0, 0, 0, 77, 0, 576, 0, 0, 0, 583, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 504, 505, 0, 0, 0, 578, 0, 0, 579, 0, 0, 0, 241, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 79, 79, 79, + 79, 79, 183, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 591, 0, 0, 0, 0, 0, 0, 0, 258, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 599, 591, 0, 599, 599, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 79, 79, 181, 79, 79, 79, 79, 79, 79, 183, 79, 181, 181, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 183, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 0, 0, 0, 0, 591, 0, 0, 586, 1, 0, 0, 599, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 79, 183, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 77, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 241, 181, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 181, 79, 81, 0, 0, 0, 77, 0, 576, 1, 0, 0, 583, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 504, 505, 0, 0, 0, 578, 1, 0, 579, 0, 0, 0, 241, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 191, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 502, 193, 193, 504, 505, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 529, 530, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 502, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 79, 79, 183, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 241, 181, 79, 183, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 502, 529, 530, 502, 79, 79, 79, 79, 79, 79, 79, 79, 183, 79, 79, 79, 79, 79, 183, 79, 79, 79, 183, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 79, 79, 79, 183, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 241, 79, 79, 79, 79, 79, 79, 181, 79, 183, 79, 183, 79, 79, 183, 79, 79, 79, 79, 79, 79, 79, 502, 183, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 81, 0, 0, 0, 0, 0, 0, 77, 79, 79, 79, 79, 79, @@ -319,6 +319,18 @@ "width":0, "x":87, "y":43 + }, + { + "gid":576, + "height":32, + "id":19, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":32, + "x":64, + "y":160 }], "opacity":1, "type":"objectgroup", @@ -339,7 +351,7 @@ "y":0 }], "nextlayerid":7, - "nextobjectid":19, + "nextobjectid":20, "orientation":"orthogonal", "renderorder":"right-down", "tiledversion":"1.11.2", diff --git a/assets/maps/prop.tsj b/assets/maps/prop.tsj index 0be65cb..fa16098 100644 --- a/assets/maps/prop.tsj +++ b/assets/maps/prop.tsj @@ -100,7 +100,13 @@ "id":10, "image":"..\/textures\/Props\/face-block.png", "imageheight":32, - "imagewidth":32 + "imagewidth":32, + "properties":[ + { + "name":"solid", + "type":"bool", + "value":true + }] }, { "id":11, diff --git a/assets/maps/tileset.tsj b/assets/maps/tileset.tsj index ee1eb7b..23baa52 100644 --- a/assets/maps/tileset.tsj +++ b/assets/maps/tileset.tsj @@ -9,6 +9,15 @@ "tiledversion":"1.11.2", "tileheight":16, "tiles":[ + { + "id":0, + "properties":[ + { + "name":"solid", + "type":"bool", + "value":true + }] + }, { "id":26, "properties":[ diff --git a/src/engine/component/collider_component.h b/src/engine/component/collider_component.h index a360279..f19f485 100644 --- a/src/engine/component/collider_component.h +++ b/src/engine/component/collider_component.h @@ -51,7 +51,7 @@ public: void updateOffset(); // --- Getters --- - const TransformComponent* getTransform() const { return transform_; } ///< @brief 获取缓存的TransformComponent + TransformComponent* getTransform() const { return transform_; } ///< @brief 获取缓存的TransformComponent const engine::physics::Collider* getCollider() const { return collider_.get(); } ///< @brief 获取 Collider 对象。 const glm::vec2& getOffset() const { return offset_; } ///< @brief 获取当前计算出的偏移量。 engine::utils::Alignment getAlignment() const { return alignment_; } ///< @brief 获取设置的对齐锚点。 diff --git a/src/engine/physics/physics_engine.cpp b/src/engine/physics/physics_engine.cpp index 4e345be..ff47873 100644 --- a/src/engine/physics/physics_engine.cpp +++ b/src/engine/physics/physics_engine.cpp @@ -84,9 +84,17 @@ void PhysicsEngine::checkObjectCollisions() /* --- 通过保护性测试后,正式执行逻辑 --- */ if (collision::checkCollision(*cc_a, *cc_b)) { - // TODO: 并不是所有碰撞都需要插入collision_pairs_,未来会添加过滤条件 - // 记录碰撞对 - collision_pairs_.emplace_back(obj_a, obj_b); + // 如果是可移动物体与SOLID物体碰撞,则直接处理位置变化,不用记录碰撞对 + if (obj_a->getTag() != "solid" && obj_b->getTag() == "solid") { + resolveSolidObjectCollisions(obj_a, obj_b); + } + else if (obj_a->getTag() == "solid" && obj_b->getTag() != "solid") { + resolveSolidObjectCollisions(obj_b, obj_a); + } + else { + // 记录碰撞对 + collision_pairs_.emplace_back(obj_a, obj_b); + } } } } @@ -181,8 +189,54 @@ void PhysicsEngine::resolveTileCollisions(engine::component::PhysicsComponent* p } } // 更新物体位置,并限制最大速度 - tc->setPosition(new_obj_pos); + tc->translate(new_obj_pos - obj_pos); // 使用translate方法,避免直接设置位置,因为碰撞盒可能有偏移量 pc->velocity_ = glm::clamp(pc->velocity_, -max_speed_, max_speed_); } +void PhysicsEngine::resolveSolidObjectCollisions(engine::object::GameObject* move_obj, engine::object::GameObject* solid_obj) +{ + // 进入此函数前,已经检查了各个组件的有效性,因此直接进行计算 + auto* move_tc = move_obj->getComponent(); + auto* move_pc = move_obj->getComponent(); + auto* move_cc = move_obj->getComponent(); + auto* solid_cc = solid_obj->getComponent(); + + // 这里只能获取期望位置,无法获取当前帧初始位置,因此无法进行轴分离碰撞检测 + /* 未来可以进行重构,让这里可以获取初始位置。但是我们展示另外一种处理方法 */ + auto move_aabb = move_cc->getWorldAABB(); + auto solid_aabb = solid_cc->getWorldAABB(); + + // --- 使用最小平移向量解决碰撞问题 --- + auto move_center = move_aabb.position + move_aabb.size / 2.0f; + auto solid_center = solid_aabb.position + solid_aabb.size / 2.0f; + // 计算两个包围盒的重叠部分 + auto overlap = glm::vec2(move_aabb.size / 2.0f + solid_aabb.size / 2.0f) - glm::abs(move_center - solid_center); + if (overlap.x < 0.1f && overlap.y < 0.1f) return; // 如果重叠部分太小,则认为没有碰撞 + + if (overlap.x < overlap.y) { // 如果重叠部分在x方向上更小,则认为碰撞发生在x方向上(推出x方向平移向量最小) + if (move_center.x < solid_center.x) { + // 移动物体在左边,让它贴着右边SOLID物体(相当于向左移出重叠部分),y方向正常移动 + move_tc->translate(glm::vec2(-overlap.x, 0.0f)); + // 如果速度为正(向右移动),则归零 (if判断不可少,否则可能出现错误吸附) + if (move_pc->velocity_.x > 0.0f) move_pc->velocity_.x = 0.0f; + } + else { + // 移动物体在右边,让它贴着左边SOLID物体(相当于向右移出重叠部分),y方向正常移动 + move_tc->translate(glm::vec2(overlap.x, 0.0f)); + if (move_pc->velocity_.x < 0.0f) move_pc->velocity_.x = 0.0f; + } + } else { // 重叠部分在y方向上更小,则认为碰撞发生在y方向上(推出y方向平移向量最小) + if (move_center.y < solid_center.y) { + // 移动物体在上面,让它贴着下面SOLID物体(相当于向上移出重叠部分),x方向正常移动 + move_tc->translate(glm::vec2(0.0f, -overlap.y)); + if (move_pc->velocity_.y > 0.0f) move_pc->velocity_.y = 0.0f; + } + else { + // 移动物体在下面,让它贴着上面SOLID物体(相当于向下移出重叠部分),x方向正常移动 + move_tc->translate(glm::vec2(0.0f, overlap.y)); + if (move_pc->velocity_.y < 0.0f) move_pc->velocity_.y = 0.0f; + } + } +} + } // namespace engine::physics \ No newline at end of file diff --git a/src/engine/physics/physics_engine.h b/src/engine/physics/physics_engine.h index 3c44f3b..01912ae 100644 --- a/src/engine/physics/physics_engine.h +++ b/src/engine/physics/physics_engine.h @@ -44,9 +44,6 @@ public: void unregisterCollisionLayer(engine::component::TileLayerComponent* layer);///< @brief 注销用于碰撞检测的 TileLayerComponent void update(float delta_time); ///< @brief 核心循环:更新所有注册的物理组件的状态 - void checkObjectCollisions(); ///< @brief 检测并处理对象之间的碰撞,并记录需要游戏逻辑处理的碰撞对。 - /// @brief 检测并处理游戏对象和瓦片层之间的碰撞。 - void resolveTileCollisions(engine::component::PhysicsComponent* pc, float delta_time); // 设置器/获取器 void setGravity(const glm::vec2& gravity) { gravity_ = gravity; } ///< @brief 设置全局重力加速度 @@ -57,6 +54,13 @@ public: const std::vector>& getCollisionPairs() const { return collision_pairs_; }; + +private: + void checkObjectCollisions(); ///< @brief 检测并处理对象之间的碰撞,并记录需要游戏逻辑处理的碰撞对。 + /// @brief 检测并处理游戏对象和瓦片层之间的碰撞。 + void resolveTileCollisions(engine::component::PhysicsComponent* pc, float delta_time); + /// @brief 处理可移动物体与SOLID物体的碰撞。 + void resolveSolidObjectCollisions(engine::object::GameObject* move_obj, engine::object::GameObject* solid_obj); }; } // namespace engine::physics \ No newline at end of file diff --git a/src/engine/scene/level_loader.cpp b/src/engine/scene/level_loader.cpp index a2ab7e3..9672729 100644 --- a/src/engine/scene/level_loader.cpp +++ b/src/engine/scene/level_loader.cpp @@ -3,6 +3,8 @@ #include "../component/transform_component.h" #include "../component/tilelayer_component.h" #include "../component/sprite_component.h" +#include "../component/collider_component.h" +#include "../component/physics_component.h" #include "../object/game_object.h" #include "../scene/scene.h" #include "../core/context.h" @@ -186,6 +188,47 @@ void LevelLoader::loadObjectLayer(const nlohmann::json& layer_json, Scene& scene game_object->addComponent(position, scale, rotation); game_object->addComponent(std::move(tile_info.sprite), scene.getContext().getResourceManager()); + // 获取瓦片json信息 1. 必然存在,因为getTileInfoByGid(gid)函数已经顺利执行 + // 2. 这里再获取json,实际上检索了两次,未来可以优化 + auto tile_json = getTileJsonByGid(gid); + + // 获取碰信息:如果是SOLID类型,则添加物理组件,且图片源矩形区域就是碰撞盒大小 + if (tile_info.type == engine::component::TileType::SOLID) { + auto collider = std::make_unique(src_size); + game_object->addComponent(std::move(collider)); + // 物理组件不受重力影响 + game_object->addComponent(&scene.getContext().getPhysicsEngine(), false); + // 设置标签方便物理引擎检索 + game_object->setTag("solid"); + } + // 如果非SOLID类型,检查自定义碰撞盒是否存在 + else if (auto rect = getColliderRect(tile_json); rect) { + // 如果有,添加碰撞组件 + auto collider = std::make_unique(rect->size); + auto* cc = game_object->addComponent(std::move(collider)); + cc->setOffset(rect->position); // 自定义碰撞盒的坐标是相对于图片坐标,也就是针对Transform的偏移量 + // 和物理组件(默认不受重力影响) + game_object->addComponent(&scene.getContext().getPhysicsEngine(), false); + } + + // 获取标签信息并设置 + auto tag = getTileProperty(tile_json, "tag"); + if (tag) { + game_object->setTag(tag.value()); + } + + // 获取重力信息并设置 + auto gravity = getTileProperty(tile_json, "gravity"); + if (gravity) { + auto pc = game_object->getComponent(); + if (pc) { + pc->setUseGravity(gravity.value()); + } else { + spdlog::warn("对象 '{}' 在设置重力信息时没有物理组件,请检查地图设置。", object_name); + game_object->addComponent(&scene.getContext().getPhysicsEngine(), gravity.value()); + } + } + // 添加到场景中 scene.addGameObject(std::move(game_object)); spdlog::info("加载对象: '{}' 完成", object_name); @@ -193,6 +236,22 @@ void LevelLoader::loadObjectLayer(const nlohmann::json& layer_json, Scene& scene } } +std::optional LevelLoader::getColliderRect(const nlohmann::json &tile_json) +{ + if (!tile_json.contains("objectgroup")) return std::nullopt; + auto& objectgroup = tile_json["objectgroup"]; + if (!objectgroup.contains("objects")) return std::nullopt; + auto& objects = objectgroup["objects"]; + for (const auto& object : objects) { // 一个图片只支持一个碰撞器。如果有多个,则返回第一个不为空的 + auto rect = engine::utils::Rect(glm::vec2(object.value("x", 0.0f), object.value("y", 0.0f)), + glm::vec2(object.value("width", 0.0f), object.value("height", 0.0f))); + if (rect.size.x > 0 && rect.size.y > 0) { + return rect; + } + } + return std::nullopt; // 如果没找到碰撞器,则返回空 +} + engine::component::TileType LevelLoader::getTileType(const nlohmann::json &tile_json) { if (tile_json.contains("properties")) { @@ -297,6 +356,33 @@ engine::component::TileInfo LevelLoader::getTileInfoByGid(int gid) return engine::component::TileInfo(); } +std::optional LevelLoader::getTileJsonByGid(int gid) const +{ + // 1. 查找tileset_data_中键小于等于gid的最近元素 + auto tileset_it = tileset_data_.upper_bound(gid); + if (tileset_it == tileset_data_.begin()) { + spdlog::error("gid为 {} 的瓦片未找到图块集。", gid); + return std::nullopt; + } + --tileset_it; + // 2. 获取图块集json对象 + const auto& tileset = tileset_it->second; + auto local_id = gid - tileset_it->first; // 计算瓦片在图块集中的局部ID + if (!tileset.contains("tiles")) { // 没有tiles字段的话不符合数据格式要求,直接返回空 + spdlog::error("Tileset 文件 '{}' 缺少 'tiles' 属性。", tileset_it->first); + return std::nullopt; + } + // 3. 遍历tiles数组,根据id查找对应的瓦片并返回瓦片json + const auto& tiles_json = tileset["tiles"]; + for (const auto& tile_json : tiles_json) { + auto tile_id = tile_json.value("id", 0); + if (tile_id == local_id) { // 找到对应的瓦片,返回瓦片json + return tile_json; + } + } + return std::nullopt; +} + void LevelLoader::loadTileset(const std::string& tileset_path, int first_gid) { std::ifstream tileset_file(tileset_path); diff --git a/src/engine/scene/level_loader.h b/src/engine/scene/level_loader.h index 956172a..1df77d1 100644 --- a/src/engine/scene/level_loader.h +++ b/src/engine/scene/level_loader.h @@ -4,6 +4,7 @@ #include #include #include +#include "../utils/math.h" namespace engine::component { struct TileInfo; @@ -38,6 +39,34 @@ private: void loadTileLayer(const nlohmann::json& layer_json, Scene& scene); ///< @brief 加载瓦片图层 void loadObjectLayer(const nlohmann::json& layer_json, Scene& scene); ///< @brief 加载对象图层 + /** + * @brief 获取瓦片属性 + * @tparam T 属性类型 + * @param tile_json 瓦片json数据 + * @param property_name 属性名称 + * @return 属性值,如果属性不存在则返回 std::nullopt + */ + template + std::optional getTileProperty(const nlohmann::json& tile_json, const std::string& property_name) { + if (!tile_json.contains("properties")) return std::nullopt; + const auto& properties = tile_json["properties"]; + for (const auto& property : properties) { + if (property.contains("name") && property["name"] == property_name) { + if (property.contains("value")) { + return property["value"].get(); + } + } + } + return std::nullopt; + } + + /** + * @brief 获取瓦片碰撞器矩形 + * @param tile_json 瓦片json数据 + * @return 碰撞器矩形,如果碰撞器不存在则返回 std::nullopt + */ + std::optional getColliderRect(const nlohmann::json& tile_json); + /** * @brief 根据瓦片json对象获取瓦片类型 * @param tile_json 瓦片json数据 @@ -60,6 +89,13 @@ private: */ engine::component::TileInfo getTileInfoByGid(int gid); + /** + * @brief 根据全局 ID 获取瓦片json对象 (用于对象层获取瓦片信息) + * @param gid 全局 ID + * @return 瓦片json对象 + */ + std::optional getTileJsonByGid(int gid) const; + /** * @brief 加载 Tiled tileset 文件 (.tsj)。 * @param tileset_path Tileset 文件路径。 diff --git a/src/game/scene/game_scene.cpp b/src/game/scene/game_scene.cpp index 3cfd9cd..f9806d1 100644 --- a/src/game/scene/game_scene.cpp +++ b/src/game/scene/game_scene.cpp @@ -36,8 +36,12 @@ void GameScene::init() { } } - // 创建 test_object - createTestObject(); + // 获取玩家对象 + player_ = findGameObjectByName("player"); + if (!player_) { + spdlog::error("未找到玩家对象"); + return; + } Scene::init(); spdlog::trace("GameScene 初始化完成。"); @@ -54,7 +58,7 @@ void GameScene::render() { void GameScene::handleInput() { Scene::handleInput(); - TestObject(); + TestPlayer(); } void GameScene::clean() { @@ -63,34 +67,6 @@ void GameScene::clean() { // --- 测试方法 --- -void GameScene::createTestObject() { - spdlog::trace("在 GameScene 中创建 test_object..."); - auto test_object = std::make_unique("test_object"); - test_object_ = test_object.get(); - - // 添加组件 - test_object->addComponent(glm::vec2(100.0f, 100.0f)); - test_object->addComponent("assets/textures/Props/big-crate.png", context_.getResourceManager()); - test_object->addComponent(&context_.getPhysicsEngine()); - test_object->addComponent( - std::make_unique(glm::vec2(32.0f, 32.0f)) - ); - // 将创建好的 GameObject 添加到场景中 (一定要用std::move,否则传递的是左值) - addGameObject(std::move(test_object)); - - // 添加第二个游戏对象(不受重力影响),用于测试碰撞 - auto test_object2 = std::make_unique("test_object2"); - test_object2->addComponent(glm::vec2(50.0f, 50.0f)); - test_object2->addComponent("assets/textures/Props/big-crate.png", context_.getResourceManager()); - test_object2->addComponent(&context_.getPhysicsEngine(), false); - test_object2->addComponent( - std::make_unique(16.0f) - ); - addGameObject(std::move(test_object2)); - - spdlog::trace("test_object 创建并添加到 GameScene 中。"); -} - void GameScene::testCamera() { auto& camera = context_.getCamera(); auto& input_manager = context_.getInputManager(); @@ -100,11 +76,11 @@ void GameScene::testCamera() { if (input_manager.isActionDown("move_right")) camera.move(glm::vec2(1, 0)); } -void GameScene::TestObject() +void GameScene::TestPlayer() { - if (!test_object_) return; + if (!player_) return; auto& input_manager = context_.getInputManager(); - auto* pc = test_object_->getComponent(); + auto* pc = player_->getComponent(); if (!pc) return; if (input_manager.isActionDown("move_left")) { pc->velocity_.x = -100.0f; diff --git a/src/game/scene/game_scene.h b/src/game/scene/game_scene.h index 8d02ab9..411d51d 100644 --- a/src/game/scene/game_scene.h +++ b/src/game/scene/game_scene.h @@ -13,7 +13,7 @@ namespace game::scene { * @brief 主要的游戏场景,包含玩家、敌人、关卡元素等。 */ class GameScene final: public engine::scene::Scene { - engine::object::GameObject* test_object_ = nullptr; ///< @brief 保存测试对象的指针,方便访问 + engine::object::GameObject* player_ = nullptr; ///< @brief 保存玩家对象的指针,方便访问 public: GameScene(std::string name, engine::core::Context& context, engine::scene::SceneManager& scene_manager); @@ -27,9 +27,8 @@ public: private: // 测试函数 - void createTestObject(); void testCamera(); - void TestObject(); + void TestPlayer(); void TestCollisionPairs(); };