Test collision system for TestGame
This commit is contained in:
@@ -8,6 +8,8 @@ if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
|
||||
message(FATAL_ERROR "In-source builds are not allowed!")
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
set(CMAKE_BUILD_PARALLEL_LEVEL 8)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
@@ -73,6 +75,9 @@ target_include_directories(Editor2D PUBLIC
|
||||
target_link_libraries(Editor2D PUBLIC Event J3ML jlog ReWindow JGL JUI mcolor jjx)
|
||||
|
||||
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/assets/"
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/assets)
|
||||
|
||||
add_executable(Editor2DApp main.cpp app.rc)
|
||||
target_link_libraries(Editor2DApp PUBLIC Editor2D)
|
||||
|
||||
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
BIN
assets/player.png
Normal file
BIN
assets/player.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
@@ -188,8 +188,8 @@ public:
|
||||
loaded_tileset->cols = 80;
|
||||
loaded_tileset->rows = 60;
|
||||
loaded_tileset->file_path = "tileset.json";
|
||||
loaded_tileset->texture_path = "../megacommando.png";
|
||||
loaded_tilesheet = new JGL::Texture("../megacommando.png");
|
||||
loaded_tileset->texture_path = "assets/megacommando.png";
|
||||
loaded_tilesheet = new JGL::Texture("assets/megacommando.png");
|
||||
|
||||
write_file_contents("tileset.json", json::deparse(loaded_tileset->Serialize()));
|
||||
|
||||
|
@@ -89,7 +89,7 @@ public:
|
||||
tileset.file_path = "tileset.json";
|
||||
tileset.cols = 80;
|
||||
tileset.rows = 64;
|
||||
tileset.texture_path = "../megacommando.png";
|
||||
tileset.texture_path = "assets/megacommando.png";
|
||||
tileset.tex_width = 1024;
|
||||
tileset.tex_height = 1280;
|
||||
return tileset;
|
||||
|
77
include/SimpleAABBSolver.hpp
Normal file
77
include/SimpleAABBSolver.hpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
#include "J3ML/Geometry/AABB2D.hpp"
|
||||
#include "J3ML/LinearAlgebra/Vector2.hpp"
|
||||
|
||||
namespace Solver {
|
||||
|
||||
/// Enumeration flags indicating cardinal direction to 8 degrees of freedom.
|
||||
enum Face : unsigned int
|
||||
{
|
||||
NORTH = 1,
|
||||
SOUTH = 2,
|
||||
EAST = 4,
|
||||
WEST = 8,
|
||||
TOP = NORTH,
|
||||
BOTTOM = SOUTH,
|
||||
LEFT = EAST,
|
||||
RIGHT = WEST,
|
||||
NORTHEAST = NORTH | EAST,
|
||||
NORTHWEST = NORTH | WEST,
|
||||
SOUTHEAST = SOUTH | EAST,
|
||||
SOUTHWEST = SOUTH | WEST
|
||||
};
|
||||
|
||||
/// A structure containing the results of a raycast test.
|
||||
struct RaycastHit
|
||||
{
|
||||
bool Hit;
|
||||
Vector2 Intersection;
|
||||
Vector2 SurfaceNormal;
|
||||
};
|
||||
|
||||
|
||||
struct LineSegment2D
|
||||
{
|
||||
Vector2 A;
|
||||
Vector2 B;
|
||||
Vector2 Midpoint() const
|
||||
{
|
||||
return Vector2(A.x + B.x, A.y + B.y) / 2.f;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct LineSegTestResult
|
||||
{
|
||||
bool intersects;
|
||||
Vector2 intersection;
|
||||
Face face;
|
||||
};
|
||||
|
||||
static LineSegTestResult LineSegment2DvsAABB2D(Vector2 linesegA, Vector2 linesegB, Vector2 rectCenter, Vector2 rectSize);
|
||||
|
||||
|
||||
static LineSegTestResult LineSegment2DvsAABB2D(LineSegment2D seg, AABB2D rect);
|
||||
static LineSegTestResult LineSegment2Dvs(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2);
|
||||
|
||||
static LineSegTestResult LineSegment2Dvs(LineSegment2D s1, LineSegment2D s2);
|
||||
|
||||
bool AABB2Dvs(const Vector2& posA, const Vector2& sizeA, const Vector2& posB, const Vector2& sizeB);
|
||||
|
||||
bool AABB2Dvs(const AABB2D& a, const AABB2D& b);
|
||||
|
||||
bool AABB2DvsPoint(const Vector2& pos, const Vector2& size, const Vector2& point);
|
||||
|
||||
bool AABB2DvsPoint(const AABB2D& box, const Vector2& point);
|
||||
|
||||
Vector2 SolveAABBvsPoint(const Vector2& pos, const Vector2& size, const Vector2& point);
|
||||
|
||||
Vector2 SolveAABB(const Vector2& posA, const Vector2& sizeA, const Vector2& posB, const Vector2& sizeB);
|
||||
|
||||
Vector2 GetNormalForAABB(const Vector2& separation, const Vector2& velocity);
|
||||
|
||||
Vector2 SolveAABB(const AABB2D& a, const AABB2D& b);
|
||||
|
||||
|
||||
}
|
@@ -18,7 +18,7 @@ void CreateSampleLevel()
|
||||
void CreateSampleTileset()
|
||||
{
|
||||
// TODO: Read texture size via std_image.
|
||||
Tileset tileset("tileset", "../megacommando.png", 1024, 1280, 16, 16, 0);
|
||||
Tileset tileset("tileset", "assets/megacommando.png", 1024, 1280, 16, 16, 0);
|
||||
|
||||
write_file_contents("tileset.json", json::deparse(tileset.Serialize()));
|
||||
}
|
||||
|
@@ -365,7 +365,7 @@ void EditorApp::CreateWidgets()
|
||||
});
|
||||
|
||||
file->AddButton("Open");
|
||||
file->AddButton("Save", [this]{SaveTestFile();});
|
||||
file->AddButton("Save", [this]{SaveCurrentLevel();});
|
||||
file->AddButton("Save As");
|
||||
file->AddSeparator(2_px);
|
||||
file->AddButton("About", [this]{app_info_dialog->Toggle(); });
|
||||
|
222
src/SimpleAABBSolver.cpp
Normal file
222
src/SimpleAABBSolver.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
|
||||
#include <SimpleAABBSolver.hpp>
|
||||
|
||||
namespace Solver {
|
||||
|
||||
|
||||
|
||||
LineSegTestResult LineSegment2DvsAABB2D(Vector2 linesegA,
|
||||
Vector2 linesegB, Vector2 rectCenter, Vector2 rectSize)
|
||||
{
|
||||
|
||||
LineSegment2D seg = LineSegment2D(linesegA, linesegB);
|
||||
AABB2D aabb = AABB2D(rectCenter-rectSize, rectCenter+rectSize);
|
||||
|
||||
return LineSegment2DvsAABB2D(seg, aabb);
|
||||
|
||||
}
|
||||
|
||||
LineSegTestResult LineSegment2DvsAABB2D(LineSegment2D seg, AABB2D rect)
|
||||
{
|
||||
Vector2 intersection = Vector2::Zero;
|
||||
Face collidingface = TOP;
|
||||
|
||||
Vector2 rect_top_left = rect.minPoint;
|
||||
Vector2 rect_bottom_right = rect.maxPoint;
|
||||
Vector2 rect_bottom_left = {rect.minPoint.x, rect.maxPoint.y};
|
||||
Vector2 rect_top_right = {rect.maxPoint.x, rect.minPoint.y};
|
||||
|
||||
LineSegment2D rect_top = {rect_top_left, rect_top_right};
|
||||
LineSegment2D rect_left = {rect_top_left, rect_bottom_left};
|
||||
LineSegment2D rect_bottom = {rect_bottom_left, rect_bottom_right};
|
||||
LineSegment2D rect_right = {rect_top_right, rect_bottom_right};
|
||||
|
||||
auto top_hits = LineSegment2Dvs(seg, rect_top);
|
||||
auto bottom_hits = LineSegment2Dvs(seg, rect_bottom);
|
||||
auto left_hits = LineSegment2Dvs(seg, rect_top);
|
||||
auto right_hits = LineSegment2Dvs(seg, rect_right);
|
||||
|
||||
if (top_hits.intersects || bottom_hits.intersects || right_hits.intersects || left_hits.intersects)
|
||||
{
|
||||
intersection = seg.B;
|
||||
|
||||
if (top_hits.intersects && seg.A.Distance(top_hits.intersection) < seg.A.Distance(intersection))
|
||||
return {true, top_hits.intersection, TOP};
|
||||
|
||||
if (bottom_hits.intersects && seg.A.Distance(bottom_hits.intersection) < seg.A.Distance(intersection))
|
||||
return {true, bottom_hits.intersection, BOTTOM};
|
||||
|
||||
if (left_hits.intersects && seg.A.Distance(left_hits.intersection) < seg.A.Distance(intersection))
|
||||
return {true, left_hits.intersection, LEFT};
|
||||
|
||||
if (right_hits.intersects && seg.A.Distance(right_hits.intersection) < seg.A.Distance(intersection))
|
||||
return {true, left_hits.intersection, RIGHT};
|
||||
|
||||
}
|
||||
return {false};
|
||||
}
|
||||
|
||||
LineSegTestResult LineSegment2Dvs(Vector2 a1, Vector2 a2, Vector2 b1,Vector2 b2)
|
||||
{
|
||||
|
||||
Vector2 b = a2 - a1;
|
||||
Vector2 d = b2 - b1;
|
||||
float bDotDPerp = b.x * d.y - b.y * d.x;
|
||||
|
||||
// If b dot d == 0, it means that the lines are parallel, and have infinite intersection points.
|
||||
if (bDotDPerp == 0.f)
|
||||
return {false};
|
||||
|
||||
Vector2 c = b1 - a1;
|
||||
|
||||
float t = (c.x * d.y - c.y * d.x) / bDotDPerp;
|
||||
if (t < 0.f || t > 1.f)
|
||||
return {false};
|
||||
|
||||
float u = (c.x * b.y - c.y * b.x) / bDotDPerp;
|
||||
if (u < 0.f || u > 1.f)
|
||||
return {false};
|
||||
|
||||
return {true, a1+t*b};
|
||||
}
|
||||
|
||||
LineSegTestResult LineSegment2Dvs(LineSegment2D s1, LineSegment2D s2)
|
||||
{
|
||||
return LineSegment2Dvs(s1.A, s1.B, s2.A, s2.B);
|
||||
}
|
||||
|
||||
bool AABB2Dvs(const Vector2& posA, const Vector2& sizeA, const Vector2& posB,
|
||||
const Vector2& sizeB)
|
||||
{
|
||||
float absDistanceX = J3ML::Math::Abs(posA.x - posB.x);
|
||||
float absDistanceY = J3ML::Math::Abs(posA.y - posB.y);
|
||||
|
||||
float sumWidth = sizeA.x + sizeB.x;
|
||||
float sumHeight = sizeA.y + sizeB.y;
|
||||
|
||||
if (absDistanceY >= sumHeight || absDistanceX >= sumWidth)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AABB2Dvs(const AABB2D& a, const AABB2D& b)
|
||||
{
|
||||
Vector2 posA = a.minPoint + Vector2(a.Width()/2.f, a.Height()/2.f);
|
||||
|
||||
Vector2 posB = b.minPoint + Vector2(b.Width()/2.f, b.Height()/2.f);
|
||||
|
||||
Vector2 sizeA = Vector2{a.Width(), a.Height()} / 2.f;
|
||||
Vector2 sizeB = Vector2{b.Width(), b.Height()} / 2.f;
|
||||
return AABB2Dvs(posA, sizeA, posB, sizeB);
|
||||
}
|
||||
|
||||
Vector2 SolveAABB(const Vector2& posA, const Vector2& sizeA, const Vector2& posB,
|
||||
const Vector2& sizeB)
|
||||
{
|
||||
float distanceX = posA.x - posB.x;
|
||||
float distanceY = posA.y - posB.y;
|
||||
|
||||
float absDistanceX = J3ML::Math::Abs(distanceX);
|
||||
float absDistanceY = J3ML::Math::Abs(distanceY);
|
||||
|
||||
float sumWidth = sizeA.x + sizeB.x;
|
||||
float sumHeight = sizeA.y + sizeB.y;
|
||||
|
||||
float sx = sumWidth - absDistanceX;
|
||||
float sy = sumHeight - absDistanceY;
|
||||
|
||||
if (sx > sy)
|
||||
{
|
||||
if (sy > 0)
|
||||
sx = 0;
|
||||
} else {
|
||||
if (sx > 0)
|
||||
sy = 0;
|
||||
}
|
||||
|
||||
if (distanceX < 0)
|
||||
{
|
||||
sx = -sx;
|
||||
}
|
||||
if (distanceY < 0)
|
||||
sy = -sy;
|
||||
|
||||
return {sx, sy};
|
||||
}
|
||||
|
||||
Vector2 GetNormalForAABB(const Vector2& separation, const Vector2& velocity)
|
||||
{
|
||||
float d = J3ML::Math::Sqrt(separation.x * separation.x + separation.y * separation.y);
|
||||
|
||||
float nx = separation.x / d;
|
||||
float ny = separation.y / d;
|
||||
|
||||
float ps = velocity.x * nx + velocity.y * ny;
|
||||
|
||||
if (ps <= 0)
|
||||
return {nx, ny};
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
Vector2 SolveAABB(const AABB2D& a, const AABB2D& b)
|
||||
{
|
||||
Vector2 posA = a.minPoint + Vector2(a.Width()/2.f, a.Height()/2.f);
|
||||
|
||||
Vector2 posB = a.minPoint + Vector2(b.Width()/2.f, b.Height()/2.f);
|
||||
|
||||
Vector2 sizeA = Vector2{a.Width(), a.Height()} / 2.f;
|
||||
Vector2 sizeB = Vector2{b.Width(), b.Height()} / 2.f;
|
||||
return SolveAABB(posA, sizeA, posB, sizeB);
|
||||
}
|
||||
|
||||
bool AABB2DvsPoint(const Vector2 &pos, const Vector2 &size, const Vector2 &point) {
|
||||
float absDistanceX = J3ML::Math::Abs(pos.x - point.x);
|
||||
float absDistanceY = J3ML::Math::Abs(pos.y - point.y);
|
||||
|
||||
float sumWidth = size.x;
|
||||
float sumHeight = size.y;
|
||||
|
||||
if (absDistanceY >= sumHeight || absDistanceX >= sumWidth)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AABB2DvsPoint(const AABB2D &box, const Vector2 &point) {
|
||||
Vector2 pos = box.minPoint + Vector2(box.Width()/2.f, box.Height()/2.f);
|
||||
Vector2 size = Vector2{box.Width(), box.Height()} / 2.f;
|
||||
return AABB2DvsPoint(pos, size, point);
|
||||
}
|
||||
|
||||
Vector2 SolveAABBvsPoint(const Vector2 &pos, const Vector2 &size, const Vector2 &point) {
|
||||
float distanceX = pos.x - point.x;
|
||||
float distanceY = pos.y - point.y;
|
||||
|
||||
float absDistanceX = J3ML::Math::Abs(distanceX);
|
||||
float absDistanceY = J3ML::Math::Abs(distanceY);
|
||||
|
||||
float sumWidth = size.x;
|
||||
float sumHeight = size.y;
|
||||
|
||||
float sx = sumWidth - absDistanceX;
|
||||
float sy = sumHeight - absDistanceY;
|
||||
|
||||
if (sx > sy)
|
||||
{
|
||||
if (sy > 0)
|
||||
sx = 0;
|
||||
} else {
|
||||
if (sx > 0)
|
||||
sy = 0;
|
||||
}
|
||||
|
||||
if (distanceX < 0)
|
||||
{
|
||||
sx = -sx;
|
||||
}
|
||||
if (distanceY < 0)
|
||||
sy = -sy;
|
||||
|
||||
return {sx, sy};
|
||||
}
|
||||
|
||||
}
|
163
testgame.cpp
163
testgame.cpp
@@ -6,15 +6,64 @@
|
||||
#include <JUI/Widgets/FpsGraph.hpp>
|
||||
#include <JUI/Widgets/Scene.hpp>
|
||||
#include <JUI/Widgets/Window.hpp>
|
||||
#include <SimpleAABBSolver.hpp>
|
||||
|
||||
|
||||
class GameEntity
|
||||
{
|
||||
public:
|
||||
|
||||
GameEntity(const Vector2& spawn_pos) {
|
||||
pos = spawn_pos;
|
||||
next_pos = spawn_pos;
|
||||
}
|
||||
|
||||
virtual void Update(float elapsed) = 0;
|
||||
|
||||
virtual void Draw() = 0;
|
||||
|
||||
|
||||
Vector2 pos;
|
||||
Vector2 next_pos;
|
||||
Vector2 bbox;
|
||||
Vector2 velocity;
|
||||
bool noclip = false;
|
||||
};
|
||||
|
||||
class Player : public GameEntity
|
||||
{
|
||||
public:
|
||||
Player(const Vector2& spawn_pos) : GameEntity(spawn_pos) {
|
||||
bbox = {16, 24};
|
||||
}
|
||||
|
||||
~Player() {
|
||||
|
||||
}
|
||||
|
||||
void Update(float elapsed) override {
|
||||
// Update current position to our next_position, which was computed on the last frame.
|
||||
// This means we can collision solve our next_pos in between calls to entity->Update();
|
||||
pos = next_pos;
|
||||
|
||||
float constant_move_spd = 40;
|
||||
|
||||
if (InputService::IsKeyDown(Keys::A))
|
||||
next_pos.x -= constant_move_spd * elapsed;
|
||||
|
||||
if (InputService::IsKeyDown(Keys::D))
|
||||
next_pos.x += constant_move_spd * elapsed;
|
||||
|
||||
if (InputService::IsKeyDown(Keys::W))
|
||||
next_pos.y -= constant_move_spd * elapsed;
|
||||
|
||||
if (InputService::IsKeyDown(Keys::S))
|
||||
next_pos.y += constant_move_spd * elapsed;
|
||||
}
|
||||
|
||||
void Draw() override {
|
||||
J2D::FillRect(Colors::Blue, pos, bbox);
|
||||
}
|
||||
};
|
||||
|
||||
class TestGameAppWindow : public ReWindow::OpenGLWindow
|
||||
@@ -29,6 +78,8 @@ public:
|
||||
|
||||
bool data_ready = false;
|
||||
|
||||
std::vector<GameEntity*> entities;
|
||||
|
||||
TestGameAppWindow() : ReWindow::OpenGLWindow("TestGameAppWindow", 1024, 768, 2, 1)
|
||||
{
|
||||
|
||||
@@ -43,8 +94,11 @@ public:
|
||||
using namespace JUI::UDimLiterals;
|
||||
scene = new JUI::Scene();
|
||||
|
||||
|
||||
auto* fps_graph_window = new JUI::Window(scene);
|
||||
fps_graph_window->Size({500_px, 50_px});
|
||||
fps_graph_window->AnchorPoint({1, 0.f});
|
||||
fps_graph_window->Position({100_percent, 0_percent});
|
||||
auto* fps_graph = new JUI::FpsGraph(fps_graph_window->Content());
|
||||
fps_graph->Size({500_px, 50_px});
|
||||
|
||||
@@ -75,6 +129,8 @@ public:
|
||||
return false;
|
||||
JGL::Update(vec_size);
|
||||
|
||||
|
||||
|
||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
@@ -84,11 +140,114 @@ public:
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // NOTE: This MUST be called for text rendering to work properly!!!
|
||||
|
||||
Load();
|
||||
|
||||
scene->SetViewportSize(Vector2(vec_size));
|
||||
|
||||
|
||||
|
||||
auto* plr = new Player({50, 50});
|
||||
entities.emplace_back(plr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CollisionSolve(float elapsed) {
|
||||
for (auto* entity : entities) {
|
||||
int coll_tests = 0;
|
||||
int coll_hits = 0;
|
||||
|
||||
for (auto* layer : loaded_level->layers) {
|
||||
// TODO: if layer collides.
|
||||
|
||||
int cell_width = layer->cell_width;
|
||||
int cell_height = layer->cell_height;
|
||||
|
||||
int ent_tile_tl_x = Math::Floor(entity->pos.x / cell_width);
|
||||
int ent_tile_tl_y = Math::Floor(entity->pos.y / cell_height);
|
||||
|
||||
int occupies_h_tiles = Math::Floor(entity->bbox.x / cell_width);
|
||||
int occupies_v_tiles = Math::Floor(entity->bbox.y / cell_height);
|
||||
|
||||
Vector2 cell_bbox = Vector2(layer->cell_width, layer->cell_height);
|
||||
|
||||
|
||||
for (int x = -1; x <= occupies_h_tiles; x++) {
|
||||
for (int y = -1; y <= occupies_v_tiles; y++) {
|
||||
|
||||
|
||||
int cell_x = ent_tile_tl_x + x;
|
||||
int cell_y = ent_tile_tl_y + y;
|
||||
|
||||
Vector2 cell_topleft = Vector2(cell_x, cell_y) * cell_bbox;
|
||||
|
||||
Vector2i cell_i = Vector2i(cell_x, cell_y);
|
||||
|
||||
if (cell_x < 0) continue; // Out of bounds to the left.
|
||||
if (cell_y < 0) continue; // Out of bounds to the top.
|
||||
|
||||
if (cell_x >= layer->rows) continue; // Out of bounds to the right.
|
||||
if (cell_y >= layer->cols) continue;// Out of bounds to the bottom.
|
||||
|
||||
auto cell = loaded_level->layers[0]->cells[cell_x][cell_y];
|
||||
|
||||
if (cell < 0) continue; // Empty cell.
|
||||
|
||||
coll_tests++;
|
||||
|
||||
auto cell_aabb = AABB2D(Vector2(cell_topleft), cell_bbox);
|
||||
|
||||
Vector2 ent_halfbox = (entity->bbox / 2.f);
|
||||
Vector2 ent_centroid = entity->next_pos + ent_halfbox;
|
||||
|
||||
Vector2 tile_halfbox = cell_bbox / 2.f;
|
||||
Vector2 tile_centroid = cell_topleft + tile_halfbox;
|
||||
|
||||
if (Solver::AABB2Dvs(ent_centroid, ent_halfbox, tile_centroid, tile_halfbox)) {
|
||||
coll_hits++;
|
||||
|
||||
Vector2 separation = Solver::SolveAABB(ent_centroid, ent_halfbox, tile_centroid, tile_halfbox);
|
||||
|
||||
Vector2 normal = Solver::GetNormalForAABB(separation, entity->velocity);
|
||||
|
||||
|
||||
if (normal.x == 0 && normal.y == 0) continue; // Why though?
|
||||
|
||||
// Touched top.
|
||||
if (normal.y == -1) { }
|
||||
|
||||
// Touched bottom.
|
||||
if (normal.y == 1) { }
|
||||
|
||||
// Touched left, I think.
|
||||
if (normal.x == -1) { }
|
||||
|
||||
// Touched right, I think.
|
||||
if (normal.x == -1) { }
|
||||
|
||||
entity->next_pos += separation;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Update(float elapsed)
|
||||
{
|
||||
scene->Update(elapsed);
|
||||
|
||||
Vector2 window_dimensions(GetWidth(), GetHeight());
|
||||
|
||||
scene->SetViewportSize(window_dimensions);
|
||||
JGL::Update(Vector2i(GetWidth(), GetHeight()));
|
||||
|
||||
CollisionSolve(elapsed);
|
||||
|
||||
for (auto* entity : entities) {
|
||||
entity->Update(elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawLayer(const Layer* layer) const
|
||||
@@ -130,6 +289,10 @@ public:
|
||||
DrawLevel(loaded_level);
|
||||
|
||||
|
||||
for (auto* entity : entities) {
|
||||
entity->Draw();
|
||||
}
|
||||
|
||||
J2D::End();
|
||||
scene->Draw();
|
||||
}
|
||||
|
Reference in New Issue
Block a user