Refactoring testgame code.
This commit is contained in:
@@ -35,48 +35,10 @@ class Entity
|
|||||||
json::value metadata;
|
json::value metadata;
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
Entity();
|
Entity();
|
||||||
Entity(const json::value& json)
|
Entity(const json::value& json);
|
||||||
{
|
|
||||||
Deserialize(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
json::value Serialize() const
|
json::value Serialize() const;
|
||||||
{
|
|
||||||
json::object data;
|
|
||||||
data["name"] = this->name;
|
|
||||||
data["type"] = this->type;
|
|
||||||
data["x"] = this->x;
|
|
||||||
data["y"] = this->y;
|
|
||||||
data["width"] = this->width;
|
|
||||||
data["height"] = this->height;
|
|
||||||
data["rotation"] = this->rotation;
|
|
||||||
data["flip-h"] = this->flip_h;
|
|
||||||
data["flip-v"] = this->flip_v;
|
|
||||||
data["z-index"] = (float)this->z_index;
|
|
||||||
data["color"] = JsonConversions::deparse_color_to_hex(overlay_color);
|
|
||||||
data["metadata"] = this->metadata;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Deserialize(const json::value& json)
|
|
||||||
{
|
|
||||||
name = json["name"].as_string();
|
|
||||||
type = json["type"].as_string();
|
|
||||||
x = json["x"].number.value();
|
|
||||||
y = json["y"].number.value();
|
|
||||||
width = json["width"].number.value_or(0);
|
|
||||||
height = json["height"].number.value_or(0);
|
|
||||||
rotation = json["rotation"].number.value_or(0);
|
|
||||||
//flip_h = json["flip-h"].boolean.value();
|
|
||||||
//flip_v = json["flip-v"].boolean.value();
|
|
||||||
z_index = json["z-index"].number.value();
|
|
||||||
// TODO: sscanf is bad!!!
|
|
||||||
//overlay_color = JsonConversions::parse_color(json["color"]);
|
|
||||||
metadata = json["metadata"];
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
void Deserialize(const json::value& json);
|
||||||
};
|
};
|
7
include/TestGame/Crate.hpp
Normal file
7
include/TestGame/Crate.hpp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace TestGame {
|
||||||
|
class Crate : public GameEntity {
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
26
include/TestGame/GameEntity.hpp
Normal file
26
include/TestGame/GameEntity.hpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Data/Entity.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace TestGame {
|
||||||
|
class GameEntity : public Entity
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
bool on_ground = true;
|
||||||
|
};
|
||||||
|
}
|
3
include/TestGame/PhysicsTweaker.hpp
Normal file
3
include/TestGame/PhysicsTweaker.hpp
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
54
include/TestGame/Player.hpp
Normal file
54
include/TestGame/Player.hpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "GameEntity.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace TestGame {
|
||||||
|
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;
|
||||||
|
|
||||||
|
velocity.x *= 1 - (elapsed * air_resistance);
|
||||||
|
//velocity.y *= 1 - (elapsed * air_resistance);
|
||||||
|
|
||||||
|
//if (on_ground)
|
||||||
|
//velocity.x *= friction;
|
||||||
|
|
||||||
|
velocity.y += (elapsed*gravity*mass);
|
||||||
|
|
||||||
|
next_pos += velocity * elapsed;
|
||||||
|
|
||||||
|
if (Math::Abs(velocity.y) > 2)
|
||||||
|
on_ground = false;
|
||||||
|
|
||||||
|
|
||||||
|
if (InputService::IsKeyDown(Keys::A))
|
||||||
|
velocity.x -= acceleration * elapsed;
|
||||||
|
|
||||||
|
if (InputService::IsKeyDown(Keys::D))
|
||||||
|
velocity.x += acceleration * elapsed;
|
||||||
|
|
||||||
|
if (InputService::IsKeyDown(Keys::W))
|
||||||
|
velocity.y -= acceleration * elapsed;
|
||||||
|
|
||||||
|
if (InputService::IsKeyDown(Keys::S))
|
||||||
|
velocity.y += acceleration * elapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw() override {
|
||||||
|
J2D::FillRect(Colors::Blue, pos, bbox);
|
||||||
|
J2D::DrawPartialSprite(player_tex, pos, {0,0}, bbox);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
162
include/TestGame/TestGameApp.hpp
Normal file
162
include/TestGame/TestGameApp.hpp
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
#include "GameEntity.hpp"
|
||||||
|
#include "Player.hpp"
|
||||||
|
#include "App/EditorCamera.hpp"
|
||||||
|
#include "Data/Level.hpp"
|
||||||
|
#include "JUI/Widgets/Scene.hpp"
|
||||||
|
#include "ReWindow/types/Window.h"
|
||||||
|
|
||||||
|
namespace TestGame
|
||||||
|
{
|
||||||
|
constexpr int GL_MAJOR = 2;
|
||||||
|
constexpr int GL_MINOR = 1;
|
||||||
|
|
||||||
|
constexpr int START_APP_WIDTH = 1024;
|
||||||
|
constexpr int START_APP_HEIGHT = 768;
|
||||||
|
static const std::string START_APP_TITLE = "Redacted Software 2D Level Editor - Map Testing Game";
|
||||||
|
|
||||||
|
class TestGameAppWindow : public ReWindow::OpenGLWindow
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EditorCamera camera;
|
||||||
|
Level* loaded_level = nullptr;
|
||||||
|
Tileset* loaded_tileset = nullptr;
|
||||||
|
JGL::Texture* loaded_tilesheet = nullptr;
|
||||||
|
|
||||||
|
JUI::Scene* scene = nullptr;
|
||||||
|
|
||||||
|
|
||||||
|
Player* player = nullptr;
|
||||||
|
|
||||||
|
bool data_ready = false;
|
||||||
|
|
||||||
|
std::vector<GameEntity*> entities;
|
||||||
|
|
||||||
|
TestGameAppWindow();
|
||||||
|
|
||||||
|
~TestGameAppWindow() override;
|
||||||
|
|
||||||
|
|
||||||
|
/// Initializes and styles all the JUI widgets used by the application.
|
||||||
|
/// This function is called from Load().
|
||||||
|
void CreateUI();
|
||||||
|
|
||||||
|
/// Load all resources.
|
||||||
|
/// This is called after the application window is initially opened, and the OpenGL context is created.
|
||||||
|
/// But is always called before the first pass of the gameloop (Update, Draw, Step, etc).
|
||||||
|
void Load();
|
||||||
|
|
||||||
|
bool Open() override;
|
||||||
|
|
||||||
|
/// Performs collision testing and response between entities and tiles.
|
||||||
|
void CollisionSolve(float elapsed);
|
||||||
|
|
||||||
|
void CameraUpdate(float elapsed);
|
||||||
|
|
||||||
|
void Update(float elapsed);
|
||||||
|
|
||||||
|
|
||||||
|
void DrawLayer(const Layer* layer) const;
|
||||||
|
|
||||||
|
|
||||||
|
void ApplyOriginTransformation() {
|
||||||
|
glTranslatef(GetWidth() / 2.f, GetHeight() / 2.f, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyCameraTransformation() {
|
||||||
|
ApplyOriginTransformation();
|
||||||
|
glRotatef(camera.rotation.current, 0, 0, 1);
|
||||||
|
glScalef(camera.scale.current, camera.scale.current, 1);
|
||||||
|
glTranslatef(-camera.translation.current.x, -camera.translation.current.y, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawLevel(const Level* level) const
|
||||||
|
{
|
||||||
|
for (const auto* layer : level->layers)
|
||||||
|
{
|
||||||
|
DrawLayer(layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawWorldSpace() {
|
||||||
|
|
||||||
|
if (!data_ready) return; // Don't try to draw the level if not loaded.
|
||||||
|
|
||||||
|
DrawLevel(loaded_level);
|
||||||
|
|
||||||
|
for (auto* entity : entities) {
|
||||||
|
entity->Draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Render() {
|
||||||
|
glClearColor(0, 0, 0, 1);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
J2D::Begin();
|
||||||
|
{
|
||||||
|
glPushMatrix();
|
||||||
|
{
|
||||||
|
ApplyCameraTransformation();
|
||||||
|
DrawWorldSpace();
|
||||||
|
}
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
J2D::End();
|
||||||
|
|
||||||
|
scene->Draw();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma region ReWindow Overrides
|
||||||
|
void OnRefresh(float elapsed) override
|
||||||
|
{
|
||||||
|
Update(elapsed);
|
||||||
|
Render();
|
||||||
|
SwapBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum JUI::MouseButton ToJUIEnum(const MouseButton& btn) {
|
||||||
|
if (btn == MouseButtons::Left) return JUI::MouseButton::Left;
|
||||||
|
if (btn == MouseButtons::Middle) return JUI::MouseButton::Middle;
|
||||||
|
if (btn == MouseButtons::Right) return JUI::MouseButton::Right;
|
||||||
|
|
||||||
|
// Default condition.
|
||||||
|
return JUI::MouseButton::Left;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnMouseButtonDown(const ReWindow::MouseButtonDownEvent &e) override {
|
||||||
|
auto btn = ToJUIEnum(e.Button);
|
||||||
|
|
||||||
|
if (scene->ObserveMouseInput(btn, true)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent &e) override {
|
||||||
|
auto btn = ToJUIEnum(e.Button);
|
||||||
|
if (scene->ObserveMouseInput(btn, false)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnMouseMove(const ReWindow::MouseMoveEvent &e) override {
|
||||||
|
Vector2 mposv2(e.Position.x, e.Position.y);
|
||||||
|
if (scene->ObserveMouseMovement(mposv2)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnKeyDown(const ReWindow::KeyDownEvent &e) override {
|
||||||
|
if (scene->ObserveKeyInput(e.key, true)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnKeyUp(const ReWindow::KeyUpEvent &e) override {
|
||||||
|
if (scene->ObserveKeyInput(e.key, false)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnFocusGain(const ReWindow::RWindowEvent &e) override {
|
||||||
|
focused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnFocusLost(const ReWindow::RWindowEvent &e) override {
|
||||||
|
focused = false;
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@@ -1 +1,40 @@
|
|||||||
#include <Data/Entity.hpp>
|
#include <Data/Entity.hpp>
|
||||||
|
|
||||||
|
Entity::Entity(const json::value &json) {
|
||||||
|
Deserialize(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
json::value Entity::Serialize() const {
|
||||||
|
json::object data;
|
||||||
|
data["name"] = this->name;
|
||||||
|
data["type"] = this->type;
|
||||||
|
data["x"] = this->x;
|
||||||
|
data["y"] = this->y;
|
||||||
|
data["width"] = this->width;
|
||||||
|
data["height"] = this->height;
|
||||||
|
data["rotation"] = this->rotation;
|
||||||
|
data["flip-h"] = this->flip_h;
|
||||||
|
data["flip-v"] = this->flip_v;
|
||||||
|
data["z-index"] = (float)this->z_index;
|
||||||
|
data["color"] = JsonConversions::deparse_color_to_hex(overlay_color);
|
||||||
|
data["metadata"] = this->metadata;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entity::Deserialize(const json::value &json) {
|
||||||
|
name = json["name"].as_string();
|
||||||
|
type = json["type"].as_string();
|
||||||
|
x = json["x"].number.value();
|
||||||
|
y = json["y"].number.value();
|
||||||
|
width = json["width"].number.value_or(0);
|
||||||
|
height = json["height"].number.value_or(0);
|
||||||
|
rotation = json["rotation"].number.value_or(0);
|
||||||
|
//flip_h = json["flip-h"].boolean.value();
|
||||||
|
//flip_v = json["flip-v"].boolean.value();
|
||||||
|
z_index = json["z-index"].number.value();
|
||||||
|
// TODO: sscanf is bad!!!
|
||||||
|
//overlay_color = JsonConversions::parse_color(json["color"]);
|
||||||
|
metadata = json["metadata"];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
282
src/TestGame/TestGameApp.cpp
Normal file
282
src/TestGame/TestGameApp.cpp
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
#include <TestGame/TestGameApp.hpp>
|
||||||
|
|
||||||
|
#include "JUI/Widgets/FpsGraph.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
TestGame::TestGameAppWindow::TestGameAppWindow():
|
||||||
|
ReWindow::OpenGLWindow(
|
||||||
|
START_APP_TITLE,
|
||||||
|
START_APP_WIDTH, START_APP_HEIGHT,
|
||||||
|
GL_MAJOR, GL_MINOR),
|
||||||
|
camera() {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestGame::TestGameAppWindow::~TestGameAppWindow() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestGame::TestGameAppWindow::CreateUI() {
|
||||||
|
using namespace JUI::UDimLiterals;
|
||||||
|
scene = new JUI::Scene();
|
||||||
|
|
||||||
|
auto* fps_graph = new JUI::FpsGraph(scene);
|
||||||
|
// TODO: Revise to use PseudoDockedElementAtBottomOfViewport coming in the next JUI release.
|
||||||
|
fps_graph->Size({100_percent, 50_px});
|
||||||
|
fps_graph->AnchorPoint({1, 1});
|
||||||
|
fps_graph->Position({100_percent, 100_percent});
|
||||||
|
fps_graph->BGColor(Colors::Transparent);
|
||||||
|
fps_graph->BorderColor(Colors::Transparent);
|
||||||
|
|
||||||
|
|
||||||
|
auto* phys_params_slider_window = new JUI::Window(scene);
|
||||||
|
phys_params_slider_window->Title("Physics Parameters");
|
||||||
|
|
||||||
|
auto* layout = new JUI::VerticalListLayout(phys_params_slider_window->Content());
|
||||||
|
|
||||||
|
auto* grav_slider = new LabeledSlider(layout);
|
||||||
|
grav_slider->Size({100_percent, 20_px});
|
||||||
|
grav_slider->Minimum(0); grav_slider->Maximum(1); grav_slider->Interval(1.f/10000.f);
|
||||||
|
grav_slider->CurrentValue(0.098f);
|
||||||
|
grav_slider->TextColor(Colors::Black);
|
||||||
|
grav_slider->Content(std::format("Gravity: {}", gravity));
|
||||||
|
|
||||||
|
grav_slider->ValueChanged += [&, grav_slider](float value) mutable {
|
||||||
|
gravity = value * 100.f;
|
||||||
|
grav_slider->Content(std::format("Gravity: {}", Math::Round(gravity, 3)));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto* air_resist_slider = new LabeledSlider(layout);
|
||||||
|
air_resist_slider->Size({100_percent, 20_px});
|
||||||
|
air_resist_slider->Minimum(0); grav_slider->Maximum(1); air_resist_slider->Interval(1.f/10000.f);
|
||||||
|
air_resist_slider->CurrentValue(0.12f);
|
||||||
|
air_resist_slider->Content(std::format("Air Resistance: {}", gravity));
|
||||||
|
air_resist_slider->TextColor(Colors::Black);
|
||||||
|
|
||||||
|
air_resist_slider->ValueChanged += [&, air_resist_slider](float value) mutable {
|
||||||
|
air_resistance = value * 100.f;
|
||||||
|
air_resist_slider->Content(std::format("Air Resistance: {}", Math::Floor(air_resistance)));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
auto* mass_slider = new LabeledSlider(layout);
|
||||||
|
mass_slider->Size({100_percent, 20_px});
|
||||||
|
mass_slider->Minimum(0); mass_slider->Maximum(1); mass_slider->Interval(1/10000.f);
|
||||||
|
mass_slider->CurrentValue(0.08f);
|
||||||
|
mass_slider->TextColor(Colors::Black);
|
||||||
|
mass_slider->Content(std::format("Player Mass: {}", gravity));
|
||||||
|
|
||||||
|
mass_slider->ValueChanged += [&, mass_slider](float value) mutable {
|
||||||
|
mass = value * 100.f;
|
||||||
|
mass_slider->Content(std::format("Player Mass: {}", mass));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto* accel_slider = new LabeledSlider(layout);
|
||||||
|
accel_slider->Size({100_percent, 20_px});
|
||||||
|
accel_slider->Minimum(0); accel_slider->Maximum(1); accel_slider->Interval(1/10000.f);
|
||||||
|
accel_slider->CurrentValue(0.12f);
|
||||||
|
accel_slider->TextColor(Colors::Black);
|
||||||
|
accel_slider->Content(std::format("Player Accel: {}", acceleration));
|
||||||
|
|
||||||
|
accel_slider->ValueChanged += [&, accel_slider](float value) mutable {
|
||||||
|
acceleration = value * 10000.f;
|
||||||
|
accel_slider->Content(std::format("Player Accel: {}", acceleration));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestGame::TestGameAppWindow::Load() {
|
||||||
|
CreateUI();
|
||||||
|
|
||||||
|
player_tex = new JGL::Texture("assets/player.png", FilteringMode::NEAREST);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: More sophisticated order-of-operations is required.
|
||||||
|
// 1. Initialize elements of widgets that are independent of the level itself / the level depends on.
|
||||||
|
// 2. Initialize the level.
|
||||||
|
// 3. Initialize widgets that are dependent on the level itself.
|
||||||
|
loaded_level = new Level(std::filesystem::path("level.json"));
|
||||||
|
|
||||||
|
loaded_tileset = new Tileset(std::filesystem::path(loaded_level->tileset_path));
|
||||||
|
|
||||||
|
loaded_tilesheet = new JGL::Texture(loaded_tileset->texture_path, FilteringMode::NEAREST);
|
||||||
|
|
||||||
|
for (auto layer : loaded_level->layers)
|
||||||
|
layer->Load();
|
||||||
|
|
||||||
|
data_ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestGame::TestGameAppWindow::Open() {
|
||||||
|
if (!OpenGLWindow::Open()) return false;
|
||||||
|
|
||||||
|
auto size = GetSize();
|
||||||
|
auto vec_size = Vector2i(size.x, size.y);
|
||||||
|
if (!JGL::Init(vec_size, 0.f, 1.f))
|
||||||
|
return false;
|
||||||
|
JGL::Update(vec_size);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||||
|
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glDepthFunc(GL_LESS);
|
||||||
|
glDepthMask(GL_TRUE);
|
||||||
|
// TODO: Delete when we update to the next release of JGL
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // NOTE: This MUST be called for text rendering to work properly!!!
|
||||||
|
|
||||||
|
Load();
|
||||||
|
|
||||||
|
scene->SetViewportSize(Vector2(vec_size));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
auto* plr = new Player({50, 50});
|
||||||
|
entities.emplace_back(plr);
|
||||||
|
player = plr;
|
||||||
|
}
|
||||||
|
|
||||||
|
camera.DefaultState();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestGame::TestGameAppWindow::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->next_pos.x) / cell_width;
|
||||||
|
int ent_tile_tl_y = Math::Floor(entity->next_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+1; x++) {
|
||||||
|
for (int y = -1; y <= occupies_v_tiles+1; y++) {
|
||||||
|
|
||||||
|
int cell_x = ent_tile_tl_x + x;
|
||||||
|
int cell_y = ent_tile_tl_y + y;
|
||||||
|
|
||||||
|
Vector2 cell_topleft = Vector2(cell_x*cell_width, cell_y*cell_height);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
entity->velocity.y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Touched bottom.
|
||||||
|
if (normal.y == 1) {
|
||||||
|
entity->velocity.y *= -0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Touched left, I think.
|
||||||
|
if (normal.x == -1) { }
|
||||||
|
|
||||||
|
// Touched right, I think.
|
||||||
|
if (normal.x == -1) { }
|
||||||
|
|
||||||
|
entity->next_pos += separation;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestGame::TestGameAppWindow::CameraUpdate(float elapsed) {
|
||||||
|
float move_rate = 120;
|
||||||
|
float zoom_rate = 0.2f;
|
||||||
|
|
||||||
|
if (IsKeyDown(Keys::Minus))
|
||||||
|
camera.scale.goal -= zoom_rate*elapsed;
|
||||||
|
if (IsKeyDown(Keys::Equals))
|
||||||
|
camera.scale.goal += zoom_rate*elapsed;
|
||||||
|
|
||||||
|
camera.Update(elapsed);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestGame::TestGameAppWindow::Update(float elapsed) {
|
||||||
|
CameraUpdate(elapsed);
|
||||||
|
camera.translation.goal = player->pos;
|
||||||
|
scene->Update(elapsed);
|
||||||
|
|
||||||
|
Vector2 window_dimensions(GetWidth(), GetHeight());
|
||||||
|
|
||||||
|
scene->SetViewportSize(window_dimensions);
|
||||||
|
camera.screenSize = window_dimensions;
|
||||||
|
|
||||||
|
JGL::Update(Vector2i(GetWidth(), GetHeight()));
|
||||||
|
|
||||||
|
CollisionSolve(elapsed);
|
||||||
|
|
||||||
|
for (auto* entity : entities) {
|
||||||
|
entity->Update(elapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!focused)
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(48));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestGame::TestGameAppWindow::DrawLayer(const Layer *layer) const {
|
||||||
|
for (int gx = 0; gx < layer->rows; gx++)
|
||||||
|
{
|
||||||
|
for (int gy = 0; gy < layer->cols; gy++)
|
||||||
|
{
|
||||||
|
auto quad_idx = layer->cells[gx][gy];
|
||||||
|
|
||||||
|
Vector2 pos(gx*layer->cell_width, gy*layer->cell_height);
|
||||||
|
|
||||||
|
if (quad_idx < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto quad = loaded_tileset->quads[quad_idx];
|
||||||
|
|
||||||
|
J2D::DrawPartialSprite(loaded_tilesheet, pos, Vector2(quad.x, quad.y), Vector2(quad.w, quad.h));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
449
testgame.cpp
449
testgame.cpp
@@ -54,456 +54,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
bool on_ground = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
velocity.x *= 1 - (elapsed * air_resistance);
|
|
||||||
//velocity.y *= 1 - (elapsed * air_resistance);
|
|
||||||
|
|
||||||
//if (on_ground)
|
|
||||||
//velocity.x *= friction;
|
|
||||||
|
|
||||||
velocity.y += (elapsed*gravity*mass);
|
|
||||||
|
|
||||||
next_pos += velocity * elapsed;
|
|
||||||
|
|
||||||
if (Math::Abs(velocity.y) > 2)
|
|
||||||
on_ground = false;
|
|
||||||
|
|
||||||
|
|
||||||
if (InputService::IsKeyDown(Keys::A))
|
|
||||||
velocity.x -= acceleration * elapsed;
|
|
||||||
|
|
||||||
if (InputService::IsKeyDown(Keys::D))
|
|
||||||
velocity.x += acceleration * elapsed;
|
|
||||||
|
|
||||||
if (InputService::IsKeyDown(Keys::W))
|
|
||||||
velocity.y -= acceleration * elapsed;
|
|
||||||
|
|
||||||
if (InputService::IsKeyDown(Keys::S))
|
|
||||||
velocity.y += acceleration * elapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Draw() override {
|
|
||||||
J2D::FillRect(Colors::Blue, pos, bbox);
|
|
||||||
J2D::DrawPartialSprite(player_tex, pos, {0,0}, bbox);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TestGameAppWindow : public ReWindow::OpenGLWindow
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
EditorCamera camera;
|
|
||||||
Level* loaded_level = nullptr;
|
|
||||||
Tileset* loaded_tileset = nullptr;
|
|
||||||
JGL::Texture* loaded_tilesheet = nullptr;
|
|
||||||
|
|
||||||
JUI::Scene* scene = nullptr;
|
|
||||||
|
|
||||||
|
|
||||||
Player* player = nullptr;
|
|
||||||
|
|
||||||
bool data_ready = false;
|
|
||||||
|
|
||||||
std::vector<GameEntity*> entities;
|
|
||||||
|
|
||||||
TestGameAppWindow() : ReWindow::OpenGLWindow("TestGameAppWindow", 1024, 768, 2, 1)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
~TestGameAppWindow() override
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Load()
|
|
||||||
{
|
|
||||||
using namespace JUI::UDimLiterals;
|
|
||||||
scene = new JUI::Scene();
|
|
||||||
|
|
||||||
player_tex = new JGL::Texture("assets/player.png", FilteringMode::NEAREST);
|
|
||||||
|
|
||||||
auto* fps_graph = new JUI::FpsGraph(scene);
|
|
||||||
// TODO: Revise to use PseudoDockedElementAtBottomOfViewport coming in the next JUI release.
|
|
||||||
fps_graph->Size({100_percent, 50_px});
|
|
||||||
fps_graph->AnchorPoint({1, 1});
|
|
||||||
fps_graph->Position({100_percent, 100_percent});
|
|
||||||
fps_graph->BGColor(Colors::Transparent);
|
|
||||||
fps_graph->BorderColor(Colors::Transparent);
|
|
||||||
|
|
||||||
|
|
||||||
auto* phys_params_slider_window = new JUI::Window(scene);
|
|
||||||
phys_params_slider_window->Title("Physics Parameters");
|
|
||||||
|
|
||||||
auto* layout = new JUI::VerticalListLayout(phys_params_slider_window->Content());
|
|
||||||
|
|
||||||
auto* grav_slider = new LabeledSlider(layout);
|
|
||||||
grav_slider->Size({100_percent, 20_px});
|
|
||||||
grav_slider->Minimum(0); grav_slider->Maximum(1); grav_slider->Interval(1.f/10000.f);
|
|
||||||
grav_slider->CurrentValue(0.098f);
|
|
||||||
grav_slider->TextColor(Colors::Black);
|
|
||||||
grav_slider->Content(std::format("Gravity: {}", gravity));
|
|
||||||
|
|
||||||
grav_slider->ValueChanged += [&, grav_slider](float value) mutable {
|
|
||||||
gravity = value * 100.f;
|
|
||||||
grav_slider->Content(std::format("Gravity: {}", Math::Round(gravity, 3)));
|
|
||||||
};
|
|
||||||
|
|
||||||
auto* air_resist_slider = new LabeledSlider(layout);
|
|
||||||
air_resist_slider->Size({100_percent, 20_px});
|
|
||||||
air_resist_slider->Minimum(0); grav_slider->Maximum(1); air_resist_slider->Interval(1.f/10000.f);
|
|
||||||
air_resist_slider->CurrentValue(0.12f);
|
|
||||||
air_resist_slider->Content(std::format("Air Resistance: {}", gravity));
|
|
||||||
air_resist_slider->TextColor(Colors::Black);
|
|
||||||
|
|
||||||
air_resist_slider->ValueChanged += [&, air_resist_slider](float value) mutable {
|
|
||||||
air_resistance = value * 100.f;
|
|
||||||
air_resist_slider->Content(std::format("Air Resistance: {}", Math::Floor(air_resistance)));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
auto* mass_slider = new LabeledSlider(layout);
|
|
||||||
mass_slider->Size({100_percent, 20_px});
|
|
||||||
mass_slider->Minimum(0); mass_slider->Maximum(1); mass_slider->Interval(1/10000.f);
|
|
||||||
mass_slider->CurrentValue(0.08f);
|
|
||||||
mass_slider->TextColor(Colors::Black);
|
|
||||||
mass_slider->Content(std::format("Player Mass: {}", gravity));
|
|
||||||
|
|
||||||
mass_slider->ValueChanged += [&, mass_slider](float value) mutable {
|
|
||||||
mass = value * 100.f;
|
|
||||||
mass_slider->Content(std::format("Player Mass: {}", mass));
|
|
||||||
};
|
|
||||||
|
|
||||||
auto* accel_slider = new LabeledSlider(layout);
|
|
||||||
accel_slider->Size({100_percent, 20_px});
|
|
||||||
accel_slider->Minimum(0); accel_slider->Maximum(1); accel_slider->Interval(1/10000.f);
|
|
||||||
accel_slider->CurrentValue(0.12f);
|
|
||||||
accel_slider->TextColor(Colors::Black);
|
|
||||||
accel_slider->Content(std::format("Player Accel: {}", acceleration));
|
|
||||||
|
|
||||||
accel_slider->ValueChanged += [&, accel_slider](float value) mutable {
|
|
||||||
acceleration = value * 10000.f;
|
|
||||||
accel_slider->Content(std::format("Player Accel: {}", acceleration));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: More sophisticated order-of-operations is required.
|
|
||||||
// 1. Initialize elements of widgets that are independent of the level itself / the level depends on.
|
|
||||||
// 2. Initialize the level.
|
|
||||||
// 3. Initialize widgets that are dependent on the level itself.
|
|
||||||
loaded_level = new Level(std::filesystem::path("level.json"));
|
|
||||||
|
|
||||||
loaded_tileset = new Tileset(std::filesystem::path(loaded_level->tileset_path));
|
|
||||||
|
|
||||||
loaded_tilesheet = new JGL::Texture(loaded_tileset->texture_path, FilteringMode::NEAREST);
|
|
||||||
|
|
||||||
for (auto layer : loaded_level->layers)
|
|
||||||
layer->Load();
|
|
||||||
|
|
||||||
data_ready = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Open() override
|
|
||||||
{
|
|
||||||
if (!OpenGLWindow::Open()) return false;
|
|
||||||
|
|
||||||
auto size = GetSize();
|
|
||||||
auto vec_size = Vector2i(size.x, size.y);
|
|
||||||
if (!JGL::Init(vec_size, 0.f, 1.f))
|
|
||||||
return false;
|
|
||||||
JGL::Update(vec_size);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
glDepthFunc(GL_LESS);
|
|
||||||
glDepthMask(GL_TRUE);
|
|
||||||
// TODO: Delete when we update to the next release of JGL
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // NOTE: This MUST be called for text rendering to work properly!!!
|
|
||||||
|
|
||||||
Load();
|
|
||||||
|
|
||||||
scene->SetViewportSize(Vector2(vec_size));
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < 100; i++) {
|
|
||||||
auto* plr = new Player({50, 50});
|
|
||||||
entities.emplace_back(plr);
|
|
||||||
player = plr;
|
|
||||||
}
|
|
||||||
|
|
||||||
camera.DefaultState();
|
|
||||||
|
|
||||||
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->next_pos.x) / cell_width;
|
|
||||||
int ent_tile_tl_y = Math::Floor(entity->next_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+1; x++) {
|
|
||||||
for (int y = -1; y <= occupies_v_tiles+1; y++) {
|
|
||||||
|
|
||||||
int cell_x = ent_tile_tl_x + x;
|
|
||||||
int cell_y = ent_tile_tl_y + y;
|
|
||||||
|
|
||||||
Vector2 cell_topleft = Vector2(cell_x*cell_width, cell_y*cell_height);
|
|
||||||
|
|
||||||
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) {
|
|
||||||
entity->velocity.y = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Touched bottom.
|
|
||||||
if (normal.y == 1) {
|
|
||||||
entity->velocity.y *= -0.5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Touched left, I think.
|
|
||||||
if (normal.x == -1) { }
|
|
||||||
|
|
||||||
// Touched right, I think.
|
|
||||||
if (normal.x == -1) { }
|
|
||||||
|
|
||||||
entity->next_pos += separation;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CameraUpdate(float elapsed)
|
|
||||||
{
|
|
||||||
float move_rate = 120;
|
|
||||||
float zoom_rate = 0.2f;
|
|
||||||
|
|
||||||
if (IsKeyDown(Keys::Minus))
|
|
||||||
camera.scale.goal -= zoom_rate*elapsed;
|
|
||||||
if (IsKeyDown(Keys::Equals))
|
|
||||||
camera.scale.goal += zoom_rate*elapsed;
|
|
||||||
|
|
||||||
camera.Update(elapsed);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Update(float elapsed)
|
|
||||||
{
|
|
||||||
CameraUpdate(elapsed);
|
|
||||||
camera.translation.goal = player->pos;
|
|
||||||
scene->Update(elapsed);
|
|
||||||
|
|
||||||
Vector2 window_dimensions(GetWidth(), GetHeight());
|
|
||||||
|
|
||||||
scene->SetViewportSize(window_dimensions);
|
|
||||||
camera.screenSize = window_dimensions;
|
|
||||||
|
|
||||||
JGL::Update(Vector2i(GetWidth(), GetHeight()));
|
|
||||||
|
|
||||||
CollisionSolve(elapsed);
|
|
||||||
|
|
||||||
for (auto* entity : entities) {
|
|
||||||
entity->Update(elapsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!focused)
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(48));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void OnFocusGain(const ReWindow::RWindowEvent &e) override {
|
|
||||||
focused = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnFocusLost(const ReWindow::RWindowEvent &e) override {
|
|
||||||
focused = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrawLayer(const Layer* layer) const
|
|
||||||
{
|
|
||||||
for (int gx = 0; gx < layer->rows; gx++)
|
|
||||||
{
|
|
||||||
for (int gy = 0; gy < layer->cols; gy++)
|
|
||||||
{
|
|
||||||
auto quad_idx = layer->cells[gx][gy];
|
|
||||||
|
|
||||||
Vector2 pos(gx*layer->cell_width, gy*layer->cell_height);
|
|
||||||
|
|
||||||
if (quad_idx < 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto quad = loaded_tileset->quads[quad_idx];
|
|
||||||
|
|
||||||
J2D::DrawPartialSprite(loaded_tilesheet, pos, Vector2(quad.x, quad.y), Vector2(quad.w, quad.h));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrawLevel(const Level* level) const
|
|
||||||
{
|
|
||||||
for (const auto* layer : level->layers)
|
|
||||||
{
|
|
||||||
DrawLayer(layer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Draw()
|
|
||||||
{
|
|
||||||
glClearColor(0, 0, 0, 1);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
J2D::Begin();
|
|
||||||
glPushMatrix();
|
|
||||||
glTranslatef(GetWidth() / 2.f, GetHeight() / 2.f, 0);
|
|
||||||
|
|
||||||
glRotatef(camera.rotation.current, 0, 0, 1);
|
|
||||||
glScalef(camera.scale.current, camera.scale.current, 1);
|
|
||||||
glTranslatef(-camera.translation.current.x, -camera.translation.current.y, 0);
|
|
||||||
|
|
||||||
if (data_ready)
|
|
||||||
DrawLevel(loaded_level);
|
|
||||||
|
|
||||||
|
|
||||||
for (auto* entity : entities) {
|
|
||||||
entity->Draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
glPopMatrix();
|
|
||||||
J2D::End();
|
|
||||||
scene->Draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void OnRefresh(float elapsed) override
|
|
||||||
{
|
|
||||||
Update(elapsed);
|
|
||||||
Draw();
|
|
||||||
SwapBuffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum JUI::MouseButton ToJUIEnum(const MouseButton& btn) {
|
|
||||||
if (btn == MouseButtons::Left) return JUI::MouseButton::Left;
|
|
||||||
if (btn == MouseButtons::Middle) return JUI::MouseButton::Middle;
|
|
||||||
if (btn == MouseButtons::Right) return JUI::MouseButton::Right;
|
|
||||||
|
|
||||||
// Default condition.
|
|
||||||
return JUI::MouseButton::Left;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnMouseButtonDown(const ReWindow::MouseButtonDownEvent &e) override {
|
|
||||||
auto btn = ToJUIEnum(e.Button);
|
|
||||||
|
|
||||||
if (scene->ObserveMouseInput(btn, true)) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent &e) override {
|
|
||||||
auto btn = ToJUIEnum(e.Button);
|
|
||||||
if (scene->ObserveMouseInput(btn, false)) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnMouseMove(const ReWindow::MouseMoveEvent &e) override {
|
|
||||||
Vector2 mposv2(e.Position.x, e.Position.y);
|
|
||||||
if (scene->ObserveMouseMovement(mposv2)) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnKeyDown(const ReWindow::KeyDownEvent &e) override {
|
|
||||||
if (scene->ObserveKeyInput(e.key, true)) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnKeyUp(const ReWindow::KeyUpEvent &e) override {
|
|
||||||
if (scene->ObserveKeyInput(e.key, false)) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
|
Reference in New Issue
Block a user