mirror of
https://github.com/rwengine/openrw.git
synced 2024-09-18 16:32:32 +02:00
Implement Game Object ID allocation
This commit is contained in:
parent
25f62a0a47
commit
5d3ac6218e
@ -15,6 +15,7 @@ class GameState;
|
||||
|
||||
class CutsceneObject;
|
||||
class WorkContext;
|
||||
#include <objects/ObjectTypes.hpp>
|
||||
|
||||
class GameObject;
|
||||
class CharacterObject;
|
||||
@ -96,6 +97,11 @@ public:
|
||||
*/
|
||||
CharacterObject* createPedestrian(const uint16_t id, const glm::vec3& pos, const glm::quat& rot = glm::quat());
|
||||
|
||||
/**
|
||||
* Inserts the given game object into the world.
|
||||
*/
|
||||
void insertObject(GameObject* object);
|
||||
|
||||
/**
|
||||
* Destroys an existing Object
|
||||
*/
|
||||
@ -159,11 +165,9 @@ public:
|
||||
SoundManager sound;
|
||||
|
||||
/**
|
||||
* @brief objects All active GameObjects in the world.
|
||||
* @todo add some mechanism to allow objects to be "locked" preventing deletion.
|
||||
* @todo add deletion queue to allow objects to self delete.
|
||||
* The active GameObjects within the world, mapped to their allocated ID
|
||||
*/
|
||||
std::set<GameObject*> objects;
|
||||
std::map<GameObjectID, GameObject*> objects;
|
||||
|
||||
std::set<GameObject*> characters;
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#define _GAMEOBJECT_HPP_
|
||||
|
||||
#include <engine/RWTypes.hpp>
|
||||
#include <objects/ObjectTypes.hpp>
|
||||
#include <loaders/LoaderIDE.hpp>
|
||||
#include <loaders/LoaderIPL.hpp>
|
||||
#include <render/Model.hpp>
|
||||
@ -27,7 +28,7 @@ class GameObject
|
||||
{
|
||||
glm::vec3 _lastPosition;
|
||||
glm::quat _lastRotation;
|
||||
|
||||
GameObjectID objectID;
|
||||
public:
|
||||
glm::vec3 position;
|
||||
glm::quat rotation;
|
||||
@ -58,7 +59,7 @@ public:
|
||||
bool visible;
|
||||
|
||||
GameObject(GameWorld* engine, const glm::vec3& pos, const glm::quat& rot, ModelRef model)
|
||||
: _lastPosition(pos), _lastRotation(rot), position(pos), rotation(rot),
|
||||
: _lastPosition(pos), _lastRotation(rot), objectID(-1), position(pos), rotation(rot),
|
||||
model(model), engine(engine), animator(nullptr), skeleton(nullptr), mHealth(0.f),
|
||||
inWater(false), _lastHeight(std::numeric_limits<float>::max()), visible(true),
|
||||
lifetime(GameObject::UnknownLifetime)
|
||||
@ -66,6 +67,12 @@ public:
|
||||
|
||||
virtual ~GameObject();
|
||||
|
||||
GameObjectID getGameObjectID() const { return objectID; }
|
||||
/**
|
||||
* Do not call this, use GameWorld::insertObject
|
||||
*/
|
||||
void setGameObjectID(GameObjectID id) { objectID = id; }
|
||||
|
||||
/**
|
||||
* @brief Enumeration of possible object types.
|
||||
*/
|
||||
|
8
rwengine/include/objects/ObjectTypes.hpp
Normal file
8
rwengine/include/objects/ObjectTypes.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* all wordly GameObjects are associated with a 32-bit identifier
|
||||
*/
|
||||
typedef uint32_t GameObjectID;
|
@ -47,7 +47,8 @@ void PlayerController::enterNearestVehicle()
|
||||
auto world = character->engine;
|
||||
VehicleObject* nearest = nullptr; float d = 10.f;
|
||||
|
||||
for( GameObject* object : world->objects ) {
|
||||
for( auto& p : world->objects ) {
|
||||
auto object = p.second;
|
||||
if( object->type() == GameObject::Vehicle ) {
|
||||
float vd = glm::length( character->getPosition() - object->getPosition());
|
||||
if( vd < d ) {
|
||||
|
@ -95,8 +95,8 @@ GameWorld::GameWorld(Logger* log, WorkContext* work, GameData* dat)
|
||||
|
||||
GameWorld::~GameWorld()
|
||||
{
|
||||
for(auto o : objects) {
|
||||
delete o;
|
||||
for(auto& p : objects) {
|
||||
delete p.second;
|
||||
}
|
||||
|
||||
delete dynamicsWorld;
|
||||
@ -126,7 +126,8 @@ bool GameWorld::placeItems(const std::string& name)
|
||||
}
|
||||
|
||||
// Attempt to Associate LODs.
|
||||
for(GameObject* object : objects) {
|
||||
for(auto& p: objects) {
|
||||
auto object = p.second;
|
||||
if( object->type() == GameObject::Instance ) {
|
||||
InstanceObject* instance = static_cast<InstanceObject*>(object);
|
||||
if( !instance->object->LOD ) {
|
||||
@ -192,7 +193,7 @@ InstanceObject *GameWorld::createInstance(const uint16_t id, const glm::vec3& po
|
||||
oi, nullptr, dydata
|
||||
);
|
||||
|
||||
objects.insert(instance);
|
||||
insertObject(instance);
|
||||
|
||||
if( shouldBeOnGrid(instance) )
|
||||
{
|
||||
@ -219,16 +220,16 @@ void GameWorld::createTraffic(const glm::vec3& near)
|
||||
|
||||
void GameWorld::cleanupTraffic(const glm::vec3& focus)
|
||||
{
|
||||
for ( GameObject* object : objects )
|
||||
for ( auto& p : objects )
|
||||
{
|
||||
if ( object->getLifetime() != GameObject::TrafficLifetime )
|
||||
if ( p.second->getLifetime() != GameObject::TrafficLifetime )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( glm::distance( focus, object->getPosition() ) >= 100.f )
|
||||
if ( glm::distance( focus, p.second->getPosition() ) >= 100.f )
|
||||
{
|
||||
destroyObjectQueued( object );
|
||||
destroyObjectQueued( p.second );
|
||||
}
|
||||
}
|
||||
destroyQueuedObjects();
|
||||
@ -301,7 +302,7 @@ CutsceneObject *GameWorld::createCutsceneObject(const uint16_t id, const glm::ve
|
||||
pos,
|
||||
m);
|
||||
|
||||
objects.insert(instance);
|
||||
insertObject( instance );
|
||||
|
||||
|
||||
return instance;
|
||||
@ -365,7 +366,7 @@ VehicleObject *GameWorld::createVehicle(const uint16_t id, const glm::vec3& pos,
|
||||
|
||||
auto vehicle = new VehicleObject{ this, pos, rot, m, vti, info->second, prim, sec };
|
||||
|
||||
objects.insert(vehicle);
|
||||
insertObject( vehicle );
|
||||
|
||||
return vehicle;
|
||||
}
|
||||
@ -404,7 +405,7 @@ CharacterObject* GameWorld::createPedestrian(const uint16_t id, const glm::vec3
|
||||
|
||||
if(m && m->resource) {
|
||||
auto ped = new CharacterObject( this, pos, rot, m, pt );
|
||||
objects.insert(ped);
|
||||
insertObject(ped);
|
||||
characters.insert(ped);
|
||||
new DefaultAIController(ped);
|
||||
return ped;
|
||||
@ -413,6 +414,19 @@ CharacterObject* GameWorld::createPedestrian(const uint16_t id, const glm::vec3
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GameWorld::insertObject(GameObject* object)
|
||||
{
|
||||
// Find the lowest free GameObjectID.
|
||||
GameObjectID availID = 1;
|
||||
for( auto& p : objects )
|
||||
{
|
||||
if( p.first == availID ) availID++;
|
||||
}
|
||||
|
||||
object->setGameObjectID( availID );
|
||||
objects[availID] = object;
|
||||
}
|
||||
|
||||
void GameWorld::destroyObject(GameObject* object)
|
||||
{
|
||||
auto coord = worldToGrid(glm::vec2(object->getPosition()));
|
||||
@ -423,7 +437,7 @@ void GameWorld::destroyObject(GameObject* object)
|
||||
auto index = (coord.x * WORLD_GRID_WIDTH) + coord.y;
|
||||
worldGrid[index].instances.erase(object);
|
||||
|
||||
auto iterator = objects.find(object);
|
||||
auto iterator = objects.find(object->getGameObjectID());
|
||||
if( iterator != objects.end() ) {
|
||||
delete object;
|
||||
objects.erase(iterator);
|
||||
@ -653,7 +667,8 @@ void GameWorld::PhysicsTickCallback(btDynamicsWorld *physWorld, btScalar timeSte
|
||||
{
|
||||
GameWorld* world = static_cast<GameWorld*>(physWorld->getWorldUserInfo());
|
||||
|
||||
for( GameObject* object : world->objects ) {
|
||||
for( auto& p : world->objects ) {
|
||||
GameObject* object = p.second;
|
||||
if( object->type() == GameObject::Vehicle ) {
|
||||
static_cast<VehicleObject*>(object)->tickPhysics(timeStep);
|
||||
}
|
||||
@ -708,7 +723,8 @@ void GameWorld::startCutscene()
|
||||
|
||||
void GameWorld::clearCutscene()
|
||||
{
|
||||
for(auto o : objects) {
|
||||
for(auto& p : objects) {
|
||||
auto o = p.second;
|
||||
if( o->type() == GameObject::Cutscene ) {
|
||||
destroyObjectQueued(o);
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ void WeaponItem::fireProjectile()
|
||||
_wepData
|
||||
});
|
||||
|
||||
_character->engine->objects.insert( projectile );
|
||||
_character->engine->insertObject( projectile );
|
||||
}
|
||||
|
||||
void WeaponItem::primary(bool active)
|
||||
|
@ -49,7 +49,8 @@ void ProjectileObject::explode()
|
||||
const float damage = _info.weapon->damage;
|
||||
|
||||
/// @todo accelerate this with bullet instead of doing this stupid loop.
|
||||
for(auto& o : engine->objects) {
|
||||
for(auto& p : engine->objects) {
|
||||
auto o = p.second;
|
||||
if( o == this ) continue;
|
||||
switch( o->type() ) {
|
||||
case GameObject::Instance:
|
||||
|
@ -297,7 +297,8 @@ void GameRenderer::renderWorld(GameWorld* world, const ViewCamera &camera, float
|
||||
renderer->pushDebugGroup("Objects");
|
||||
renderer->pushDebugGroup("Dynamic");
|
||||
|
||||
for( GameObject* object : world->objects ) {
|
||||
for( auto& p : world->objects ) {
|
||||
auto object = p.second;
|
||||
if(! object->visible )
|
||||
{
|
||||
continue;
|
||||
|
@ -99,9 +99,9 @@ void game_create_character(const ScriptArguments& args)
|
||||
it != args.getWorld()->objects.end();
|
||||
++it)
|
||||
{
|
||||
if( (*it)->type() == GameObject::Character && glm::distance(position, (*it)->getPosition()) < replaceThreshold )
|
||||
if( it->second->type() == GameObject::Character && glm::distance(position, it->second->getPosition()) < replaceThreshold )
|
||||
{
|
||||
args.getWorld()->destroyObjectQueued(*it);
|
||||
args.getWorld()->destroyObjectQueued(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,9 +139,9 @@ void game_create_vehicle(const ScriptArguments& args)
|
||||
it != args.getWorld()->objects.end();
|
||||
++it)
|
||||
{
|
||||
if( (*it)->type() == GameObject::Vehicle && glm::distance(position, (*it)->getPosition()) < replaceThreshold )
|
||||
if( it->second->type() == GameObject::Vehicle && glm::distance(position, it->second->getPosition()) < replaceThreshold )
|
||||
{
|
||||
args.getWorld()->destroyObjectQueued(*it);
|
||||
args.getWorld()->destroyObjectQueued(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
@ -538,8 +538,9 @@ bool game_objects_in_volume(const ScriptArguments& args)
|
||||
bool objects = args[9].integer;
|
||||
bool particles = args[10].integer;
|
||||
|
||||
for(GameObject* object : args.getWorld()->objects)
|
||||
for(auto& pair : args.getWorld()->objects)
|
||||
{
|
||||
GameObject* object = pair.second;
|
||||
switch( object->type() )
|
||||
{
|
||||
case GameObject::Instance:
|
||||
@ -682,7 +683,7 @@ void game_create_pickup(const ScriptArguments& args)
|
||||
|
||||
auto pickup = new GenericPickup(args.getWorld(), pos, id, type);
|
||||
|
||||
args.getWorld()->objects.insert(pickup);
|
||||
args.getWorld()->insertObject( pickup );
|
||||
|
||||
*args[5].handle = pickup;
|
||||
}
|
||||
@ -842,7 +843,8 @@ void game_set_close_object_visible(const ScriptArguments& args)
|
||||
|
||||
std::transform(model.begin(), model.end(), model.begin(), ::tolower);
|
||||
|
||||
for(auto o : args.getWorld()->objects) {
|
||||
for(auto& p : args.getWorld()->objects) {
|
||||
auto o = p.second;
|
||||
if( o->type() == GameObject::Instance ) {
|
||||
if( !o->model ) continue;
|
||||
if( o->model->name != model ) continue;
|
||||
@ -899,7 +901,8 @@ void game_change_nearest_model(const ScriptArguments& args)
|
||||
auto nobj = args.getWorld()->data->findObjectType<ObjectData>(newobjectid);
|
||||
|
||||
/// @todo Objects need to adopt the new object ID, not just the model.
|
||||
for(auto o : args.getWorld()->objects) {
|
||||
for(auto p : args.getWorld()->objects) {
|
||||
auto o = p.second;
|
||||
if( o->type() == GameObject::Instance ) {
|
||||
if( !o->model ) continue;
|
||||
if( o->model->name != oldmodel ) continue;
|
||||
|
19
rwengine/tests/test_GameWorld.cpp
Normal file
19
rwengine/tests/test_GameWorld.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <engine/GameWorld.hpp>
|
||||
#include <engine/GameData.hpp>
|
||||
#include <test_globals.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(GameWorldTests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_gameobject_id)
|
||||
{
|
||||
GameData gd(&Global::get().log, &Global::get().work, Global::getGamePath());
|
||||
GameWorld gw(&Global::get().log, &Global::get().work, &gd);
|
||||
|
||||
auto object1 = gw.createInstance(1337, glm::vec3(100.f, 0.f, 0.f));
|
||||
auto object2 = gw.createInstance(1337, glm::vec3(100.f, 0.f, 0.f));
|
||||
|
||||
BOOST_CHECK_NE( object1->getObjectID(), object2->getObjectID() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
@ -314,7 +314,8 @@ void RWGame::tick(float dt)
|
||||
}
|
||||
}
|
||||
|
||||
for( GameObject* object : world->objects ) {
|
||||
for( auto& p : world->objects ) {
|
||||
GameObject* object = p.second;
|
||||
object->_updateLastTransform();
|
||||
object->tick(dt);
|
||||
}
|
||||
@ -502,8 +503,9 @@ void RWGame::renderDebugStats(float time, Renderer::ProfileInfo& worldRenderTime
|
||||
|
||||
// Count the number of interesting objects.
|
||||
int peds = 0, cars = 0;
|
||||
for( GameObject* object : world->objects )
|
||||
for( auto& p : world->objects )
|
||||
{
|
||||
GameObject* object = p.second;
|
||||
switch ( object->type() )
|
||||
{
|
||||
case GameObject::Character: peds++; break;
|
||||
|
@ -36,7 +36,7 @@ void IngameState::startTest()
|
||||
glm::vec3 itemspawn( 276.5f, -609.f, 36.5f);
|
||||
for( auto& w : getWorld()->data->weaponData ) {
|
||||
if( w.first == "unarmed" ) continue;
|
||||
getWorld()->objects.insert(new ItemPickup(getWorld(), itemspawn,
|
||||
getWorld()->insertObject(new ItemPickup(getWorld(), itemspawn,
|
||||
w.second));
|
||||
itemspawn.x += 2.5f;
|
||||
}
|
||||
|
@ -8,18 +8,19 @@ BOOST_AUTO_TEST_SUITE(LifetimeTests)
|
||||
BOOST_AUTO_TEST_CASE(test_cleanup)
|
||||
{
|
||||
GameObject* f = Global::get().e->createInstance(1337, glm::vec3(0.f, 0.f, 1000.f));
|
||||
auto id = f->getGameObjectID();
|
||||
|
||||
f->setLifetime(GameObject::TrafficLifetime);
|
||||
|
||||
{
|
||||
auto search = Global::get().e->objects.find( f );
|
||||
auto search = Global::get().e->objects.find( id );
|
||||
BOOST_CHECK( search != Global::get().e->objects.end() );
|
||||
}
|
||||
|
||||
Global::get().e->cleanupTraffic(glm::vec3(0.f, 0.f, 0.f));
|
||||
|
||||
{
|
||||
auto search = Global::get().e->objects.find( f );
|
||||
auto search = Global::get().e->objects.find( id );
|
||||
BOOST_CHECK( search == Global::get().e->objects.end() );
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ BOOST_AUTO_TEST_CASE(test_pickup_interaction)
|
||||
|
||||
TestPickup* p = new TestPickup(Global::get().e, { 30.f, 0.f, 0.f } );
|
||||
|
||||
Global::get().e->objects.insert(p);
|
||||
Global::get().e->insertObject(p);
|
||||
|
||||
BOOST_CHECK( ! p->picked_up );
|
||||
|
||||
@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE(test_item_pickup)
|
||||
|
||||
ItemPickup* p = new ItemPickup(Global::get().e, { 30.f, 0.f, 0.f }, item );
|
||||
|
||||
Global::get().e->objects.insert(p);
|
||||
Global::get().e->insertObject(p);
|
||||
|
||||
// Check the characters inventory is empty.
|
||||
BOOST_CHECK( character->getInventory().empty() );
|
||||
|
@ -42,7 +42,7 @@ BOOST_AUTO_TEST_CASE(TestProjectile)
|
||||
wepdata
|
||||
});
|
||||
|
||||
Global::get().e->objects.insert( projectile );
|
||||
Global::get().e->insertObject( projectile );
|
||||
|
||||
BOOST_CHECK( character->mHealth == 100.f );
|
||||
|
||||
@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(TestProjectile)
|
||||
wepdata
|
||||
});
|
||||
|
||||
Global::get().e->objects.insert( projectile );
|
||||
Global::get().e->insertObject( projectile );
|
||||
|
||||
BOOST_CHECK( character->mHealth == 100.f );
|
||||
|
||||
@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(TestProjectile)
|
||||
wepdata
|
||||
});
|
||||
|
||||
Global::get().e->objects.insert( projectile );
|
||||
Global::get().e->insertObject( projectile );
|
||||
|
||||
BOOST_CHECK( character->mHealth == 100.f );
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user