Impl. controlling a walker
All checks were successful
Build and Publish WASM version of demo / Build-And-Deploy (push) Successful in 18s

This commit is contained in:
Stephen Seo 2023-08-15 11:53:26 +09:00
parent 64c5bcd1bc
commit 5008dd2067
4 changed files with 239 additions and 42 deletions

View file

@ -10,6 +10,7 @@
#endif #endif
// third party includes // third party includes
#include <raylib.h>
#include <raymath.h> #include <raymath.h>
// local includes // local includes
@ -45,7 +46,11 @@ TRunnerScreen::TRunnerScreen(std::weak_ptr<ScreenStack> stack)
camera_target{0.0F, 0.0F, 0.0F}, camera_target{0.0F, 0.0F, 0.0F},
mouse_hit{0.0F, 0.0F, 0.0F}, mouse_hit{0.0F, 0.0F, 0.0F},
idx_hit(SURFACE_UNIT_WIDTH / 2 + idx_hit(SURFACE_UNIT_WIDTH / 2 +
(SURFACE_UNIT_HEIGHT / 2) * SURFACE_UNIT_WIDTH) { (SURFACE_UNIT_HEIGHT / 2) * SURFACE_UNIT_WIDTH),
controlled_walker_idx(std::nullopt),
left_text_width(MeasureText("Left", BUTTON_FONT_SIZE)),
right_text_width(MeasureText("Right", BUTTON_FONT_SIZE)),
forward_text_width(MeasureText("Forward", BUTTON_FONT_SIZE)) {
#ifndef NDEBUG #ifndef NDEBUG
std::cout << "idx_hit initialized to " << idx_hit << std::endl; std::cout << "idx_hit initialized to " << idx_hit << std::endl;
#endif #endif
@ -219,6 +224,45 @@ TRunnerScreen::~TRunnerScreen() {
} }
bool TRunnerScreen::update(float dt) { bool TRunnerScreen::update(float dt) {
if (controlled_walker_idx.has_value()) {
auto walker_body_pos =
walkers[controlled_walker_idx.value()].get_body_pos();
camera_target = walker_body_pos + Vector3{0.0F, 1.0F, 0.0F};
float rotation = walkers[controlled_walker_idx.value()].get_rotation();
Vector3 offset = get_rotation_matrix_about_y(rotation + PI) *
Vector3{1.0F, 0.0F, 0.0F} * 4.0F +
Vector3{0.0F, 4.0F, 0.0F};
camera_pos = walkers[controlled_walker_idx.value()].get_body_pos() + offset;
}
if (controlled_walker_idx.has_value() && IsMouseButtonDown(0)) {
// Check if clicked on button.
if (GetTouchX() >= 0 && GetTouchX() <= left_text_width &&
GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE &&
GetTouchY() <= GetScreenHeight()) {
walkers[controlled_walker_idx.value()].player_turn_left();
goto post_check_click;
} else if (GetTouchX() >= left_text_width &&
GetTouchX() <= left_text_width + right_text_width &&
GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE &&
GetTouchY() <= GetScreenHeight()) {
walkers[controlled_walker_idx.value()].player_turn_right();
goto post_check_click;
} else if (int width_mid = (left_text_width + right_text_width) / 2 -
forward_text_width / 2;
GetTouchX() >= width_mid &&
GetTouchX() <= width_mid + forward_text_width &&
GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE * 2 &&
GetTouchY() <= GetScreenHeight() - BUTTON_FONT_SIZE) {
walkers[controlled_walker_idx.value()].player_go_forward();
goto post_check_click;
}
}
if (IsMouseButtonPressed(0)) { if (IsMouseButtonPressed(0)) {
float press_x = GetTouchX(); float press_x = GetTouchX();
float press_y = GetTouchY(); float press_y = GetTouchY();
@ -230,6 +274,23 @@ bool TRunnerScreen::update(float dt) {
<< ray.direction.y << ", " << ray.direction.z << std::endl; << ray.direction.y << ", " << ray.direction.z << std::endl;
#endif #endif
// Check if clicked on a Walker.
for (unsigned int idx = 0; idx < walkers.size(); ++idx) {
if (auto walker_bb = walkers[idx].get_body_bb();
GetRayCollisionBox(ray, walker_bb).hit) {
if (controlled_walker_idx.has_value()) {
walkers[controlled_walker_idx.value()].set_player_controlled(false);
}
controlled_walker_idx = idx;
walkers[controlled_walker_idx.value()].set_player_controlled(true);
idx_hit = SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT;
goto post_check_click;
}
}
// Check if clicked on ground.
for (unsigned int idx = 0; idx < SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT; for (unsigned int idx = 0; idx < SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT;
++idx) { ++idx) {
int x = idx % SURFACE_UNIT_WIDTH; int x = idx % SURFACE_UNIT_WIDTH;
@ -266,6 +327,11 @@ bool TRunnerScreen::update(float dt) {
this->camera_pos.z = 0.0F; this->camera_pos.z = 0.0F;
} }
this->camera_target.y += 1.0F; this->camera_target.y += 1.0F;
if (this->controlled_walker_idx.has_value()) {
this->walkers[this->controlled_walker_idx.value()]
.set_player_controlled(false);
this->controlled_walker_idx = std::nullopt;
}
}; };
if (auto bb_collision = GetRayCollisionBox(ray, surface_bbs[idx]); if (auto bb_collision = GetRayCollisionBox(ray, surface_bbs[idx]);
@ -273,16 +339,22 @@ bool TRunnerScreen::update(float dt) {
if (auto collision = GetRayCollisionTriangle(ray, nw, sw, ne); if (auto collision = GetRayCollisionTriangle(ray, nw, sw, ne);
collision.hit) { collision.hit) {
on_collide_fn(collision.point); on_collide_fn(collision.point);
break; goto post_check_click;
} else if (auto collision = GetRayCollisionTriangle(ray, ne, sw, se); } else if (auto collision = GetRayCollisionTriangle(ray, ne, sw, se);
collision.hit) { collision.hit) {
on_collide_fn(collision.point); on_collide_fn(collision.point);
break; goto post_check_click;
} }
} }
} }
} else if (IsMouseButtonReleased(0)) {
if (controlled_walker_idx.has_value()) {
walkers[controlled_walker_idx.value()].player_idle();
}
} }
post_check_click:
camera_to_targets(dt); camera_to_targets(dt);
for (auto &walker : walkers) { for (auto &walker : walkers) {
@ -323,9 +395,34 @@ bool TRunnerScreen::draw() {
} }
// TODO DEBUG // TODO DEBUG
DrawLine3D(Vector3{0.0F, 3.0F, 0.0F}, mouse_hit, BLACK); if (!controlled_walker_idx.has_value()) {
DrawLine3D(Vector3{0.0F, 3.0F, 0.0F}, mouse_hit, BLACK);
}
EndMode3D(); EndMode3D();
if (controlled_walker_idx.has_value()) {
int total_width = 0;
DrawRectangle(0, GetScreenHeight() - BUTTON_FONT_SIZE, left_text_width,
BUTTON_FONT_SIZE, Color{255, 255, 255, 120});
DrawText("Left", 0, GetScreenHeight() - BUTTON_FONT_SIZE, BUTTON_FONT_SIZE,
BLACK);
total_width += left_text_width;
DrawRectangle(total_width, GetScreenHeight() - BUTTON_FONT_SIZE,
right_text_width, BUTTON_FONT_SIZE,
Color{255, 255, 255, 120});
DrawText("Right", total_width, GetScreenHeight() - BUTTON_FONT_SIZE,
BUTTON_FONT_SIZE, BLACK);
total_width = (total_width + right_text_width) / 2 - forward_text_width / 2;
DrawRectangle(total_width, GetScreenHeight() - BUTTON_FONT_SIZE * 2,
forward_text_width, BUTTON_FONT_SIZE,
Color{255, 255, 255, 120});
DrawText("Forward", total_width, GetScreenHeight() - BUTTON_FONT_SIZE * 2,
BUTTON_FONT_SIZE, BLACK);
}
EndDrawing(); EndDrawing();
return false; return false;

View file

@ -20,6 +20,8 @@ constexpr float CAMERA_UPDATE_RATE = 1.0F;
constexpr float SURFACE_HEIGHT_INTERVAL = 0.7F; constexpr float SURFACE_HEIGHT_INTERVAL = 0.7F;
constexpr int BUTTON_FONT_SIZE = 30;
class TRunnerScreen : public Screen { class TRunnerScreen : public Screen {
public: public:
TRunnerScreen(std::weak_ptr<ScreenStack> stack); TRunnerScreen(std::weak_ptr<ScreenStack> stack);
@ -67,6 +69,10 @@ class TRunnerScreen : public Screen {
Vector3 camera_target; Vector3 camera_target;
Vector3 mouse_hit; Vector3 mouse_hit;
unsigned int idx_hit; unsigned int idx_hit;
std::optional<unsigned int> controlled_walker_idx;
const int left_text_width;
const int right_text_width;
const int forward_text_width;
void camera_to_targets(float dt); void camera_to_targets(float dt);
}; };

View file

@ -154,3 +154,45 @@ void Walker::set_body_pos(Vector3 pos) {
flags |= 1; flags |= 1;
} }
} }
void Walker::set_player_controlled(bool player_controlled) {
if (player_controlled) {
flags &= ~0x3B;
flags |= 8;
target_body_pos = body_pos;
} else {
flags &= ~0x38;
}
}
void Walker::player_idle() { flags &= ~0x30; }
void Walker::player_turn_left() {
flags &= ~0x30;
flags |= 0x10;
target_body_pos = body_pos;
}
void Walker::player_turn_right() {
flags &= ~0x30;
flags |= 0x20;
target_body_pos = body_pos;
}
void Walker::player_go_forward() { flags |= 0x30; }
BoundingBox Walker::get_body_bb() const {
return BoundingBox{
.min = body_pos - Vector3{0.5F,
0.5F + BODY_IDLE_MOVE_AMOUNT *
std::sin(body_idle_move_timer + PI),
0.5F},
.max = body_pos + Vector3{0.5F,
0.5F + BODY_IDLE_MOVE_AMOUNT *
std::sin(body_idle_move_timer + PI),
0.5F}};
}
float Walker::get_rotation() const { return rotation; }
Vector3 Walker::get_body_pos() const { return body_pos; }

View file

@ -43,6 +43,16 @@ class Walker {
void set_body_pos(Vector3 pos); void set_body_pos(Vector3 pos);
void set_player_controlled(bool player_controlled);
void player_idle();
void player_turn_left();
void player_turn_right();
void player_go_forward();
BoundingBox get_body_bb() const;
float get_rotation() const;
Vector3 get_body_pos() const;
private: private:
Vector3 body_pos; Vector3 body_pos;
Vector3 target_body_pos; Vector3 target_body_pos;
@ -63,6 +73,11 @@ class Walker {
// ???? ??01 - rotating to move // ???? ??01 - rotating to move
// ???? ??10 - moving // ???? ??10 - moving
// ???? ?1?? - auto roaming // ???? ?1?? - auto roaming
// ???? 1??? - player controlled
// ??00 ???? - player controlled: idle
// ??01 ???? - player controlled: turn left
// ??10 ???? - player controlled: turn right
// ??11 ???? - player controlled: go forward
unsigned int flags; unsigned int flags;
const float body_height; const float body_height;
@ -79,7 +94,7 @@ class Walker {
template <std::size_t BBCount> template <std::size_t BBCount>
void Walker::update(float dt, const std::array<BoundingBox, BBCount> &bbs, void Walker::update(float dt, const std::array<BoundingBox, BBCount> &bbs,
unsigned int width, unsigned int height) { unsigned int width, unsigned int height) {
if ((flags & 4) != 0 && (flags & 3) == 0) { if ((flags & 8) == 0 && (flags & 4) != 0 && (flags & 3) == 0) {
roaming_timer += dt; roaming_timer += dt;
if (roaming_timer > roaming_time) { if (roaming_timer > roaming_time) {
roaming_timer = 0.0F; roaming_timer = 0.0F;
@ -122,49 +137,72 @@ void Walker::update(float dt, const std::array<BoundingBox, BBCount> &bbs,
} }
// body rotation // body rotation
if ((flags & 3) == 1) { if ((flags & 8) == 0) {
float diff = target_rotation - rotation; if ((flags & 3) == 1) {
if (diff > PI) { float diff = target_rotation - rotation;
rotation -= dt * BODY_ROTATION_SPEED; if (diff > PI) {
if (rotation < 0.0F) { rotation -= dt * BODY_ROTATION_SPEED;
rotation += PI * 2.0F; if (rotation < 0.0F) {
rotation += PI * 2.0F;
}
} else if (diff < -PI) {
rotation += dt * BODY_ROTATION_SPEED;
if (rotation > PI * 2.0F) {
rotation -= PI * 2.0F;
}
} else if (diff > 0.0F) {
rotation += dt * BODY_ROTATION_SPEED;
if (rotation > PI * 2.0F) {
rotation -= PI * 2.0F;
}
} else {
rotation -= dt * BODY_ROTATION_SPEED;
if (rotation < 0.0F) {
rotation += PI * 2.0F;
}
} }
} else if (diff < -PI) {
rotation += dt * BODY_ROTATION_SPEED; if (std::abs(target_rotation - rotation) < dt * BODY_ROTATION_SPEED) {
if (rotation > PI * 2.0F) { rotation = target_rotation;
rotation -= PI * 2.0F; flags &= ~3;
} flags |= 2;
} else if (diff > 0.0F) {
rotation += dt * BODY_ROTATION_SPEED;
if (rotation > PI * 2.0F) {
rotation -= PI * 2.0F;
}
} else {
rotation -= dt * BODY_ROTATION_SPEED;
if (rotation < 0.0F) {
rotation += PI * 2.0F;
} }
} }
} else {
if (std::abs(target_rotation - rotation) < dt * BODY_ROTATION_SPEED) { if ((flags & 0x30) == 0x10) {
rotation = target_rotation; rotation += dt * BODY_ROTATION_SPEED;
flags &= ~3; } else if ((flags & 0x30) == 0x20) {
flags |= 2; rotation -= dt * BODY_ROTATION_SPEED;
} }
} }
const Matrix rotationMatrix = get_rotation_matrix_about_y(rotation);
// body to target pos // body to target pos
if ((flags & 3) == 2) { if ((flags & 8) == 0) {
float diff = Vector3Distance(target_body_pos, body_pos); if ((flags & 3) == 2) {
body_pos = body_pos + Vector3Normalize(target_body_pos - body_pos) * float diff = Vector3Distance(target_body_pos, body_pos);
(dt * BODY_TARGET_SPEED); body_pos = body_pos + Vector3Normalize(target_body_pos - body_pos) *
if (Vector3Distance(target_body_pos, body_pos) > diff) { (dt * BODY_TARGET_SPEED);
flags &= ~3; if (Vector3Distance(target_body_pos, body_pos) > diff) {
body_pos = target_body_pos; flags &= ~3;
body_pos = target_body_pos;
}
} }
} else if ((flags & 0x30) == 0x30) {
Vector3 dir = rotationMatrix * Vector3{1.0F, 0.0F, 0.0F};
Vector3 prev_body_pos = body_pos;
body_pos = body_pos + dir * (dt * BODY_TARGET_SPEED);
if (body_pos.x < SURFACE_X_OFFSET - (float)SURFACE_UNIT_WIDTH ||
body_pos.x > SURFACE_X_OFFSET + (float)SURFACE_UNIT_WIDTH ||
body_pos.z < SURFACE_Y_OFFSET - (float)SURFACE_UNIT_HEIGHT ||
body_pos.z > SURFACE_Y_OFFSET + (float)SURFACE_UNIT_HEIGHT) {
body_pos = prev_body_pos;
}
target_body_pos = body_pos + dir * 1.0F;
} }
// moving legs // moving legs
const Matrix rotationMatrix = get_rotation_matrix_about_y(rotation);
const auto update_leg_fn = [this, &bbs, dt, &rotationMatrix]( const auto update_leg_fn = [this, &bbs, dt, &rotationMatrix](
Vector3 &leg_target, Vector3 &leg_pos, Vector3 &leg_target, Vector3 &leg_pos,
unsigned int &flags, unsigned int &flags,
@ -271,10 +309,24 @@ void Walker::update(float dt, const std::array<BoundingBox, BBCount> &bbs,
((nw_flags & 7) == 1 ? 1 : 0) + ((ne_flags & 7) == 1 ? 1 : 0) + ((nw_flags & 7) == 1 ? 1 : 0) + ((ne_flags & 7) == 1 ? 1 : 0) +
((se_flags & 7) == 1 ? 1 : 0)); ((se_flags & 7) == 1 ? 1 : 0));
if ((flags & 3) == 0) { if ((flags & 8) == 0) {
body_idle_move_timer += dt * BODY_IDLE_TIMER_RATE; if ((flags & 3) == 0) {
if (body_idle_move_timer > PI * 2.0F) { body_idle_move_timer += dt * BODY_IDLE_TIMER_RATE;
body_idle_move_timer -= PI * 2.0F; if (body_idle_move_timer > PI * 2.0F) {
body_idle_move_timer -= PI * 2.0F;
}
} else if (!FloatEquals(body_idle_move_timer, 0.0F)) {
if (body_idle_move_timer < PI) {
body_idle_move_timer += dt * BODY_IDLE_TIMER_RATE;
if (body_idle_move_timer > PI) {
body_idle_move_timer = 0;
}
} else {
body_idle_move_timer += dt * BODY_IDLE_TIMER_RATE;
if (body_idle_move_timer > PI * 2.0F) {
body_idle_move_timer = 0.0F;
}
}
} }
} else if (!FloatEquals(body_idle_move_timer, 0.0F)) { } else if (!FloatEquals(body_idle_move_timer, 0.0F)) {
if (body_idle_move_timer < PI) { if (body_idle_move_timer < PI) {