Skrakle Posted April 14, 2015 Share Posted April 14, 2015 I'm trying to use these classes to write my scripts: #ifndef _ISCRIPT_H #define _ISCRIPT_H // derive your "script" classes from this interface to make it an "entity script" class IScript { protected: Entity* entity; public: IScript(Entity* e) { entity = e; } virtual void Collision(Entity* entity, float* position, float* normal, float speed); virtual void Draw(); virtual void DrawEach(Camera* camera); virtual void PostRender(Camera* camera); virtual void UpdateMatrix(); virtual void UpdatePhysics(); virtual void UpdateWorld(); }; // example "script" that you would have to make class PlayerScript : public IScript { public: PlayerScript() :IScript(entity) {} // default constructor virtual void Collision(Entity* entity, float* position, float* normal, float speed) { } virtual void Update() { } }; // define each global hook function here and the same idea applies to all. cast the user data on the entity // to IScript and if it's not NULL call that instance method void CollisionHook(Entity* entity0, Entity* entity1, float* position, float* normal, float speed) { IScript* script = (IScript*)entity0->GetUserData(); if (script != NULL) { script->Collision(entity1, position, normal, speed); } } void UpdateWorldHook(Entity* entity) { IScript* script = (IScript*)entity->GetUserData(); if (script != NULL) { script->UpdateWorld(); } } void UpdatePhysicsHook(Entity* entity) { IScript* script = (IScript*)entity->GetUserData(); if (script != NULL) { script->UpdatePhysics(); } } #endif I'm getting multiple unresolved external errors: LNK2001: unresolved external symbol "public: virtual void __thiscall IScript::Collision(class Leadwerks::Entity *,float *,float *,float)" (?Collision@IScript@@UAEXPAVEntity@Leadwerks@@PAM1M@Z) C:\Users\Admin\Documents\Leadwerks\Projects\TestProject\Projects\Windows\App.obj TestProject APP.CPP #include "App.h" #include "IScript.h" using namespace Leadwerks; App::App() : window(NULL), context(NULL), world(NULL), camera(NULL) {} App::~App() { delete world; delete window; } bool App::Start() { window = Leadwerks::Window::Create("TestProject", 250, 0, 1024, 768); context = Context::Create(window); world = World::Create(); IScript* script = new PlayerScript(); } What am i missing? Quote Link to comment Share on other sites More sharing options...
Ma-Shell Posted April 14, 2015 Share Posted April 14, 2015 In your derived PlayerScript-class, you defined your methods as "virtual", which they aren't. They are only "virtual" in their parent-class. The compiler is searching for their implementations, which it can't find. Just leave out the "virtual"-keyword in the PlayerScript-class Quote Link to comment Share on other sites More sharing options...
Ma-Shell Posted April 14, 2015 Share Posted April 14, 2015 You will also have to override ALL the other virtual functions of the parent-class, if you want a class which is not virtual. Quote Link to comment Share on other sites More sharing options...
Skrakle Posted April 14, 2015 Author Share Posted April 14, 2015 I removed virtual from the PlayerScript class like you suggested and left it in the parent and i'm still getting unresolved externals. class IScript { protected: Entity* entity; public: IScript(Entity* e) { entity = e; } virtual void Collision(Entity* entity, float* position, float* normal, float speed); }; class PlayerScript : public IScript { public: PlayerScript() :IScript(entity) {} void Collision(Entity* entity, float* position, float* normal, float speed) { } }; Quote Link to comment Share on other sites More sharing options...
Ma-Shell Posted April 14, 2015 Share Posted April 14, 2015 You also have to make your functions in the base class "pure virtual". This is done by setting them to 0. The function from the base-class should read: virtual void Collision(Entity* entity, float* position, float* normal, float speed)=0; For more info on the difference between virtual and pure virtual refer to http://stackoverflow.com/questions/1306778/c-virtual-pure-virtual-explained 1 Quote Link to comment Share on other sites More sharing options...
Rick Posted April 14, 2015 Share Posted April 14, 2015 PlayerScript() :IScript(entity) {} What is entity here? It's been awhile since I've worked heavily in C++ but I believe your PlayerScript ctor also needs the Entity entity in it's param list. PlayerScript(Entity* entity) : IScript(entity) { } Quote Link to comment Share on other sites More sharing options...
Skrakle Posted April 14, 2015 Author Share Posted April 14, 2015 PlayerScript() :IScript(entity) {} What is entity here? It's been awhile since I've worked heavily in C++ but I believe your PlayerScript ctor also needs the Entity entity in it's param list. PlayerScript(Entity* entity) : IScript(entity) { } The entity is in the parent class and parameter is passed in the PlayerScript constructor: PlayerScript() :IScript(entity) {} // default constructor You also have to make your functions in the base class "pure virtual". This is done by setting them to 0. The function from the base-class should read: virtual void Collision(Entity* entity, float* position, float* normal, float speed)=0; For more info on the difference between virtual and pure virtual refer to http://stackoverflow.com/questions/1306778/c-virtual-pure-virtual-explained That did it, i'll go read up on the link you provided, thanks a lot! Quote Link to comment Share on other sites More sharing options...
Rick Posted April 14, 2015 Share Posted April 14, 2015 The entity is in the parent class and parameter is passed in the PlayerScript constructor: PlayerScript() :IScript(entity) {} // default constructor hmm, how do you create these objects? I assume like: IScript* e; e = new PlayerScript(); This would mean you aren't attaching any LE entity because inside PlayerScript ctor you are passing it's parent "entity" variable to it's parent (which entity will be null) and assigning it to itself (which again will be null). How are you ever getting the entity in question to that variable? This should compile but I would think it would give a run-time error. Is this working for you? I know I've been out of the C++ game for some time but this seems strange. Quote Link to comment Share on other sites More sharing options...
Skrakle Posted April 14, 2015 Author Share Posted April 14, 2015 The entity is in the parent class and parameter is passed in the PlayerScript constructor: PlayerScript() :IScript(entity) {} // default constructor hmm, how do you create these objects? I assume like: IScript* e; e = new PlayerScript(); This would mean you aren't attaching any LE entity because inside PlayerScript ctor you are passing it's parent "entity" variable to it's parent (which entity will be null) and assigning it to itself (which again will be null). How are you ever getting the entity in question to that variable? This should compile but I would think it would give a run-time error. Is this working for you? I know I've been out of the C++ game for some time but this seems strange. In App::Start, after loading the entity, i use this: IScript* script = new PlayerScript(); entities[0]->SetUserData((void*)script); entities[0]->AddHook(Entity::CollisionHook, (void*)CollisionHook); I've tested the callbacks, they are being triggered. I'd like to know if when the app terminates, do i have to remove the hooks or LE does it automatically? Quote Link to comment Share on other sites More sharing options...
Rick Posted April 14, 2015 Share Posted April 14, 2015 Yeah, what you have works, but you aren't setting your IScript entity member anywhere and I don't see where you are using it either. I think if you try to use it you'd get an error saying it was NULL. Quote Link to comment Share on other sites More sharing options...
Skrakle Posted April 14, 2015 Author Share Posted April 14, 2015 Yeah, what you have works, but you aren't setting your IScript entity member anywhere and I don't see where you are using it either. I think if you try to use it you'd get an error saying it was NULL. You're right, entity does return NULL when first instantiated and SetUserData((void*)script); seems to set it as i'm able to view the entity position in the callback trigger. Quote Link to comment Share on other sites More sharing options...
Rick Posted April 14, 2015 Share Posted April 14, 2015 SetUserData((void*)script); wouldn't be setting your IScripts entity variable. I think you are able to view then entity position in the callback trigger because your callback function has the same name for it's parameter as your member variable ie. "entity" and when inside a function it'll use the function parameter if there is a name conflict. So when you do something like the below notice your function parameter is also named entity just like your member variable. Inside that function when you use the entity variable it's referring to the parameter 'entity' not your class member 'entity'. Inside your callbacks trying changing it to this->entity and I think it would fail. You might not even need that member variable 'entity' honestly. I always used it because I set the callbacks inside the base class instead of outside of it like you are doing. I wanted my code that looped over each entity to be clean of that stuff and have the class be self contained in what it does. If you were to do that then you'd have to also pass the entity into your child classes so you can pass it along to your base class. 1 Quote Link to comment Share on other sites More sharing options...
Skrakle Posted April 14, 2015 Author Share Posted April 14, 2015 SetUserData((void*)script); wouldn't be setting your IScripts entity variable. I think you are able to view then entity position in the callback trigger because your callback function has the same name for it's parameter as your member variable ie. "entity" and when inside a function it'll use the function parameter if there is a name conflict. So when you do something like the below notice your function parameter is also named entity just like your member variable. Inside that function when you use the entity variable it's referring to the parameter 'entity' not your class member 'entity'. Inside your callbacks trying changing it to this->entity and I think it would fail. You might not even need that member variable 'entity' honestly. I always used it because I set the callbacks inside the base class instead of outside of it like you are doing. I wanted my code that looped over each entity to be clean of that stuff and have the class be self contained in what it does. If you were to do that then you'd have to also pass the entity into your child classes so you can pass it along to your base class. Ahh i see what you mean now, i would've scratched my head at some point wondering why the iscript entity would be null. So i added this in the PlayerScript: void SetEntity(Entity* entity) { ent = entity; // i renamed the member for ent } And i'm setting it up like this: IScript* script = new PlayerScript(); script->SetEntity(entities[0]); It's all working now, thanks for noticing that! Quote Link to comment Share on other sites More sharing options...
Rick Posted April 14, 2015 Share Posted April 14, 2015 That's one way to do it, but honestly if you kept the way you had the IScript ctor and just passed the entity to the PlayerScript() (and any other children of IScript) it would work as well. IScript* script = new PlayerScript(entities[0]); I'm shocked what you have even works. You said you added SetEntity() at the PlayerScript level but I think you must have meant at the IScript level since your script pointer is an IScript and wouldn't be able to see SetEntity() function as the PlayerScript level unless you casted script to PlayerScript which in your example code you didn't. Quote Link to comment Share on other sites More sharing options...
Skrakle Posted April 14, 2015 Author Share Posted April 14, 2015 Here's how both classes look like now: class IScript { protected: Entity* ent; public: IScript() {} virtual void Collision(Entity* entity, float* position, float* normal, float speed) = 0; virtual void UpdateWorld() = 0; virtual void UpdatePhysics() = 0; virtual void SetEntity(Entity* entity) = 0; }; class PlayerScript : public IScript { public: PlayerScript() :IScript() {} void Collision(Entity* entity, float* position, float* normal, float speed) { } void UpdateWorld() { } void UpdatePhysics() { } void SetEntity(Entity* entity) { ent = entity; } }; I've changed my constructor for PlayerScript(Entity* e) :IScript() { ent = e; } and removed SetEntity method and now using IScript* script = new PlayerScript(entities[0]); like you suggested. Thanks! Quote Link to comment Share on other sites More sharing options...
Rick Posted April 14, 2015 Share Posted April 14, 2015 Generally speaking with something like this your children would just be pass-thru's to get the variable to it's parent so it's parent can set the variable itself so you don't have to do it in each future child you create. PlayerScript(Entity* e) : IScript(e) { } IScript(Entity* e) : ent(e) { } Right now you only have a PlayerScript child and aren't doing much in IScript but you may in the future have more children of IScript and do more common things that all IScript children should have (this is the main idea behind having a base class right). If you wanted to take it even futher you can set the hooks inside IScript ctor even. IScript(Entity* e) :ent(e) { e->SetUserData((void*)this); e->AddHook(Entity::CollisionHook, (void*)CollisionHook); // etc } It's nice because now your IScript class is doing a job. It's job is to set the user data and hooks. So now you can easily reuse or release this class to the world and we/you don't have worry about setting the hooks anymore (or if you reuse it down the line you don't have to worry either) during entity looping. So think about the purpose of this class. It's to handle the hooks so you can derive your own child scripts from it. Therefore it should register against the hooks for us. Now I'm not saying you will release this but that's the mentality to have with object oriented programming. Pretend you will release it or at least that you will reuse it in other projects (because you probably will) and make it easy on us/your future self. Now your C hook functions could even be inside the IScript.cpp file (not part of the class but just inside the .cpp file itself so everything about this script assigning functionality is contained in 1 header and 1 source file). Now all you have to do for future projects or tell your future users of this class to just include IScript.h/cpp into our projects, derive our own script classes from it, and create instances of the object and that's it! The rest is encapsulated from the user of your class (user or your future self). You now have a nice self contained class doing it's job. Making our job easier 1 Quote Link to comment Share on other sites More sharing options...
Skrakle Posted April 16, 2015 Author Share Posted April 16, 2015 I'm passing several objects in my IScript initializer: class PlayerScript : public IScript { public: PlayerScript(Entity* e, Map_Entity* me, Window* w) :IScript() { m_entity = e; map_entity = me; m_window = w; } void Collision(Entity* entity, float* position, float* normal, float speed) { } Map_Entity is a simple class with variables that i'm passing to the script when initialized: class Map_Entity { public: float speed; std::string boneWeapon; std::string boneShield; uint8 anim_speeds[5]; }; Problem is, the Map_Entity data passed to the script doesn't retain their values after exiting the constructor method. I could copy every values manually or do a memcpy but i'd rather not if i don't have to. I've been reading for hours and looking at a lot of examples and still can't figure it out. Quote Link to comment Share on other sites More sharing options...
Rick Posted April 16, 2015 Share Posted April 16, 2015 so us the code where you make that object. both the header and source. also if all those vars are inside IScript you should pass them to the IScript ctor and set them there and not in the child ctor. Quote Link to comment Share on other sites More sharing options...
Skrakle Posted April 16, 2015 Author Share Posted April 16, 2015 In App.h Map_Entity* map_characters[100]; In App.cpp (App::Start) entities[0] = Model::Load("Models/Characters/Dwarf/dwarfmale_run.mdl"); map_characters[0] = helpers->InitModel(entities[0], ENTITY_TYPE::PC, "Rygar", Vec3(0, 0, -5.73), 0.02, 1, COLLISION_TRIGGER, Entity::CharacterPhysics, "dwarfmale_run_bone_64", "dwarfmale_run_bone_LFingerRing"); IScript* script = new PlayerScript(entities[0], map_characters[0], window); In Helpers.h #ifndef _HELPERS_H #define _HELPERS_H using namespace Leadwerks; class Map_Entity; enum ENTITY_TYPE : uint8 { NPC = 0, PC = 1 }; class Helpers { public: Helpers() {} Map_Entity* InitModel(Entity* entity, ENTITY_TYPE entity_type, std::string name, Vec3 position, float scale, float mass, const int collision_type, const int physics_mode, std::string boneWeapon, std::string boneShield); }; #endif In Helpers.cpp #include "../entities/entity.h" #include "helpers.h" using namespace Leadwerks; Map_Entity* Helpers::InitModel(Entity* entity, ENTITY_TYPE entity_type, std::string name, Vec3 position, float scale, float mass, const int collision_type, const int physics_mode, std::stringboneWeapon, std::string boneShield) { Map_Entity* new_entity = new Map_Entity(); if (new_entity != NULL) { delete new_entity; } if (entity != NULL) { entity->SetScale(scale); entity->SetMass(mass); entity->SetPosition(position); entity->SetGravityMode(true); entity->SetCollisionType(collision_type); entity->SetPhysicsMode(physics_mode); entity->Hide(); new_entity->boneWeapon = boneWeapon; new_entity->boneShield = boneShield; new_entity->aniSpeed = 35; new_entity->entity_type = entity_type; new_entity->name = name; new_entity->set_mass_delay = 0; new_entity->anim_length[0] = entity->GetAnimationLength("Run"); new_entity->anim_starttime = 0; for (int aa = 0; aa < sizeof(new_entity->anim_speeds) / sizeof(*new_entity->anim_speeds); aa++) { new_entity->anim_speeds[aa] = 30; } new_entity->speed = 5; return new_entity; } return NULL; }; Quote Link to comment Share on other sites More sharing options...
Rick Posted April 16, 2015 Share Posted April 16, 2015 why do you delete new_entity if != null? you created a new one in memory then you delete the memory. Quote Link to comment Share on other sites More sharing options...
Skrakle Posted April 16, 2015 Author Share Posted April 16, 2015 why do you delete new_entity if != null? you created a new one in memory then you delete the memory. Damn that was meant for an Entity class, which was part of my Map_Entity class previously but removed because accessing members or methods within the script caused a crash and forgot to get rid of that delete. Thanks again for pointing out my silly mistake! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.