added npc

This commit is contained in:
Vlad 2026-02-10 12:42:19 +06:00
parent 629c9ba7b1
commit 5df2216da6
2 changed files with 160 additions and 58 deletions

View File

@ -10,13 +10,14 @@ namespace ZL {
void LocalClient::Connect(const std::string& host, uint16_t port) { void LocalClient::Connect(const std::string& host, uint16_t port) {
generateBoxes(); generateBoxes();
initializeNPCs();
lastUpdateMs = std::chrono::duration_cast<std::chrono::milliseconds>( lastUpdateMs = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count(); std::chrono::system_clock::now().time_since_epoch()).count();
} }
void LocalClient::generateBoxes() { void LocalClient::generateBoxes() {
serverBoxes.clear(); serverBoxes.clear();
std::random_device rd; std::random_device rd;
std::mt19937 gen(rd()); std::mt19937 gen(rd());
@ -64,20 +65,101 @@ namespace ZL {
std::cout << "LocalClient: Generated " << serverBoxes.size() << " boxes\n"; std::cout << "LocalClient: Generated " << serverBoxes.size() << " boxes\n";
} }
Eigen::Vector3f LocalClient::generateRandomPosition() {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> distrib(-50.0, 50.0);
return Eigen::Vector3f(
(float)distrib(gen),
(float)distrib(gen),
(float)distrib(gen) + 45000.0f
);
}
void LocalClient::initializeNPCs() {
npcs.clear();
for (int i = 0; i < 3; ++i) {
LocalNPC npc;
npc.id = 100 + i;
npc.speed = 20.0f + (i * 5.0f);
npc.currentState.id = npc.id;
npc.currentState.position = generateRandomPosition();
npc.currentState.rotation = Eigen::Matrix3f::Identity();
npc.currentState.velocity = npc.speed;
npc.targetPosition = generateRandomPosition();
npc.lastStateUpdateMs = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
npc.stateHistory.add_state(npc.currentState);
npcs.push_back(npc);
std::cout << "LocalClient: Created NPC with id=" << npc.id
<< " at pos (" << npc.currentState.position.x() << ", "
<< npc.currentState.position.y() << ", "
<< npc.currentState.position.z() << ")" << std::endl;
}
}
void LocalClient::updateNPCs() {
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
for (auto& npc : npcs) {
uint64_t deltaMs = now_ms - npc.lastStateUpdateMs;
float dt = deltaMs / 1000.0f;
npc.lastStateUpdateMs = now_ms;
Eigen::Vector3f direction = npc.targetPosition - npc.currentState.position;
float distance = direction.norm();
if (distance < 5.0f) {
npc.targetPosition = generateRandomPosition();
direction = npc.targetPosition - npc.currentState.position;
distance = direction.norm();
}
if (distance > 0.001f) {
direction.normalize();
npc.currentState.position += direction * npc.speed * dt;
npc.currentState.velocity = npc.speed;
Eigen::Vector3f localForward(0.0f, 0.0f, -1.0f);
Eigen::Vector3f worldForward = direction;
Eigen::Vector3f cross = localForward.cross(worldForward);
float dot = localForward.dot(worldForward);
if (cross.norm() > 0.001f) {
float angle = std::atan2(cross.norm(), dot);
cross.normalize();
Eigen::AngleAxisf aa(angle * 0.05f, cross);
npc.currentState.rotation = npc.currentState.rotation * aa.toRotationMatrix();
}
}
npc.currentState.lastUpdateServerTime = std::chrono::system_clock::time_point(
std::chrono::milliseconds(now_ms));
npc.stateHistory.add_state(npc.currentState);
}
}
void LocalClient::Poll() { void LocalClient::Poll() {
updatePhysics(); updatePhysics();
updateNPCs();
checkCollisions(); checkCollisions();
} }
void LocalClient::updatePhysics() { void LocalClient::updatePhysics() {
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>( auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count(); std::chrono::system_clock::now().time_since_epoch()).count();
if (lastUpdateMs == 0) { if (lastUpdateMs == 0) {
lastUpdateMs = now_ms; lastUpdateMs = now_ms;
return; return;
} }
uint64_t deltaMs = now_ms - lastUpdateMs; uint64_t deltaMs = now_ms - lastUpdateMs;
float dt = deltaMs / 1000.0f; float dt = deltaMs / 1000.0f;
lastUpdateMs = now_ms; lastUpdateMs = now_ms;
@ -147,7 +229,7 @@ namespace ZL {
std::cout << "LocalClient: Box " << boxIdx << " destroyed by projectile from player " std::cout << "LocalClient: Box " << boxIdx << " destroyed by projectile from player "
<< projectiles[projIdx].shooterId << std::endl; << projectiles[projIdx].shooterId << std::endl;
if (std::find(projIndicesToRemove.begin(), projIndicesToRemove.end(), (int)projIdx) if (std::find(projIndicesToRemove.begin(), projIndicesToRemove.end(), (int)projIdx)
== projIndicesToRemove.end()) { == projIndicesToRemove.end()) {
projIndicesToRemove.push_back(static_cast<int>(projIdx)); projIndicesToRemove.push_back(static_cast<int>(projIdx));
} }
@ -198,7 +280,7 @@ namespace ZL {
tokens.push_back(token); tokens.push_back(token);
} }
return tokens; return tokens;
}(message, ':'); }(message, ':');
if (parts.empty()) return; if (parts.empty()) return;
@ -252,8 +334,8 @@ namespace ZL {
pinfo.velocity = velocity; pinfo.velocity = velocity;
pendingProjectiles.push_back(pinfo); pendingProjectiles.push_back(pinfo);
std::cout << "LocalClient: Created projectile at pos (" << shotPos.x() << ", " std::cout << "LocalClient: Created projectile at pos (" << shotPos.x() << ", "
<< shotPos.y() << ", " << shotPos.z() << ") vel (" << pr.vel.x() << ", " << shotPos.y() << ", " << shotPos.z() << ") vel (" << pr.vel.x() << ", "
<< pr.vel.y() << ", " << pr.vel.z() << ")" << std::endl; << pr.vel.y() << ", " << pr.vel.z() << ")" << std::endl;
} }
} }
@ -265,6 +347,14 @@ namespace ZL {
return result; return result;
} }
std::unordered_map<int, ClientStateInterval> LocalClient::getRemotePlayers() {
std::unordered_map<int, ClientStateInterval> result;
for (const auto& npc : npcs) {
result[npc.id] = npc.stateHistory;
}
return result;
}
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> LocalClient::getServerBoxes() { std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> LocalClient::getServerBoxes() {
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> result; std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> result;
for (const auto& box : serverBoxes) { for (const auto& box : serverBoxes) {

View File

@ -9,67 +9,79 @@
namespace ZL { namespace ZL {
struct LocalServerBox { struct LocalServerBox {
Eigen::Vector3f position; Eigen::Vector3f position;
Eigen::Matrix3f rotation; Eigen::Matrix3f rotation;
float collisionRadius = 2.0f; float collisionRadius = 2.0f;
bool destroyed = false; bool destroyed = false;
}; };
struct LocalProjectile { struct LocalProjectile {
int shooterId = -1; int shooterId = -1;
uint64_t spawnMs = 0; uint64_t spawnMs = 0;
Eigen::Vector3f pos; Eigen::Vector3f pos;
Eigen::Vector3f vel; Eigen::Vector3f vel;
float lifeMs = 5000.0f; float lifeMs = 5000.0f;
}; };
class LocalClient : public INetworkClient { struct LocalNPC {
private: int id = -1;
std::queue<std::string> messageQueue; ClientState currentState;
std::vector<LocalServerBox> serverBoxes; ClientStateInterval stateHistory;
std::vector<LocalProjectile> projectiles; Eigen::Vector3f targetPosition;
std::vector<ProjectileInfo> pendingProjectiles; float speed = 30.0f;
std::vector<DeathInfo> pendingDeaths; uint64_t lastStateUpdateMs = 0;
std::vector<BoxDestroyedInfo> pendingBoxDestructions; };
std::vector<int> pendingRespawns;
uint64_t lastUpdateMs = 0; class LocalClient : public INetworkClient {
ClientState localPlayerState; private:
bool hasLocalPlayerState = false; std::queue<std::string> messageQueue;
std::vector<LocalServerBox> serverBoxes;
std::vector<LocalProjectile> projectiles;
std::vector<ProjectileInfo> pendingProjectiles;
std::vector<DeathInfo> pendingDeaths;
std::vector<BoxDestroyedInfo> pendingBoxDestructions;
std::vector<int> pendingRespawns;
uint64_t lastUpdateMs = 0;
ClientState localPlayerState;
bool hasLocalPlayerState = false;
std::vector<LocalNPC> npcs;
void updatePhysics();
void checkCollisions();
void generateBoxes();
void initializeNPCs();
void updateNPCs();
Eigen::Vector3f generateRandomPosition();
public:
void Connect(const std::string& host, uint16_t port) override;
void updatePhysics(); void Poll() override;
void checkCollisions();
void generateBoxes();
public: void Send(const std::string& message) override;
void Connect(const std::string& host, uint16_t port) override;
void Poll() override; bool IsConnected() const override { return true; }
int GetClientId() const override { return 1; }
std::vector<ProjectileInfo> getPendingProjectiles() override;
void Send(const std::string& message) override; std::unordered_map<int, ClientStateInterval> getRemotePlayers() override;
bool IsConnected() const override { return true; } std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> getServerBoxes() override;
int GetClientId() const override { return 1; }
std::vector<ProjectileInfo> getPendingProjectiles() override;
std::unordered_map<int, ClientStateInterval> getRemotePlayers() override { std::vector<DeathInfo> getPendingDeaths() override;
return std::unordered_map<int, ClientStateInterval>();
}
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> getServerBoxes() override; std::vector<int> getPendingRespawns() override {
return {};
}
std::vector<DeathInfo> getPendingDeaths() override; std::vector<BoxDestroyedInfo> getPendingBoxDestructions() override;
std::vector<int> getPendingRespawns() override { void setLocalPlayerState(const ClientState& state) {
return {}; localPlayerState = state;
} hasLocalPlayerState = true;
}
std::vector<BoxDestroyedInfo> getPendingBoxDestructions() override; };
void setLocalPlayerState(const ClientState& state) {
localPlayerState = state;
hasLocalPlayerState = true;
}
};
} }