Skip to content

Implemented Hexagonal Body and Curved Bullets #183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,218 changes: 1,218 additions & 0 deletions data.out

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
cmake -S . -B ./build -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake
cd build
make
./src/battle_game/battle_game
2 changes: 1 addition & 1 deletion src/battle_game/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ void PushEventGenerateBullet(uint32_t unit_id,
- 压入一个子弹生成事件
- 和 Unit 类中的 GenerateBullet 函数相联系

### 随机变量
### **随机变量**

游戏中我们常常会见到随机机制,为了方便日后我们加入联机功能,我们需要使随机机制具有一致性。
因此我们希望你编写的代码中所有涉及到的随机变量都通过以下函数获得:
Expand Down
1 change: 1 addition & 0 deletions src/battle_game/core/bullets/bullets.h
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
#pragma once
#include "battle_game/core/bullets/cannon_ball.h"
#include "battle_game/core/bullets/curve_bullet.h"
56 changes: 56 additions & 0 deletions src/battle_game/core/bullets/curve_bullet.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include "curve_bullet.h"
#include "battle_game/core/game_core.h"

namespace battle_game::bullet {

CurveBullet::CurveBullet(GameCore *core,
uint32_t id,
uint32_t unit_id,
uint32_t player_id,
glm::vec2 position,
float rotation,
float damage_scale,
glm::vec2 velocity,
float angular_velocity)
: Bullet(core, id, unit_id, player_id, position, rotation, damage_scale),
velocity_(velocity),
angular_velocity_(angular_velocity) {}

void CurveBullet::Update() {
// 旋转速度向量
velocity_ = Rotate(velocity_, angular_velocity_ * kSecondPerTick);

// 更新位置
position_ += velocity_ * kSecondPerTick;

// 更新旋转角度以朝向运动方向
rotation_ = std::atan2(velocity_.y, velocity_.x) - glm::radians(90.0f);

// 碰撞检测
bool should_die = false;
if (game_core_->IsBlockedByObstacles(position_)) {
should_die = true;
}

auto &units = game_core_->GetUnits();
for (auto &unit : units) {
if (unit.first == unit_id_) continue;
if (unit.second->IsHit(position_)) {
game_core_->PushEventDealDamage(unit.first, id_, damage_scale_ * 10.0f);
should_die = true;
}
}

if (should_die) {
game_core_->PushEventRemoveBullet(id_);
}
}

void CurveBullet::Render() {
SetTransformation(position_, rotation_, glm::vec2{0.1f});
SetColor(game_core_->GetPlayerColor(player_id_));
SetTexture(BATTLE_GAME_ASSETS_DIR "textures/particle3.png");
DrawModel(0);
}

}
24 changes: 24 additions & 0 deletions src/battle_game/core/bullets/curve_bullet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once
#include "battle_game/core/bullet.h"

namespace battle_game::bullet {
class CurveBullet : public Bullet {
public:
CurveBullet(GameCore *core,
uint32_t id,
uint32_t unit_id,
uint32_t player_id,
glm::vec2 position,
float rotation,
float damage_scale,
glm::vec2 velocity,
float angular_velocity);

void Update() override;
void Render() override;

private:
glm::vec2 velocity_;
float angular_velocity_;
};
}
1 change: 1 addition & 0 deletions src/battle_game/core/selectable_units.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ void GameCore::GeneratePrimaryUnitList() {
* TODO: Add Your Unit Here!
* */
ADD_SELECTABLE_UNIT(unit::Tank);
ADD_SELECTABLE_UNIT(unit::ya_tank);

unit.reset();
}
Expand Down
235 changes: 235 additions & 0 deletions src/battle_game/core/units/yanan.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
#include "tiny_tank.h"

#include "battle_game/core/bullets/bullets.h"
#include "battle_game/core/game_core.h"
#include "battle_game/graphics/graphics.h"

namespace battle_game::unit {

namespace {
uint32_t tank_body_model_index = 0xffffffffu;
uint32_t tank_turret_model_index = 0xffffffffu;
} // namespace

ya_tank::ya_tank(GameCore *game_core, uint32_t id, uint32_t player_id)
: Unit(game_core, id, player_id) {
if (!~tank_body_model_index) {
auto mgr = AssetsManager::GetInstance();
{
/* ya_tank Body */
// tank_body_model_index = mgr->RegisterModel(
// {
// {{-0.8f, 0.8f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}},
// {{-0.8f, -1.0f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}},
// {{0.8f, 0.8f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}},
// {{0.8f, -1.0f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}},
// // distinguish front and back
// {{0.6f, 1.0f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}},
// {{-0.6f, 1.0f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}},
// },
// {0, 1, 2, 1, 2, 3, 0, 2, 5, 2, 4, 5});

/* Tank Body - Direction Hexagon */
tank_body_model_index = mgr->RegisterModel(
{
// 六边形基本顶点
{{0.0f, 1.0f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, // 0 上
{{0.866f, 0.5f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, // 1 右上
{{0.866f, -0.5f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, // 2 右下
{{0.0f, -1.0f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, // 3 下
{{-0.866f, -0.5f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, // 4 左下
{{-0.866f, 0.5f}, {0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, // 5 左上
// 前部朝向标记点
{{0.0f, 0.7f}, {0.0f, 0.0f}, {0.7f, 0.7f, 0.7f, 1.0f}}, // 6 内部点
},
{
// 外部六边形
0, 1, 6, // 右上三角形
1, 2, 6, // 右中三角形
2, 3, 6, // 右下三角形
3, 4, 6, // 左下三角形
4, 5, 6, // 左中三角形
5, 0, 6 // 左上三角形
}
);
}

{
/* ya_tank Turret */
std::vector<ObjectVertex> turret_vertices;
std::vector<uint32_t> turret_indices;
const int precision = 60;
const float inv_precision = 1.0f / float(precision);
for (int i = 0; i < precision; i++) {
auto theta = (float(i) + 0.5f) * inv_precision;
theta *= glm::pi<float>() * 2.0f;
auto sin_theta = std::sin(theta);
auto cos_theta = std::cos(theta);
turret_vertices.push_back({{sin_theta * 0.5f, cos_theta * 0.5f},
{0.0f, 0.0f},
{0.7f, 0.7f, 0.7f, 1.0f}});
turret_indices.push_back(i);
turret_indices.push_back((i + 1) % precision);
turret_indices.push_back(precision);
}
turret_vertices.push_back(
{{0.0f, 0.0f}, {0.0f, 0.0f}, {0.7f, 0.7f, 0.7f, 1.0f}});
turret_vertices.push_back(
{{-0.1f, 0.0f}, {0.0f, 0.0f}, {0.7f, 0.7f, 0.7f, 1.0f}});
turret_vertices.push_back(
{{0.1f, 0.0f}, {0.0f, 0.0f}, {0.7f, 0.7f, 0.7f, 1.0f}});
turret_vertices.push_back(
{{-0.1f, 1.2f}, {0.0f, 0.0f}, {0.7f, 0.7f, 0.7f, 1.0f}});
turret_vertices.push_back(
{{0.1f, 1.2f}, {0.0f, 0.0f}, {0.7f, 0.7f, 0.7f, 1.0f}});
turret_indices.push_back(precision + 1 + 0);
turret_indices.push_back(precision + 1 + 1);
turret_indices.push_back(precision + 1 + 2);
turret_indices.push_back(precision + 1 + 1);
turret_indices.push_back(precision + 1 + 2);
turret_indices.push_back(precision + 1 + 3);
tank_turret_model_index =
mgr->RegisterModel(turret_vertices, turret_indices);
}
}
}

void ya_tank::Render() {
battle_game::SetTransformation(position_, rotation_);
battle_game::SetTexture(0);
battle_game::SetColor(game_core_->GetPlayerColor(player_id_));
battle_game::DrawModel(tank_body_model_index);
battle_game::SetRotation(turret_rotation_);
battle_game::DrawModel(tank_turret_model_index);
}

void ya_tank::Update() {
TankMove(3.0f, glm::radians(180.0f));
TurretRotate();
Fire();
MyFire();
}

void ya_tank::TankMove(float move_speed, float rotate_angular_speed) {
auto player = game_core_->GetPlayer(player_id_);
if (player) {
auto &input_data = player->GetInputData();
glm::vec2 offset{0.0f};
if (input_data.key_down[GLFW_KEY_W]) {
offset.y += 1.0f;
}
if (input_data.key_down[GLFW_KEY_S]) {
offset.y -= 1.0f;
}
float speed = move_speed * GetSpeedScale();
offset *= kSecondPerTick * speed;
auto new_position =
position_ + glm::vec2{glm::rotate(glm::mat4{1.0f}, rotation_,
glm::vec3{0.0f, 0.0f, 1.0f}) *
glm::vec4{offset, 0.0f, 0.0f}};
if (!game_core_->IsBlockedByObstacles(new_position)) {
game_core_->PushEventMoveUnit(id_, new_position);
}
float rotation_offset = 0.0f;
if (input_data.key_down[GLFW_KEY_A]) {
rotation_offset += 1.0f;
}
if (input_data.key_down[GLFW_KEY_D]) {
rotation_offset -= 1.0f;
}
rotation_offset *= kSecondPerTick * rotate_angular_speed * GetSpeedScale();
game_core_->PushEventRotateUnit(id_, rotation_ + rotation_offset);
}
}

void ya_tank::TurretRotate() {
auto player = game_core_->GetPlayer(player_id_);
if (player) {
auto &input_data = player->GetInputData();
auto diff = input_data.mouse_cursor_position - position_;
if (glm::length(diff) < 1e-4) {
turret_rotation_ = rotation_;
} else {
turret_rotation_ = std::atan2(diff.y, diff.x) - glm::radians(90.0f);
}
}
}



void ya_tank::Fire() {
if (fire_count_down_ == 0) {
auto player = game_core_->GetPlayer(player_id_);
if (player) {
auto &input_data = player->GetInputData();
if (input_data.mouse_button_down[GLFW_MOUSE_BUTTON_LEFT]) {
auto velocity = Rotate(glm::vec2{0.0f, 20.0f}, turret_rotation_);
GenerateBullet<bullet::CannonBall>(
position_ + Rotate({0.0f, 1.2f}, turret_rotation_),
turret_rotation_, GetDamageScale(), velocity);
fire_count_down_ = kTickPerSecond; // Fire interval 1 second.
}
}
}
if (fire_count_down_) {
fire_count_down_--;
}
}

void ya_tank::MyFire() {
if (fire_count_down_ == 0) {
auto player = game_core_->GetPlayer(player_id_);
if (player) {
auto &input_data = player->GetInputData();
if (input_data.key_down[GLFW_KEY_1]) {
// 基础参数
float base_speed = 20.0f;
float curve_angle = glm::radians(30.0f);
float angular_speed = 3.0f; // 角速度

// 左侧子弹: 逆时针旋转
glm::vec2 velocity_left = Rotate(
glm::vec2{std::sin(curve_angle) * base_speed,
std::cos(curve_angle) * base_speed},
turret_rotation_);

// 右侧子弹: 顺时针旋转
glm::vec2 velocity_right = Rotate(
glm::vec2{-std::sin(curve_angle) * base_speed,
std::cos(curve_angle) * base_speed},
turret_rotation_);

// 发射左侧曲线子弹
GenerateBullet<bullet::CurveBullet>(
position_ + Rotate({-0.5f, 1.2f}, turret_rotation_),
turret_rotation_, GetDamageScale(), velocity_left, angular_speed);

// 发射右侧曲线子弹
GenerateBullet<bullet::CurveBullet>(
position_ + Rotate({0.5f, 1.2f}, turret_rotation_),
turret_rotation_, GetDamageScale(), velocity_right, -angular_speed);

fire_count_down_ = kTickPerSecond;
}
}
}
if (fire_count_down_) {
fire_count_down_--;
}
}

bool ya_tank::IsHit(glm::vec2 position) const {
position = WorldToLocal(position);
return position.x > -0.8f && position.x < 0.8f && position.y > -1.0f &&
position.y < 1.0f && position.x + position.y < 1.6f &&
position.y - position.x < 1.6f;
}

const char *ya_tank::UnitName() const {
return "ya_hex_tank";
}

const char *ya_tank::Author() const {
return "An Yan 2024010836";
}
} // namespace battle_game::unit
24 changes: 24 additions & 0 deletions src/battle_game/core/units/yanan.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once
#include "battle_game/core/unit.h"

namespace battle_game::unit {
class ya_tank : public Unit {
public:
ya_tank(GameCore *game_core, uint32_t id, uint32_t player_id);
void Render() override;
void Update() override;
[[nodiscard]] bool IsHit(glm::vec2 position) const override;

protected:
void TankMove(float move_speed, float rotate_angular_speed);
void TurretRotate();
void Fire();
void MyFire();
[[nodiscard]] const char *UnitName() const override;
[[nodiscard]] const char *Author() const override;

float turret_rotation_{0.0f};
uint32_t fire_count_down_{0};
uint32_t mine_count_down_{0};
};
} // namespace battle_game::unit